nativescript 9.0.6-rc.2 → 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/.d.ts +0 -2
- package/lib/bootstrap.js +0 -1
- package/lib/constants.js +1 -0
- package/lib/controllers/run-controller.js +1 -3
- package/lib/definitions/nativescript-dev-xcode.d.ts +25 -1
- package/lib/definitions/project.d.ts +35 -4
- package/lib/helpers/livesync-command-helper.js +1 -13
- package/lib/services/bundler/bundler-compiler-service.js +2 -6
- package/lib/services/ios-project-service.js +0 -1
- package/lib/services/ios-watch-app-service.js +663 -16
- package/package.json +1 -1
- package/lib/definitions/devtools-host-service.d.ts +0 -44
- package/lib/services/devtools-host-service.js +0 -178
package/lib/.d.ts
CHANGED
|
@@ -256,7 +256,6 @@
|
|
|
256
256
|
/// <reference path="definitions/data.d.ts" />
|
|
257
257
|
/// <reference path="definitions/debug.d.ts" />
|
|
258
258
|
/// <reference path="definitions/deploy.d.ts" />
|
|
259
|
-
/// <reference path="definitions/devtools-host-service.d.ts" />
|
|
260
259
|
/// <reference path="definitions/file-log-service.d.ts" />
|
|
261
260
|
/// <reference path="definitions/files-hash-service.d.ts" />
|
|
262
261
|
/// <reference path="definitions/gradle.d.ts" />
|
|
@@ -357,7 +356,6 @@
|
|
|
357
356
|
/// <reference path="services/debug-data-service.ts" />
|
|
358
357
|
/// <reference path="services/debug-service-base.ts" />
|
|
359
358
|
/// <reference path="services/device/device-install-app-service.ts" />
|
|
360
|
-
/// <reference path="services/devtools-host-service.ts" />
|
|
361
359
|
/// <reference path="services/doctor-service.ts" />
|
|
362
360
|
/// <reference path="services/extensibility-service.ts" />
|
|
363
361
|
/// <reference path="services/files-hash-service.ts" />
|
package/lib/bootstrap.js
CHANGED
|
@@ -191,7 +191,6 @@ yok_1.injector.require("testInitializationService", "./services/test-initializat
|
|
|
191
191
|
yok_1.injector.require("networkConnectivityValidator", "./helpers/network-connectivity-validator");
|
|
192
192
|
yok_1.injector.requirePublic("cleanupService", "./services/cleanup-service");
|
|
193
193
|
yok_1.injector.require("bundlerCompilerService", "./services/bundler/bundler-compiler-service");
|
|
194
|
-
yok_1.injector.require("devtoolsHostService", "./services/devtools-host-service");
|
|
195
194
|
yok_1.injector.require("applePortalSessionService", "./services/apple-portal/apple-portal-session-service");
|
|
196
195
|
yok_1.injector.require("applePortalCookieService", "./services/apple-portal/apple-portal-cookie-service");
|
|
197
196
|
yok_1.injector.require("applePortalApplicationService", "./services/apple-portal/apple-portal-application-service");
|
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 = {
|
|
@@ -15,7 +15,7 @@ const util = require("util");
|
|
|
15
15
|
const _ = require("lodash");
|
|
16
16
|
const yok_1 = require("../common/yok");
|
|
17
17
|
class RunController extends events_1.EventEmitter {
|
|
18
|
-
constructor($analyticsService, $buildController, $debugController, $deviceInstallAppService, $devicesService, $errors, $injector, $hmrStatusService, $hooksService, $liveSyncServiceResolver, $liveSyncProcessDataService, $logger, $mobileHelper, $platformsDataService, $pluginsService, $prepareController, $prepareDataService, $prepareNativePlatformService, $projectChangesService, $projectDataService
|
|
18
|
+
constructor($analyticsService, $buildController, $debugController, $deviceInstallAppService, $devicesService, $errors, $injector, $hmrStatusService, $hooksService, $liveSyncServiceResolver, $liveSyncProcessDataService, $logger, $mobileHelper, $platformsDataService, $pluginsService, $prepareController, $prepareDataService, $prepareNativePlatformService, $projectChangesService, $projectDataService) {
|
|
19
19
|
super();
|
|
20
20
|
this.$analyticsService = $analyticsService;
|
|
21
21
|
this.$buildController = $buildController;
|
|
@@ -37,7 +37,6 @@ class RunController extends events_1.EventEmitter {
|
|
|
37
37
|
this.$prepareNativePlatformService = $prepareNativePlatformService;
|
|
38
38
|
this.$projectChangesService = $projectChangesService;
|
|
39
39
|
this.$projectDataService = $projectDataService;
|
|
40
|
-
this.$devtoolsHostService = $devtoolsHostService;
|
|
41
40
|
this.prepareReadyEventHandler = null;
|
|
42
41
|
}
|
|
43
42
|
async run(runData) {
|
|
@@ -110,7 +109,6 @@ class RunController extends events_1.EventEmitter {
|
|
|
110
109
|
await liveSyncProcessInfo.actionsChain;
|
|
111
110
|
}
|
|
112
111
|
liveSyncProcessInfo.deviceDescriptors = [];
|
|
113
|
-
await this.$devtoolsHostService.stopAll();
|
|
114
112
|
if (this.prepareReadyEventHandler) {
|
|
115
113
|
this.$prepareController.removeListener(constants_2.PREPARE_READY_EVENT_NAME, this.prepareReadyEventHandler);
|
|
116
114
|
this.prepareReadyEventHandler = null;
|
|
@@ -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
|
|
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;
|
|
@@ -5,7 +5,7 @@ const _ = require("lodash");
|
|
|
5
5
|
const yok_1 = require("../common/yok");
|
|
6
6
|
const constants_1 = require("../constants");
|
|
7
7
|
class LiveSyncCommandHelper {
|
|
8
|
-
constructor($androidBundleValidatorHelper, $buildDataService, $projectData, $options, $deployController, $iosDeviceOperations, $mobileHelper, $devicesService, $injector, $buildController, $analyticsService, $errors, $iOSSimulatorLogProvider, $cleanupService, $runController
|
|
8
|
+
constructor($androidBundleValidatorHelper, $buildDataService, $projectData, $options, $deployController, $iosDeviceOperations, $mobileHelper, $devicesService, $injector, $buildController, $analyticsService, $errors, $iOSSimulatorLogProvider, $cleanupService, $runController) {
|
|
9
9
|
this.$androidBundleValidatorHelper = $androidBundleValidatorHelper;
|
|
10
10
|
this.$buildDataService = $buildDataService;
|
|
11
11
|
this.$projectData = $projectData;
|
|
@@ -21,7 +21,6 @@ class LiveSyncCommandHelper {
|
|
|
21
21
|
this.$iOSSimulatorLogProvider = $iOSSimulatorLogProvider;
|
|
22
22
|
this.$cleanupService = $cleanupService;
|
|
23
23
|
this.$runController = $runController;
|
|
24
|
-
this.$devtoolsHostService = $devtoolsHostService;
|
|
25
24
|
}
|
|
26
25
|
get $platformsDataService() {
|
|
27
26
|
return this.$injector.resolve("platformsDataService");
|
|
@@ -126,7 +125,6 @@ class LiveSyncCommandHelper {
|
|
|
126
125
|
return;
|
|
127
126
|
}
|
|
128
127
|
else {
|
|
129
|
-
await this.startDevtoolsHostIfDebugging(deviceDescriptors);
|
|
130
128
|
await this.$runController.run({
|
|
131
129
|
liveSyncInfo,
|
|
132
130
|
deviceDescriptors,
|
|
@@ -152,16 +150,6 @@ class LiveSyncCommandHelper {
|
|
|
152
150
|
}
|
|
153
151
|
return result;
|
|
154
152
|
}
|
|
155
|
-
async startDevtoolsHostIfDebugging(deviceDescriptors) {
|
|
156
|
-
const platforms = _.uniq(deviceDescriptors
|
|
157
|
-
.filter((d) => { var _a; return d.debuggingEnabled && ((_a = d.buildData) === null || _a === void 0 ? void 0 : _a.platform); })
|
|
158
|
-
.map((d) => d.buildData.platform.toLowerCase()));
|
|
159
|
-
for (const platform of platforms) {
|
|
160
|
-
// DevtoolsHostService swallows port/bind failures and returns null;
|
|
161
|
-
// a missing source-map origin degrades DevTools gracefully.
|
|
162
|
-
await this.$devtoolsHostService.start(this.$projectData, platform);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
153
|
async executeLiveSyncOperationCore(devices, platform, additionalOptions) {
|
|
166
154
|
if (!devices || !devices.length) {
|
|
167
155
|
if (platform) {
|
|
@@ -19,7 +19,7 @@ const package_path_helper_1 = require("../../helpers/package-path-helper");
|
|
|
19
19
|
const debugLog = false;
|
|
20
20
|
class BundlerCompilerService extends events_1.EventEmitter {
|
|
21
21
|
constructor($options, $errors, $childProcess, $fs, $hooksService, $hostInfo, $logger, $mobileHelper, $cleanupService, $packageManager, $packageInstallationManager, // private $sharedEventBus: ISharedEventBus
|
|
22
|
-
$projectConfigService
|
|
22
|
+
$projectConfigService) {
|
|
23
23
|
super();
|
|
24
24
|
this.$options = $options;
|
|
25
25
|
this.$errors = $errors;
|
|
@@ -33,7 +33,6 @@ class BundlerCompilerService extends events_1.EventEmitter {
|
|
|
33
33
|
this.$packageManager = $packageManager;
|
|
34
34
|
this.$packageInstallationManager = $packageInstallationManager;
|
|
35
35
|
this.$projectConfigService = $projectConfigService;
|
|
36
|
-
this.$devtoolsHostService = $devtoolsHostService;
|
|
37
36
|
this.bundlerProcesses = {};
|
|
38
37
|
this.expectedHashes = {};
|
|
39
38
|
}
|
|
@@ -97,6 +96,7 @@ class BundlerCompilerService extends events_1.EventEmitter {
|
|
|
97
96
|
console.log("Vite first build completed, resolving compileWithWatch");
|
|
98
97
|
}
|
|
99
98
|
resolve(childProcess);
|
|
99
|
+
return;
|
|
100
100
|
}
|
|
101
101
|
// Transform Vite message to match webpack format
|
|
102
102
|
const files = message.emittedFiles.map((file) => path.join(platformData.appDestinationDirectoryPath, this.$options.hostProjectModuleName, file));
|
|
@@ -363,10 +363,6 @@ class BundlerCompilerService extends events_1.EventEmitter {
|
|
|
363
363
|
if (prepareData.uniqueBundle > 0) {
|
|
364
364
|
envData.uniqueBundle = prepareData.uniqueBundle;
|
|
365
365
|
}
|
|
366
|
-
const devtoolsOrigin = this.$devtoolsHostService.getOrigin(platform);
|
|
367
|
-
if (devtoolsOrigin) {
|
|
368
|
-
envData.devtoolsHost = devtoolsOrigin;
|
|
369
|
-
}
|
|
370
366
|
return envData;
|
|
371
367
|
}
|
|
372
368
|
async buildEnvCommandLineParams(envData, platformData, projectData, prepareData) {
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
26
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
]
|
|
58
|
-
|
|
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,44 +0,0 @@
|
|
|
1
|
-
import { IProjectData } from "./project";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Origin of a running loopback HTTP server for a single platform,
|
|
5
|
-
* e.g. "http://127.0.0.1:41500".
|
|
6
|
-
*/
|
|
7
|
-
export interface IDevtoolsHostOrigin {
|
|
8
|
-
platform: string;
|
|
9
|
-
origin: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Loopback HTTP server (per platform) that serves the webpack output
|
|
14
|
-
* directory with CORS so Chrome DevTools (served from
|
|
15
|
-
* https://chrome-devtools-frontend.appspot.com) can fetch .map / .js
|
|
16
|
-
* files while a debug session is active.
|
|
17
|
-
*
|
|
18
|
-
* Bound to 127.0.0.1 only; never exposed to the network.
|
|
19
|
-
*/
|
|
20
|
-
export interface IDevtoolsHostService {
|
|
21
|
-
/**
|
|
22
|
-
* Start (or return existing) HTTP server for a platform. Idempotent
|
|
23
|
-
* per platform — subsequent calls return the same origin.
|
|
24
|
-
*/
|
|
25
|
-
start(
|
|
26
|
-
projectData: IProjectData,
|
|
27
|
-
platform: string,
|
|
28
|
-
): Promise<IDevtoolsHostOrigin | null>;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Stop the server for a single platform. Quiet no-op if nothing running.
|
|
32
|
-
*/
|
|
33
|
-
stop(platform: string): Promise<void>;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Stop all running servers.
|
|
37
|
-
*/
|
|
38
|
-
stopAll(): Promise<void>;
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Current origin for a platform, or null if not running.
|
|
42
|
-
*/
|
|
43
|
-
getOrigin(platform: string): string | null;
|
|
44
|
-
}
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DevtoolsHostService = void 0;
|
|
4
|
-
const http = require("http");
|
|
5
|
-
const path = require("path");
|
|
6
|
-
const fs = require("fs");
|
|
7
|
-
const constants_1 = require("../constants");
|
|
8
|
-
const yok_1 = require("../common/yok");
|
|
9
|
-
const LOOPBACK_HOST = "127.0.0.1";
|
|
10
|
-
const PORT_RANGE_START = 41500;
|
|
11
|
-
const PORT_RANGE_END = 41999;
|
|
12
|
-
// Allowed Chrome DevTools origins. Bundled DevTools (devtools://devtools)
|
|
13
|
-
// is the default flow opened by chrome://inspect; the appspot frontend is
|
|
14
|
-
// used when the CLI prints a hosted URL. Any other origin (including
|
|
15
|
-
// custom NS DevTools forks) gets a permissive ACAO since loopback bind
|
|
16
|
-
// is the real security boundary anyway.
|
|
17
|
-
const KNOWN_DEVTOOLS_ORIGINS = new Set([
|
|
18
|
-
"devtools://devtools",
|
|
19
|
-
"https://chrome-devtools-frontend.appspot.com",
|
|
20
|
-
]);
|
|
21
|
-
const CONTENT_TYPES = {
|
|
22
|
-
".map": "application/json; charset=utf-8",
|
|
23
|
-
".json": "application/json; charset=utf-8",
|
|
24
|
-
".js": "application/javascript; charset=utf-8",
|
|
25
|
-
".mjs": "application/javascript; charset=utf-8",
|
|
26
|
-
".css": "text/css; charset=utf-8",
|
|
27
|
-
};
|
|
28
|
-
class DevtoolsHostService {
|
|
29
|
-
constructor($net, $logger, $platformsDataService) {
|
|
30
|
-
this.$net = $net;
|
|
31
|
-
this.$logger = $logger;
|
|
32
|
-
this.$platformsDataService = $platformsDataService;
|
|
33
|
-
this.servers = new Map();
|
|
34
|
-
}
|
|
35
|
-
async start(projectData, platform) {
|
|
36
|
-
var _a, _b;
|
|
37
|
-
const key = platform.toLowerCase();
|
|
38
|
-
const existing = this.servers.get(key);
|
|
39
|
-
if (existing) {
|
|
40
|
-
return { platform: key, origin: this.formatOrigin(existing.port) };
|
|
41
|
-
}
|
|
42
|
-
const platformData = this.$platformsDataService.getPlatformData(platform, projectData);
|
|
43
|
-
// Webpack writes to <appDestinationDirectoryPath>/app on both iOS
|
|
44
|
-
// (platforms/ios/<appName>/app) and Android
|
|
45
|
-
// (platforms/android/app/src/main/assets/app). Match that exactly so
|
|
46
|
-
// requests for /bundle.mjs.map resolve to the actual emitted file.
|
|
47
|
-
const rootDir = (platformData === null || platformData === void 0 ? void 0 : platformData.appDestinationDirectoryPath)
|
|
48
|
-
? path.join(platformData.appDestinationDirectoryPath, constants_1.APP_FOLDER_NAME)
|
|
49
|
-
: null;
|
|
50
|
-
if (!rootDir) {
|
|
51
|
-
this.$logger.warn(`DevTools host: unable to resolve output directory for ${platform}.`);
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
let port;
|
|
55
|
-
try {
|
|
56
|
-
port = await this.$net.getAvailablePortInRange(PORT_RANGE_START, PORT_RANGE_END);
|
|
57
|
-
}
|
|
58
|
-
catch (err) {
|
|
59
|
-
this.$logger.warn(`DevTools host: no free port in ${PORT_RANGE_START}-${PORT_RANGE_END}. Source maps will not load in Chrome DevTools. (${(err === null || err === void 0 ? void 0 : err.message) || err})`);
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
const server = http.createServer((req, res) => this.handleRequest(req, res, rootDir));
|
|
63
|
-
try {
|
|
64
|
-
await new Promise((resolve, reject) => {
|
|
65
|
-
const onError = (err) => reject(err);
|
|
66
|
-
server.once("error", onError);
|
|
67
|
-
server.listen(port, LOOPBACK_HOST, () => {
|
|
68
|
-
server.off("error", onError);
|
|
69
|
-
resolve();
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
catch (err) {
|
|
74
|
-
this.$logger.warn(`DevTools host: failed to bind ${LOOPBACK_HOST}:${port} (${(err === null || err === void 0 ? void 0 : err.message) || err}).`);
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
// `.unref()` so a lingering server doesn't keep the CLI process alive.
|
|
78
|
-
server.unref();
|
|
79
|
-
const actualPort = (_b = (_a = server.address()) === null || _a === void 0 ? void 0 : _a.port) !== null && _b !== void 0 ? _b : port;
|
|
80
|
-
const entry = { server, port: actualPort, rootDir };
|
|
81
|
-
this.servers.set(key, entry);
|
|
82
|
-
const origin = this.formatOrigin(actualPort);
|
|
83
|
-
this.$logger.info(`DevTools host (${platform}) serving ${rootDir} at ${origin}`);
|
|
84
|
-
return { platform: key, origin };
|
|
85
|
-
}
|
|
86
|
-
async stop(platform) {
|
|
87
|
-
const key = platform.toLowerCase();
|
|
88
|
-
const entry = this.servers.get(key);
|
|
89
|
-
if (!entry) {
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
this.servers.delete(key);
|
|
93
|
-
await new Promise((resolve) => {
|
|
94
|
-
entry.server.close(() => resolve());
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
async stopAll() {
|
|
98
|
-
const platforms = Array.from(this.servers.keys());
|
|
99
|
-
await Promise.all(platforms.map((p) => this.stop(p)));
|
|
100
|
-
}
|
|
101
|
-
getOrigin(platform) {
|
|
102
|
-
const entry = this.servers.get(platform.toLowerCase());
|
|
103
|
-
return entry ? this.formatOrigin(entry.port) : null;
|
|
104
|
-
}
|
|
105
|
-
formatOrigin(port) {
|
|
106
|
-
return `http://${LOOPBACK_HOST}:${port}`;
|
|
107
|
-
}
|
|
108
|
-
handleRequest(req, res, rootDir) {
|
|
109
|
-
const requestOrigin = req.headers.origin;
|
|
110
|
-
const allowOrigin = requestOrigin && KNOWN_DEVTOOLS_ORIGINS.has(requestOrigin)
|
|
111
|
-
? requestOrigin
|
|
112
|
-
: "*";
|
|
113
|
-
res.setHeader("Access-Control-Allow-Origin", allowOrigin);
|
|
114
|
-
res.setHeader("Vary", "Origin");
|
|
115
|
-
res.setHeader("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS");
|
|
116
|
-
res.setHeader("Access-Control-Allow-Headers", "*");
|
|
117
|
-
res.setHeader("Cache-Control", "no-cache");
|
|
118
|
-
if (req.method === "OPTIONS") {
|
|
119
|
-
res.writeHead(204);
|
|
120
|
-
res.end();
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
124
|
-
res.writeHead(405);
|
|
125
|
-
res.end();
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
let urlPath;
|
|
129
|
-
try {
|
|
130
|
-
urlPath = decodeURIComponent(new URL(req.url || "/", "http://x").pathname);
|
|
131
|
-
}
|
|
132
|
-
catch (_a) {
|
|
133
|
-
res.writeHead(400);
|
|
134
|
-
res.end();
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
const ext = path.extname(urlPath).toLowerCase();
|
|
138
|
-
if (!CONTENT_TYPES[ext]) {
|
|
139
|
-
res.writeHead(403);
|
|
140
|
-
res.end();
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
const resolvedRoot = path.resolve(rootDir);
|
|
144
|
-
const filePath = path.resolve(resolvedRoot, "." + urlPath);
|
|
145
|
-
if (filePath !== resolvedRoot &&
|
|
146
|
-
!filePath.startsWith(resolvedRoot + path.sep)) {
|
|
147
|
-
res.writeHead(403);
|
|
148
|
-
res.end();
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
fs.stat(filePath, (statErr, stats) => {
|
|
152
|
-
if (statErr || !stats.isFile()) {
|
|
153
|
-
res.writeHead(404);
|
|
154
|
-
res.end();
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
res.setHeader("Content-Type", CONTENT_TYPES[ext]);
|
|
158
|
-
res.setHeader("Content-Length", String(stats.size));
|
|
159
|
-
if (req.method === "HEAD") {
|
|
160
|
-
res.writeHead(200);
|
|
161
|
-
res.end();
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
const stream = fs.createReadStream(filePath);
|
|
165
|
-
stream.on("error", () => {
|
|
166
|
-
if (!res.headersSent) {
|
|
167
|
-
res.writeHead(500);
|
|
168
|
-
}
|
|
169
|
-
res.end();
|
|
170
|
-
});
|
|
171
|
-
res.writeHead(200);
|
|
172
|
-
stream.pipe(res);
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
exports.DevtoolsHostService = DevtoolsHostService;
|
|
177
|
-
yok_1.injector.register("devtoolsHostService", DevtoolsHostService);
|
|
178
|
-
//# sourceMappingURL=devtools-host-service.js.map
|