nativescript 9.0.6 → 9.0.7-dev.0

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.
package/lib/constants.js CHANGED
@@ -324,6 +324,7 @@ var IOSNativeTargetTypes;
324
324
  IOSNativeTargetTypes["watchApp"] = "watch_app";
325
325
  IOSNativeTargetTypes["watchExtension"] = "watch_extension";
326
326
  IOSNativeTargetTypes["appExtension"] = "app_extension";
327
+ IOSNativeTargetTypes["application"] = "application";
327
328
  })(IOSNativeTargetTypes || (exports.IOSNativeTargetTypes = IOSNativeTargetTypes = {}));
328
329
  const pathToLoggerAppendersDir = (0, path_1.join)(__dirname, "common", "logger", "appenders");
329
330
  exports.LoggerAppenders = {
@@ -8,16 +8,27 @@ declare module "nativescript-dev-xcode" {
8
8
  }
9
9
 
10
10
  class project {
11
+ hash: any;
12
+ filepath: string;
11
13
  constructor(filename: string);
12
14
 
13
15
  parse(callback: () => void): void;
14
16
  parseSync(): void;
15
17
 
18
+ generateUuid(): string;
19
+
16
20
  writeSync(options: any): string;
17
21
 
18
22
  addFramework(filepath: string, options?: Options): void;
19
23
  removeFramework(filePath: string, options?: Options): void;
20
24
 
25
+
26
+ getProductFile(watchApptarget: target): any;
27
+ addToPbxFrameworksBuildPhase(file);
28
+ addToPbxCopyfilesBuildPhase(file, comment: string, targetid: string);
29
+ pbxFrameworksBuildPhaseObj(targetid: string): any;
30
+ pbxBuildFileSection(): {[k: string] : any};
31
+
21
32
  addPbxGroup(
22
33
  filePathsArray: any[],
23
34
  name: string,
@@ -27,17 +38,30 @@ declare module "nativescript-dev-xcode" {
27
38
 
28
39
  removePbxGroup(groupName: string, path: string): void;
29
40
 
41
+ addTargetDependency(target: string, dependencyTargets: string[]);
42
+
43
+ findTargetKey(name: string);
44
+ pbxTargetByName(name: string): target;
45
+ pbxNativeTargetSection(): {[key: string]: any};
46
+
30
47
  addToHeaderSearchPaths(options?: Options): void;
31
48
  removeFromHeaderSearchPaths(options?: Options): void;
32
49
  updateBuildProperty(key: string, value: any): void;
33
50
 
34
51
  pbxXCBuildConfigurationSection(): any;
35
52
 
53
+ buildPhaseObject(
54
+ buildPhaseType: string,
55
+ comment: string,
56
+ target: tstring
57
+ )
58
+
36
59
  addTarget(
37
60
  targetName: string,
38
61
  targetType: string,
39
62
  targetPath?: string,
40
- parentTarget?: string
63
+ parentTarget?: string,
64
+ productTargetType?: string
41
65
  ): target;
42
66
  addBuildPhase(
43
67
  filePathsArray: string[],
@@ -601,9 +601,7 @@ interface INativePrepare {
601
601
  }
602
602
 
603
603
  interface IBuildConfig
604
- extends IAndroidBuildOptionsSettings,
605
- IiOSBuildConfig,
606
- IProjectDir {
604
+ extends IAndroidBuildOptionsSettings, IiOSBuildConfig, IProjectDir {
607
605
  clean?: boolean;
608
606
  architectures?: string[];
609
607
  buildOutputStdio?: string;
@@ -615,7 +613,8 @@ interface IBuildConfig
615
613
  * Describes iOS-specific build configuration properties
616
614
  */
617
615
  interface IiOSBuildConfig
618
- extends IBuildForDevice,
616
+ extends
617
+ IBuildForDevice,
619
618
  IiCloudContainerEnvironment,
620
619
  IDeviceIdentifier,
621
620
  IProvision,
@@ -865,6 +864,7 @@ interface IAddExtensionsFromPathOptions extends IAddTargetFromPathOptions {
865
864
 
866
865
  interface IAddWatchAppFromPathOptions extends IAddTargetFromPathOptions {
867
866
  watchAppFolderPath: string;
867
+ disableStubBinary?: boolean;
868
868
  }
869
869
 
870
870
  interface IRemoveExtensionsOptions {
@@ -873,6 +873,37 @@ interface IRemoveExtensionsOptions {
873
873
 
874
874
  interface IRemoveWatchAppOptions extends IRemoveExtensionsOptions {}
875
875
 
876
+ interface IWatchAppJSONConfigModule {
877
+ name?: string;
878
+ path: string;
879
+ targetType?: string;
880
+ embed?: boolean;
881
+ frameworks?: Array<string | Record<string, string>>;
882
+ dependencies?: string[];
883
+ headerSearchPaths?: string[];
884
+ resources?: string[];
885
+ src?: string[];
886
+ linkerFlags?: string[];
887
+ buildConfigurationProperties?: Record<string, string>;
888
+ SPMPackages?: Array<IOSSPMPackage | string>;
889
+ }
890
+ interface IWatchAppJSONConfig {
891
+ targetType?: string;
892
+ forceAddEmbedWatchContent?: boolean;
893
+ sharedModulesBuildConfigurationProperties?: Record<string, string>;
894
+ basedir?: string;
895
+ infoPlistPath?: string;
896
+ xcprivacyPath?: string;
897
+ importSourcesFromMainFolder?: boolean;
898
+ importResourcesFromMainFolder?: boolean;
899
+ resources?: string[];
900
+ src?: string[];
901
+ resourcesExclude?: string[];
902
+ srcExclude?: string[];
903
+ modules: IWatchAppConfigModule[];
904
+ SPMPackages?: Array<IOSSPMPackage>;
905
+ }
906
+
876
907
  interface IRubyFunction {
877
908
  functionName: string;
878
909
  functionParameters?: string;
@@ -96,6 +96,7 @@ class BundlerCompilerService extends events_1.EventEmitter {
96
96
  console.log("Vite first build completed, resolving compileWithWatch");
97
97
  }
98
98
  resolve(childProcess);
99
+ return;
99
100
  }
100
101
  // Transform Vite message to match webpack format
101
102
  const files = message.emittedFiles.map((file) => path.join(platformData.appDestinationDirectoryPath, this.$options.hostProjectModuleName, file));
@@ -468,7 +468,6 @@ class IOSProjectService extends projectServiceBaseLib.PlatformProjectServiceBase
468
468
  }
469
469
  }
470
470
  }
471
- this.$iOSWatchAppService.removeWatchApp({ pbxProjPath });
472
471
  const addedWatchApp = await this.$iOSWatchAppService.addWatchAppFromPath({
473
472
  watchAppFolderPath: path.join(resourcesDirectoryPath, platformData.normalizedPlatformName),
474
473
  projectData,
@@ -4,34 +4,208 @@ exports.IOSWatchAppService = void 0;
4
4
  const path = require("path");
5
5
  const constants_1 = require("../constants");
6
6
  const yok_1 = require("../common/yok");
7
+ const trapezedev_project_1 = require("@nstudio/trapezedev-project");
8
+ const minimatch_1 = require("minimatch");
9
+ const sourceExtensions = [
10
+ ".swift",
11
+ ".m",
12
+ ".mm",
13
+ ".c",
14
+ ".cpp",
15
+ ".cc",
16
+ ".cxx",
17
+ ".h",
18
+ ".hpp",
19
+ ];
20
+ const resourceExtensions = [
21
+ ".png",
22
+ ".jpg",
23
+ ".jpeg",
24
+ ".gif",
25
+ ".svg",
26
+ ".pdf", // Images
27
+ ".ttf",
28
+ ".otf",
29
+ ".woff",
30
+ ".woff2", // Fonts
31
+ ".xcassets", // Asset catalogs
32
+ ".storyboard",
33
+ ".xib", // Interface files
34
+ ".strings",
35
+ ".stringsdict", // Localization
36
+ ".json",
37
+ ".xml",
38
+ ".plist", // Data files
39
+ ".m4a",
40
+ ".mp3",
41
+ ".wav",
42
+ ".caf", // Audio
43
+ ".mp4",
44
+ ".mov", // Video
45
+ ".bundle", // Resource bundles
46
+ ];
47
+ const WATCH_APP_IDENTIFIER = "watchkitapp";
48
+ const WACTCH_EXTENSION_IDENTIFIER = "watchkitextension";
49
+ const CONFIG_FILE_WATCHAPP = "watchapp.json";
50
+ const CONFIG_FILE_EXTENSION = "extension.json";
51
+ const RESOURCES_TO_IGNORE = [
52
+ CONFIG_FILE_WATCHAPP,
53
+ CONFIG_FILE_EXTENSION,
54
+ "node_modules",
55
+ ];
7
56
  class IOSWatchAppService {
8
- constructor($fs, $pbxprojDomXcode, $xcode, $iOSNativeTargetService) {
57
+ constructor($fs, $pbxprojDomXcode, $xcode, $iOSNativeTargetService, $logger) {
9
58
  this.$fs = $fs;
10
59
  this.$pbxprojDomXcode = $pbxprojDomXcode;
11
60
  this.$xcode = $xcode;
12
61
  this.$iOSNativeTargetService = $iOSNativeTargetService;
62
+ this.$logger = $logger;
13
63
  }
14
- async addWatchAppFromPath({ watchAppFolderPath, projectData, platformData, pbxProjPath, }) {
64
+ addResourceFile(project, path, opt, group = "WatchResources") {
65
+ const file = project.addResourceFile(path, opt, group);
66
+ project.addToResourcesPbxGroup(file, group);
67
+ }
68
+ addSourceFile(project, path, opt, group = "WatchSrc") {
69
+ const file = project.addSourceFile(path, opt, group);
70
+ project.addToResourcesPbxGroup(file, group);
71
+ }
72
+ async addWatchAppFromPath({ watchAppFolderPath, projectData, platformData, pbxProjPath, disableStubBinary = false, }) {
73
+ var _a, _b;
15
74
  const targetUuids = [];
75
+ const targetNames = [];
16
76
  const appPath = path.join(watchAppFolderPath, constants_1.IOS_WATCHAPP_FOLDER);
17
77
  const extensionPath = path.join(watchAppFolderPath, constants_1.IOS_WATCHAPP_EXTENSION_FOLDER);
18
- if (!this.$fs.exists(appPath) || !this.$fs.exists(extensionPath)) {
78
+ const hasWatchExtension = this.$fs.exists(extensionPath);
79
+ // Check if watchapp exists - it's required
80
+ if (!this.$fs.exists(appPath)) {
19
81
  return false;
20
82
  }
21
83
  const appFolder = this.$iOSNativeTargetService.getTargetDirectories(appPath)[0];
22
- const extensionFolder = this.$iOSNativeTargetService.getTargetDirectories(extensionPath)[0];
23
84
  const project = new this.$xcode.project(pbxProjPath);
24
85
  project.parseSync();
25
- const watchApptarget = this.$iOSNativeTargetService.addTargetToProject(appPath, appFolder, constants_1.IOSNativeTargetTypes.watchApp, project, platformData, project.getFirstTarget().uuid);
26
- this.configureTarget(appFolder, path.join(appPath, appFolder), `${projectData.projectIdentifiers.ios}.${IOSWatchAppService.WATCH_APP_IDENTIFIER}`, "watchapp.json", watchApptarget, project);
86
+ const configPath = path.join(path.join(appPath, appFolder), "watchapp.json");
87
+ const config = this.$fs.exists(configPath)
88
+ ? this.$fs.readJson(configPath)
89
+ : null;
90
+ const targetType = (_a = config === null || config === void 0 ? void 0 : config.targetType) !== null && _a !== void 0 ? _a : constants_1.IOSNativeTargetTypes.watchApp;
91
+ project.removeTargetsByProductType(constants_1.IOSNativeTargetProductTypes.watchApp);
92
+ project.removeTargetsByProductType(targetType);
93
+ const parentTargetUuid = project.getFirstTarget().uuid;
94
+ const watchApptarget = this.addTarget(appPath, appFolder, targetType, project, platformData, parentTargetUuid, constants_1.IOSNativeTargetTypes.watchApp);
95
+ await this.configureTarget(appFolder, path.join(appPath, appFolder), `${projectData.projectIdentifiers.ios}.${WATCH_APP_IDENTIFIER}`, configPath, config, watchApptarget, project, projectData, platformData, pbxProjPath, !hasWatchExtension);
27
96
  targetUuids.push(watchApptarget.uuid);
28
- const watchExtensionTarget = this.$iOSNativeTargetService.addTargetToProject(extensionPath, extensionFolder, constants_1.IOSNativeTargetTypes.watchExtension, project, platformData, watchApptarget.uuid);
29
- this.configureTarget(extensionFolder, path.join(extensionPath, extensionFolder), `${projectData.projectIdentifiers.ios}.${IOSWatchAppService.WATCH_APP_IDENTIFIER}.${IOSWatchAppService.WACTCH_EXTENSION_IDENTIFIER}`, "extension.json", watchExtensionTarget, project);
30
- targetUuids.push(watchExtensionTarget.uuid);
97
+ targetNames.push(appFolder);
98
+ // Extension is optional (Xcode 14+ supports single target)
99
+ if (hasWatchExtension) {
100
+ const extensionFolder = this.$iOSNativeTargetService.getTargetDirectories(extensionPath)[0];
101
+ const configPath = path.join(path.join(extensionPath, extensionFolder), "extension.json");
102
+ const config = this.$fs.exists(configPath)
103
+ ? this.$fs.readJson(configPath)
104
+ : null;
105
+ const targetType = (_b = config === null || config === void 0 ? void 0 : config.targetType) !== null && _b !== void 0 ? _b : constants_1.IOSNativeTargetTypes.watchExtension;
106
+ project.removeTargetsByProductType(constants_1.IOSNativeTargetProductTypes.watchExtension);
107
+ project.removeTargetsByProductType(targetType);
108
+ const watchExtensionTarget = this.addTarget(extensionPath, extensionFolder, targetType, project, platformData, watchApptarget.uuid);
109
+ await this.configureTarget(extensionFolder, path.join(extensionPath, extensionFolder), `${projectData.projectIdentifiers.ios}.${WATCH_APP_IDENTIFIER}.${WACTCH_EXTENSION_IDENTIFIER}`, configPath, config, watchExtensionTarget, project, projectData, platformData, pbxProjPath);
110
+ targetUuids.push(watchExtensionTarget.uuid);
111
+ targetNames.push(extensionFolder);
112
+ }
113
+ else {
114
+ this.$logger.debug("No watch extension found - using single target mode (Xcode 14+)");
115
+ }
31
116
  this.$fs.writeFile(pbxProjPath, project.writeSync({ omitEmptyValues: true }));
117
+ // Add SPM packages (file needs to be saved first)
118
+ const watchSPMPackages = this.getWatchSPMPackages(platformData);
119
+ await this.applySPMPackagesToTargets(targetNames, platformData, projectData.projectDir, watchSPMPackages);
120
+ // nothing done after we dont need to reload project
32
121
  this.$iOSNativeTargetService.prepareSigning(targetUuids, projectData, pbxProjPath);
122
+ if (disableStubBinary) {
123
+ this.applyWatchAppStubBinaryOverrides(appFolder, pbxProjPath);
124
+ }
33
125
  return true;
34
126
  }
127
+ addTarget(targetRootPath, targetFolder, targetType, project, platformData, parentTarget, productTargetType) {
128
+ const targetPath = path.join(targetRootPath, targetFolder);
129
+ const targetRelativePath = path.relative(platformData.projectRoot, targetPath);
130
+ const target = project.addTarget(targetFolder, targetType, targetRelativePath, parentTarget, productTargetType);
131
+ // Add build phases
132
+ project.addBuildPhase([], "PBXSourcesBuildPhase", "Sources", target.uuid);
133
+ project.addBuildPhase([], "PBXResourcesBuildPhase", "Resources", target.uuid);
134
+ project.addBuildPhase([], "PBXFrameworksBuildPhase", "Frameworks", target.uuid);
135
+ project.addBuildPhase([], "PBXCopyFilesBuildPhase", "Embed Frameworks", target.uuid, "frameworks");
136
+ project.addToHeaderSearchPaths(targetPath, target.pbxNativeTarget.productName);
137
+ return target;
138
+ }
139
+ /**
140
+ * Recursively add source files from a directory to a target
141
+ */
142
+ addSourceFilesFromDirectory(dirPath, targetUuid, project, platformData, groupName, excludePatterns) {
143
+ const items = this.getFolderFiles(dirPath, platformData.projectRoot, excludePatterns);
144
+ for (const item of items) {
145
+ const relativePath = path.relative(platformData.projectRoot, item);
146
+ // Check if file is a source file by extension
147
+ const ext = path.extname(item).toLowerCase();
148
+ if (sourceExtensions.includes(ext)) {
149
+ this.$logger.debug(`Adding source file: ${relativePath}`);
150
+ this.addSourceFile(project, relativePath, { target: targetUuid }, groupName);
151
+ }
152
+ }
153
+ }
154
+ async addTargetResources(watchAppFolderPath, targetUuids, project, platformData, groupName, excludePatterns) {
155
+ try {
156
+ if (!this.$fs.exists(watchAppFolderPath)) {
157
+ return;
158
+ }
159
+ for (let i = 0; i < targetUuids.length; i++) {
160
+ const targetUuid = targetUuids[i];
161
+ this.addResourcesFromDirectory(watchAppFolderPath, targetUuid, project, platformData, groupName, excludePatterns);
162
+ }
163
+ this.$logger.debug("Watch app resources added successfully");
164
+ }
165
+ catch (err) {
166
+ this.$logger.warn(`Error adding watch app resources: ${err.message}`);
167
+ }
168
+ }
169
+ /**
170
+ * Recursively add resources from a directory to a target
171
+ */
172
+ addResourcesFromDirectory(dirPath, targetUuid, project, platformData, groupName, excludePatterns) {
173
+ const items = this.$fs.readDirectory(dirPath);
174
+ for (const item of items) {
175
+ // Skip hidden files and excluded files/directories
176
+ if (item.startsWith(".") || RESOURCES_TO_IGNORE.indexOf(item) !== -1) {
177
+ continue;
178
+ }
179
+ const itemPath = path.join(dirPath, item);
180
+ const stats = this.$fs.getFsStats(itemPath);
181
+ const relativePath = path.relative(platformData.projectRoot, itemPath);
182
+ // Check if file/directory should be excluded based on patterns
183
+ if (excludePatterns &&
184
+ this.shouldExclude(relativePath, excludePatterns)) {
185
+ this.$logger.debug(`Excluding from resources: ${relativePath}`);
186
+ continue;
187
+ }
188
+ if (stats.isDirectory()) {
189
+ // Special handling for .xcassets, .bundle, and other resource bundles
190
+ if (item.endsWith(".xcassets") || item.endsWith(".bundle")) {
191
+ this.$logger.debug(`Adding resource bundle: ${relativePath}`);
192
+ this.addResourceFile(project, relativePath, { target: targetUuid }, groupName);
193
+ }
194
+ else {
195
+ // Recursively scan subdirectories
196
+ this.addResourcesFromDirectory(itemPath, targetUuid, project, platformData, groupName, excludePatterns);
197
+ }
198
+ }
199
+ else {
200
+ // Check if file is a resource by extension
201
+ const ext = path.extname(item).toLowerCase();
202
+ if (resourceExtensions.includes(ext)) {
203
+ this.$logger.debug(`Adding resource file: ${relativePath}`);
204
+ this.addResourceFile(project, relativePath, { target: targetUuid }, groupName);
205
+ }
206
+ }
207
+ }
208
+ }
35
209
  removeWatchApp({ pbxProjPath }) {
36
210
  const project = new this.$xcode.project(pbxProjPath);
37
211
  project.parseSync();
@@ -43,24 +217,497 @@ class IOSWatchAppService {
43
217
  const watchAppPath = path.join(projectData.getAppResourcesDirectoryPath(), platformData.normalizedPlatformName, constants_1.IOS_WATCHAPP_FOLDER);
44
218
  return this.$fs.exists(watchAppPath);
45
219
  }
46
- configureTarget(targetName, targetPath, identifier, configurationFileName, target, project) {
47
- const targetConfigurationJsonPath = path.join(targetPath, configurationFileName);
220
+ async configureTarget(targetName, targetPath, identifier, configPath, config, target, project, projectData, platformData, pbxProjPath, disableStubBinary = false) {
48
221
  const identifierParts = identifier.split(".");
49
222
  identifierParts.pop();
50
223
  const wkAppBundleIdentifier = identifierParts.join(".");
51
- this.$iOSNativeTargetService.setXcodeTargetBuildConfigurationProperties([
224
+ // Build configuration properties
225
+ const buildConfigProperties = [
52
226
  { name: "PRODUCT_BUNDLE_IDENTIFIER", value: identifier },
53
227
  { name: "SDKROOT", value: "watchos" },
54
228
  { name: "TARGETED_DEVICE_FAMILY", value: constants_1.IOSDeviceTargets.watchos },
55
229
  { name: "WATCHOS_DEPLOYMENT_TARGET", value: 5.2 },
56
230
  { name: "WK_APP_BUNDLE_IDENTIFIER", value: wkAppBundleIdentifier },
57
- ], targetName, project);
58
- this.$iOSNativeTargetService.setConfigurationsFromJsonFile(targetConfigurationJsonPath, target.uuid, targetName, project);
231
+ ];
232
+ if (disableStubBinary) {
233
+ buildConfigProperties.push({ name: "PRODUCT_BINARY_SOURCE_PATH", value: '""' }, { name: "PRODUCT_TYPE_HAS_STUB_BINARY", value: "NO" });
234
+ }
235
+ const resourcesGroup = targetName + "Resources";
236
+ project.addPbxGroup([], resourcesGroup, project.filepath, null, {
237
+ isMain: true,
238
+ target: target.uuid,
239
+ filesRelativeToProject: true,
240
+ });
241
+ const srcGroup = targetName + "Src";
242
+ project.addPbxGroup([], srcGroup, project.filepath, null, {
243
+ isMain: true,
244
+ target: target.uuid,
245
+ filesRelativeToProject: true,
246
+ });
247
+ let basedir;
248
+ if (config === null || config === void 0 ? void 0 : config.basedir) {
249
+ basedir = path.resolve(path.dirname(configPath), config.basedir);
250
+ if (!this.$fs.exists(basedir)) {
251
+ this.$logger.warn(`Basedir not found, using config directory: ${basedir}`);
252
+ basedir = path.dirname(configPath);
253
+ }
254
+ }
255
+ else {
256
+ basedir = path.dirname(configPath);
257
+ }
258
+ const resourcesExclude = (config === null || config === void 0 ? void 0 : config.resourcesExclude) || [];
259
+ const srcExclude = (config === null || config === void 0 ? void 0 : config.srcExclude) || [];
260
+ // Handle custom Info.plist path
261
+ if (config === null || config === void 0 ? void 0 : config.infoPlistPath) {
262
+ const infoPlistPath = path.resolve(basedir, config.infoPlistPath);
263
+ if (this.$fs.exists(infoPlistPath)) {
264
+ const relativeInfoPlistPath = path.relative(platformData.projectRoot, infoPlistPath);
265
+ buildConfigProperties.push({
266
+ name: "INFOPLIST_FILE",
267
+ value: `"${infoPlistPath}"`,
268
+ });
269
+ resourcesExclude.push(relativeInfoPlistPath);
270
+ }
271
+ else {
272
+ this.$logger.warn(`Custom Info.plist not found at: ${infoPlistPath}`);
273
+ }
274
+ }
275
+ // Handle custom xcprivacy file path
276
+ if (config === null || config === void 0 ? void 0 : config.xcprivacyPath) {
277
+ const xcprivacyPath = path.resolve(basedir, config.xcprivacyPath);
278
+ if (this.$fs.exists(xcprivacyPath)) {
279
+ const relativeXcprivacyPath = path.relative(platformData.projectRoot, xcprivacyPath);
280
+ this.addResourceFile(project, xcprivacyPath, { target: target.uuid }, targetName + "Resources");
281
+ resourcesExclude.push(relativeXcprivacyPath);
282
+ }
283
+ else {
284
+ this.$logger.warn(`Custom xcprivacy file not found at: ${xcprivacyPath}`);
285
+ }
286
+ }
287
+ this.$iOSNativeTargetService.setXcodeTargetBuildConfigurationProperties(buildConfigProperties, targetName, project);
288
+ this.$iOSNativeTargetService.setConfigurationsFromJsonFile(configPath, target.uuid, targetName, project);
59
289
  project.addToHeaderSearchPaths(targetPath, target.pbxNativeTarget.productName);
290
+ if ((config === null || config === void 0 ? void 0 : config.importSourcesFromMainFolder) !== false) {
291
+ await this.addSourceFilesFromDirectory(path.dirname(configPath), target.uuid, project, platformData, targetName + "Src", srcExclude);
292
+ }
293
+ if ((config === null || config === void 0 ? void 0 : config.importResourcesFromMainFolder) !== false) {
294
+ await this.addTargetResources(path.dirname(configPath), [target.uuid], project, platformData, resourcesGroup, resourcesExclude);
295
+ }
296
+ if (config) {
297
+ // Process additional configurations
298
+ await this.processWatchAppConfiguration(config, basedir, targetName, target, project, projectData, platformData, pbxProjPath, srcExclude, resourcesExclude);
299
+ }
300
+ }
301
+ applyWatchAppStubBinaryOverrides(targetName, pbxProjPath) {
302
+ const project = new this.$xcode.project(pbxProjPath);
303
+ project.parseSync();
304
+ this.$iOSNativeTargetService.setXcodeTargetBuildConfigurationProperties([
305
+ { name: "PRODUCT_BINARY_SOURCE_PATH", value: '""' },
306
+ { name: "PRODUCT_TYPE_HAS_STUB_BINARY", value: "NO" },
307
+ ], targetName, project);
308
+ this.$fs.writeFile(pbxProjPath, project.writeSync({ omitEmptyValues: true }));
309
+ }
310
+ async processWatchAppConfiguration(config, basedir, targetName, target, project, projectData, platformData, pbxProjPath, srcExclude, resourcesExclude) {
311
+ this.$logger.debug(`processWatchAppConfiguration ${JSON.stringify(config)}`);
312
+ // Handle custom resources
313
+ if (config.resources && Array.isArray(config.resources)) {
314
+ this.$logger.debug(`Processing ${config.resources.length} custom resource(s) for watch target: ${targetName}`);
315
+ for (const resourcePath of config.resources) {
316
+ this.addCustomResource(resourcePath, target.uuid, project, projectData, platformData, targetName + "Resources", resourcesExclude, basedir);
317
+ }
318
+ }
319
+ if (config.src && Array.isArray(config.src)) {
320
+ this.$logger.debug(`Processing ${config.src.length} custom source file(s) for watch target: ${targetName}`);
321
+ for (const srcPath of config.src) {
322
+ this.addCustomSourceFile(srcPath, target.uuid, project, projectData, platformData, srcExclude, targetName + "Src", basedir);
323
+ }
324
+ }
325
+ if (config.SPMPackages && Array.isArray(config.SPMPackages)) {
326
+ // to be able to add SPM the file needs to be saved
327
+ // but it means we need to reload it again after spm packages addition
328
+ this.$fs.writeFile(pbxProjPath, project.writeSync({ omitEmptyValues: true }));
329
+ await this.applySPMPackagesToTargets([targetName], platformData, basedir, config.SPMPackages);
330
+ project.parseSync();
331
+ }
332
+ if (config.modules && Array.isArray(config.modules)) {
333
+ this.$logger.debug(`Processing ${config.modules.length} module(s) for watch target: ${targetName}`);
334
+ for (const moduleDef of config.modules) {
335
+ await this.addModuleDependency(moduleDef, config, targetName, target, project, projectData, platformData, srcExclude, resourcesExclude, basedir);
336
+ }
337
+ }
338
+ }
339
+ addCustomResource(resourcePath, targetUuid, project, projectData, platformData, groupName, excludePatterns, basedir) {
340
+ const resolvedPath = this.resolvePathWithBasedir(resourcePath, basedir, projectData.projectDir);
341
+ if (!this.$fs.exists(resolvedPath)) {
342
+ this.$logger.warn(`Custom resource not found, skipping: ${resourcePath}`);
343
+ return;
344
+ }
345
+ const relativePath = path.relative(platformData.projectRoot, resolvedPath);
346
+ if (excludePatterns && this.shouldExclude(relativePath, excludePatterns)) {
347
+ this.$logger.debug(`Excluding from resources: ${relativePath}`);
348
+ return;
349
+ }
350
+ const stats = this.$fs.getFsStats(resolvedPath);
351
+ if (stats.isDirectory()) {
352
+ this.$logger.debug(`Recursively adding files from resource directory: ${resourcePath}`);
353
+ if (relativePath.endsWith(".xcassets") ||
354
+ relativePath.endsWith(".bundle")) {
355
+ this.$logger.debug(`Adding resource bundle: ${relativePath} for target:${targetUuid}`);
356
+ this.addResourceFile(project, relativePath, { target: targetUuid }, groupName);
357
+ }
358
+ else {
359
+ this.addAllResourcesRecursively(resolvedPath, targetUuid, project, platformData, groupName, excludePatterns);
360
+ }
361
+ }
362
+ else {
363
+ this.$logger.debug(`Adding custom resource file: ${relativePath}`);
364
+ this.addResourceFile(project, relativePath, { target: targetUuid }, groupName);
365
+ }
366
+ }
367
+ addCustomSourceFile(srcPath, targetUuid, project, projectData, platformData, excludePatterns, groupName, basedir) {
368
+ const resolvedPath = this.resolvePathWithBasedir(srcPath, basedir, projectData.projectDir);
369
+ if (!this.$fs.exists(resolvedPath)) {
370
+ this.$logger.warn(`Custom source file/folder not found, skipping: ${srcPath}`);
371
+ return;
372
+ }
373
+ const relativePath = path.relative(platformData.projectRoot, resolvedPath);
374
+ if (excludePatterns && this.shouldExclude(relativePath, excludePatterns)) {
375
+ this.$logger.debug(`Excluding from src: ${relativePath}`);
376
+ return;
377
+ }
378
+ const stats = this.$fs.getFsStats(resolvedPath);
379
+ if (stats.isDirectory()) {
380
+ this.$logger.debug(`Adding custom source directory: ${relativePath}`);
381
+ this.addAllSourceFilesFromDirectory(resolvedPath, targetUuid, project, platformData, groupName, excludePatterns);
382
+ }
383
+ else {
384
+ this.$logger.debug(`Adding custom source file: ${relativePath}`);
385
+ this.addSourceFile(project, relativePath, { target: targetUuid }, groupName);
386
+ }
387
+ }
388
+ resolvePathWithBasedir(relativePath, basedir, fallbackDir) {
389
+ return basedir
390
+ ? path.resolve(basedir, relativePath)
391
+ : path.resolve(fallbackDir, relativePath);
392
+ }
393
+ addAllSourceFilesFromDirectory(dirPath, targetUuid, project, platformData, groupName, excludePatterns) {
394
+ const items = this.getFolderFiles(dirPath, platformData.projectRoot, excludePatterns);
395
+ for (const item of items) {
396
+ const relativePath = path.relative(platformData.projectRoot, item);
397
+ // Check if file is a source file by extension
398
+ const ext = path.extname(item).toLowerCase();
399
+ if (sourceExtensions.includes(ext)) {
400
+ this.$logger.debug(`Adding source file: ${relativePath}`);
401
+ this.addSourceFile(project, relativePath, { target: targetUuid }, groupName);
402
+ }
403
+ }
404
+ }
405
+ addAllResourcesRecursively(dirPath, targetUuid, project, platformData, groupName, excludePatterns) {
406
+ const items = this.$fs.readDirectory(dirPath);
407
+ for (const item of items) {
408
+ if (item.startsWith(".")) {
409
+ continue;
410
+ }
411
+ const itemPath = path.join(dirPath, item);
412
+ const stats = this.$fs.getFsStats(itemPath);
413
+ const relativePath = path.relative(platformData.projectRoot, itemPath);
414
+ if (excludePatterns &&
415
+ this.shouldExclude(relativePath, excludePatterns)) {
416
+ this.$logger.debug(`Excluding from resources: ${relativePath}`);
417
+ return;
418
+ }
419
+ if (stats.isDirectory()) {
420
+ // Special handling for .xcassets, .bundle - add as bundles, not recursively
421
+ if (item.endsWith(".xcassets") || item.endsWith(".bundle")) {
422
+ this.$logger.debug(`Adding resource bundle: ${relativePath} for target:${targetUuid}`);
423
+ this.addResourceFile(project, relativePath, { target: targetUuid }, groupName);
424
+ }
425
+ else {
426
+ this.addAllResourcesRecursively(itemPath, targetUuid, project, platformData, groupName, excludePatterns);
427
+ }
428
+ }
429
+ else {
430
+ this.$logger.debug(`Adding resource file: ${relativePath}`);
431
+ this.addResourceFile(project, relativePath, { target: targetUuid }, groupName);
432
+ }
433
+ }
434
+ }
435
+ async addModuleDependency(moduleDef, config, targetName, target, project, projectData, platformData, srcExclude, resourcesExclude, basedir) {
436
+ const modulePath = moduleDef.path
437
+ ? this.resolvePathWithBasedir(moduleDef.path, basedir, projectData.projectDir)
438
+ : null;
439
+ if (!modulePath || !this.$fs.exists(modulePath)) {
440
+ this.$logger.warn(`Module path not found, skipping module: ${modulePath}`);
441
+ return;
442
+ }
443
+ const relativePath = path.relative(platformData.projectRoot, modulePath);
444
+ const stats = this.$fs.getFsStats(modulePath);
445
+ const isFramework = modulePath.endsWith(".framework") || modulePath.endsWith(".xcframework");
446
+ const isFolder = stats.isDirectory() && !isFramework;
447
+ this.$logger.debug(`Adding module dependency: ${JSON.stringify(moduleDef)} to ${targetName}, basedir:${basedir}, isFramework:${isFramework} isFolder:${isFolder}`);
448
+ if (isFramework) {
449
+ // Handle compiled frameworks (xcframework, framework)
450
+ this.addCompiledFramework(moduleDef, relativePath, targetName, target, project);
451
+ }
452
+ else if (isFolder) {
453
+ // Handle folder-based modules
454
+ await this.addFolderModule(moduleDef, modulePath, relativePath, targetName, target, config, project, basedir, srcExclude, resourcesExclude, projectData, platformData);
455
+ }
456
+ else {
457
+ this.$logger.warn(`Unknown module type for: ${modulePath}`);
458
+ }
459
+ if (moduleDef.headerSearchPaths &&
460
+ Array.isArray(moduleDef.headerSearchPaths)) {
461
+ for (const headerPath of moduleDef.headerSearchPaths) {
462
+ const resolvedPath = this.resolvePathWithBasedir(headerPath, basedir, projectData.projectDir);
463
+ const relPath = path.relative(platformData.projectRoot, resolvedPath);
464
+ project.addToHeaderSearchPaths(relPath, targetName);
465
+ this.$logger.debug(`Added header search path: ${relPath}`);
466
+ }
467
+ }
468
+ if (moduleDef.linkerFlags && Array.isArray(moduleDef.linkerFlags)) {
469
+ this.addLinkerFlags(moduleDef.linkerFlags, targetName, project);
470
+ }
471
+ }
472
+ addCompiledFramework(moduleDef, relativePath, targetName, target, project) {
473
+ const moduleName = moduleDef.name;
474
+ project.addFramework(relativePath, {
475
+ target: target.uuid,
476
+ customFramework: true,
477
+ embed: moduleDef.embed !== false, // Default to true
478
+ });
479
+ const frameworkDir = path.dirname(relativePath);
480
+ project.addBuildProperty("FRAMEWORK_SEARCH_PATHS", `"$(inherited)" "${frameworkDir}"`, null, targetName);
481
+ this.$logger.debug(`Added compiled framework ${moduleName} at ${relativePath}`);
482
+ }
483
+ getFolderFiles(dirPath, rootPath, excludePatterns) {
484
+ const result = [];
485
+ const files = this.$fs
486
+ .readDirectory(dirPath)
487
+ .filter((fileName) => !fileName.startsWith("."));
488
+ for (const item of files) {
489
+ const itemPath = path.join(dirPath, item);
490
+ const stats = this.$fs.getFsStats(itemPath);
491
+ const relativePath = path.relative(rootPath, itemPath);
492
+ if (excludePatterns &&
493
+ this.shouldExclude(relativePath, excludePatterns)) {
494
+ this.$logger.debug(`Excluding from src: ${relativePath}`);
495
+ continue;
496
+ }
497
+ if (stats.isDirectory()) {
498
+ result.push(...this.getFolderFiles(itemPath, rootPath, excludePatterns));
499
+ }
500
+ else {
501
+ result.push(itemPath);
502
+ }
503
+ }
504
+ return result;
505
+ }
506
+ addBuildPhaseIfNotExisting(project, buildPhaseType, comment, target) {
507
+ let buildPhase = project.buildPhaseObject(buildPhaseType, comment, target);
508
+ if (!buildPhase) {
509
+ project.addBuildPhase([], buildPhaseType, comment, target);
510
+ }
511
+ }
512
+ async addFolderModule(moduleDef, modulePath, relativePath, targetName, target, config, project, basedir, srcExclude, resourcesExclude, projectData, platformData) {
513
+ var _a;
514
+ const moduleName = moduleDef.name || path.basename(modulePath);
515
+ const targetRelativePath = path.relative(platformData.projectRoot, modulePath);
516
+ const moduleTarget = project.addTarget(moduleName, (_a = moduleDef.targetType) !== null && _a !== void 0 ? _a : "framework", targetRelativePath, target.uuid);
517
+ this.$logger.debug(`Adding folder module ${moduleName} with path ${modulePath} with target uuid:${moduleTarget.uuid}`);
518
+ const { path: filePath, name, dependencies, frameworks, buildConfigurationProperties, src, resources, SPMPackages, ...otherProps } = moduleDef;
519
+ project.addFramework(moduleName + ".framework", {
520
+ target: target.uuid,
521
+ basename: moduleName,
522
+ path: moduleName + ".framework",
523
+ customFramework: true,
524
+ explicitFileType: "wrapper.framework",
525
+ ...otherProps,
526
+ });
527
+ // Add build phases
528
+ project.addBuildPhase([], "PBXSourcesBuildPhase", "Sources", moduleTarget.uuid);
529
+ project.addBuildPhase([], "PBXResourcesBuildPhase", "Resources", moduleTarget.uuid);
530
+ project.addBuildPhase([], "PBXFrameworksBuildPhase", "Frameworks", moduleTarget.uuid);
531
+ project.addBuildPhase([], "PBXCopyFilesBuildPhase", "Embed Frameworks", moduleTarget.uuid, "frameworks");
532
+ const files = this.getFolderFiles(modulePath, platformData.projectRoot, srcExclude);
533
+ this.$logger.debug(`module ${moduleName} has ${files.length} files`);
534
+ if (files.length > 0) {
535
+ project.addPbxGroup(files, moduleName, modulePath, null, {
536
+ isMain: true,
537
+ target: moduleTarget.uuid,
538
+ filesRelativeToProject: true,
539
+ });
540
+ }
541
+ if (moduleDef.frameworks && Array.isArray(moduleDef.frameworks)) {
542
+ this.$logger.debug(`Adding ${moduleDef.frameworks.length} framework(s) for module ${JSON.stringify(moduleDef)}`);
543
+ for (const framework of moduleDef.frameworks) {
544
+ this.$logger.debug(`Adding framework ${JSON.stringify(framework)} for module ${JSON.stringify(moduleDef)}`);
545
+ if (typeof framework === "string") {
546
+ project.addFramework(framework, { target: moduleTarget.uuid });
547
+ }
548
+ else {
549
+ project.addFramework(framework.path, {
550
+ target: moduleTarget.uuid,
551
+ ...framework,
552
+ });
553
+ }
554
+ this.$logger.debug(`Added framework dependency: ${framework}`);
555
+ }
556
+ }
557
+ if (moduleDef.src && Array.isArray(moduleDef.src)) {
558
+ this.$logger.debug(`Processing ${config.src.length} custom source file(s) for target: ${moduleName}`);
559
+ for (const srcPath of moduleDef.src) {
560
+ this.addCustomSourceFile(srcPath, moduleTarget.uuid, project, projectData, platformData, srcExclude, moduleName + "Src", basedir);
561
+ }
562
+ }
563
+ if (moduleDef.resources && Array.isArray(moduleDef.resources)) {
564
+ this.$logger.debug(`Processing ${moduleDef.resources.length} custom resource(s) for target: ${moduleName}/${moduleTarget.uuid}`);
565
+ for (const resourcePath of moduleDef.resources) {
566
+ this.addCustomResource(resourcePath, moduleTarget.uuid, project, projectData, platformData, targetName + "Resources", resourcesExclude, basedir);
567
+ }
568
+ }
569
+ if (moduleDef.dependencies && Array.isArray(moduleDef.dependencies)) {
570
+ const currentTargets = project.pbxNativeTargetSection();
571
+ const currentTargetsArray = Object.keys(currentTargets)
572
+ .map((k) => currentTargets[k]["name"]
573
+ ? { uuid: k, name: currentTargets[k]["name"] }
574
+ : null)
575
+ .filter((t) => !!t);
576
+ const targets = moduleDef.dependencies
577
+ .map((dependency) => currentTargetsArray.find((t) => t.name === `\"${dependency}\"`))
578
+ .filter((s) => !!s);
579
+ if (targets.length) {
580
+ this.$logger.debug(`Adding target dependencies ${moduleDef.dependencies} with uuids:${targets.map((t) => t.uuid)} for module ${moduleDef.name}`);
581
+ project.addTargetDependency(moduleTarget.uuid, targets.map((t) => t.uuid));
582
+ }
583
+ }
584
+ if (moduleDef.SPMPackages && Array.isArray(moduleDef.SPMPackages)) {
585
+ // to be able to add SPM the file needs to be saved
586
+ // but it means we need to reload it again after spm packages addition
587
+ this.$fs.writeFile(project.filepath, project.writeSync({ omitEmptyValues: true }));
588
+ await this.applySPMPackagesToTargets([moduleName], platformData, basedir, moduleDef.SPMPackages.map((t) => {
589
+ if (typeof t === "string") {
590
+ return config.SPMPackages.find((s) => s.name === t);
591
+ }
592
+ return t;
593
+ }));
594
+ project.parseSync();
595
+ }
596
+ if (moduleDef.buildConfigurationProperties ||
597
+ config.sharedModulesBuildConfigurationProperties) {
598
+ const configurationProperties = {
599
+ ...(config.sharedModulesBuildConfigurationProperties || {}),
600
+ ...(moduleDef.buildConfigurationProperties || {}),
601
+ };
602
+ this.$iOSNativeTargetService.setXcodeTargetBuildConfigurationProperties(Object.keys(configurationProperties).map((k) => ({
603
+ name: k,
604
+ value: configurationProperties[k],
605
+ })), moduleName, project);
606
+ }
607
+ this.$logger.debug(`Added folder-based module ${moduleName} at ${relativePath}`);
608
+ }
609
+ /**
610
+ * Add linker flags to a target's build settings
611
+ */
612
+ addLinkerFlags(flags, targetName, project) {
613
+ for (const flag of flags) {
614
+ const currentFlags = this.getBuildProperty("OTHER_LDFLAGS", targetName, project);
615
+ const flagsArray = currentFlags
616
+ ? Array.isArray(currentFlags)
617
+ ? currentFlags
618
+ : [currentFlags]
619
+ : ['"$(inherited)"'];
620
+ if (!flagsArray.includes(flag)) {
621
+ flagsArray.push(flag);
622
+ }
623
+ project.addBuildProperty("OTHER_LDFLAGS", flagsArray, null, targetName);
624
+ this.$logger.debug(`Added linker flag: ${flag}`);
625
+ }
626
+ }
627
+ /**
628
+ * Get build property value for a specific target
629
+ */
630
+ getBuildProperty(propertyName, targetName, project) {
631
+ // Access the project hash to read build settings
632
+ const projectHash = project.hash;
633
+ if (!projectHash) {
634
+ return null;
635
+ }
636
+ const configurations = projectHash.project.objects.XCBuildConfiguration;
637
+ if (!configurations) {
638
+ return null;
639
+ }
640
+ for (const key in configurations) {
641
+ const config = configurations[key];
642
+ if (config &&
643
+ config.buildSettings &&
644
+ (config.buildSettings.PRODUCT_NAME === targetName ||
645
+ config.buildSettings.PRODUCT_NAME === `"${targetName}"`)) {
646
+ return config.buildSettings[propertyName];
647
+ }
648
+ }
649
+ return null;
650
+ }
651
+ /**
652
+ * Check if a path should be excluded based on glob patterns
653
+ */
654
+ shouldExclude(filePath, excludePatterns) {
655
+ for (const pattern of excludePatterns) {
656
+ const matcher = new minimatch_1.Minimatch(pattern, { dot: true });
657
+ if (matcher.match(filePath)) {
658
+ return true;
659
+ }
660
+ }
661
+ return false;
662
+ }
663
+ /**
664
+ * Apply SPM packages to watch app targets
665
+ */
666
+ async applySPMPackagesToTargets(targetNames, platformData, basedir, watchSPMPackages) {
667
+ try {
668
+ this.$logger.debug(`applySPMPackagesToTargets ${JSON.stringify(watchSPMPackages)}`);
669
+ if (watchSPMPackages.length === 0) {
670
+ return;
671
+ }
672
+ this.$logger.debug(`Applying ${watchSPMPackages.length} SPM package(s) to targets:${targetNames}`);
673
+ const project = new trapezedev_project_1.MobileProject(platformData.projectRoot, {
674
+ ios: {
675
+ path: ".",
676
+ },
677
+ enableAndroid: false,
678
+ });
679
+ await project.load();
680
+ if (!project.ios) {
681
+ this.$logger.debug("No iOS project found via trapeze");
682
+ return;
683
+ }
684
+ // Add SPM packages to each watch target
685
+ for (const pkg of watchSPMPackages) {
686
+ if ("path" in pkg) {
687
+ pkg.path = path.resolve(basedir, pkg.path);
688
+ }
689
+ this.$logger.debug(`Adding SPM package ${JSON.stringify(pkg)} to targets ${targetNames}`);
690
+ for (const targetName of targetNames) {
691
+ project.ios.addSPMPackage(targetName, pkg);
692
+ }
693
+ }
694
+ await project.commit();
695
+ this.$logger.debug(`Successfully applied SPM packages to targets ${targetNames}`);
696
+ }
697
+ catch (err) {
698
+ this.$logger.debug(`Error applying SPM packages to targets ${targetNames} "`, err);
699
+ }
700
+ }
701
+ /**
702
+ * Get SPM packages configured for watch app targets
703
+ */
704
+ getWatchSPMPackages(platformData) {
705
+ const $projectConfigService = yok_1.injector.resolve("projectConfigService");
706
+ // Check for watch-specific SPM packages in config
707
+ const watchPackages = $projectConfigService.getValue(`${platformData.platformNameLowerCase}.watchApp.SPMPackages`, []);
708
+ return watchPackages;
60
709
  }
61
710
  }
62
711
  exports.IOSWatchAppService = IOSWatchAppService;
63
- IOSWatchAppService.WATCH_APP_IDENTIFIER = "watchkitapp";
64
- IOSWatchAppService.WACTCH_EXTENSION_IDENTIFIER = "watchkitextension";
65
712
  yok_1.injector.register("iOSWatchAppService", IOSWatchAppService);
66
713
  //# sourceMappingURL=ios-watch-app-service.js.map
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.0.6",
4
+ "version": "9.0.7-dev.0",
5
5
  "author": "NativeScript <oss@nativescript.org>",
6
6
  "description": "Command-line interface for building NativeScript projects",
7
7
  "bin": {