nativescript 9.1.0-alpha.10 → 9.1.0-alpha.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,18 +8,18 @@ declare global {
8
8
  setupSigningForDevice(
9
9
  projectRoot: string,
10
10
  projectData: IProjectData,
11
- buildConfig: IOSBuildData
11
+ buildConfig: IOSBuildData,
12
12
  ): Promise<void>;
13
13
  setupSigningFromTeam(
14
14
  projectRoot: string,
15
15
  projectData: IProjectData,
16
- teamId: string
16
+ teamId: string,
17
17
  ): Promise<void>;
18
18
  setupSigningFromProvision(
19
19
  projectRoot: string,
20
20
  projectData: IProjectData,
21
21
  provision?: string,
22
- mobileProvisionData?: any
22
+ mobileProvisionData?: any,
23
23
  ): Promise<void>;
24
24
  }
25
25
 
@@ -27,17 +27,17 @@ declare global {
27
27
  buildForSimulator(
28
28
  platformData: IPlatformData,
29
29
  projectData: IProjectData,
30
- buildConfig: IBuildConfig
30
+ buildConfig: IBuildConfig,
31
31
  ): Promise<void>;
32
32
  buildForDevice(
33
33
  platformData: IPlatformData,
34
34
  projectData: IProjectData,
35
- buildConfig: IBuildConfig
35
+ buildConfig: IBuildConfig,
36
36
  ): Promise<string>;
37
37
  buildForAppStore(
38
38
  platformData: IPlatformData,
39
39
  projectData: IProjectData,
40
- buildConfig: IBuildConfig
40
+ buildConfig: IBuildConfig,
41
41
  ): Promise<string>;
42
42
  }
43
43
 
@@ -47,35 +47,44 @@ declare global {
47
47
  applySPMPackages(
48
48
  platformData: IPlatformData,
49
49
  projectData: IProjectData,
50
- pluginSpmPackages?: IosSPMPackage[]
50
+ pluginSpmPackages?: IosSPMPackage[],
51
51
  );
52
52
  getSPMPackages(
53
53
  projectData: IProjectData,
54
- platform: string
54
+ platform: string,
55
55
  ): IosSPMPackage[];
56
+ resolveSPMDependencies(
57
+ platformData: IPlatformData,
58
+ projectData: IProjectData,
59
+ options?: { showProgress?: boolean },
60
+ ): Promise<void>;
61
+ ensureSPMDependenciesResolved(
62
+ platformData: IPlatformData,
63
+ projectData: IProjectData,
64
+ ): Promise<void>;
56
65
  }
57
66
 
58
67
  interface IXcodebuildArgsService {
59
68
  getBuildForSimulatorArgs(
60
69
  platformData: IPlatformData,
61
70
  projectData: IProjectData,
62
- buildConfig: IBuildConfig
71
+ buildConfig: IBuildConfig,
63
72
  ): Promise<string[]>;
64
73
  getBuildForDeviceArgs(
65
74
  platformData: IPlatformData,
66
75
  projectData: IProjectData,
67
- buildConfig: IBuildConfig
76
+ buildConfig: IBuildConfig,
68
77
  ): Promise<string[]>;
69
78
  getXcodeProjectArgs(
70
79
  platformData: IPlatformData,
71
- projectData: IProjectData
80
+ projectData: IProjectData,
72
81
  ): string[];
73
82
  }
74
83
 
75
84
  interface IXcodebuildCommandService {
76
85
  executeCommand(
77
86
  args: string[],
78
- options: IXcodebuildCommandOptions
87
+ options: IXcodebuildCommandOptions,
79
88
  ): Promise<ISpawnResult>;
80
89
  }
81
90
 
@@ -84,18 +93,24 @@ declare global {
84
93
  cwd: string;
85
94
  stdio?: string;
86
95
  spawnOptions?: any;
96
+ /**
97
+ * When provided, xcodebuild's output is piped (rather than inherited) and
98
+ * forwarded here so the caller can render its own progress UI (e.g. a
99
+ * spinner for Swift Package resolution/download activity).
100
+ */
101
+ onProgress?: (chunk: { data: string; pipe: string }) => void;
87
102
  }
