diff --git a/src/api/PBXFileReference.ts b/src/api/PBXFileReference.ts index 1180422..a94fe95 100644 --- a/src/api/PBXFileReference.ts +++ b/src/api/PBXFileReference.ts @@ -103,12 +103,9 @@ export class PBXFileReference extends AbstractObject { } protected setupDefaults() { - if (this.props.fileEncoding == null) { - this.props.fileEncoding = 4; - } - // if (this.sourceTree == null) { - // this.sourceTree = "SOURCE_ROOT"; - // } + // Note: fileEncoding and includeInIndex are intentionally NOT set as defaults. + // Xcode only includes these properties when explicitly set. Setting them + // automatically would cause unnecessary changes when round-tripping projects. if ( !this.props.lastKnownFileType && @@ -118,10 +115,6 @@ export class PBXFileReference extends AbstractObject { this.setLastKnownFileType(); } - if (this.props.includeInIndex == null) { - this.props.includeInIndex = 0; - } - if (this.props.name == null && this.props.path) { const name = path.basename(this.props.path); // If the values are the same then skip setting name. @@ -132,11 +125,6 @@ export class PBXFileReference extends AbstractObject { if (!this.props.sourceTree) { this.props.sourceTree = getPossibleDefaultSourceTree(this.props); } - - // Clear the includeInIndex flag for framework files - if (this.props.path && path.extname(this.props.path) === ".framework") { - this.props.includeInIndex = undefined; - } } getParent() { return getParent(this); diff --git a/src/api/PBXSourcesBuildPhase.ts b/src/api/PBXSourcesBuildPhase.ts index 0e27c09..3cd53cb 100644 --- a/src/api/PBXSourcesBuildPhase.ts +++ b/src/api/PBXSourcesBuildPhase.ts @@ -246,18 +246,9 @@ export class PBXShellScriptBuildPhase extends AbstractBuildPhase< this.props.shellScript = "# Type a script or drag a script file from your workspace to insert its path.\n"; } - if (!this.props.outputFileListPaths) { - this.props.outputFileListPaths = []; - } - if (!this.props.outputPaths) { - this.props.outputPaths = []; - } - if (!this.props.inputFileListPaths) { - this.props.inputFileListPaths = []; - } - if (!this.props.inputPaths) { - this.props.inputPaths = []; - } + // Note: inputPaths, outputPaths, inputFileListPaths, outputFileListPaths + // are intentionally NOT initialized to empty arrays. Xcode omits these + // properties when they are empty. super.setupDefaults(); } } diff --git a/src/api/__tests__/PBXFileReference.test.ts b/src/api/__tests__/PBXFileReference.test.ts index 0326623..d0f5248 100644 --- a/src/api/__tests__/PBXFileReference.test.ts +++ b/src/api/__tests__/PBXFileReference.test.ts @@ -75,8 +75,6 @@ describe("PBXFileReference", () => { expect(ref.uuid).toBe("XX4DFF38D47332D6BF0183XX"); expect(ref.props).toEqual({ - fileEncoding: 4, - includeInIndex: undefined, isa: "PBXFileReference", name: "SwiftUI.framework", path: "System/Library/Frameworks/SwiftUI.framework", @@ -85,28 +83,23 @@ describe("PBXFileReference", () => { }); }); - it("should set default file encoding", () => { - const xcproj = XcodeProject.open(WORKING_FIXTURE); - const ref = PBXFileReference.create(xcproj, { - path: "test.swift", - }); + // Note: fileEncoding and includeInIndex are no longer set by default + // to avoid adding properties when round-tripping projects. + // Users can set these explicitly if needed. - expect(ref.props.fileEncoding).toBe(4); - }); - - it("should set includeInIndex for regular files", () => { + it("should not set fileEncoding by default", () => { const xcproj = XcodeProject.open(WORKING_FIXTURE); const ref = PBXFileReference.create(xcproj, { path: "test.swift", }); - expect(ref.props.includeInIndex).toBe(0); + expect(ref.props.fileEncoding).toBeUndefined(); }); - it("should clear includeInIndex for framework files", () => { + it("should not set includeInIndex by default", () => { const xcproj = XcodeProject.open(WORKING_FIXTURE); const ref = PBXFileReference.create(xcproj, { - path: "TestFramework.framework", + path: "test.swift", }); expect(ref.props.includeInIndex).toBeUndefined(); @@ -141,8 +134,6 @@ describe("PBXFileReference", () => { }); expect(ref.props).toEqual({ - fileEncoding: 4, - includeInIndex: 0, isa: "PBXFileReference", lastKnownFileType: "sourcecode.swift", name: "funky.swift", @@ -157,8 +148,6 @@ describe("PBXFileReference", () => { }); expect(ref.props).toEqual({ - fileEncoding: 4, - includeInIndex: 0, isa: "PBXFileReference", lastKnownFileType: "text.css", name: "funky.css", @@ -173,8 +162,6 @@ describe("PBXFileReference", () => { }); expect(ref.props).toEqual({ - fileEncoding: 4, - includeInIndex: 0, isa: "PBXFileReference", lastKnownFileType: "text.html", name: "funky.html", @@ -189,8 +176,6 @@ describe("PBXFileReference", () => { }); expect(ref.props).toEqual({ - fileEncoding: 4, - includeInIndex: 0, isa: "PBXFileReference", lastKnownFileType: "text.json", name: "funky.json", @@ -205,8 +190,6 @@ describe("PBXFileReference", () => { }); expect(ref.props).toEqual({ - fileEncoding: 4, - includeInIndex: 0, isa: "PBXFileReference", lastKnownFileType: "sourcecode.javascript", name: "funky.js", @@ -221,8 +204,6 @@ describe("PBXFileReference", () => { }); expect(ref.props).toEqual({ - fileEncoding: 4, - includeInIndex: 0, isa: "PBXFileReference", name: "funky", path: "fun/funky", diff --git a/src/json/__tests__/fixtures/project-with-entitlements.pbxproj b/src/json/__tests__/fixtures/project-with-entitlements.pbxproj index fc0fbc9..d1dc521 100644 --- a/src/json/__tests__/fixtures/project-with-entitlements.pbxproj +++ b/src/json/__tests__/fixtures/project-with-entitlements.pbxproj @@ -276,6 +276,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = testapp/example.entitlements; CURRENT_PROJECT_VERSION = 1; ENABLE_BITCODE = NO; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -290,7 +291,6 @@ "-ObjC", "-lc++", ); - CODE_SIGN_ENTITLEMENTS = testapp/example.entitlements; PRODUCT_BUNDLE_IDENTIFIER = org.name.testproject; PRODUCT_NAME = testproject; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -305,6 +305,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = testapp/example.entitlements; CURRENT_PROJECT_VERSION = 1; INFOPLIST_FILE = testproject/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; @@ -314,7 +315,6 @@ "-ObjC", "-lc++", ); - CODE_SIGN_ENTITLEMENTS = testapp/example.entitlements; PRODUCT_BUNDLE_IDENTIFIER = org.name.testproject; PRODUCT_NAME = testproject; SWIFT_VERSION = 5.0; diff --git a/src/json/__tests__/fixtures/project.pbxproj b/src/json/__tests__/fixtures/project.pbxproj index 138cf29..63c2394 100644 --- a/src/json/__tests__/fixtures/project.pbxproj +++ b/src/json/__tests__/fixtures/project.pbxproj @@ -352,7 +352,6 @@ COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; - TARGETED_DEVICE_FAMILY = "1,2"; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -378,6 +377,7 @@ MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; @@ -421,7 +421,6 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - TARGETED_DEVICE_FAMILY = 1; IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)"; LIBRARY_SEARCH_PATHS = ( @@ -431,6 +430,7 @@ ); MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = 1; VALIDATE_PRODUCT = YES; }; name = Release; diff --git a/src/json/writer.ts b/src/json/writer.ts index fff6767..aadebe4 100644 --- a/src/json/writer.ts +++ b/src/json/writer.ts @@ -176,7 +176,14 @@ export class Writer { } private writeObject(object: JSONObject, isBase?: boolean) { - Object.entries(object).forEach(([key, value]) => { + Object.entries(object) + .sort(([a], [b]) => { + // isa always comes first, then case-sensitive ASCII alphabetical + if (a === "isa") return -1; + if (b === "isa") return 1; + return a < b ? -1 : a > b ? 1 : 0; + }) + .forEach(([key, value]) => { if (this.options.skipNullishValues && value == null) { return; } else if (value instanceof Buffer) { @@ -281,7 +288,14 @@ export class Writer { ) => { line.push(this.formatId(key) + " = {"); - Object.entries(value).forEach(([key, obj]) => { + Object.entries(value) + .sort(([a], [b]) => { + // isa always comes first, then case-sensitive ASCII alphabetical + if (a === "isa") return -1; + if (b === "isa") return 1; + return a < b ? -1 : a > b ? 1 : 0; + }) + .forEach(([key, obj]) => { if (this.options.skipNullishValues && obj == null) { return; } else if (obj instanceof Buffer) {