xcparse
Version:
pbxproj parser
212 lines • 7.04 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Writer = void 0;
const referenceBuilder_1 = require("./referenceBuilder");
const unicode_1 = require("./unicode");
let EOL = "\n";
try {
EOL = require("os").EOL;
}
catch { }
function isObject(value) {
return (typeof value === "object" && value !== null && !(value instanceof Buffer));
}
/** Ensure string values that use invalid characters are wrapped in quotes. */
function ensureQuotes(value) {
value = (0, unicode_1.addQuotes)(value);
// Seems like no hyphen is the wehhh
if (/^[\w_$/:.]+$/.test(value)) {
// if (/^[\w_$/:.-]+$/.test(value)) {
return value;
}
return `"${value}"`;
}
// TODO: How to handle buffer? <xx xx xx>
function formatData(data) {
return `<${data.toString()}>`;
}
function getSortedObjects(objects) {
const sorted = {};
// sort by isa
Object.entries(objects).forEach(([id, object]) => {
if (!sorted[object.isa]) {
sorted[object.isa] = [];
}
sorted[object.isa].push([id, object]);
});
// alphabetize by isa like Xcode
return Object.entries(sorted).sort();
}
class Writer {
constructor(project, options = {}) {
this.project = project;
this.options = options;
this.indent = 0;
this.contents = "";
this.comments = {};
this.comments = (0, referenceBuilder_1.createReferenceList)(project);
this.writeShebang();
this.writeProject();
}
pad(x) {
// \t might also work...
const tab = this.options.tab ?? "\t";
return x > 0 ? tab + this.pad(x - 1) : "";
// return x > 0 ? " " + pad(x - 1) : "";
}
getResults() {
return this.contents;
}
println(string) {
this.contents += this.pad(this.indent);
this.contents += string;
this.contents += EOL;
}
write(string) {
this.contents += this.pad(this.indent);
this.contents += string;
}
printAssignLn(key, value) {
return this.println(key + " = " + value + ";");
}
flush(string) {
const current = this.indent;
this.indent = 0;
this.write(string);
this.indent = current;
}
writeShebang() {
const headComment = this.options?.shebang ?? "!$*UTF8*$!";
this.println(`// ${headComment}`);
}
/** Format ID with optional comment reference. */
formatId(id, cmt = this.comments[id]) {
if (cmt) {
// 13B07F961A680F5B00A75B9A /* yolo87.app */
return `${id} /* ${cmt} */`;
}
// If there is no reference then we might need to wrap with quotes.
return ensureQuotes(id);
}
writeProject() {
this.println("{");
if (this.project) {
this.indent++;
this.writeObject(this.project, true);
this.indent--;
}
this.println("}");
}
writeObject(object, isBase) {
Object.entries(object).forEach(([key, value]) => {
if (this.options.skipNullishValues && value == null) {
return;
}
else if (value instanceof Buffer) {
this.printAssignLn(ensureQuotes(key), formatData(value));
}
else if (Array.isArray(value)) {
this.writeArray(key, value);
}
else if (isObject(value)) {
// Deeper empty objects should be inlined.
if (!isBase && !Object.keys(value).length) {
this.println(ensureQuotes(key) + " = {};");
return;
}
this.println(ensureQuotes(key) + " = {");
this.indent++;
if (isBase && key === "objects") {
this.writePbxObjects(value);
}
else {
this.writeObject(value, isBase);
}
this.indent--;
this.println("};");
}
else {
this.printAssignLn(ensureQuotes(key), key === "remoteGlobalIDString"
? ensureQuotes(value)
: this.formatId(value));
}
});
}
writePbxObjects(projectObjects) {
getSortedObjects(projectObjects).forEach(([isa, objects]) => {
this.flush(EOL);
this.flush(`/* Begin ${isa} section */` + EOL);
objects.forEach(([id, obj]) => this.writeObjectInclusive(id, obj));
this.flush(`/* End ${isa} section */` + EOL);
});
}
writeArray(key, value) {
this.println(ensureQuotes(key) + " = (");
this.indent++;
value.forEach((item) => {
// TODO: Nested arrays?
if (item instanceof Buffer) {
this.println(formatData(item) + ",");
}
else if (item == null) {
return;
}
else if (isObject(item)) {
this.println("{");
if (item) {
this.indent++;
this.writeObject(item);
this.indent--;
}
this.println("},");
}
else {
this.println(this.formatId(String(item)) + ",");
}
});
this.indent--;
this.println(");");
}
writeObjectInclusive(key, value) {
if ((0, referenceBuilder_1.isPBXBuildFile)(value) || (0, referenceBuilder_1.isPBXFileReference)(value)) {
return this.writeObjectWithoutIndent(key, value);
}
this.println(this.formatId(key) + " = {");
/* foo = { */
this.indent++;
/* */ this.writeObject(value);
this.indent--;
/* }; */
this.println("};");
}
writeObjectWithoutIndent(key, value) {
const line = [];
const buildInline = (key, value) => {
line.push(this.formatId(key) + " = {");
Object.entries(value).forEach(([key, obj]) => {
if (this.options.skipNullishValues && obj == null) {
return;
}
else if (obj instanceof Buffer) {
line.push(ensureQuotes(key) + " = " + formatData(obj) + "; ");
}
else if (Array.isArray(obj)) {
line.push(ensureQuotes(key) + " = (");
obj.forEach((item) => line.push(ensureQuotes(item) + ", "));
line.push("); ");
}
else if (isObject(obj)) {
buildInline(key, obj);
}
else {
line.push(ensureQuotes(key) + " = " + this.formatId(obj) + "; ");
}
});
line.push("}; ");
};
buildInline(key, value);
this.println(line.join("").trim());
}
}
exports.Writer = Writer;
//# sourceMappingURL=writer.js.map