88
103
 
89
104
  interface IExportOptionsPlistService {
90
105
  createDevelopmentExportOptionsPlist(
91
106
  archivePath: string,
92
107
  projectData: IProjectData,
93
- buildConfig: IBuildConfig
108
+ buildConfig: IBuildConfig,
94
109
  ): Promise<IExportOptionsPlistOutput>;
95
110
  createDistributionExportOptionsPlist(
96
111
  projectRoot: string,
97
112
  projectData: IProjectData,
98
- buildConfig: IBuildConfig
113
+ buildConfig: IBuildConfig,
99
114
  ): Promise<IExportOptionsPlistOutput>;
100
115
  }
101
116
 
@@ -201,7 +201,10 @@ class AndroidProjectService extends projectServiceBaseLib.PlatformProjectService
201
201
  };
202
202
  }
203
203
  async createProject(frameworkDir, frameworkVersion, projectData) {
204
- if (semver.lt(frameworkVersion, AndroidProjectService.MIN_RUNTIME_VERSION_WITH_GRADLE)) {
204
+ var _a;
205
+ const packageName = ((_a = projectData.nsConfig.android) === null || _a === void 0 ? void 0 : _a.runtimePackageName) || constants.SCOPED_ANDROID_RUNTIME_NAME;
206
+ if (packageName === constants.SCOPED_ANDROID_RUNTIME_NAME &&
207
+ semver.lt(frameworkVersion, AndroidProjectService.MIN_RUNTIME_VERSION_WITH_GRADLE)) {
205
208
  this.$errors.fail(`The NativeScript CLI requires Android runtime ${AndroidProjectService.MIN_RUNTIME_VERSION_WITH_GRADLE} or later to work properly.`);
206
209
  }
207
210
  this.$fs.ensureDirectoryExists(this.getPlatformData(projectData).projectRoot);
@@ -288,7 +291,10 @@ class AndroidProjectService extends projectServiceBaseLib.PlatformProjectService
288
291
  return null;
289
292
  }
290
293
  async updatePlatform(currentVersion, newVersion, canUpdate, projectData, addPlatform, removePlatforms) {
291
- if (semver.eq(newVersion, AndroidProjectService.MIN_RUNTIME_VERSION_WITH_GRADLE)) {
294
+ var _a;
295
+ const packageName = ((_a = projectData.nsConfig.android) === null || _a === void 0 ? void 0 : _a.runtimePackageName) || constants.SCOPED_ANDROID_RUNTIME_NAME;
296
+ if (packageName === constants.SCOPED_ANDROID_RUNTIME_NAME &&
297
+ semver.eq(newVersion, AndroidProjectService.MIN_RUNTIME_VERSION_WITH_GRADLE)) {
292
298
  const platformLowercase = this.getPlatformData(projectData).normalizedPlatformName.toLowerCase();
293
299
  await removePlatforms([platformLowercase.split("@")[0]]);
294
300
  await addPlatform(platformLowercase);
@@ -3,11 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SPMService = void 0;
4
4
  const yok_1 = require("../../common/yok");
5
5
  const trapezedev_project_1 = require("@nstudio/trapezedev-project");
6
+ const color_1 = require("../../color");
6
7
  const path = require("path");
7
8
  class SPMService {
8
- constructor($logger, $projectConfigService, $xcodebuildCommandService, $xcodebuildArgsService) {
9
+ constructor($logger, $fs, $projectConfigService, $terminalSpinnerService, $xcodebuildCommandService, $xcodebuildArgsService) {
9
10
  this.$logger = $logger;
11
+ this.$fs = $fs;
10
12
  this.$projectConfigService = $projectConfigService;
13
+ this.$terminalSpinnerService = $terminalSpinnerService;
11
14
  this.$xcodebuildCommandService = $xcodebuildCommandService;
12
15
  this.$xcodebuildArgsService = $xcodebuildArgsService;
13
16
  }
@@ -24,7 +27,7 @@ class SPMService {
24
27
  mergePluginSPMPackages(appPackages, pluginPackages) {
25
28
  // include swift packages from plugin configs
26
29
  // but allow app packages to override plugin packages with the same name
27
- const appPackageNames = new Set(appPackages.map(pkg => pkg.name));
30
+ const appPackageNames = new Set(appPackages.map((pkg) => pkg.name));
28
31
  for (const pluginPkg of pluginPackages) {
29
32
  if (appPackageNames.has(pluginPkg.name)) {
30
33
  this.$logger.trace(`SPM: app package overrides plugin package: ${pluginPkg.name}`);
@@ -39,7 +42,7 @@ class SPMService {
39
42
  // return this.getSPMPackages(projectData).length > 0;
40
43
  // }
41
44
  async applySPMPackages(platformData, projectData, pluginSpmPackages) {
42
- var _a;
45
+ var _a, _b;
43
46
  try {
44
47
  const spmPackages = this.getSPMPackages(projectData, platformData.platformNameLowerCase);
45
48
  if (pluginSpmPackages === null || pluginSpmPackages === void 0 ? void 0 : pluginSpmPackages.length) {
@@ -79,23 +82,199 @@ class SPMService {
79
82
  }
80
83
  await project.commit();
81
84
  // finally resolve the dependencies
82
- await this.resolveSPMDependencies(platformData, projectData);
85
+ await this.resolveSPMDependencies(platformData, projectData, {
86
+ showProgress: true,
87
+ });
83
88
  }
84
89
  catch (err) {
90
+ // best-effort, but don't bury the failure below trace level — a red
91
+ // resolve spinner with no visible reason is confusing. Warn with the
92
+ // message, keep the full error at trace.
93
+ this.$logger.warn(`Failed to apply Swift Package dependencies: ${(_b = err === null || err === void 0 ? void 0 : err.message) !== null && _b !== void 0 ? _b : err}`);
85
94
  this.$logger.trace("SPM: error applying SPM packages: ", err);
86
95
  }
87
96
  }
88
- async resolveSPMDependencies(platformData, projectData) {
89
- await this.$xcodebuildCommandService.executeCommand(this.$xcodebuildArgsService
97
+ /**
98
+ * Resolves (downloads + pins) the Swift Package dependencies referenced by
99
+ * the Xcode project. On a first build this is where the NativeScript runtime
100
+ * (now distributed as a remote Swift package) and any other SPM packages are
101
+ * actually downloaded — which can take a while. Pass `showProgress` to render
102
+ * a live spinner so the CLI doesn't look stalled while that happens.
103
+ *
104
+ * In verbose mode (`--log trace`) the condensed spinner is bypassed and
105
+ * xcodebuild's raw resolution log is streamed straight through instead.
106
+ */
107
+ async resolveSPMDependencies(platformData, projectData, options) {
108
+ const args = this.$xcodebuildArgsService
90
109
  .getXcodeProjectArgs(platformData, projectData)
91
110
  .concat([
92
111
  "-destination",
93
112
  "generic/platform=iOS",
94
113
  "-resolvePackageDependencies",
95
- ]), {
96
- cwd: projectData.projectDir,
97
- message: "Resolving SPM dependencies...",
98
- });
114
+ ]);
115
+ // Without progress, or when verbose: let xcodebuild's own resolution log
116
+ // stream straight to the terminal (inherited stdio). Verbose users want
117
+ // the raw log, not a condensed spinner that hides it.
118
+ if (!(options === null || options === void 0 ? void 0 : options.showProgress) || this.$logger.isVerbose()) {
119
+ await this.$xcodebuildCommandService.executeCommand(args, {
120
+ cwd: projectData.projectDir,
121
+ message: "Resolving Swift Package dependencies...",
122
+ });
123
+ return;
124
+ }
125
+ const spinner = this.$terminalSpinnerService.createSpinner();
126
+ const startedAt = Date.now();
127
+ let activity = "Resolving Swift Package dependencies";
128
+ let lineBuffer = "";
129
+ const render = () => {
130
+ const elapsed = Math.round((Date.now() - startedAt) / 1000);
131
+ spinner.text = `${activity}… ${color_1.color.dim(`(${this.formatElapsed(elapsed)})`)}`;
132
+ };
133
+ // keep the elapsed timer ticking even when xcodebuild is silent (e.g.
134
+ // while a binary artifact downloads) so the user can see it's alive.
135
+ const ticker = setInterval(render, 1000);
136
+ const onProgress = (chunk) => {
137
+ lineBuffer += chunk.data;
138
+ const lines = lineBuffer.split("\n");
139
+ // keep the last (possibly partial) line in the buffer
140
+ lineBuffer = lines.pop();
141
+ for (const line of lines) {
142
+ const described = this.describeSPMActivity(line);
143
+ if (described) {
144
+ activity = described;
145
+ render();
146
+ }
147
+ }
148
+ };
149
+ render();
150
+ spinner.start();
151
+ try {
152
+ await this.$xcodebuildCommandService.executeCommand(args, {
153
+ cwd: projectData.projectDir,
154
+ onProgress,
155
+ });
156
+ spinner.succeed(color_1.color.green("Swift Package dependencies resolved"));
157
+ }
158
+ catch (err) {
159
+ spinner.fail(color_1.color.red("Failed to resolve Swift Package dependencies"));
160
+ throw err;
161
+ }
162
+ finally {
163
+ clearInterval(ticker);
164
+ }
165
+ }
166
+ /**
167
+ * Best-effort pre-resolve before a build so the (potentially slow) first-time
168
+ * Swift package download happens under a clear progress indicator instead of
169
+ * silently inside the subsequent "Xcode build..." step. No-op when the
170
+ * project has no SPM references or they're already resolved.
171
+ */
172
+ async ensureSPMDependenciesResolved(platformData, projectData) {
173
+ if (!this.hasSPMReferences(platformData, projectData)) {
174
+ this.$logger.trace("SPM: project has no Swift Package references.");
175
+ return;
176
+ }
177
+ if (this.arePackagesResolved(platformData, projectData)) {
178
+ this.$logger.trace("SPM: Swift Package dependencies already resolved; skipping pre-resolve.");
179
+ return;
180
+ }
181
+ try {
182
+ await this.resolveSPMDependencies(platformData, projectData, {
183
+ showProgress: true,
184
+ });
185
+ }
186
+ catch (err) {
187
+ // non-fatal: the build itself will resolve packages and surface the
188
+ // authoritative error if something is genuinely wrong.
189
+ this.$logger.trace("SPM: pre-resolve failed (continuing): ", err);
190
+ }
191
+ }
192
+ /**
193
+ * Maps a raw xcodebuild/SwiftPM resolution log line to a concise,
194
+ * user-facing activity description (or null for lines we don't surface).
195
+ */
196
+ describeSPMActivity(line) {
197
+ const trimmed = line.trim();
198
+ if (!trimmed) {
199
+ return null;
200
+ }
201
+ // the big, otherwise-silent wait on a first build: a binary artifact
202
+ // (the NativeScript runtime xcframework) downloading.
203
+ if (/Downloading binary artifact/i.test(trimmed)) {
204
+ if (/ios-spm|nativescript/i.test(trimmed)) {
205
+ return "Downloading the NativeScript runtime (first build only)";
206
+ }
207
+ return "Downloading Swift Package binaries (first build only)";
208
+ }
209
+ if (/^Fetching\b/i.test(trimmed)) {
210
+ return this.describePackageActivity("Fetching", trimmed);
211
+ }
212
+ if (/^Cloning\b/i.test(trimmed)) {
213
+ return this.describePackageActivity("Cloning", trimmed);
214
+ }
215
+ if (/Computing version for/i.test(trimmed)) {
216
+ return "Computing package versions";
217
+ }
218
+ if (/Resolve Package Graph/i.test(trimmed)) {
219
+ return "Resolving Swift Package graph";
220
+ }
221
+ if (/Resolved source packages/i.test(trimmed)) {
222
+ return "Finalizing Swift Package dependencies";
223
+ }
224
+ return null;
225
+ }
226
+ /**
227
+ * Builds a stable, self-explanatory activity label with the package being
228
+ * worked on in parentheses, e.g. "Fetching Swift Packages (Auth0.swift)".
229
+ */
230
+ describePackageActivity(verb, line) {
231
+ const packageRef = this.shortenPackageRef(line);
232
+ return packageRef
233
+ ? `${verb} Swift Packages (${packageRef})`
234
+ : `${verb} Swift Packages`;
235
+ }
236
+ /**
237
+ * Extracts a short, readable name from a SwiftPM repo URL/log line, or null
238
+ * when the line contains no URL to name the package by.
239
+ */
240
+ shortenPackageRef(line) {
241
+ const match = line.match(/https?:\/\/\S+/);
242
+ if (!match) {
243
+ return null;
244
+ }
245
+ return path
246
+ .basename(match[0])
247
+ .replace(/\.git$/, "")
248
+ .replace(/[)\s].*$/, "");
249
+ }
250
+ /** Formats an elapsed duration in whole seconds as "5m 15s". */
251
+ formatElapsed(totalSeconds) {
252
+ const minutes = Math.floor(totalSeconds / 60);
253
+ const seconds = totalSeconds % 60;
254
+ return `${minutes}m ${seconds}s`;
255
+ }
256
+ /** True when the Xcode project references any Swift packages. */
257
+ hasSPMReferences(platformData, projectData) {
258
+ const pbxprojPath = path.join(platformData.projectRoot, `${projectData.projectName}.xcodeproj`, "project.pbxproj");
259
+ if (!this.$fs.exists(pbxprojPath)) {
260
+ return false;
261
+ }
262
+ const contents = this.$fs.readText(pbxprojPath);
263
+ return (contents.includes("XCRemoteSwiftPackageReference") ||
264
+ contents.includes("XCLocalSwiftPackageReference") ||
265
+ contents.includes("packageReferences"));
266
+ }
267
+ /**
268
+ * True when a Package.resolved already exists for the project — i.e. packages
269
+ * have been resolved at least once, so the build's own resolve step will be a
270
+ * fast no-op rather than a slow, silent first-time download.
271
+ */
272
+ arePackagesResolved(platformData, projectData) {
273
+ const candidates = [
274
+ path.join(platformData.projectRoot, `${projectData.projectName}.xcworkspace`, "xcshareddata", "swiftpm", "Package.resolved"),
275
+ path.join(platformData.projectRoot, `${projectData.projectName}.xcodeproj`, "project.xcworkspace", "xcshareddata", "swiftpm", "Package.resolved"),
276
+ ];
277
+ return candidates.some((p) => this.$fs.exists(p));
99
278
  }
100
279
  }
101
280
  exports.SPMService = SPMService;
@@ -10,9 +10,22 @@ class XcodebuildCommandService {
10
10
  this.$logger = $logger;
11
11
  }
12
12
  async executeCommand(args, options) {
13
- const { message, cwd, stdio, spawnOptions } = options;
14
- this.$logger.info(message || "Xcode build...");
15
- const childProcessOptions = { cwd, stdio: stdio || "inherit" };
13
+ const { message, cwd, stdio, spawnOptions, onProgress } = options;
14
+ // A caller rendering its own progress UI owns stdout, so skip the
15
+ // default "Xcode build..." line that would otherwise clobber it.
16
+ if (!onProgress) {
17
+ this.$logger.info(message || "Xcode build...");
18
+ }
19
+ const childProcessOptions = {
20
+ cwd,
21
+ stdio: onProgress ? "pipe" : stdio || "inherit",
22
+ };
23
+ let detachProgress;
24
+ if (onProgress) {
25
+ const handler = (chunk) => onProgress(chunk);
26
+ this.$childProcess.on(constants.BUILD_OUTPUT_EVENT_NAME, handler);
27
+ detachProgress = () => this.$childProcess.removeListener(constants.BUILD_OUTPUT_EVENT_NAME, handler);
28
+ }
16
29
  try {
17
30
  const commandResult = await this.$childProcess.spawnFromEvent("xcodebuild", args, "exit", childProcessOptions, spawnOptions || {
18
31
  emitOptions: { eventName: constants.BUILD_OUTPUT_EVENT_NAME },
@@ -23,6 +36,9 @@ class XcodebuildCommandService {
23
36
  catch (err) {
24
37
  this.$errors.fail(err.message);
25
38
  }
39
+ finally {
40
+ detachProgress === null || detachProgress === void 0 ? void 0 : detachProgress();
41
+ }
26
42
  }
27
43
  }
28
44
  exports.XcodebuildCommandService = XcodebuildCommandService;
@@ -243,6 +243,10 @@ class IOSProjectService extends projectServiceBaseLib.PlatformProjectServiceBase
243
243
  }
244
244
  async buildProject(projectRoot, projectData, buildData) {
245
245
  const platformData = this.getPlatformData(projectData);
246
+ // On a first build, the runtime (and any other Swift packages) download
247
+ // here. Pre-resolve under a clear spinner so the subsequent
248
+ // "Xcode build..." step doesn't appear to hang while that happens.
249
+ await this.$spmService.ensureSPMDependenciesResolved(platformData, projectData);
246
250
  const handler = (data) => {
247
251
  this.emit(constants.BUILD_OUTPUT_EVENT_NAME, data);
248
252
  };
@@ -356,7 +360,18 @@ class IOSProjectService extends projectServiceBaseLib.PlatformProjectServiceBase
356
360
  project.addFramework(relativeStaticLibPath);
357
361
  const relativeHeaderSearchPath = path.join(this.getLibSubpathRelativeToProjectPath(headersSubpath, projectData));
358
362
  project.addToHeaderSearchPaths({ relativePath: relativeHeaderSearchPath });
359
- this.generateModulemap(headersSubpath, libraryName);
363
+ // Write the generated modulemap into a CLI-managed folder under the
364
+ // platform root (never into node_modules). The modulemap references the
365
+ // plugin's headers in-place via relative paths, so nothing is copied.
366
+ const modulemapDir = path.join(this.getPlatformData(projectData).projectRoot, IOSProjectService.GENERATED_PLUGINS_DIR_NAME, libraryName);
367
+ const hasModulemap = this.generateModulemap(headersSubpath, libraryName, modulemapDir);
368
+ if (hasModulemap) {
369
+ // Put the modulemap dir on the header search path so clang discovers
370
+ // the module there instead of inside node_modules.
371
+ project.addToHeaderSearchPaths({
372
+ relativePath: this.getLibSubpathRelativeToProjectPath(modulemapDir, projectData),
373
+ });
374
+ }
360
375
  this.savePbxProj(project, projectData);
361
376
  }
362
377
  async prepareProject(projectData, prepareData) {
@@ -905,23 +920,46 @@ class IOSProjectService extends projectServiceBaseLib.PlatformProjectServiceBase
905
920
  project.removeFromHeaderSearchPaths({
906
921
  relativePath: relativeHeaderSearchPath,
907
922
  });
923
+ // Remove the generated modulemap dir search path (see addStaticLibrary)
924
+ const modulemapDir = path.join(this.getPlatformData(projectData).projectRoot, IOSProjectService.GENERATED_PLUGINS_DIR_NAME, path.basename(staticLibPath, ".a"));
925
+ project.removeFromHeaderSearchPaths({
926
+ relativePath: this.getLibSubpathRelativeToProjectPath(modulemapDir, projectData),
927
+ });
908
928
  });
909
929
  this.savePbxProj(project, projectData);
910
930
  }
911
- generateModulemap(headersFolderPath, libraryName) {
931
+ generateModulemap(headersFolderPath, libraryName, modulemapDir) {
932
+ const modulemapPath = path.join(modulemapDir, "module.modulemap");
933
+ // A plugin may ship a `.a` without an `include/{lib}` headers folder. In
934
+ // that case there's nothing to expose as a module - clean up any stale
935
+ // modulemap and bail out instead of letting readDirectory throw.
936
+ if (!this.$fs.exists(headersFolderPath)) {
937
+ if (this.$fs.exists(modulemapPath)) {
938
+ this.$fs.deleteFile(modulemapPath);
939
+ }
940
+ return false;
941
+ }
912
942
  const headersFilter = (fileName, containingFolderPath) => path.extname(fileName) === ".h" &&
913
943
  this.$fs.getFsStats(path.join(containingFolderPath, fileName)).isFile();
914
944
  const headersFolderContents = this.$fs.readDirectory(headersFolderPath);
915
- let headers = _(headersFolderContents)
916
- .filter((item) => headersFilter(item, headersFolderPath))
917
- .value();
918
- if (!headers.length) {
919
- this.$fs.deleteFile(path.join(headersFolderPath, "module.modulemap"));
920
- return;
945
+ const headerFiles = headersFolderContents.filter((item) => headersFilter(item, headersFolderPath));
946
+ if (!headerFiles.length) {
947
+ if (this.$fs.exists(modulemapPath)) {
948
+ this.$fs.deleteFile(modulemapPath);
949
+ }
950
+ return false;
921
951
  }
922
- headers = _.map(headers, (value) => `header "${value}"`);
952
+ // Reference the plugin's headers (still in node_modules) relative to the
953
+ // generated modulemap's location, so we don't copy headers or write into
954
+ // node_modules.
955
+ const headers = _.map(headerFiles, (value) => {
956
+ const relativeHeaderPath = path.relative(modulemapDir, path.join(headersFolderPath, value));
957
+ return `header "${relativeHeaderPath}"`;
958
+ });
923
959
  const modulemap = `module ${libraryName} { explicit module ${libraryName} { ${headers.join(" ")} } }`;
924
- this.$fs.writeFile(path.join(headersFolderPath, "module.modulemap"), modulemap);
960
+ this.$fs.ensureDirectoryExists(modulemapDir);
961
+ this.$fs.writeFile(modulemapPath, modulemap);
962
+ return true;
925
963
  }
926
964
  async mergeProjectXcconfigFiles(projectData) {
927
965
  const platformData = this.getPlatformData(projectData);
@@ -995,6 +1033,9 @@ class IOSProjectService extends projectServiceBaseLib.PlatformProjectServiceBase
995
1033
  exports.IOSProjectService = IOSProjectService;
996
1034
  IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER = "__PROJECT_NAME__";
997
1035
  IOSProjectService.IOS_PLATFORM_NAME = "ios";
1036
+ // CLI-managed folder under the platform root where we write generated
1037
+ // artifacts (e.g. plugin modulemaps) so we never write into node_modules
1038
+ IOSProjectService.GENERATED_PLUGINS_DIR_NAME = ".plugins";
998
1039
  __decorate([
999
1040
  (0, helpers_2.hook)("buildIOS")
1000
1041
  ], IOSProjectService.prototype, "buildProject", null);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nativescript",
3
3
  "main": "./lib/nativescript-cli-lib.js",
4
- "version": "9.1.0-alpha.10",
4
+ "version": "9.1.0-alpha.12",
5
5
  "author": "NativeScript <oss@nativescript.org>",
6
6
  "description": "Command-line interface for building NativeScript projects",
7
7
  "bin": {