From 7e1f41815210b290a35a0e8004ff51cf5207ef35 Mon Sep 17 00:00:00 2001 From: fatme Date: Wed, 30 Jan 2019 16:59:21 +0200 Subject: [PATCH 1/3] fix: remove duplicated platforms from project's podfile fixes https://github.com/NativeScript/nativescript-cli/issues/3604 --- lib/bootstrap.ts | 1 + lib/definitions/project.d.ts | 21 ++ lib/services/cocoapods-platform-manager.ts | 130 ++++++++++++ lib/services/cocoapods-service.ts | 20 +- lib/services/ios-project-service.ts | 2 +- test/cocoapods-service.ts | 232 +++++++++++++++++++++ test/ios-project-service.ts | 37 +++- 7 files changed, 429 insertions(+), 14 deletions(-) create mode 100644 lib/services/cocoapods-platform-manager.ts diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index e1f545549f..4681d497ef 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -17,6 +17,7 @@ $injector.require("iOSProvisionService", "./services/ios-provision-service"); $injector.require("xcconfigService", "./services/xcconfig-service"); $injector.require("cocoapodsService", "./services/cocoapods-service"); +$injector.require("cocoaPodsPlatformManager", "./services/cocoapods-platform-manager"); $injector.require("projectTemplatesService", "./services/project-templates-service"); $injector.require("projectNameService", "./services/project-name-service"); diff --git a/lib/definitions/project.d.ts b/lib/definitions/project.d.ts index 4af5382abc..aea94962e5 100644 --- a/lib/definitions/project.d.ts +++ b/lib/definitions/project.d.ts @@ -542,7 +542,28 @@ interface ICocoaPodsService { mergePodXcconfigFile(projectData: IProjectData, platformData: IPlatformData, opts: IRelease): Promise; } +interface ICocoaPodsPlatformManager { + addPlatformSection(projectData: IProjectData, podfilePlatformData: IPodfilePlatformData, projectPodfileContent: string): string; + removePlatformSection(moduleName: string, projectPodFileContent: string, podfilePath: string): string; + replacePlatformRow(podfileContent: string, podfilePath: string): { replacedContent: string, podfilePlatformData: IPodfilePlatformData }; +} + interface IRubyFunction { functionName: string; functionParameters?: string; } + +interface IPodfilePlatformData { + /** + * The content of the whole pod's platform row + */ + content: string; + /** + * The version of the pod's platform + */ + version: string; + /** + * The path to the pod's file + */ + path: string; +} diff --git a/lib/services/cocoapods-platform-manager.ts b/lib/services/cocoapods-platform-manager.ts new file mode 100644 index 0000000000..c4341d4a24 --- /dev/null +++ b/lib/services/cocoapods-platform-manager.ts @@ -0,0 +1,130 @@ +import { EOL } from "os"; +import * as path from "path"; +import * as semver from "semver"; +import { PODFILE_NAME } from "../constants"; + +export class CocoaPodsPlatformManager implements ICocoaPodsPlatformManager { + public addPlatformSection(projectData: IProjectData, podfilePlatformData: IPodfilePlatformData, projectPodfileContent: string): string { + const platformSectionData = this.getPlatformSectionData(projectPodfileContent); + if (platformSectionData && platformSectionData.podfilePlatformData) { + const shouldReplacePlatformSection = this.shouldReplacePlatformSection(projectData, platformSectionData.podfilePlatformData, podfilePlatformData); + if (shouldReplacePlatformSection) { + const newSection = this.buildPlatformSection(podfilePlatformData); + projectPodfileContent = projectPodfileContent.replace(platformSectionData.platformSectionContent, newSection); + } + } else { + projectPodfileContent += this.buildPlatformSection(podfilePlatformData); + } + + return projectPodfileContent; + } + + public removePlatformSection(moduleName: string, projectPodfileContent: string, podfilePath: string): string { + const platformSectionData = this.getPlatformSectionData(projectPodfileContent); + if (platformSectionData && platformSectionData.podfilePlatformData && platformSectionData.podfilePlatformData.path === podfilePath) { + const podfileContentRegExp = new RegExp(`# Begin Podfile - ([\\s\\S]*?)# End Podfile`, "mg"); + const allPodfiles = projectPodfileContent.match(podfileContentRegExp) || []; + const selectedPlatformData = this.selectPlatformDataFromProjectPodfile(allPodfiles); + const newPlatformSection = selectedPlatformData ? this.buildPlatformSection(selectedPlatformData) : ""; + const regExp = new RegExp(`${EOL}${platformSectionData.platformSectionContent}${EOL}`, "mg"); + projectPodfileContent = projectPodfileContent.replace(regExp, newPlatformSection); + } + + return projectPodfileContent; + } + + public replacePlatformRow(podfileContent: string, podfilePath: string): { replacedContent: string, podfilePlatformData: IPodfilePlatformData } { + let podfilePlatformData: IPodfilePlatformData = null; + const platformRowRegExp = new RegExp(`^\\s*?(platform\\b\\s*?\\:\\s*?ios\\b(?:,\\s*?['"](.+)['"])?)`, "gm"); + const replacedContent = podfileContent.replace(platformRowRegExp, (substring: string, firstGroup: string, secondGroup: string) => { + podfilePlatformData = { content: firstGroup, version: secondGroup, path: podfilePath }; + return `# ${substring.trim()}`; + }); + + return { replacedContent, podfilePlatformData }; + } + + private getPlatformSectionData(projectPodfileContent: string): { podfilePlatformData: IPodfilePlatformData, platformSectionContent: string } { + const platformSectionRegExp = new RegExp(`${this.getPlatformSectionHeader()} ([\\s\\S]*?)with (.*)[\\s\\S]*?${this.getPlatformSectionFooter()}`, "m"); + const match = platformSectionRegExp.exec(projectPodfileContent); + let result = null; + if (match && match[0]) { + result = { + platformSectionContent: match[0], + podfilePlatformData: { + path: match[1].trim(), + content: "", + version: match[2] + } + }; + } + + return result; + } + + private selectPlatformDataFromProjectPodfile(allPodfiles: string[]): IPodfilePlatformData { + const platformRowRegExp = new RegExp(`^\\s*?#\\s*?(platform\\b\\s*?\\:\\s*?ios\\b(?:,\\s*?['"](.+)['"])?)`, "m"); + const podfilePathRegExp = new RegExp(`# Begin Podfile - ([\\s\\S]*?)${EOL}`); + let selectedPlatformData: IPodfilePlatformData = null; + _.each(allPodfiles, podfileContent => { + const platformMatch = platformRowRegExp.exec(podfileContent); + const podfilePathMatch: any[] = podfilePathRegExp.exec(podfileContent) || []; + // platform without version -> select it with highest priority + if (platformMatch && platformMatch[0] && !platformMatch[2]) { + selectedPlatformData = { + version: null, + content: platformMatch[1], + path: podfilePathMatch[1] + }; + return false; + } + + // platform with version + if (platformMatch && platformMatch[0] && platformMatch[2]) { + if (!selectedPlatformData || semver.gt(semver.coerce(platformMatch[2]), semver.coerce(selectedPlatformData.version))) { + selectedPlatformData = { + version: platformMatch[2], + content: platformMatch[1], + path: podfilePathMatch[1] + }; + } + } + }); + + return selectedPlatformData; + } + + private shouldReplacePlatformSection(projectData: IProjectData, oldPodfilePlatformData: IPodfilePlatformData, currentPodfilePlatformData: IPodfilePlatformData): boolean { + // The selected platform should be replaced in the following cases: + // 1. When the pod file is from App_Resources and the selected platform is not from App_Resources + // 2. When the pod file is from App_Resources, the selected platform is also from App_Resources + // and the pod's version is greater than the selected one + // 3. When the pod file doesn't have platform's version -> `platform :ios` + // 4. When the pod file has a version greater than the selected platform's version + const appResourcesPodfilePath = path.join(projectData.getAppResourcesDirectoryPath(), "iOS", PODFILE_NAME); + const isFromAppResources = oldPodfilePlatformData.path !== appResourcesPodfilePath && currentPodfilePlatformData.path === appResourcesPodfilePath; + const isFromAppResourcesWithGreaterPlatformVersion = oldPodfilePlatformData.path === appResourcesPodfilePath && currentPodfilePlatformData.path === appResourcesPodfilePath && semver.gt(semver.coerce(currentPodfilePlatformData.version), semver.coerce(oldPodfilePlatformData.version)); + const isPodfileWithGreaterPlatformVersion = !currentPodfilePlatformData.version || semver.gt(semver.coerce(currentPodfilePlatformData.version), semver.coerce(oldPodfilePlatformData.version)); + const result = isFromAppResources || isFromAppResourcesWithGreaterPlatformVersion || isPodfileWithGreaterPlatformVersion; + return result; + } + + private buildPlatformSection(podfilePlatformData: IPodfilePlatformData) { + let result = `${this.getPlatformSectionHeader()} ${podfilePlatformData.path} with`; + if (podfilePlatformData.version) { + result += ` ${podfilePlatformData.version}`; + } + + result += `${EOL}${podfilePlatformData.content}${EOL}${this.getPlatformSectionFooter()}`; + return result; + } + + private getPlatformSectionHeader(): string { + return '# NativeScriptPlatformSection'; + } + + private getPlatformSectionFooter(): string { + return '# End NativeScriptPlatformSection'; + } +} +$injector.register("cocoaPodsPlatformManager", CocoaPodsPlatformManager); diff --git a/lib/services/cocoapods-service.ts b/lib/services/cocoapods-service.ts index 042970e5c4..b25a7daa13 100644 --- a/lib/services/cocoapods-service.ts +++ b/lib/services/cocoapods-service.ts @@ -6,7 +6,9 @@ export class CocoaPodsService implements ICocoaPodsService { private static PODFILE_POST_INSTALL_SECTION_NAME = "post_install"; private static INSTALLER_BLOCK_PARAMETER_NAME = "installer"; - constructor(private $fs: IFileSystem, + constructor( + private $cocoaPodsPlatformManager: ICocoaPodsPlatformManager, + private $fs: IFileSystem, private $childProcess: IChildProcess, private $errors: IErrors, private $xcprojService: IXcprojService, @@ -79,7 +81,7 @@ export class CocoaPodsService implements ICocoaPodsService { return; } - const { podfileContent, replacedFunctions } = this.buildPodfileContent(podfilePath, moduleName); + const { podfileContent, replacedFunctions, podfilePlatformData } = this.buildPodfileContent(podfilePath, moduleName); const pathToProjectPodfile = this.getProjectPodfilePath(nativeProjectPath); const projectPodfileContent = this.$fs.exists(pathToProjectPodfile) ? this.$fs.readText(pathToProjectPodfile).trim() : ""; @@ -92,19 +94,23 @@ export class CocoaPodsService implements ICocoaPodsService { finalPodfileContent = this.addPostInstallHook(replacedFunctions, finalPodfileContent, podfileContent); } + if (podfilePlatformData) { + finalPodfileContent = this.$cocoaPodsPlatformManager.addPlatformSection(projectData, podfilePlatformData, finalPodfileContent); + } + finalPodfileContent = `${podfileContent}${EOL}${finalPodfileContent}`; this.saveProjectPodfile(projectData, finalPodfileContent, nativeProjectPath); } } public removePodfileFromProject(moduleName: string, podfilePath: string, projectData: IProjectData, projectRoot: string): void { - if (this.$fs.exists(this.getProjectPodfilePath(projectRoot))) { let projectPodFileContent = this.$fs.readText(this.getProjectPodfilePath(projectRoot)); // Remove the data between #Begin Podfile and #EndPodfile const regExpToRemove = new RegExp(`${this.getPluginPodfileHeader(podfilePath)}[\\s\\S]*?${this.getPluginPodfileEnd()}`, "mg"); projectPodFileContent = projectPodFileContent.replace(regExpToRemove, ""); projectPodFileContent = this.removePostInstallHook(moduleName, projectPodFileContent); + projectPodFileContent = this.$cocoaPodsPlatformManager.removePlatformSection(moduleName, projectPodFileContent, podfilePath); const defaultPodfileBeginning = this.getPodfileHeader(projectData.projectName); const defaultContentWithPostInstallHook = `${defaultPodfileBeginning}${EOL}${this.getPostInstallHookHeader()}end${EOL}end`; @@ -226,13 +232,15 @@ export class CocoaPodsService implements ICocoaPodsService { return `${CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME} do |${CocoaPodsService.INSTALLER_BLOCK_PARAMETER_NAME}|${EOL}`; } - private buildPodfileContent(pluginPodFilePath: string, pluginName: string): { podfileContent: string, replacedFunctions: IRubyFunction[] } { + private buildPodfileContent(pluginPodFilePath: string, pluginName: string): { podfileContent: string, replacedFunctions: IRubyFunction[], podfilePlatformData: IPodfilePlatformData } { const pluginPodfileContent = this.$fs.readText(pluginPodFilePath); - const { replacedContent, newFunctions: replacedFunctions } = this.replaceHookContent(CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME, pluginPodfileContent, pluginName); + const data = this.replaceHookContent(CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME, pluginPodfileContent, pluginName); + const { replacedContent, podfilePlatformData } = this.$cocoaPodsPlatformManager.replacePlatformRow(data.replacedContent, pluginPodFilePath); return { podfileContent: `${this.getPluginPodfileHeader(pluginPodFilePath)}${EOL}${replacedContent}${EOL}${this.getPluginPodfileEnd()}`, - replacedFunctions + replacedFunctions: data.newFunctions, + podfilePlatformData }; } diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index ec80a893b2..950df5bea4 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -985,7 +985,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f const projectPodfilePath = this.$cocoapodsService.getProjectPodfilePath(platformData.projectRoot); if (this.$fs.exists(projectPodfilePath)) { await this.$cocoapodsService.executePodInstall(platformData.projectRoot, this.$xcprojService.getXcodeprojPath(projectData, platformData)); - // The `pod install` command adds a new target to the .pbxproject. This target adds additional build phases to xcodebuild. + // The `pod install` command adds a new target to the .pbxproject. This target adds additional build phases to Xcode project. // Some of these phases relies on env variables (like PODS_PODFILE_DIR_PATH or PODS_ROOT). // These variables are produced from merge of pod's xcconfig file and project's xcconfig file. // So the correct order is `pod install` to be executed before merging pod's xcconfig file. diff --git a/test/cocoapods-service.ts b/test/cocoapods-service.ts index df8a0d64c5..d264c686d5 100644 --- a/test/cocoapods-service.ts +++ b/test/cocoapods-service.ts @@ -4,6 +4,8 @@ import { CocoaPodsService } from "../lib/services/cocoapods-service"; import { EOL } from "os"; import { LoggerStub, ErrorsStub } from "./stubs"; import { XcconfigService } from "../lib/services/xcconfig-service"; +import * as path from "path"; +import { CocoaPodsPlatformManager } from "../lib/services/cocoapods-platform-manager"; interface IMergePodfileHooksTestCase { input: string; @@ -24,6 +26,8 @@ function createTestInjector(): IInjector { testInjector.register("logger", LoggerStub); testInjector.register("config", {}); testInjector.register("xcconfigService", XcconfigService); + testInjector.register("projectData", ({})); + testInjector.register("cocoaPodsPlatformManager", CocoaPodsPlatformManager); return testInjector; } @@ -905,4 +909,232 @@ end` }); }); + + describe("remove duplicated platfoms from project podfile", () => { + const projectRoot = "my/project/platforms/ios"; + const projectPodfilePath = path.join(projectRoot, "testProjectPodfilePath"); + const testCases = [ + { + name: "should not change the Podfile when no platform", + pods: [{ + name: 'plugin1', + path: 'path/to/my/plugin1/platforms/ios/Podfile', + content: `pod 'Firebase', '~> 3.1'` + }], + expectedProjectPodfileContent: `use_frameworks! + +target "projectName" do +# Begin Podfile - path/to/my/plugin1/platforms/ios/Podfile +pod 'Firebase', '~> 3.1' +# End Podfile +end` + }, + { + name: "should not change the Podfile when there is only one platform", + pods: [{ + name: 'plugin2', + path: 'path/to/my/plugin2/platforms/ios/Podfile', + content: `platform :ios, '9.0'` + }], + expectedProjectPodfileContent: `use_frameworks! + +target "projectName" do +# Begin Podfile - path/to/my/plugin2/platforms/ios/Podfile +# platform :ios, '9.0' +# End Podfile + +# NativeScriptPlatformSection path/to/my/plugin2/platforms/ios/Podfile with 9.0 +platform :ios, '9.0' +# End NativeScriptPlatformSection +end` + }, + { + name: "should select the platform from Podfile in App_Resources", + pods: [{ + name: 'plugin1', + path: 'my/full/path/to/plugin1/platforms/ios/Podfile', + content: `platform :ios, '10.0'` + }, { + name: 'plugin2', + path: 'my/full/path/to/plugin2/platforms/ios/Podfile', + content: `pod 'myPod' ~> 0.3.4` + }, { + name: 'App_Resources', + path: 'my/full/path/to/app/App_Resources/iOS/Podfile', + content: `platform :ios, '9.0'` + }], + expectedProjectPodfileContent: `use_frameworks! + +target "projectName" do +# Begin Podfile - my/full/path/to/app/App_Resources/iOS/Podfile +# platform :ios, '9.0' +# End Podfile + +# Begin Podfile - my/full/path/to/plugin2/platforms/ios/Podfile +pod 'myPod' ~> 0.3.4 +# End Podfile + +# Begin Podfile - my/full/path/to/plugin1/platforms/ios/Podfile +# platform :ios, '10.0' +# End Podfile + +# NativeScriptPlatformSection my/full/path/to/app/App_Resources/iOS/Podfile with 9.0 +platform :ios, '9.0' +# End NativeScriptPlatformSection +end` + }, + { + name: "should select the platform with highest version from plugins when no Podfile in App_Resources", + pods: [{ + name: 'pluginWithPlatform', + path: 'node_modules/myFirstPluginWithPlatform/Podfile', + content: `platform :ios, '9.0'` + }, { + name: 'mySecondPluginWithPlatform', + path: 'node_modules/mySecondPluginWithPlatform/Podfile', + content: `platform :ios, '10.0'` + }, { + name: 'myPluginWithoutPlatform', + path: 'node_modules/myPluginWithoutPlatform/Podfile', + content: `pod 'myPod' ~> 0.3.4` + }], + expectedProjectPodfileContent: `use_frameworks! + +target "projectName" do +# Begin Podfile - node_modules/myPluginWithoutPlatform/Podfile +pod 'myPod' ~> 0.3.4 +# End Podfile + +# Begin Podfile - node_modules/mySecondPluginWithPlatform/Podfile +# platform :ios, '10.0' +# End Podfile + +# Begin Podfile - node_modules/myFirstPluginWithPlatform/Podfile +# platform :ios, '9.0' +# End Podfile + +# NativeScriptPlatformSection node_modules/mySecondPluginWithPlatform/Podfile with 10.0 +platform :ios, '10.0' +# End NativeScriptPlatformSection +end` + }, + { + name: "should select the platform without version when no Podfile in App_Resources", + pods: [{ + name: 'myPluginWithoutPlatform', + path: 'node_modules/myPluginWithoutPlatform/Podfile', + content: `pod 'myPod' ~> 0.3.4` + }, { + name: 'mySecondPluginWithPlatform', + path: 'node_modules/mySecondPluginWithPlatform/Podfile', + content: `platform :ios, '10.0'` + }, { + name: 'myFirstPluginWithPlatform', + path: 'node_modules/myFirstPluginWithPlatform/Podfile', + content: `platform :ios` + }], + expectedProjectPodfileContent: `use_frameworks! + +target "projectName" do +# Begin Podfile - node_modules/myFirstPluginWithPlatform/Podfile +# platform :ios +# End Podfile + +# Begin Podfile - node_modules/mySecondPluginWithPlatform/Podfile +# platform :ios, '10.0' +# End Podfile + +# Begin Podfile - node_modules/myPluginWithoutPlatform/Podfile +pod 'myPod' ~> 0.3.4 +# End Podfile# NativeScriptPlatformSection node_modules/myFirstPluginWithPlatform/Podfile with +platform :ios +# End NativeScriptPlatformSection +end` + }, + { + name: "should select platform from plugins when the podfile in App_Resources/iOS/Podfile has no platform", + pods: [{ + name: "mySecondPluginWithPlatform", + path: "node_modules/ mypath with spaces/mySecondPluginWithPlatform/Podfile", + content: `platform :ios, '10.0'` + }, { + name: "myPluginWithoutPlatform", + path: "node_modules/myPluginWithoutPlatform/Podfile", + content: `pod 'myPod' ~> 0.3.4` + }, { + name: "myFirstPluginWithPlatform", + path: "node_modules/myFirstPluginWithPlatform/Podfile", + content: `platform :ios, '11.0'` + }, { + name: "App_Resources", + path: "my/full/path/to/app/App_Resources/iOS/Podfile", + content: `pod: 'mySecondPlatformPod' ~> 2.0.0${EOL}pod: 'platformKit' ~> 1.0` + }], + expectedProjectPodfileContent: `use_frameworks! + +target "projectName" do +# Begin Podfile - my/full/path/to/app/App_Resources/iOS/Podfile +pod: 'mySecondPlatformPod' ~> 2.0.0 +pod: 'platformKit' ~> 1.0 +# End Podfile + +# Begin Podfile - node_modules/myFirstPluginWithPlatform/Podfile +# platform :ios, '11.0' +# End Podfile + +# Begin Podfile - node_modules/myPluginWithoutPlatform/Podfile +pod 'myPod' ~> 0.3.4 +# End Podfile + +# Begin Podfile - node_modules/ mypath with spaces/mySecondPluginWithPlatform/Podfile +# platform :ios, '10.0' +# End Podfile + +# NativeScriptPlatformSection node_modules/myFirstPluginWithPlatform/Podfile with 11.0 +platform :ios, '11.0' +# End NativeScriptPlatformSection +end` + } + ]; + + beforeEach(() => { + cocoapodsService.getProjectPodfilePath = () => projectPodfilePath; + }); + + _.each(testCases, testCase => { + it(testCase.name, async () => { + const podsPaths = testCase.pods.map(p => p.path); + let projectPodfileContent = ""; + + const projectData = testInjector.resolve("projectData"); + projectData.getAppResourcesDirectoryPath = () => "my/full/path/to/app/App_Resources"; + projectData.projectName = "projectName"; + + const fs = testInjector.resolve("fs"); + fs.exists = (filePath: string) => projectPodfilePath === filePath || _.includes(podsPaths, filePath); + fs.readText = (filePath: string) => { + if (filePath === projectPodfilePath) { + return projectPodfileContent; + } + + const pod = _.find(testCase.pods, p => p.path === filePath); + if (pod) { + return pod.content; + } + }; + fs.writeFile = (filePath: string, fileContent: string) => { + if (filePath === projectPodfilePath) { + projectPodfileContent = fileContent; + } + }; + cocoapodsService.removePodfileFromProject = () => ({}); + + for (const pod of testCase.pods) { + await cocoapodsService.applyPodfileToProject(pod.name, pod.path, projectData, projectPodfilePath); + } + + assert.deepEqual(projectPodfileContent, testCase.expectedProjectPodfileContent); + }); + }); + }); }); diff --git a/test/ios-project-service.ts b/test/ios-project-service.ts index d5ec4e47b1..4371a41618 100644 --- a/test/ios-project-service.ts +++ b/test/ios-project-service.ts @@ -37,6 +37,7 @@ import { BUILD_XCCONFIG_FILE_NAME } from "../lib/constants"; import { ProjectDataStub } from "./stubs"; import { xcode } from "../lib/node/xcode"; import temp = require("temp"); +import { CocoaPodsPlatformManager } from "../lib/services/cocoapods-platform-manager"; temp.track(); class IOSSimulatorDiscoveryMock extends DeviceDiscovery { @@ -66,6 +67,7 @@ function createTestInjector(projectPath: string, projectName: string, xcode?: IX testInjector.register("iOSEntitlementsService", IOSEntitlementsService); testInjector.register("logger", LoggerLib.Logger); testInjector.register("options", OptionsLib.Options); + testInjector.register("cocoaPodsPlatformManager", CocoaPodsPlatformManager); const projectData = Object.assign({}, ProjectDataStub, { platformsDir: path.join(projectPath, "platforms"), projectName: projectName, @@ -390,11 +392,18 @@ describe("Cocoapods support", () => { assert.isTrue(fs.exists(projectPodfilePath)); const actualProjectPodfileContent = fs.readText(projectPodfilePath); + const expectedPluginPodfileContent = ["source 'https://github.com/CocoaPods/Specs.git'", "# platform :ios, '8.1'", "pod 'GoogleMaps'"].join("\n"); + const expectedPlatformSection = [ + `# NativeScriptPlatformSection ${basePodfilePath} with 8.1`, + "platform :ios, '8.1'", + "# End NativeScriptPlatformSection", + ].join("\n"); const expectedProjectPodfileContent = ["use_frameworks!\n", `target "${projectName}" do`, `# Begin Podfile - ${basePodfilePath}`, - `${pluginPodfileContent}`, - "# End Podfile", + expectedPluginPodfileContent, + "# End Podfile\n", + expectedPlatformSection, "end"] .join("\n"); assert.equal(actualProjectPodfileContent, expectedProjectPodfileContent); @@ -403,7 +412,6 @@ describe("Cocoapods support", () => { cocoapodsService.applyPodfileToProject(basePodfileModuleName, basePodfilePath, projectData, iOSProjectService.getPlatformData(projectData).projectRoot); assert.isFalse(fs.exists(projectPodfilePath)); - }); it("adds plugin with Podfile", async () => { @@ -462,11 +470,18 @@ describe("Cocoapods support", () => { assert.isTrue(fs.exists(projectPodfilePath)); const actualProjectPodfileContent = fs.readText(projectPodfilePath); + const expectedPluginPodfileContent = ["source 'https://github.com/CocoaPods/Specs.git'", "# platform :ios, '8.1'", "pod 'GoogleMaps'"].join("\n"); + const expectedPlatformSection = [ + `# NativeScriptPlatformSection ${pluginPodfilePath} with 8.1`, + "platform :ios, '8.1'", + "# End NativeScriptPlatformSection", + ].join("\n"); const expectedProjectPodfileContent = ["use_frameworks!\n", `target "${projectName}" do`, `# Begin Podfile - ${pluginPodfilePath}`, - `${pluginPodfileContent}`, - "# End Podfile", + expectedPluginPodfileContent, + "# End Podfile\n", + expectedPlatformSection, "end"] .join("\n"); assert.equal(actualProjectPodfileContent, expectedProjectPodfileContent); @@ -537,11 +552,18 @@ describe("Cocoapods support", () => { assert.isTrue(fs.exists(projectPodfilePath)); const actualProjectPodfileContent = fs.readText(projectPodfilePath); + const expectedPluginPodfileContent = ["source 'https://github.com/CocoaPods/Specs.git'", "# platform :ios, '8.1'", "pod 'GoogleMaps'"].join("\n"); + const expectedPlatformSection = [ + `# NativeScriptPlatformSection ${pluginPodfilePath} with 8.1`, + "platform :ios, '8.1'", + "# End NativeScriptPlatformSection", + ].join("\n"); const expectedProjectPodfileContent = ["use_frameworks!\n", `target "${projectName}" do`, `# Begin Podfile - ${pluginPodfilePath}`, - `${pluginPodfileContent}`, - "# End Podfile", + expectedPluginPodfileContent, + "# End Podfile\n", + expectedPlatformSection, "end"] .join("\n"); assert.equal(actualProjectPodfileContent, expectedProjectPodfileContent); @@ -1386,6 +1408,7 @@ describe("handleNativeDependenciesChange", () => { cocoapodsService.executePodInstall = async () => executedCocoapodsMethods.push("podInstall"); cocoapodsService.mergePodXcconfigFile = async () => executedCocoapodsMethods.push("podMerge"); cocoapodsService.applyPodfileFromAppResources = async () => ({}); + cocoapodsService.removeDuplicatedPlatfomsFromProjectPodFile = async () => ({}); cocoapodsService.getProjectPodfilePath = () => projectPodfilePath; const fs = testInjector.resolve("fs"); From d15a6da0c576eb4cf0023d3baa51be133f20e8f6 Mon Sep 17 00:00:00 2001 From: fatme Date: Tue, 19 Feb 2019 00:26:40 +0200 Subject: [PATCH 2/3] chore: fix regexp --- lib/services/cocoapods-platform-manager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/services/cocoapods-platform-manager.ts b/lib/services/cocoapods-platform-manager.ts index c4341d4a24..a5d0da9774 100644 --- a/lib/services/cocoapods-platform-manager.ts +++ b/lib/services/cocoapods-platform-manager.ts @@ -26,7 +26,7 @@ export class CocoaPodsPlatformManager implements ICocoaPodsPlatformManager { const allPodfiles = projectPodfileContent.match(podfileContentRegExp) || []; const selectedPlatformData = this.selectPlatformDataFromProjectPodfile(allPodfiles); const newPlatformSection = selectedPlatformData ? this.buildPlatformSection(selectedPlatformData) : ""; - const regExp = new RegExp(`${EOL}${platformSectionData.platformSectionContent}${EOL}`, "mg"); + const regExp = new RegExp(`${platformSectionData.platformSectionContent}`, "mg"); projectPodfileContent = projectPodfileContent.replace(regExp, newPlatformSection); } From 0472afb156e2eb26dc0fc2865ca0d82a8665a0a2 Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Fri, 22 Feb 2019 14:09:02 +0200 Subject: [PATCH 3/3] chore: fix regexp when removing platforms section When removing platforms section, fix the regexp, so the removal procedure will keep the file the same as before the platform section was added. Currently, after removing the platform section, the file had additional empty lines inside it. --- lib/services/cocoapods-platform-manager.ts | 2 +- test/ios-project-service.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/services/cocoapods-platform-manager.ts b/lib/services/cocoapods-platform-manager.ts index a5d0da9774..8f34fd4952 100644 --- a/lib/services/cocoapods-platform-manager.ts +++ b/lib/services/cocoapods-platform-manager.ts @@ -26,7 +26,7 @@ export class CocoaPodsPlatformManager implements ICocoaPodsPlatformManager { const allPodfiles = projectPodfileContent.match(podfileContentRegExp) || []; const selectedPlatformData = this.selectPlatformDataFromProjectPodfile(allPodfiles); const newPlatformSection = selectedPlatformData ? this.buildPlatformSection(selectedPlatformData) : ""; - const regExp = new RegExp(`${platformSectionData.platformSectionContent}`, "mg"); + const regExp = new RegExp(`\\r?\\n${platformSectionData.platformSectionContent}\\r?\\n`, "mg"); projectPodfileContent = projectPodfileContent.replace(regExp, newPlatformSection); } diff --git a/test/ios-project-service.ts b/test/ios-project-service.ts index 4371a41618..020b5b95b1 100644 --- a/test/ios-project-service.ts +++ b/test/ios-project-service.ts @@ -386,10 +386,10 @@ describe("Cocoapods support", () => { projectData.podfilePath = basePodfilePath; - cocoapodsService.applyPodfileToProject(basePodfileModuleName, basePodfilePath, projectData, iOSProjectService.getPlatformData(projectData).projectRoot); + await cocoapodsService.applyPodfileToProject(basePodfileModuleName, basePodfilePath, projectData, iOSProjectService.getPlatformData(projectData).projectRoot); const projectPodfilePath = path.join(platformsFolderPath, "Podfile"); - assert.isTrue(fs.exists(projectPodfilePath)); + assert.isTrue(fs.exists(projectPodfilePath), `File ${projectPodfilePath} must exist as we have already applied Podfile to it.`); const actualProjectPodfileContent = fs.readText(projectPodfilePath); const expectedPluginPodfileContent = ["source 'https://github.com/CocoaPods/Specs.git'", "# platform :ios, '8.1'", "pod 'GoogleMaps'"].join("\n"); @@ -410,8 +410,8 @@ describe("Cocoapods support", () => { fs.deleteFile(basePodfilePath); - cocoapodsService.applyPodfileToProject(basePodfileModuleName, basePodfilePath, projectData, iOSProjectService.getPlatformData(projectData).projectRoot); - assert.isFalse(fs.exists(projectPodfilePath)); + await cocoapodsService.applyPodfileToProject(basePodfileModuleName, basePodfilePath, projectData, iOSProjectService.getPlatformData(projectData).projectRoot); + assert.isFalse(fs.exists(projectPodfilePath), `The projectPodfilePath (${projectPodfilePath}) must not exist when all Podfiles have been deleted and project is prepared again. (i.e. CLI should delete the project Podfile in this case)`); }); it("adds plugin with Podfile", async () => {