Skip to content

Commit 15be9d3

Browse files
committed
feat: initial implementation of pods override
1 parent 08dd3b1 commit 15be9d3

14 files changed

+141
-42
lines changed

lib/controllers/prepare-controller.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { hook } from "../common/helpers";
33
import { performanceLog, cache } from "../common/decorators";
44
import { EventEmitter } from "events";
55
import * as path from "path";
6-
import { PREPARE_READY_EVENT_NAME, WEBPACK_COMPILATION_COMPLETE, PACKAGE_JSON_FILE_NAME, PLATFORMS_DIR_NAME, TrackActionNames, AnalyticsEventLabelDelimiter } from "../constants";
6+
import { PREPARE_READY_EVENT_NAME, WEBPACK_COMPILATION_COMPLETE, PACKAGE_JSON_FILE_NAME, PLATFORMS_DIR_NAME, TrackActionNames, AnalyticsEventLabelDelimiter, CONFIG_NS_FILE_NAME } from "../constants";
77
interface IPlatformWatcherData {
88
hasWebpackCompilerProcess: boolean;
99
nativeFilesWatcher: choki.FSWatcher;
@@ -189,6 +189,7 @@ export class PrepareController extends EventEmitter {
189189

190190
const patterns = [
191191
path.join(projectData.projectDir, PACKAGE_JSON_FILE_NAME),
192+
path.join(projectData.projectDir, CONFIG_NS_FILE_NAME),
192193
path.join(projectData.getAppDirectoryPath(), PACKAGE_JSON_FILE_NAME),
193194
path.join(projectData.getAppResourcesRelativeDirectoryPath(), platformData.normalizedPlatformName),
194195
]

lib/definitions/platform.d.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,13 @@ interface IPlatformsDataService {
4646
}
4747

4848
interface INodeModulesBuilder {
49-
prepareNodeModules(platformData: IPlatformData, projectData: IProjectData): Promise<void>;
49+
prepareNodeModules(prepareNodeModulesData: IPrepareNodeModulesData): Promise<void>;
50+
}
51+
52+
interface IPrepareNodeModulesData {
53+
platformData: IPlatformData;
54+
projectData: IProjectData;
55+
forcePluginNativePrepare: boolean;
5056
}
5157

5258
interface INodeModulesDependenciesBuilder {

lib/definitions/plugins.d.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,18 @@ interface IPluginsService {
1212
* @returns {IPackageJsonDepedenciesResult}
1313
*/
1414
getDependenciesFromPackageJson(projectDir: string): IPackageJsonDepedenciesResult;
15-
preparePluginNativeCode(pluginData: IPluginData, platform: string, projectData: IProjectData): Promise<void>;
15+
preparePluginNativeCode(preparePluginNativeCodeData: IPreparePluginNativeCodeData): Promise<void>;
1616
convertToPluginData(cacheData: any, projectDir: string): IPluginData;
1717
isNativeScriptPlugin(pluginPackageJsonPath: string): boolean;
1818
}
1919

20+
interface IPreparePluginNativeCodeData {
21+
pluginData: IPluginData;
22+
platform: string;
23+
projectData: IProjectData;
24+
forcePluginNativePrepare: boolean;
25+
}
26+
2027
interface IPackageJsonDepedenciesResult {
2128
dependencies: IBasePluginData[],
2229
devDependencies?: IBasePluginData[]

lib/definitions/project-changes.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ interface IProjectChangesInfo extends IAddedNativePlatform {
1717
configChanged: boolean;
1818
nativeChanged: boolean;
1919
signingChanged: boolean;
20+
nsConfigChanged: boolean;
2021

2122
readonly hasChanges: boolean;
2223
readonly changesRequireBuild: boolean;

lib/definitions/project.d.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ interface INsConfig {
7575
appResourcesPath?: string;
7676
shared?: boolean;
7777
previewAppSchema?: string;
78+
overridePods?: string
7879
}
7980

8081
interface IProjectData extends ICreateProjectData {
@@ -406,10 +407,10 @@ interface ICocoaPodsService {
406407
* @param {string} moduleName The module which the Podfile is from.
407408
* @param {string} podfilePath The path to the podfile.
408409
* @param {IProjectData} projectData Information about the project.
409-
* @param {string} nativeProjectPath Path to the native Xcode project.
410+
* @param {IPlatformData} platformData Information about the platform.
410411
* @returns {Promise<void>}
411412
*/
412-
applyPodfileToProject(moduleName: string, podfilePath: string, projectData: IProjectData, nativeProjectPath: string): Promise<void>;
413+
applyPodfileToProject(moduleName: string, podfilePath: string, projectData: IProjectData, platformData: IPlatformData): Promise<void>;
413414

414415
/**
415416
* Gives the path to the plugin's Podfile.

lib/services/cocoapods-service.ts

+64-9
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
import { EOL } from "os";
22
import * as path from "path";
33
import { PluginNativeDirNames, PODFILE_NAME, NS_BASE_PODFILE } from "../constants";
4-
import { regExpEscape } from "../common/helpers";
4+
import { regExpEscape, getHash } from "../common/helpers";
55

66
export class CocoaPodsService implements ICocoaPodsService {
77
private static PODFILE_POST_INSTALL_SECTION_NAME = "post_install";
88
private static INSTALLER_BLOCK_PARAMETER_NAME = "installer";
9-
9+
private getCocoaPodsFromPodfile: Function;
1010
constructor(
1111
private $cocoaPodsPlatformManager: ICocoaPodsPlatformManager,
1212
private $fs: IFileSystem,
1313
private $childProcess: IChildProcess,
1414
private $errors: IErrors,
1515
private $logger: ILogger,
1616
private $config: IConfiguration,
17-
private $xcconfigService: IXcconfigService) { }
17+
private $xcconfigService: IXcconfigService) {
18+
this.getCocoaPodsFromPodfile = _.memoize(this._getCocoaPodsFromPodfile, getHash);
19+
}
1820

1921
public getPodfileHeader(targetName: string): string {
2022
return `use_frameworks!${EOL}${EOL}target "${targetName}" do${EOL}`;
@@ -35,7 +37,11 @@ export class CocoaPodsService implements ICocoaPodsService {
3537
const podInstallResult = await this.$childProcess.spawnFromEvent(podTool, ["install"], "close", { cwd: projectRoot, stdio: ['pipe', process.stdout, process.stdout] }, { throwError: false });
3638

3739
if (podInstallResult.exitCode !== 0) {
38-
this.$errors.fail(`'${podTool} install' command failed.${podInstallResult.stderr ? " Error is: " + podInstallResult.stderr : ""}`);
40+
// https://github.com/CocoaPods/CocoaPods/blob/92aaf0f1120d32f3487960b485fb69fcaf61486c/lib/cocoapods/resolver.rb#L498
41+
// TODO add article
42+
const versionResolutionHint = podInstallResult.exitCode === 31 ? `For more information on resolving CocoaPod issues in NativeScript read.` : "";
43+
this.$errors.fail(`'${podTool} install' command failed.${podInstallResult.stderr ? " Error is: " + podInstallResult.stderr : ""}
44+
${versionResolutionHint}`);
3945
}
4046

4147
return podInstallResult;
@@ -59,17 +65,18 @@ export class CocoaPodsService implements ICocoaPodsService {
5965
const mainPodfilePath = path.join(projectData.appResourcesDirectoryPath, normalizedPlatformName, PODFILE_NAME);
6066
const projectPodfilePath = this.getProjectPodfilePath(projectRoot);
6167
if (this.$fs.exists(projectPodfilePath) || this.$fs.exists(mainPodfilePath)) {
62-
await this.applyPodfileToProject(NS_BASE_PODFILE, mainPodfilePath, projectData, projectRoot);
68+
await this.applyPodfileToProject(NS_BASE_PODFILE, mainPodfilePath, projectData, platformData);
6369
}
6470
}
6571

66-
public async applyPodfileToProject(moduleName: string, podfilePath: string, projectData: IProjectData, nativeProjectPath: string): Promise<void> {
72+
public async applyPodfileToProject(moduleName: string, podfilePath: string, projectData: IProjectData, platformData: IPlatformData): Promise<void> {
73+
const nativeProjectPath = platformData.projectRoot;
6774
if (!this.$fs.exists(podfilePath)) {
6875
this.removePodfileFromProject(moduleName, podfilePath, projectData, nativeProjectPath);
6976
return;
7077
}
7178

72-
const { podfileContent, replacedFunctions, podfilePlatformData } = this.buildPodfileContent(podfilePath, moduleName);
79+
const { podfileContent, replacedFunctions, podfilePlatformData } = this.buildPodfileContent(podfilePath, moduleName, projectData, platformData);
7380
const pathToProjectPodfile = this.getProjectPodfilePath(nativeProjectPath);
7481
const projectPodfileContent = this.$fs.exists(pathToProjectPodfile) ? this.$fs.readText(pathToProjectPodfile).trim() : "";
7582

@@ -86,6 +93,10 @@ export class CocoaPodsService implements ICocoaPodsService {
8693
finalPodfileContent = this.$cocoaPodsPlatformManager.addPlatformSection(projectData, podfilePlatformData, finalPodfileContent);
8794
}
8895

96+
if (this.isMainPodFile(podfilePath, projectData, platformData) && projectData.nsConfig.overridePods) {
97+
finalPodfileContent = this.overridePodsFromFile(finalPodfileContent, projectData, platformData);
98+
}
99+
89100
finalPodfileContent = `${finalPodfileContent.trim()}${EOL}${EOL}${podfileContent.trim()}${EOL}`;
90101
this.saveProjectPodfile(projectData, finalPodfileContent, nativeProjectPath);
91102
}
@@ -222,10 +233,17 @@ export class CocoaPodsService implements ICocoaPodsService {
222233
return `${CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME} do |${CocoaPodsService.INSTALLER_BLOCK_PARAMETER_NAME}|${EOL}`;
223234
}
224235

225-
private buildPodfileContent(pluginPodFilePath: string, pluginName: string): { podfileContent: string, replacedFunctions: IRubyFunction[], podfilePlatformData: IPodfilePlatformData } {
236+
private buildPodfileContent(pluginPodFilePath: string, pluginName: string, projectData: IProjectData, platformData: IPlatformData): { podfileContent: string, replacedFunctions: IRubyFunction[], podfilePlatformData: IPodfilePlatformData } {
237+
const mainPodfilePath = this.getMainPodFilePath(projectData, platformData);
226238
const pluginPodfileContent = this.$fs.readText(pluginPodFilePath);
227239
const data = this.replaceHookContent(CocoaPodsService.PODFILE_POST_INSTALL_SECTION_NAME, pluginPodfileContent, pluginName);
228-
const { replacedContent, podfilePlatformData } = this.$cocoaPodsPlatformManager.replacePlatformRow(data.replacedContent, pluginPodFilePath);
240+
const cocoapodsData = this.$cocoaPodsPlatformManager.replacePlatformRow(data.replacedContent, pluginPodFilePath);
241+
const podfilePlatformData = cocoapodsData.podfilePlatformData;
242+
let replacedContent = cocoapodsData.replacedContent;
243+
244+
if (projectData.nsConfig.overridePods && mainPodfilePath !== pluginPodFilePath) {
245+
replacedContent = this.overridePodsFromFile(replacedContent, projectData, platformData);
246+
}
229247

230248
return {
231249
podfileContent: `${this.getPluginPodfileHeader(pluginPodFilePath)}${EOL}${replacedContent}${EOL}${this.getPluginPodfileEnd()}`,
@@ -234,6 +252,43 @@ export class CocoaPodsService implements ICocoaPodsService {
234252
};
235253
}
236254

255+
private getMainPodFilePath(projectData: IProjectData, platformData: IPlatformData): string {
256+
return path.join(projectData.appResourcesDirectoryPath, platformData.normalizedPlatformName, PODFILE_NAME);
257+
}
258+
259+
private isMainPodFile(podFilePath: string, projectData: IProjectData, platformData: IPlatformData): boolean {
260+
const mainPodfilePath = this.getMainPodFilePath(projectData, platformData);
261+
262+
return podFilePath === mainPodfilePath;
263+
}
264+
265+
private overridePodsFromFile(podfileContent: string, projectData: IProjectData, platformData: IPlatformData): string {
266+
const mainPodfilePath = this.getMainPodFilePath(projectData, platformData);
267+
const mainPodfileContent = this.$fs.readText(mainPodfilePath);
268+
const pods = this.getCocoaPodsFromPodfile(mainPodfileContent);
269+
_.forEach(pods, pod => {
270+
podfileContent = podfileContent.replace(new RegExp(`^[ ]*pod\\s*["']${pod}['"].*$`, "gm"), '#$&');
271+
});
272+
273+
return podfileContent;
274+
}
275+
276+
private _getCocoaPodsFromPodfile(podfileContent: string): Array<string> {
277+
const pods = [];
278+
const podsRegex = /^\s*pod\s*["'](.*?)['"].*$/gm;
279+
280+
let match = podsRegex.exec(podfileContent);
281+
while (match != null) {
282+
const podName: string = match[1];
283+
if (podName) {
284+
pods.push(podName);
285+
}
286+
287+
match = podsRegex.exec(podfileContent);
288+
}
289+
290+
return pods;
291+
}
237292
}
238293

239294
$injector.register("cocoapodsService", CocoaPodsService);

lib/services/ios-project-service.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -495,9 +495,8 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
495495
await this.prepareResources(pluginPlatformsFolderPath, pluginData, projectData);
496496
await this.prepareFrameworks(pluginPlatformsFolderPath, pluginData, projectData);
497497
await this.prepareStaticLibs(pluginPlatformsFolderPath, pluginData, projectData);
498-
499-
const projectRoot = this.getPlatformData(projectData).projectRoot;
500-
await this.$cocoapodsService.applyPodfileToProject(pluginData.name, this.$cocoapodsService.getPluginPodfilePath(pluginData), projectData, projectRoot);
498+
const platformData = this.getPlatformData(projectData);
499+
await this.$cocoapodsService.applyPodfileToProject(pluginData.name, this.$cocoapodsService.getPluginPodfilePath(pluginData), projectData, platformData);
501500
}
502501

503502
public async removePluginNativeCode(pluginData: IPluginData, projectData: IProjectData): Promise<void> {

lib/services/platform/prepare-native-platform-service.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export class PrepareNativePlatformService implements IPrepareNativePlatformServi
2424
const hasConfigChange = !changesInfo || changesInfo.configChanged;
2525
const hasChangesRequirePrepare = !changesInfo || changesInfo.changesRequirePrepare;
2626

27-
const hasChanges = hasNativeModulesChange || hasConfigChange || hasChangesRequirePrepare;
27+
const hasChanges = hasNativeModulesChange || hasConfigChange || hasChangesRequirePrepare || changesInfo.nsConfigChanged;
2828

2929
if (changesInfo.hasChanges) {
3030
await this.cleanProject(platformData, { release });
@@ -36,8 +36,8 @@ export class PrepareNativePlatformService implements IPrepareNativePlatformServi
3636
await platformData.platformProjectService.prepareProject(projectData, prepareData);
3737
}
3838

39-
if (hasNativeModulesChange) {
40-
await this.$nodeModulesBuilder.prepareNodeModules(platformData, projectData);
39+
if (hasNativeModulesChange || changesInfo.nsConfigChanged) {
40+
await this.$nodeModulesBuilder.prepareNodeModules({platformData, projectData, forcePluginNativePrepare: changesInfo.nsConfigChanged});
4141
}
4242

4343
if (hasNativeModulesChange || hasConfigChange) {

lib/services/plugins-service.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ export class PluginsService implements IPluginsService {
114114
}
115115
}
116116

117-
public async preparePluginNativeCode(pluginData: IPluginData, platform: string, projectData: IProjectData): Promise<void> {
117+
public async preparePluginNativeCode({pluginData, platform, projectData, forcePluginNativePrepare}: IPreparePluginNativeCodeData): Promise<void> {
118118
const platformData = this.$platformsDataService.getPlatformData(platform, projectData);
119119
pluginData.pluginPlatformsFolderPath = (_platform: string) => path.join(pluginData.fullPath, "platforms", _platform.toLowerCase());
120120

@@ -126,7 +126,7 @@ export class PluginsService implements IPluginsService {
126126
const oldPluginNativeHashes = allPluginsNativeHashes[pluginData.name];
127127
const currentPluginNativeHashes = await this.getPluginNativeHashes(pluginPlatformsFolderPath);
128128

129-
if (!oldPluginNativeHashes || this.$filesHashService.hasChangesInShasums(oldPluginNativeHashes, currentPluginNativeHashes)) {
129+
if (forcePluginNativePrepare || !oldPluginNativeHashes || this.$filesHashService.hasChangesInShasums(oldPluginNativeHashes, currentPluginNativeHashes)) {
130130
await platformData.platformProjectService.preparePluginNativeCode(pluginData, projectData);
131131
this.setPluginNativeHashes({
132132
pathToPluginsBuildFile,

lib/services/project-changes-service.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as path from "path";
2-
import { NativePlatformStatus, PACKAGE_JSON_FILE_NAME, APP_GRADLE_FILE_NAME, BUILD_XCCONFIG_FILE_NAME, PLATFORMS_DIR_NAME } from "../constants";
2+
import { NativePlatformStatus, PACKAGE_JSON_FILE_NAME, APP_GRADLE_FILE_NAME, BUILD_XCCONFIG_FILE_NAME, PLATFORMS_DIR_NAME, CONFIG_NS_FILE_NAME } from "../constants";
33
import { getHash, hook } from "../common/helpers";
44

55
const prepareInfoFileName = ".nsprepareinfo";
@@ -8,6 +8,7 @@ class ProjectChangesInfo implements IProjectChangesInfo {
88

99
public appResourcesChanged: boolean;
1010
public configChanged: boolean;
11+
public nsConfigChanged: boolean;
1112
public nativeChanged: boolean;
1213
public signingChanged: boolean;
1314
public nativePlatformStatus: NativePlatformStatus;
@@ -70,6 +71,10 @@ export class ProjectChangesService implements IProjectChangesService {
7071
this._changesInfo.nativeChanged = this.isProjectFileChanged(projectData.projectDir, platformData);
7172
}
7273

74+
// If this causes too much rebuilds of the plugins or uncecessary builds for Android, move overrideCocoapods to prepareInfo.
75+
this._changesInfo.nsConfigChanged = this.filesChanged([path.join(projectData.projectDir, CONFIG_NS_FILE_NAME)]);
76+
this._changesInfo.nativeChanged = this._changesInfo.nativeChanged || this._changesInfo.nsConfigChanged;
77+
7378
this.$logger.trace(`Set nativeChanged to ${this._changesInfo.nativeChanged}.`);
7479

7580
if (platformData.platformNameLowerCase === this.$devicePlatformsConstants.iOS.toLowerCase()) {
@@ -183,6 +188,7 @@ export class ProjectChangesService implements IProjectChangesService {
183188
this._changesInfo.appResourcesChanged = true;
184189
this._changesInfo.configChanged = true;
185190
this._changesInfo.nativeChanged = true;
191+
this._changesInfo.nsConfigChanged = true;
186192
return true;
187193
}
188194

lib/tools/node-modules/node-modules-builder.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export class NodeModulesBuilder implements INodeModulesBuilder {
55
private $pluginsService: IPluginsService
66
) { }
77

8-
public async prepareNodeModules(platformData: IPlatformData, projectData: IProjectData): Promise<void> {
8+
public async prepareNodeModules({platformData , projectData, forcePluginNativePrepare}: IPrepareNodeModulesData): Promise<void> {
99
const dependencies = this.$nodeModulesDependenciesBuilder.getProductionDependencies(projectData.projectDir);
1010
if (_.isEmpty(dependencies)) {
1111
return;
@@ -19,7 +19,7 @@ export class NodeModulesBuilder implements INodeModulesBuilder {
1919
if (isPlugin) {
2020
this.$logger.debug(`Successfully prepared plugin ${dependency.name} for ${platformData.normalizedPlatformName.toLowerCase()}.`);
2121
const pluginData = this.$pluginsService.convertToPluginData(dependency, projectData.projectDir);
22-
await this.$pluginsService.preparePluginNativeCode(pluginData, platformData.normalizedPlatformName.toLowerCase(), projectData);
22+
await this.$pluginsService.preparePluginNativeCode({pluginData, platform: platformData.normalizedPlatformName.toLowerCase(), projectData, forcePluginNativePrepare});
2323
}
2424
}
2525
}

0 commit comments

Comments
 (0)