From ea81a4947b76c0c3d29f7cbc0f99594356022eff Mon Sep 17 00:00:00 2001 From: Igor Randjelovic Date: Sun, 22 Nov 2020 23:57:03 +0100 Subject: [PATCH 01/18] chore: handle staleFiles --- lib/controllers/prepare-controller.ts | 2 + lib/controllers/run-controller.ts | 25 +++--- .../webpack/webpack-compiler-service.ts | 76 ++++++++++++++++++- lib/services/webpack/webpack.d.ts | 1 + 4 files changed, 90 insertions(+), 14 deletions(-) diff --git a/lib/controllers/prepare-controller.ts b/lib/controllers/prepare-controller.ts index a9d5f1ed70..258bb9c0bb 100644 --- a/lib/controllers/prepare-controller.ts +++ b/lib/controllers/prepare-controller.ts @@ -247,6 +247,7 @@ export class PrepareController extends EventEmitter { if (this.persistedData && this.persistedData.length) { this.emitPrepareEvent({ files: [], + staleFiles: [], hasOnlyHotUpdateFiles: false, hasNativeChanges: result.hasNativeChanges, hmrData: null, @@ -352,6 +353,7 @@ export class PrepareController extends EventEmitter { await this.writeRuntimePackageJson(projectData, platformData); this.emitPrepareEvent({ files: [], + staleFiles: [], hasOnlyHotUpdateFiles: false, hmrData: null, hasNativeChanges: true, diff --git a/lib/controllers/run-controller.ts b/lib/controllers/run-controller.ts index 7fa173cc04..4acabe5323 100644 --- a/lib/controllers/run-controller.ts +++ b/lib/controllers/run-controller.ts @@ -663,19 +663,16 @@ export class RunController extends EventEmitter implements IRunController { const platformLiveSyncService = this.$liveSyncServiceResolver.resolveLiveSyncService( device.deviceInfo.platform ); - const allAppFiles = - data.hmrData && - data.hmrData.fallbackFiles && - data.hmrData.fallbackFiles.length - ? data.hmrData.fallbackFiles - : data.files; + const allAppFiles = data.hmrData?.fallbackFiles?.length + ? data.hmrData.fallbackFiles + : data.files; const filesToSync = data.hasOnlyHotUpdateFiles ? data.files : allAppFiles; const watchInfo = { liveSyncDeviceData: deviceDescriptor, projectData, - filesToRemove: [], + filesToRemove: data.staleFiles ?? [], filesToSync, hmrData: data.hmrData, useHotModuleReload: liveSyncInfo.useHotModuleReload, @@ -746,6 +743,7 @@ export class RunController extends EventEmitter implements IRunController { } const watchAction = async (): Promise => { + console.time("watchAction"); const liveSyncResultInfo = await platformLiveSyncService.liveSyncWatchAction( device, watchInfo @@ -769,12 +767,17 @@ export class RunController extends EventEmitter implements IRunController { ); if (!liveSyncResultInfo.didRecover && isInHMRMode) { - const status = await this.$hmrStatusService.getHmrStatus( - device.deviceInfo.identifier, - data.hmrData.hash - ); + console.time("getHmrStatus"); + const status = HmrConstants.HMR_SUCCESS_STATUS; + // await this.$hmrStatusService.getHmrStatus( + // device.deviceInfo.identifier, + // data.hmrData.hash + // ); + console.timeEnd("getHmrStatus"); + // the timeout is assumed OK as the app could be blocked on a breakpoint if (status === HmrConstants.HMR_ERROR_STATUS) { + console.log("HMR_ERROR_STATUS!!", status); await fullSyncAction(); liveSyncResultInfo.isFullSync = true; await this.refreshApplication( diff --git a/lib/services/webpack/webpack-compiler-service.ts b/lib/services/webpack/webpack-compiler-service.ts index 3889ca6b75..aeb0aac3c8 100644 --- a/lib/services/webpack/webpack-compiler-service.ts +++ b/lib/services/webpack/webpack-compiler-service.ts @@ -28,6 +28,17 @@ import { import { ICleanupService } from "../../definitions/cleanup-service"; import { injector } from "../../common/yok"; +// todo: move out of here +interface IWebpackMessage { + type: "compilation"; + version?: number; + + isWatchMode?: boolean; + hash?: string; + emittedAssets?: string[]; + staleAssets?: string[]; +} + export class WebpackCompilerService extends EventEmitter implements IWebpackCompilerService { @@ -72,6 +83,27 @@ export class WebpackCompilerService childProcess.on("message", (message: string | IWebpackEmitMessage) => { this.$logger.trace("Message from webpack", message); + if ( + typeof message === "object" && + "version" in message && + "type" in message + ) { + // first compilation can be ignored because it will be synced regardless + // handling it here would trigger 2 syncs + if (isFirstWebpackWatchCompilation) { + isFirstWebpackWatchCompilation = false; + resolve(childProcess); + return; + } + + return this.handleHMRMessage( + message as IWebpackMessage, + platformData, + projectData, + prepareData + ); + } + if (message === "Webpack compilation complete.") { this.$logger.info("Webpack build done!"); resolve(childProcess); @@ -398,17 +430,17 @@ export class WebpackCompilerService } if (typeof envValue === "boolean") { if (envValue) { - args.push(`--env.${item}`); + args.push(`--env=${item}`); } } else { if (!Array.isArray(envValue)) { envValue = [envValue]; } - envValue.map((value: any) => args.push(`--env.${item}=${value}`)); + envValue.map((value: any) => args.push(`--env=${item}=${value}`)); } }); - + // console.log(args) return args; } @@ -473,5 +505,43 @@ export class WebpackCompilerService delete this.webpackProcesses[platform]; } } + + private handleHMRMessage( + message: IWebpackMessage, + platformData: IPlatformData, + projectData: IProjectData, + prepareData: IPrepareData + ) { + // handle new webpack hmr packets + this.$logger.trace("Received packet from webpack process:", message); + + if (message.type !== "compilation") { + return; + } + + this.$logger.trace("Webpack build done!"); + + const files = message.emittedAssets.map((asset) => + path.join(platformData.appDestinationDirectoryPath, "app", asset) + ); + const staleFiles = message.staleAssets.map((asset) => + path.join(platformData.appDestinationDirectoryPath, "app", asset) + ); + + console.log({ staleFiles }); + + console.time("hmrSync"); + + this.emit(WEBPACK_COMPILATION_COMPLETE, { + files, + staleFiles, + hasOnlyHotUpdateFiles: prepareData.hmr, + hmrData: { + hash: message.hash, + fallbackFiles: [], + }, + platform: platformData.platformNameLowerCase, + }); + } } injector.register("webpackCompilerService", WebpackCompilerService); diff --git a/lib/services/webpack/webpack.d.ts b/lib/services/webpack/webpack.d.ts index d802ae29ec..a9f6fa4eb4 100644 --- a/lib/services/webpack/webpack.d.ts +++ b/lib/services/webpack/webpack.d.ts @@ -62,6 +62,7 @@ declare global { interface IFilesChangeEventData { platform: string; files: string[]; + staleFiles: string[]; hmrData: IPlatformHmrData; hasOnlyHotUpdateFiles: boolean; hasNativeChanges: boolean; From fa140abb4f0ff04ff22750bdc897ccd4442e89c1 Mon Sep 17 00:00:00 2001 From: Igor Randjelovic Date: Thu, 11 Mar 2021 16:40:56 +0100 Subject: [PATCH 02/18] refactor: clean up karma-execution --- lib/services/karma-execution.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/services/karma-execution.ts b/lib/services/karma-execution.ts index aafee13917..c2314f6ff3 100644 --- a/lib/services/karma-execution.ts +++ b/lib/services/karma-execution.ts @@ -3,14 +3,14 @@ import * as path from "path"; process.on("message", (data: any) => { if (data.karmaConfig) { const pathToKarma = path.join( - data.karmaConfig.projectDir, - "node_modules/karma" - ), - KarmaServer = require(path.join(pathToKarma, "lib/server")), - karma = new KarmaServer(data.karmaConfig, (exitCode: number) => { - //Exit with the correct exit code and signal the manager process. - process.exit(exitCode); - }); + data.karmaConfig.projectDir, + "node_modules/karma" + ); + const KarmaServer = require(path.join(pathToKarma, "lib/server")); + const karma = new KarmaServer(data.karmaConfig, (exitCode: number) => { + // Exit with the correct exit code and signal the manager process. + process.exit(exitCode); + }); karma.start(); } From dce8d5c7d367565c4c96969b80ad1d9d1a76b4d0 Mon Sep 17 00:00:00 2001 From: Igor Randjelovic Date: Thu, 18 Mar 2021 19:13:04 +0100 Subject: [PATCH 03/18] fix(arm64): run pod install via rosetta2 --- lib/services/cocoapods-service.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/services/cocoapods-service.ts b/lib/services/cocoapods-service.ts index 4a6439d576..4e4a6dcad3 100644 --- a/lib/services/cocoapods-service.ts +++ b/lib/services/cocoapods-service.ts @@ -57,11 +57,19 @@ export class CocoaPodsService implements ICocoaPodsService { xcodeProjPath: string ): Promise { this.$logger.info("Installing pods..."); - const podTool = this.$config.USE_POD_SANDBOX ? "sandbox-pod" : "pod"; + let podTool = this.$config.USE_POD_SANDBOX ? "sandbox-pod" : "pod"; + const args = ["install"]; + + if (process.platform === "darwin" && process.arch === "arm64") { + console.log("Running on arm64 - running pod through rosetta2."); + args.unshift(podTool); + args.unshift("-x86_64"); + podTool = "arch"; + } // cocoapods print a lot of non-error information on stderr. Pipe the `stderr` to `stdout`, so we won't polute CLI's stderr output. const podInstallResult = await this.$childProcess.spawnFromEvent( podTool, - ["install"], + args, "close", { cwd: projectRoot, stdio: ["pipe", process.stdout, process.stdout] }, { throwError: false } From 803f1ed04a08404503129299c4f80279fa451a63 Mon Sep 17 00:00:00 2001 From: Igor Randjelovic Date: Wed, 24 Mar 2021 17:34:07 +0100 Subject: [PATCH 04/18] chore: webpack5 wip --- lib/controllers/run-controller.ts | 15 +++++----- .../webpack/webpack-compiler-service.ts | 30 ++++++++++++++++--- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/lib/controllers/run-controller.ts b/lib/controllers/run-controller.ts index 4acabe5323..6a4caa0d82 100644 --- a/lib/controllers/run-controller.ts +++ b/lib/controllers/run-controller.ts @@ -672,7 +672,10 @@ export class RunController extends EventEmitter implements IRunController { const watchInfo = { liveSyncDeviceData: deviceDescriptor, projectData, - filesToRemove: data.staleFiles ?? [], + // todo: remove stale files once everything is stable + // currently, watcher fires multiple times & may clean up unsynced files + // filesToRemove: data.staleFiles ?? [], + filesToRemove: [] as string[], filesToSync, hmrData: data.hmrData, useHotModuleReload: liveSyncInfo.useHotModuleReload, @@ -743,7 +746,6 @@ export class RunController extends EventEmitter implements IRunController { } const watchAction = async (): Promise => { - console.time("watchAction"); const liveSyncResultInfo = await platformLiveSyncService.liveSyncWatchAction( device, watchInfo @@ -767,17 +769,16 @@ export class RunController extends EventEmitter implements IRunController { ); if (!liveSyncResultInfo.didRecover && isInHMRMode) { - console.time("getHmrStatus"); - const status = HmrConstants.HMR_SUCCESS_STATUS; - // await this.$hmrStatusService.getHmrStatus( + // todo: figure out a faster way - perhaps use websockets... + // const status = await this.$hmrStatusService.getHmrStatus( // device.deviceInfo.identifier, // data.hmrData.hash // ); - console.timeEnd("getHmrStatus"); + // todo: remove hard-coded success status!!! + const status = HmrConstants.HMR_SUCCESS_STATUS; // the timeout is assumed OK as the app could be blocked on a breakpoint if (status === HmrConstants.HMR_ERROR_STATUS) { - console.log("HMR_ERROR_STATUS!!", status); await fullSyncAction(); liveSyncResultInfo.isFullSync = true; await this.refreshApplication( diff --git a/lib/services/webpack/webpack-compiler-service.ts b/lib/services/webpack/webpack-compiler-service.ts index aeb0aac3c8..1eeb57eec3 100644 --- a/lib/services/webpack/webpack-compiler-service.ts +++ b/lib/services/webpack/webpack-compiler-service.ts @@ -83,6 +83,7 @@ export class WebpackCompilerService childProcess.on("message", (message: string | IWebpackEmitMessage) => { this.$logger.trace("Message from webpack", message); + // if we are on webpack5 - we handle HMR in a slightly different way if ( typeof message === "object" && "version" in message && @@ -422,6 +423,27 @@ export class WebpackCompilerService } } + // todo: we can remove this when switching to our nativescript-webpack bin + // todo: CHANGE BEFORE 8.0 RELEASE!! + let separatorToUse = "."; + try { + const packagePath = require.resolve( + "@nativescript/webpack/package.json", + { + paths: [projectData.projectDir], + } + ); + const ver = semver.coerce(require(packagePath).version, { + loose: true, + }); + + if (semver.satisfies(ver, ">=5.0.0 || 5.0.0-dev")) { + separatorToUse = "="; + } + } catch (ignore) { + // + } + const args: any[] = []; envFlagNames.map((item) => { let envValue = envData[item]; @@ -430,14 +452,16 @@ export class WebpackCompilerService } if (typeof envValue === "boolean") { if (envValue) { - args.push(`--env=${item}`); + args.push(`--env${separatorToUse}${item}`); } } else { if (!Array.isArray(envValue)) { envValue = [envValue]; } - envValue.map((value: any) => args.push(`--env=${item}=${value}`)); + envValue.map((value: any) => + args.push(`--env${separatorToUse}${item}=${value}`) + ); } }); // console.log(args) @@ -530,8 +554,6 @@ export class WebpackCompilerService console.log({ staleFiles }); - console.time("hmrSync"); - this.emit(WEBPACK_COMPILATION_COMPLETE, { files, staleFiles, From e7ef716060b256fb66b857981404b8051050173c Mon Sep 17 00:00:00 2001 From: Igor Randjelovic Date: Wed, 24 Mar 2021 20:12:11 +0100 Subject: [PATCH 05/18] test: fix assertion to include new staleFiles --- test/controllers/prepare-controller.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/controllers/prepare-controller.ts b/test/controllers/prepare-controller.ts index 72467d11d1..c84335c394 100644 --- a/test/controllers/prepare-controller.ts +++ b/test/controllers/prepare-controller.ts @@ -134,6 +134,7 @@ describe("prepareController", () => { assert.deepStrictEqual(emittedEventNames[0], PREPARE_READY_EVENT_NAME); assert.deepStrictEqual(emittedEventData[0], { files: [], + staleFiles: [], hasNativeChanges: true, hasOnlyHotUpdateFiles: false, hmrData: null, From 7b5e5a30314b972a716fb3ab8a4efd2ef5a68f36 Mon Sep 17 00:00:00 2001 From: Igor Randjelovic Date: Wed, 24 Mar 2021 20:12:46 +0100 Subject: [PATCH 06/18] chore(release): 8.0.0-alpha.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index fe71805a2e..84a5dc4667 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "nativescript", - "version": "7.2.1", + "version": "8.0.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 41d326031f..269a2db221 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "nativescript", "preferGlobal": true, - "version": "7.2.1", + "version": "8.0.0-alpha.0", "author": "NativeScript ", "description": "Command-line interface for building NativeScript projects", "bin": { From 93c8e53fb4bfa1ef2d158766ea0d150b39f650dc Mon Sep 17 00:00:00 2001 From: Igor Randjelovic Date: Fri, 26 Mar 2021 22:05:27 +0100 Subject: [PATCH 07/18] chore: improved webpack5 handling --- lib/bootstrap.ts | 2 + lib/controllers/run-controller.ts | 11 +- lib/declarations.d.ts | 2 + lib/services/hmr-status-service.ts | 32 +++- .../webpack/webpack-compiler-service.ts | 140 ++++++++++++------ lib/shared-event-bus.ts | 14 ++ 6 files changed, 146 insertions(+), 55 deletions(-) create mode 100644 lib/shared-event-bus.ts diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index 024cd730dd..6eb120a8de 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -472,3 +472,5 @@ injector.require( "./services/metadata-filtering-service" ); injector.require("tempService", "./services/temp-service"); + +injector.require("sharedEventBus", "./shared-event-bus"); diff --git a/lib/controllers/run-controller.ts b/lib/controllers/run-controller.ts index 6a4caa0d82..4f0ec24042 100644 --- a/lib/controllers/run-controller.ts +++ b/lib/controllers/run-controller.ts @@ -769,13 +769,10 @@ export class RunController extends EventEmitter implements IRunController { ); if (!liveSyncResultInfo.didRecover && isInHMRMode) { - // todo: figure out a faster way - perhaps use websockets... - // const status = await this.$hmrStatusService.getHmrStatus( - // device.deviceInfo.identifier, - // data.hmrData.hash - // ); - // todo: remove hard-coded success status!!! - const status = HmrConstants.HMR_SUCCESS_STATUS; + const status = await this.$hmrStatusService.getHmrStatus( + device.deviceInfo.identifier, + data.hmrData.hash + ); // the timeout is assumed OK as the app could be blocked on a breakpoint if (status === HmrConstants.HMR_ERROR_STATUS) { diff --git a/lib/declarations.d.ts b/lib/declarations.d.ts index 8c720a1bad..f3bd70612d 100644 --- a/lib/declarations.d.ts +++ b/lib/declarations.d.ts @@ -1243,3 +1243,5 @@ interface IWatchIgnoreListService { interface INpmConfigService { getConfig(): IDictionary; } + +interface ISharedEventBus extends NodeJS.EventEmitter {} diff --git a/lib/services/hmr-status-service.ts b/lib/services/hmr-status-service.ts index 09d267e625..7e34471e76 100644 --- a/lib/services/hmr-status-service.ts +++ b/lib/services/hmr-status-service.ts @@ -6,6 +6,7 @@ import { } from "../common/constants"; import { IDictionary } from "../common/declarations"; import { injector } from "../common/yok"; +import { ISharedEventBus } from "../declarations"; export class HmrStatusService implements IHmrStatusService { public static HMR_STATUS_LOG_REGEX = /([a-z A-Z]*) hmr hash ([a-z0-9]*)\./; @@ -13,12 +14,14 @@ export class HmrStatusService implements IHmrStatusService { public static SUCCESS_MESSAGE = "Successfully applied update with"; public static FAILED_MESSAGE = "Cannot apply update with"; private hashOperationStatuses: IDictionary = {}; + private uuidToDeviceMap: IDictionary = {}; private intervals: IDictionary = {}; constructor( private $logParserService: ILogParserService, private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, - private $logger: ILogger + private $logger: ILogger, + private $sharedEventBus: ISharedEventBus ) {} public getHmrStatus( @@ -65,6 +68,33 @@ export class HmrStatusService implements IHmrStatusService { name: "failedLiveSync", platform: this.$devicePlatformsConstants.iOS.toLowerCase(), }); + + // webpack5 + // todo: figure out a better way to map Device.uuid -> CLI.DeviceId + this.$logParserService.addParseRule({ + regex: /\[HMR]\suuid\s=\s(.+)/, + handler: (matches: RegExpMatchArray, deviceId: string) => { + try { + this.uuidToDeviceMap[matches[1]] = deviceId; + } catch (err) { + // should not happen + } + }, + name: "hmrUUID", + }); + this.$sharedEventBus.on("webpack:hmr-status", (message) => { + const deviceId = this.uuidToDeviceMap[message?.data?.uuid]; + const hash = message?.hash; + const status = message?.data?.status; + + if (deviceId && hash && status) { + if (status === "success") { + this.setData(deviceId, hash, HmrConstants.HMR_SUCCESS_STATUS); + } else if (status === "failure") { + this.setData(deviceId, hash, HmrConstants.HMR_ERROR_STATUS); + } + } + }); } private handleAppCrash(matches: RegExpMatchArray, deviceId: string): void { diff --git a/lib/services/webpack/webpack-compiler-service.ts b/lib/services/webpack/webpack-compiler-service.ts index 1eeb57eec3..e5104f1615 100644 --- a/lib/services/webpack/webpack-compiler-service.ts +++ b/lib/services/webpack/webpack-compiler-service.ts @@ -13,6 +13,7 @@ import { import { IPackageManager, IPackageInstallationManager, + ISharedEventBus, } from "../../declarations"; import { IPlatformData } from "../../definitions/platform"; import { IProjectData } from "../../definitions/project"; @@ -29,14 +30,16 @@ import { ICleanupService } from "../../definitions/cleanup-service"; import { injector } from "../../common/yok"; // todo: move out of here -interface IWebpackMessage { - type: "compilation"; +interface IWebpackMessage { + type: "compilation" | "hmr-status"; version?: number; - - isWatchMode?: boolean; hash?: string; - emittedAssets?: string[]; - staleAssets?: string[]; + data?: T; +} + +interface IWebpackCompilation { + emittedAssets: string[]; + staleAssets: string[]; } export class WebpackCompilerService @@ -55,7 +58,8 @@ export class WebpackCompilerService private $mobileHelper: Mobile.IMobileHelper, private $cleanupService: ICleanupService, private $packageManager: IPackageManager, - private $packageInstallationManager: IPackageInstallationManager + private $packageInstallationManager: IPackageInstallationManager, + private $sharedEventBus: ISharedEventBus ) { super(); } @@ -97,8 +101,15 @@ export class WebpackCompilerService return; } + if ((message as IWebpackMessage).type === "hmr-status") { + // we pass message through our event-bus to be handled wherever needed + // in this case webpack-hmr-status-service listens for this event + this.$sharedEventBus.emit("webpack:hmr-status", message); + return; + } + return this.handleHMRMessage( - message as IWebpackMessage, + message as IWebpackMessage, platformData, projectData, prepareData @@ -280,7 +291,7 @@ export class WebpackCompilerService ): Promise { if (!this.$fs.exists(projectData.webpackConfigPath)) { this.$errors.fail( - `The webpack configuration file ${projectData.webpackConfigPath} does not exist. Ensure you have such file or set correct path in ${CONFIG_FILE_NAME_DISPLAY}` + `The webpack configuration file ${projectData.webpackConfigPath} does not exist. Ensure you the file exists, or update the path in ${CONFIG_FILE_NAME_DISPLAY}.` ); } @@ -308,16 +319,11 @@ export class WebpackCompilerService const args = [ ...additionalNodeArgs, - path.join( - projectData.projectDir, - "node_modules", - "webpack", - "bin", - "webpack.js" - ), + this.getWebpackExecutablePath(projectData), + this.isWebpack5(projectData) ? `build` : null, `--config=${projectData.webpackConfigPath}`, ...envParams, - ]; + ].filter(Boolean); if (prepareData.watch) { args.push("--watch"); @@ -423,27 +429,6 @@ export class WebpackCompilerService } } - // todo: we can remove this when switching to our nativescript-webpack bin - // todo: CHANGE BEFORE 8.0 RELEASE!! - let separatorToUse = "."; - try { - const packagePath = require.resolve( - "@nativescript/webpack/package.json", - { - paths: [projectData.projectDir], - } - ); - const ver = semver.coerce(require(packagePath).version, { - loose: true, - }); - - if (semver.satisfies(ver, ">=5.0.0 || 5.0.0-dev")) { - separatorToUse = "="; - } - } catch (ignore) { - // - } - const args: any[] = []; envFlagNames.map((item) => { let envValue = envData[item]; @@ -452,16 +437,14 @@ export class WebpackCompilerService } if (typeof envValue === "boolean") { if (envValue) { - args.push(`--env${separatorToUse}${item}`); + args.push(`--env.${item}`); } } else { if (!Array.isArray(envValue)) { envValue = [envValue]; } - envValue.map((value: any) => - args.push(`--env${separatorToUse}${item}=${value}`) - ); + envValue.map((value: any) => args.push(`--env.${item}=${value}`)); } }); // console.log(args) @@ -537,7 +520,7 @@ export class WebpackCompilerService prepareData: IPrepareData ) { // handle new webpack hmr packets - this.$logger.trace("Received packet from webpack process:", message); + this.$logger.trace("Received message from webpack process:", message); if (message.type !== "compilation") { return; @@ -545,25 +528,88 @@ export class WebpackCompilerService this.$logger.trace("Webpack build done!"); - const files = message.emittedAssets.map((asset) => + const files = message.data.emittedAssets.map((asset: string) => path.join(platformData.appDestinationDirectoryPath, "app", asset) ); - const staleFiles = message.staleAssets.map((asset) => + const staleFiles = message.data.staleAssets.map((asset: string) => path.join(platformData.appDestinationDirectoryPath, "app", asset) ); - console.log({ staleFiles }); + // console.log({ staleFiles }); + + // extract last hash from emitted filenames + const lastHash = (() => { + const fileWithLastHash = files.find((fileName: string) => + fileName.endsWith("hot-update.js") + ); + + if (!fileWithLastHash) { + return null; + } + const matches = fileWithLastHash.match(/\.(.+).hot-update\.js/); + + if (matches) { + return matches[1]; + } + })(); this.emit(WEBPACK_COMPILATION_COMPLETE, { files, staleFiles, hasOnlyHotUpdateFiles: prepareData.hmr, hmrData: { - hash: message.hash, + hash: lastHash || message.hash, fallbackFiles: [], }, platform: platformData.platformNameLowerCase, }); } + + private getWebpackExecutablePath(projectData: IProjectData): string { + if (this.isWebpack5(projectData)) { + const packagePath = require.resolve( + "@nativescript/webpack/package.json", + { + paths: [projectData.projectDir], + } + ); + + return path.resolve( + packagePath.replace("package.json", ""), + "dist", + "bin", + "index.js" + ); + } + + return path.join( + projectData.projectDir, + "node_modules", + "webpack", + "bin", + "webpack.js" + ); + } + + private isWebpack5(projectData: IProjectData): boolean { + try { + const packagePath = require.resolve( + "@nativescript/webpack/package.json", + { + paths: [projectData.projectDir], + } + ); + const ver = semver.coerce(require(packagePath).version); + + if (semver.satisfies(ver, ">= 5.0.0")) { + return true; + } + } catch (ignore) { + // + } + + return false; + } } + injector.register("webpackCompilerService", WebpackCompilerService); diff --git a/lib/shared-event-bus.ts b/lib/shared-event-bus.ts new file mode 100644 index 0000000000..1ca82d0200 --- /dev/null +++ b/lib/shared-event-bus.ts @@ -0,0 +1,14 @@ +import { EventEmitter } from "events"; +import { injector } from "./common/yok"; +import { ISharedEventBus } from "./declarations"; + +/** + * Event Bus used by different services to emit state changes and + * allow listening for these state changes without needing a reference + * to the emitting service. + */ +class SharedEventBus extends EventEmitter implements ISharedEventBus { + // intentionally blank +} + +injector.register("sharedEventBus", SharedEventBus); From 7327a21ce32719f395450f3c17db991c1bf767a3 Mon Sep 17 00:00:00 2001 From: Igor Randjelovic Date: Fri, 26 Mar 2021 22:59:08 +0100 Subject: [PATCH 08/18] chore: cleanup --- lib/services/hmr-status-service.ts | 38 +++++++------------ .../webpack/webpack-compiler-service.ts | 19 +++++----- 2 files changed, 22 insertions(+), 35 deletions(-) diff --git a/lib/services/hmr-status-service.ts b/lib/services/hmr-status-service.ts index 7e34471e76..57291bcd5e 100644 --- a/lib/services/hmr-status-service.ts +++ b/lib/services/hmr-status-service.ts @@ -6,7 +6,6 @@ import { } from "../common/constants"; import { IDictionary } from "../common/declarations"; import { injector } from "../common/yok"; -import { ISharedEventBus } from "../declarations"; export class HmrStatusService implements IHmrStatusService { public static HMR_STATUS_LOG_REGEX = /([a-z A-Z]*) hmr hash ([a-z0-9]*)\./; @@ -14,14 +13,12 @@ export class HmrStatusService implements IHmrStatusService { public static SUCCESS_MESSAGE = "Successfully applied update with"; public static FAILED_MESSAGE = "Cannot apply update with"; private hashOperationStatuses: IDictionary = {}; - private uuidToDeviceMap: IDictionary = {}; private intervals: IDictionary = {}; constructor( private $logParserService: ILogParserService, private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, - private $logger: ILogger, - private $sharedEventBus: ISharedEventBus + private $logger: ILogger ) {} public getHmrStatus( @@ -70,30 +67,21 @@ export class HmrStatusService implements IHmrStatusService { }); // webpack5 - // todo: figure out a better way to map Device.uuid -> CLI.DeviceId + const statusStringMap: any = { + success: HmrConstants.HMR_SUCCESS_STATUS, + failure: HmrConstants.HMR_ERROR_STATUS, + }; this.$logParserService.addParseRule({ - regex: /\[HMR]\suuid\s=\s(.+)/, + regex: /\[HMR]\[(.+)]\s*(.+)/, handler: (matches: RegExpMatchArray, deviceId: string) => { - try { - this.uuidToDeviceMap[matches[1]] = deviceId; - } catch (err) { - // should not happen - } + const [hash, status] = matches.slice(1); + console.log({ + hash, + status, + }); + this.setData(deviceId, hash, statusStringMap[status]); }, - name: "hmrUUID", - }); - this.$sharedEventBus.on("webpack:hmr-status", (message) => { - const deviceId = this.uuidToDeviceMap[message?.data?.uuid]; - const hash = message?.hash; - const status = message?.data?.status; - - if (deviceId && hash && status) { - if (status === "success") { - this.setData(deviceId, hash, HmrConstants.HMR_SUCCESS_STATUS); - } else if (status === "failure") { - this.setData(deviceId, hash, HmrConstants.HMR_ERROR_STATUS); - } - } + name: "hmr-status", }); } diff --git a/lib/services/webpack/webpack-compiler-service.ts b/lib/services/webpack/webpack-compiler-service.ts index e5104f1615..5b39119080 100644 --- a/lib/services/webpack/webpack-compiler-service.ts +++ b/lib/services/webpack/webpack-compiler-service.ts @@ -13,7 +13,6 @@ import { import { IPackageManager, IPackageInstallationManager, - ISharedEventBus, } from "../../declarations"; import { IPlatformData } from "../../definitions/platform"; import { IProjectData } from "../../definitions/project"; @@ -58,9 +57,9 @@ export class WebpackCompilerService private $mobileHelper: Mobile.IMobileHelper, private $cleanupService: ICleanupService, private $packageManager: IPackageManager, - private $packageInstallationManager: IPackageInstallationManager, - private $sharedEventBus: ISharedEventBus - ) { + private $packageInstallationManager: IPackageInstallationManager + ) // private $sharedEventBus: ISharedEventBus + { super(); } @@ -101,12 +100,12 @@ export class WebpackCompilerService return; } - if ((message as IWebpackMessage).type === "hmr-status") { - // we pass message through our event-bus to be handled wherever needed - // in this case webpack-hmr-status-service listens for this event - this.$sharedEventBus.emit("webpack:hmr-status", message); - return; - } + // if ((message as IWebpackMessage).type === "hmr-status") { + // // we pass message through our event-bus to be handled wherever needed + // // in this case webpack-hmr-status-service listens for this event + // this.$sharedEventBus.emit("webpack:hmr-status", message); + // return; + // } return this.handleHMRMessage( message as IWebpackMessage, From 1a0056619026eed269a17ddf0d27006e1c5bbb86 Mon Sep 17 00:00:00 2001 From: Igor Randjelovic Date: Fri, 26 Mar 2021 23:11:39 +0100 Subject: [PATCH 09/18] fix: ignore empty compilation --- lib/services/hmr-status-service.ts | 4 ---- lib/services/webpack/webpack-compiler-service.ts | 10 +++++++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/services/hmr-status-service.ts b/lib/services/hmr-status-service.ts index 57291bcd5e..e953d2c849 100644 --- a/lib/services/hmr-status-service.ts +++ b/lib/services/hmr-status-service.ts @@ -75,10 +75,6 @@ export class HmrStatusService implements IHmrStatusService { regex: /\[HMR]\[(.+)]\s*(.+)/, handler: (matches: RegExpMatchArray, deviceId: string) => { const [hash, status] = matches.slice(1); - console.log({ - hash, - status, - }); this.setData(deviceId, hash, statusStringMap[status]); }, name: "hmr-status", diff --git a/lib/services/webpack/webpack-compiler-service.ts b/lib/services/webpack/webpack-compiler-service.ts index 5b39119080..f6562af6ea 100644 --- a/lib/services/webpack/webpack-compiler-service.ts +++ b/lib/services/webpack/webpack-compiler-service.ts @@ -57,9 +57,8 @@ export class WebpackCompilerService private $mobileHelper: Mobile.IMobileHelper, private $cleanupService: ICleanupService, private $packageManager: IPackageManager, - private $packageInstallationManager: IPackageInstallationManager - ) // private $sharedEventBus: ISharedEventBus - { + private $packageInstallationManager: IPackageInstallationManager // private $sharedEventBus: ISharedEventBus + ) { super(); } @@ -552,6 +551,11 @@ export class WebpackCompilerService } })(); + if (!files.length) { + // ignore compilations if no new files are emitted + return; + } + this.emit(WEBPACK_COMPILATION_COMPLETE, { files, staleFiles, From f66bf5d7d330854c0d6ba78f19f283d7f2d5ae20 Mon Sep 17 00:00:00 2001 From: Igor Randjelovic Date: Fri, 26 Mar 2021 23:27:54 +0100 Subject: [PATCH 10/18] chore(release): 8.0.0-alpha.1 --- lib/services/webpack/webpack-compiler-service.ts | 2 +- package-lock.json | 2 +- package.json | 2 +- test/services/webpack/webpack-compiler-service.ts | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/services/webpack/webpack-compiler-service.ts b/lib/services/webpack/webpack-compiler-service.ts index f6562af6ea..5b309dffcd 100644 --- a/lib/services/webpack/webpack-compiler-service.ts +++ b/lib/services/webpack/webpack-compiler-service.ts @@ -289,7 +289,7 @@ export class WebpackCompilerService ): Promise { if (!this.$fs.exists(projectData.webpackConfigPath)) { this.$errors.fail( - `The webpack configuration file ${projectData.webpackConfigPath} does not exist. Ensure you the file exists, or update the path in ${CONFIG_FILE_NAME_DISPLAY}.` + `The webpack configuration file ${projectData.webpackConfigPath} does not exist. Ensure the file exists, or update the path in ${CONFIG_FILE_NAME_DISPLAY}.` ); } diff --git a/package-lock.json b/package-lock.json index 84a5dc4667..2751d95323 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "nativescript", - "version": "8.0.0-alpha.0", + "version": "8.0.0-alpha.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 269a2db221..f0a2aaa293 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "nativescript", "preferGlobal": true, - "version": "8.0.0-alpha.0", + "version": "8.0.0-alpha.1", "author": "NativeScript ", "description": "Command-line interface for building NativeScript projects", "bin": { diff --git a/test/services/webpack/webpack-compiler-service.ts b/test/services/webpack/webpack-compiler-service.ts index 3cedc774eb..cb48eacab5 100644 --- a/test/services/webpack/webpack-compiler-service.ts +++ b/test/services/webpack/webpack-compiler-service.ts @@ -211,7 +211,7 @@ describe("WebpackCompilerService", () => { { webpackConfigPath }, {} ), - `The webpack configuration file ${webpackConfigPath} does not exist. Ensure you have such file or set correct path in ${CONFIG_FILE_NAME_DISPLAY}` + `The webpack configuration file ${webpackConfigPath} does not exist. Ensure the file exists, or update the path in ${CONFIG_FILE_NAME_DISPLAY}` ); }); }); @@ -227,7 +227,7 @@ describe("WebpackCompilerService", () => { { webpackConfigPath }, {} ), - `The webpack configuration file ${webpackConfigPath} does not exist. Ensure you have such file or set correct path in ${CONFIG_FILE_NAME_DISPLAY}` + `The webpack configuration file ${webpackConfigPath} does not exist. Ensure the file exists, or update the path in ${CONFIG_FILE_NAME_DISPLAY}` ); }); }); From b3ced4ddd9b2ea06ac249498936e443f9b8d9258 Mon Sep 17 00:00:00 2001 From: Igor Randjelovic Date: Fri, 26 Mar 2021 23:43:55 +0100 Subject: [PATCH 11/18] chore(release): 8.0.0-alpha.2 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2751d95323..41839b04a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "nativescript", - "version": "8.0.0-alpha.1", + "version": "8.0.0-alpha.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index f0a2aaa293..e56cbc410b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "nativescript", "preferGlobal": true, - "version": "8.0.0-alpha.1", + "version": "8.0.0-alpha.2", "author": "NativeScript ", "description": "Command-line interface for building NativeScript projects", "bin": { From 4b1fa6197868815609f65da98ccac4295620ca27 Mon Sep 17 00:00:00 2001 From: Igor Randjelovic Date: Sun, 28 Mar 2021 19:50:07 +0200 Subject: [PATCH 12/18] chore: fix hmr regex --- lib/services/hmr-status-service.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/services/hmr-status-service.ts b/lib/services/hmr-status-service.ts index e953d2c849..288dcabd86 100644 --- a/lib/services/hmr-status-service.ts +++ b/lib/services/hmr-status-service.ts @@ -72,10 +72,13 @@ export class HmrStatusService implements IHmrStatusService { failure: HmrConstants.HMR_ERROR_STATUS, }; this.$logParserService.addParseRule({ - regex: /\[HMR]\[(.+)]\s*(.+)/, + regex: /\[HMR]\[(.+)]\s*(\w+)\s*\|/, handler: (matches: RegExpMatchArray, deviceId: string) => { const [hash, status] = matches.slice(1); - this.setData(deviceId, hash, statusStringMap[status]); + const mappedStatus = statusStringMap[status.trim()]; + if (mappedStatus) { + this.setData(deviceId, hash, statusStringMap[status]); + } }, name: "hmr-status", }); From b79ae2fc4a37e18f9ba2317f37ac3d39d63b496d Mon Sep 17 00:00:00 2001 From: Igor Randjelovic Date: Mon, 29 Mar 2021 21:51:57 +0200 Subject: [PATCH 13/18] chore(release): 8.0.0-alpha.3 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 41839b04a6..f1178ad0e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "nativescript", - "version": "8.0.0-alpha.2", + "version": "8.0.0-alpha.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index e56cbc410b..62f1ec82f4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "nativescript", "preferGlobal": true, - "version": "8.0.0-alpha.2", + "version": "8.0.0-alpha.3", "author": "NativeScript ", "description": "Command-line interface for building NativeScript projects", "bin": { From 5c871b4027ebfd8ba4500d3a098f8d5ed3dc6762 Mon Sep 17 00:00:00 2001 From: Igor Randjelovic Date: Wed, 31 Mar 2021 01:43:22 +0200 Subject: [PATCH 14/18] feat: migrations for ns8 --- lib/commands/migrate.ts | 11 +- lib/commands/update.ts | 2 +- lib/common/dispatchers.ts | 1 + lib/controllers/migrate-controller.ts | 1299 ++++++++++++--------- lib/controllers/update-controller-base.ts | 11 +- lib/definitions/migrate.d.ts | 19 +- lib/services/project-cleanup-service.ts | 18 +- tslint.json | 2 +- 8 files changed, 771 insertions(+), 592 deletions(-) diff --git a/lib/commands/migrate.ts b/lib/commands/migrate.ts index 5fce88f3d2..09cb643343 100644 --- a/lib/commands/migrate.ts +++ b/lib/commands/migrate.ts @@ -9,6 +9,7 @@ export class MigrateCommand implements ICommand { constructor( private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, private $migrateController: IMigrateController, + private $staticConfig: Config.IStaticConfig, private $projectData: IProjectData, private $logger: ILogger ) { @@ -28,18 +29,12 @@ export class MigrateCommand implements ICommand { ); if (!shouldMigrateResult) { + const cliVersion = this.$staticConfig.version; this.$logger.printMarkdown( - '__Project is compatible with NativeScript "v7.0.0". To get the latest NativeScript packages execute "ns update".__' + `__Project is compatible with NativeScript \`v${cliVersion}\`__` ); return; } - // else if (shouldMigrateResult.shouldMigrate === ShouldMigrate.ADVISED) { - // // todo: this shouldn't be here, because this is already the `ns migrate` path. - // this.$logger.printMarkdown( - // '__Project should work with NativeScript "v7.0.0" but a migration is advised. Run ns migrate to migrate.__' - // ); - // return; - // } await this.$migrateController.migrate(migrationData); } diff --git a/lib/commands/update.ts b/lib/commands/update.ts index f375b0a1cf..890b0d66bc 100644 --- a/lib/commands/update.ts +++ b/lib/commands/update.ts @@ -61,7 +61,7 @@ export class UpdateCommand implements ICommand { this.$devicePlatformsConstants.Android, this.$devicePlatformsConstants.iOS, ], - allowInvalidVersions: true, + loose: true, }); if (shouldMigrate) { diff --git a/lib/common/dispatchers.ts b/lib/common/dispatchers.ts index cc877b7afd..05c10695b4 100644 --- a/lib/common/dispatchers.ts +++ b/lib/common/dispatchers.ts @@ -37,6 +37,7 @@ export class CommandDispatcher implements ICommandDispatcher { if (this.$logger.getLevel() === "TRACE") { // CommandDispatcher is called from external CLI's only, so pass the path to their package.json + this.$logger.trace("Collecting system information..."); const sysInfo = await this.$sysInfo.getSysInfo({ pathToNativeScriptCliPackageJson: path.join( __dirname, diff --git a/lib/controllers/migrate-controller.ts b/lib/controllers/migrate-controller.ts index d8b3077d36..f49ff72b92 100644 --- a/lib/controllers/migrate-controller.ts +++ b/lib/controllers/migrate-controller.ts @@ -16,6 +16,7 @@ import { IProjectDataService, } from "../definitions/project"; import { + IDependencyVersion, IMigrateController, IMigrationData, IMigrationDependency, @@ -33,6 +34,7 @@ import { } from "../definitions/platform"; import { IPluginsService } from "../definitions/plugins"; import { + IChildProcess, IDictionary, IErrors, IFileSystem, @@ -80,7 +82,8 @@ export class MigrateController private $staticConfig: Config.IStaticConfig, private $terminalSpinnerService: ITerminalSpinnerService, private $projectCleanupService: IProjectCleanupService, - private $projectBackupService: IProjectBackupService + private $projectBackupService: IProjectBackupService, + private $childProcess: IChildProcess ) { super( $fs, @@ -114,6 +117,9 @@ export class MigrateController this.$settingsService.getProfileDir(), `should-migrate-cache-${cliVersion}.json` ); + this.$logger.trace( + `Migration cache path is: ${shouldMigrateCacheFilePath}` + ); return this.$injector.resolve("jsonFileSettingsService", { jsonFileSettingsPath: shouldMigrateCacheFilePath, }); @@ -122,7 +128,8 @@ export class MigrateController private migrationDependencies: IMigrationDependency[] = [ { packageName: constants.SCOPED_TNS_CORE_MODULES, - verifiedVersion: "7.0.0", + minVersion: "6.5.0", + desiredVersion: "~8.0.0-alpha.9", shouldAddIfMissing: true, }, { @@ -131,13 +138,14 @@ export class MigrateController }, { packageName: "@nativescript/types", - verifiedVersion: "7.0.0", + minVersion: "7.0.0", + desiredVersion: "~7.3.0", isDev: true, }, { packageName: "tns-platform-declarations", replaceWith: "@nativescript/types", - verifiedVersion: "7.0.0", + minVersion: "6.5.0", isDev: true, }, { @@ -149,133 +157,129 @@ export class MigrateController replaceWith: constants.WEBPACK_PLUGIN_NAME, shouldRemove: true, isDev: true, - shouldMigrateAction: async () => { + async shouldMigrateAction() { return true; }, migrateAction: this.migrateWebpack.bind(this), }, { packageName: constants.WEBPACK_PLUGIN_NAME, - verifiedVersion: "3.0.0", + minVersion: "3.0.0", + desiredVersion: "~5.0.0-alpha.7", shouldAddIfMissing: true, + isDev: true, }, { packageName: "nativescript-vue", - verifiedVersion: "2.8.0", - shouldMigrateAction: async ( + minVersion: "2.7.0", + desiredVersion: "~2.8.0", + async shouldMigrateAction( + dependency: IMigrationDependency, projectData: IProjectData, - allowInvalidVersions: boolean - ) => { - const dependency = { - packageName: "nativescript-vue", - verifiedVersion: "2.8.0", - isDev: false, - }; - const result = - this.hasDependency(dependency, projectData) && - (await this.shouldMigrateDependencyVersion( - dependency, - projectData, - allowInvalidVersions - )); + loose: boolean + ) { + if (!this.hasDependency(dependency, projectData)) { + return false; + } - return result; + return await this.shouldMigrateDependencyVersion( + dependency, + projectData, + loose + ); }, migrateAction: this.migrateNativeScriptVue.bind(this), }, { packageName: "nativescript-angular", replaceWith: "@nativescript/angular", - verifiedVersion: "10.0.0", + minVersion: "10.0.0", }, { packageName: "@nativescript/angular", - verifiedVersion: "10.0.0", - shouldMigrateAction: async ( + minVersion: "10.0.0", + desiredVersion: "~11.8.0", + async shouldMigrateAction( + dependency: IMigrationDependency, projectData: IProjectData, - allowInvalidVersions: boolean - ) => { - const dependency = { - packageName: "@nativescript/angular", - verifiedVersion: "10.0.0", - isDev: false, - }; - const result = - this.hasDependency(dependency, projectData) && - (await this.shouldMigrateDependencyVersion( - dependency, - projectData, - allowInvalidVersions - )); - return result; + loose: boolean + ) { + if (!this.hasDependency(dependency, projectData)) { + return false; + } + + return await this.shouldMigrateDependencyVersion( + dependency, + projectData, + loose + ); }, migrateAction: this.migrateNativeScriptAngular.bind(this), }, { packageName: "svelte-native", - verifiedVersion: "0.9.4", - shouldMigrateAction: async ( + minVersion: "0.9.0", + desiredVersion: "~0.9.4", + async shouldMigrateAction( + dependency: IMigrationDependency, projectData: IProjectData, - allowInvalidVersions: boolean - ) => { - const dependency = { - packageName: "svelte-native", - verifiedVersion: "0.9.0", // minimum version required - anything less will need a migration - isDev: false, - }; - const result = - this.hasDependency(dependency, projectData) && - (await this.shouldMigrateDependencyVersion( - dependency, - projectData, - allowInvalidVersions - )); - - return result; + loose: boolean + ) { + if (!this.hasDependency(dependency, projectData)) { + return false; + } + return await this.shouldMigrateDependencyVersion( + dependency, + projectData, + loose + ); }, migrateAction: this.migrateNativeScriptSvelte.bind(this), }, { packageName: "@nativescript/unit-test-runner", - verifiedVersion: "1.0.0", - shouldMigrateAction: async ( + minVersion: "1.0.0", + async shouldMigrateAction( + dependency: IMigrationDependency, projectData: IProjectData, - allowInvalidVersions: boolean - ) => { - const dependency = { - packageName: "@nativescript/unit-test-runner", - verifiedVersion: "1.0.0", - isDev: false, - }; - const result = - this.hasDependency(dependency, projectData) && - (await this.shouldMigrateDependencyVersion( - dependency, - projectData, - allowInvalidVersions - )); - return result; + loose: boolean + ) { + if (!this.hasDependency(dependency, projectData)) { + return false; + } + return await this.shouldMigrateDependencyVersion( + dependency, + projectData, + loose + ); }, migrateAction: this.migrateUnitTestRunner.bind(this), }, { packageName: "typescript", isDev: true, - verifiedVersion: "3.9.7", + minVersion: "3.7.0", + desiredVersion: "~4.0.0", }, ]; - get verifiedPlatformVersions(): IDictionary { + get verifiedPlatformVersions(): IDictionary { return { - [this.$devicePlatformsConstants.Android.toLowerCase()]: "6.5.3", - [this.$devicePlatformsConstants.iOS.toLowerCase()]: "6.5.2", + [this.$devicePlatformsConstants.Android.toLowerCase()]: { + minVersion: "6.5.3", + desiredVersion: "7.2.0", + }, + [this.$devicePlatformsConstants.iOS.toLowerCase()]: { + minVersion: "6.5.4", + desiredVersion: "7.2.0", + }, }; } public async shouldMigrate({ projectDir, platforms, - allowInvalidVersions = false, + loose = false, }: IMigrationData): Promise { const remainingPlatforms = []; @@ -284,14 +288,16 @@ export class MigrateController for (const platform of platforms) { const cachedResult = await this.getCachedShouldMigrate( projectDir, - platform + platform, + loose + ); + this.$logger.trace( + `Got cached result for shouldMigrate for platform: ${platform}: ${cachedResult}` ); + + // the cached result is only used if it's false, otherwise we need to check again if (cachedResult !== false) { remainingPlatforms.push(platform); - } else { - this.$logger.trace( - `Got cached result for shouldMigrate for platform: ${platform}` - ); } } @@ -299,7 +305,7 @@ export class MigrateController shouldMigrate = await this._shouldMigrate({ projectDir, platforms: remainingPlatforms, - allowInvalidVersions, + loose: loose, }); this.$logger.trace( `Executed shouldMigrate for platforms: ${remainingPlatforms}. Result is: ${shouldMigrate}` @@ -307,7 +313,11 @@ export class MigrateController if (!shouldMigrate) { for (const remainingPlatform of remainingPlatforms) { - await this.setCachedShouldMigrate(projectDir, remainingPlatform); + await this.setCachedShouldMigrate( + projectDir, + remainingPlatform, + loose + ); } } } @@ -318,12 +328,12 @@ export class MigrateController public async validate({ projectDir, platforms, - allowInvalidVersions = true, + loose = true, }: IMigrationData): Promise { const shouldMigrate = await this.shouldMigrate({ projectDir, platforms, - allowInvalidVersions, + loose, }); if (shouldMigrate) { this.$errors.fail( @@ -335,7 +345,7 @@ export class MigrateController public async migrate({ projectDir, platforms, - allowInvalidVersions = false, + loose = false, }: IMigrationData): Promise { this.spinner = this.$terminalSpinnerService.createSpinner(); const projectData = this.$projectDataService.getProjectData(projectDir); @@ -343,7 +353,7 @@ export class MigrateController this.$logger.trace("MigrationController.migrate called with", { projectDir, platforms, - allowInvalidVersions, + loose: loose, }); // ensure in git repo and require --force if not (for safety) @@ -366,7 +376,7 @@ export class MigrateController this.spinner.succeed(); // clean up project files - this.spinner.start("Cleaning up project files before migration"); + this.spinner.info("Cleaning up project files before migration"); await this.cleanUpProject(projectData); @@ -381,26 +391,25 @@ export class MigrateController this.spinner.text = "Cleaned old artifacts"; this.spinner.succeed(); - // migrate configs - this.spinner.start( - `Migrating project to use ${"nativescript.config.ts".green}` - ); + const newConfigPath = path.resolve(projectDir, "nativescript.config.ts"); + if (!this.$fs.exists(newConfigPath)) { + // migrate configs + this.spinner.start( + `Migrating project to use ${"nativescript.config.ts".green}` + ); - await this.migrateConfigs(projectDir); + await this.migrateConfigs(projectDir); - this.spinner.text = `Project has been migrated to use ${ - "nativescript.config.ts".green - }`; - this.spinner.succeed(); + this.spinner.text = `Project has been migrated to use ${ + "nativescript.config.ts".green + }`; + this.spinner.succeed(); + } // update dependencies this.spinner.start("Updating project dependencies"); - await this.migrateDependencies( - projectData, - platforms, - allowInvalidVersions - ); + await this.migrateDependencies(projectData, platforms, loose); this.spinner.text = "Project dependencies have been updated"; this.spinner.succeed(); @@ -408,13 +417,24 @@ export class MigrateController // update tsconfig const tsConfigPath = path.resolve(projectDir, "tsconfig.json"); if (this.$fs.exists(tsConfigPath)) { - this.spinner.start("Updating tsconfig.json"); + this.spinner.start(`Updating ${"tsconfig.json".yellow}`); await this.migrateTSConfig(tsConfigPath); - this.spinner.succeed("Updated tsconfig.json"); + this.spinner.succeed(`Updated ${"tsconfig.json".yellow}`); } + await this.migrateWebpack5(projectDir, projectData); + + // npx -p @nativescript/webpack@alpha nativescript-webpack init + + // run @nativescript/eslint over codebase + // this.spinner.start("Checking project code..."); + + await this.runESLint(projectDir); + + // this.spinner.succeed("Updated tsconfig.json"); + // add latest runtimes (if they were specified in the nativescript key) // this.spinner.start("Updating runtimes"); // @@ -433,7 +453,7 @@ export class MigrateController this.$logger.info(""); this.$logger.printMarkdown( "Project has been successfully migrated. The next step is to run `ns run ` to ensure everything is working properly." + - "\n\nPlease note that `ns migrate` does not make changes to your source code, you may need additional changes to complete the migration." + "\n\nPlease note that you may need additional changes to complete the migration." // + "\n\nYou may restore your project with `ns migrate restore`" ); @@ -496,7 +516,7 @@ export class MigrateController // await this.migrateDependencies( // projectData, // platforms, - // allowInvalidVersions + // loose // ); // } catch (error) { // const backupFolders = MigrateController.pathsToBackup; @@ -516,148 +536,10 @@ export class MigrateController // // this.spinner.info(MigrateController.MIGRATE_FINISH_MESSAGE); } - private async ensureGitCleanOrForce(projectDir: string): Promise { - const git: SimpleGit = simpleGit(projectDir); - const isGit = await git.checkIsRepo(); - const isForce = this.$options.force; - if (!isGit) { - // not a git repo and no --force - if (!isForce) { - this.$logger.printMarkdown( - `Running \`ns migrate\` in a non-git project is not recommended. If you want to skip this check run \`ns migrate --force\`.` - ); - this.$errors.fail("Not in Git repo."); - return false; - } - this.spinner.warn(`Not in Git repo, but using ${"--force".red}`); - return true; - } - - const isClean = (await git.status()).isClean(); - if (!isClean) { - if (!isForce) { - this.$logger.printMarkdown( - `Current git branch has uncommitted changes. Please commit the changes and try again. Alternatively run \`ns migrate --force\` to skip this check.` - ); - this.$errors.fail("Git branch not clean."); - return false; - } - this.spinner.warn(`Git branch not clean, but using ${"--force".red}`); - return true; - } - - return true; - } - - private async backupProject(projectDir: string): Promise { - const projectData = this.$projectDataService.getProjectData(projectDir); - const backup = this.$projectBackupService.getBackup("migration"); - backup.addPaths([ - ...MigrateController.pathsToBackup, - path.join(projectData.getAppDirectoryRelativePath(), "package.json"), - ]); - - try { - return backup.create(); - } catch (error) { - this.spinner.fail(`Project backup failed.`); - backup.remove(); - this.$errors.fail(`Project backup failed. Error is: ${error.message}`); - } - } - - private async migrateConfigs(projectDir: string): Promise { - const projectData = this.$projectDataService.getProjectData(projectDir); - - // package.json - const rootPackageJsonPath: any = path.resolve( - projectDir, - constants.PACKAGE_JSON_FILE_NAME - ); - // nested package.json - const embeddedPackageJsonPath = path.resolve( - projectData.projectDir, - projectData.getAppDirectoryRelativePath(), - constants.PACKAGE_JSON_FILE_NAME - ); - // nsconfig.json - const legacyNsConfigPath = path.resolve( - projectData.projectDir, - constants.CONFIG_NS_FILE_NAME - ); - - let rootPackageJsonData: any = {}; - if (this.$fs.exists(rootPackageJsonPath)) { - rootPackageJsonData = this.$fs.readJson(rootPackageJsonPath); - } - - // write the default config unless it already exists - const newConfigPath = this.$projectConfigService.writeDefaultConfig( - projectData.projectDir - ); - - // force legacy config mode - this.$projectConfigService.setForceUsingLegacyConfig(true); - - // all different sources are combined into configData (nested package.json, nsconfig and root package.json[nativescript]) - const configData = this.$projectConfigService.readConfig( - projectData.projectDir - ); - - // we no longer want to force legacy config mode - this.$projectConfigService.setForceUsingLegacyConfig(false); - - // move main key into root package.json - if (configData.main) { - rootPackageJsonData.main = configData.main; - delete configData.main; - } - - // detect appPath and App_Resources path - configData.appPath = this.detectAppPath(projectDir, configData); - configData.appResourcesPath = this.detectAppResourcesPath( - projectDir, - configData - ); - - // delete nativescript key from root package.json - if (rootPackageJsonData.nativescript) { - delete rootPackageJsonData.nativescript; - } - - // force the config service to use nativescript.config.ts - this.$projectConfigService.setForceUsingNewConfig(true); - // migrate data into nativescript.config.ts - const hasUpdatedConfigSuccessfully = await this.$projectConfigService.setValue( - "", // root - configData as { [key: string]: SupportedConfigValues } - ); - - if (!hasUpdatedConfigSuccessfully) { - if (typeof newConfigPath === "string") { - // only clean the config if it was created by the migration script - await this.$projectCleanupService.cleanPath(newConfigPath); - } - - this.$errors.fail( - `Failed to migrate project to use ${constants.CONFIG_FILE_NAME_TS}. One or more values could not be updated.` - ); - } - - // save root package.json - this.$fs.writeJson(rootPackageJsonPath, rootPackageJsonData); - - // delete migrated files - await this.$projectCleanupService.cleanPath(embeddedPackageJsonPath); - await this.$projectCleanupService.cleanPath(legacyNsConfigPath); - - return true; - } - private async _shouldMigrate({ projectDir, platforms, - allowInvalidVersions, + loose, }: IMigrationData): Promise { const isMigrate = _.get(this.$options, "argv._[0]") === "migrate"; const projectData = this.$projectDataService.getProjectData(projectDir); @@ -675,54 +557,63 @@ export class MigrateController const dependency = this.migrationDependencies[i]; const hasDependency = this.hasDependency(dependency, projectData); - if ( - hasDependency && - dependency.shouldMigrateAction && - (await dependency.shouldMigrateAction( + if (!hasDependency) { + if (dependency.shouldAddIfMissing) { + this.$logger.trace( + `${shouldMigrateCommonMessage}'${dependency.packageName}' is missing.` + ); + return true; + } + + continue; + } + + if (dependency.shouldMigrateAction) { + const shouldMigrate = await dependency.shouldMigrateAction.bind(this)( + dependency, projectData, - allowInvalidVersions - )) - ) { - this.$logger.trace( - `${shouldMigrateCommonMessage}'${dependency.packageName}' requires an update.` + loose ); - return true; + + if (shouldMigrate) { + this.$logger.trace( + `${shouldMigrateCommonMessage}'${dependency.packageName}' requires an update.` + ); + return true; + } } - if ( - hasDependency && - (dependency.replaceWith || dependency.shouldRemove) - ) { + if (dependency.replaceWith || dependency.shouldRemove) { this.$logger.trace( `${shouldMigrateCommonMessage}'${dependency.packageName}' is deprecated.` ); + + // in loose mode we ignore deprecated dependencies + if (loose) { + continue; + } + return true; } - if ( - hasDependency && - (await this.shouldMigrateDependencyVersion( - dependency, - projectData, - allowInvalidVersions - )) - ) { + const shouldUpdate = await this.shouldMigrateDependencyVersion( + dependency, + projectData, + loose + ); + + if (shouldUpdate) { this.$logger.trace( `${shouldMigrateCommonMessage}'${dependency.packageName}' should be updated.` ); - return true; - } - if (!hasDependency && dependency.shouldAddIfMissing) { - this.$logger.trace( - `${shouldMigrateCommonMessage}'${dependency.packageName}' is missing.` - ); return true; } } for (let platform of platforms) { - platform = platform && platform.toLowerCase(); + platform = platform?.toLowerCase(); + if ( !this.$platformValidationService.isValidPlatform(platform, projectData) ) { @@ -733,47 +624,111 @@ export class MigrateController platform, projectData, }); - if ( - hasRuntimeDependency && - (await this.shouldUpdateRuntimeVersion( - this.verifiedPlatformVersions[platform.toLowerCase()], - platform, - projectData, - allowInvalidVersions - )) - ) { - this.$logger.trace( - `${shouldMigrateCommonMessage}Platform '${platform}' should be updated.` - ); - return true; + + if (!hasRuntimeDependency) { + continue; } - } - } - private async getCachedShouldMigrate( - projectDir: string, - platform: string - ): Promise { - let cachedShouldMigrateValue = null; + const verifiedPlatformVersion = this.verifiedPlatformVersions[ + platform.toLowerCase() + ]; + const shouldUpdateRuntime = await this.shouldUpdateRuntimeVersion( + verifiedPlatformVersion, + platform, + projectData, + loose + ); - const cachedHash = await this.$jsonFileSettingsService.getSettingValue( - getHash(`${projectDir}${platform.toLowerCase()}`) - ); - const packageJsonHash = await this.getPackageJsonHash(projectDir); - if (cachedHash === packageJsonHash) { - cachedShouldMigrateValue = false; + if (!shouldUpdateRuntime) { + continue; + } + + this.$logger.trace( + `${shouldMigrateCommonMessage}Platform '${platform}' should be updated.` + ); + if (loose) { + this.$logger.warn( + `Platform '${platform}' should be updated. The minimum version supported is ${verifiedPlatformVersion.minVersion}` + ); + continue; + } + + return true; } - return cachedShouldMigrateValue; + return false; } - private async setCachedShouldMigrate( - projectDir: string, - platform: string - ): Promise { + private async shouldMigrateDependencyVersion( + dependency: IMigrationDependency, + projectData: IProjectData, + loose: boolean + ): Promise { + const installedVersion = await this.$packageInstallationManager.getInstalledDependencyVersion( + dependency.packageName, + projectData.projectDir + ); + + const desiredVersion = dependency.desiredVersion ?? dependency.minVersion; + const minVersion = dependency.minVersion ?? dependency.desiredVersion; + + if ( + dependency.shouldUseExactVersion && + installedVersion !== desiredVersion + ) { + return true; + } + + return this.isOutdatedVersion( + installedVersion, + { minVersion, desiredVersion }, + loose + ); + } + + private async shouldUpdateRuntimeVersion( + version: IDependencyVersion, + platform: string, + projectData: IProjectData, + loose: boolean + ): Promise { + const installedVersion = await this.getMaxRuntimeVersion({ + platform, + projectData, + }); + + return this.isOutdatedVersion(installedVersion, version, loose); + } + + private async getCachedShouldMigrate( + projectDir: string, + platform: string, + loose: boolean = false + ): Promise { + let cachedShouldMigrateValue = null; + + const cachedHash = await this.$jsonFileSettingsService.getSettingValue( + getHash(`${projectDir}${platform.toLowerCase()}`) + loose ? "-loose" : "" + ); + const packageJsonHash = await this.getPackageJsonHash(projectDir); + if (cachedHash === packageJsonHash) { + cachedShouldMigrateValue = false; + } + + return cachedShouldMigrateValue; + } + + private async setCachedShouldMigrate( + projectDir: string, + platform: string, + loose: boolean = false + ): Promise { + this.$logger.trace( + `Caching shouldMigrate result for platform ${platform} (loose = ${loose}).` + ); const packageJsonHash = await this.getPackageJsonHash(projectDir); await this.$jsonFileSettingsService.saveSetting( - getHash(`${projectDir}${platform.toLowerCase()}`), + getHash(`${projectDir}${platform.toLowerCase()}`) + loose ? "-loose" : "", packageJsonHash ); } @@ -807,6 +762,56 @@ export class MigrateController // } // } + private async ensureGitCleanOrForce(projectDir: string): Promise { + const git: SimpleGit = simpleGit(projectDir); + const isGit = await git.checkIsRepo(); + const isForce = this.$options.force; + if (!isGit) { + // not a git repo and no --force + if (!isForce) { + this.$logger.printMarkdown( + `Running \`ns migrate\` in a non-git project is not recommended. If you want to skip this check run \`ns migrate --force\`.` + ); + this.$errors.fail("Not in Git repo."); + return false; + } + this.spinner.warn(`Not in Git repo, but using ${"--force".red}`); + return true; + } + + const isClean = (await git.status()).isClean(); + if (!isClean) { + if (!isForce) { + this.$logger.printMarkdown( + `Current git branch has uncommitted changes. Please commit the changes and try again. Alternatively run \`ns migrate --force\` to skip this check.` + ); + this.$errors.fail("Git branch not clean."); + return false; + } + this.spinner.warn(`Git branch not clean, but using ${"--force".red}`); + return true; + } + + return true; + } + + private async backupProject(projectDir: string): Promise { + const projectData = this.$projectDataService.getProjectData(projectDir); + const backup = this.$projectBackupService.getBackup("migration"); + backup.addPaths([ + ...MigrateController.pathsToBackup, + path.join(projectData.getAppDirectoryRelativePath(), "package.json"), + ]); + + try { + return backup.create(); + } catch (error) { + this.spinner.fail(`Project backup failed.`); + backup.remove(); + this.$errors.fail(`Project backup failed. Error is: ${error.message}`); + } + } + private async cleanUpProject(projectData: IProjectData): Promise { await this.$projectCleanupService.clean([ constants.HOOKS_DIR_NAME, @@ -858,7 +863,7 @@ export class MigrateController [".map"], [""] ); - const cssFiles = glob.sync("*.@(le|sa|sc|c)ss", globOptions); + const cssFiles = glob.sync("*.@(less|sass|scss|css)", globOptions); const autoGeneratedCssFiles = this.getGeneratedFiles( cssFiles, [".css"], @@ -885,7 +890,7 @@ export class MigrateController generatedFileExts: string[], sourceFileExts: string[] ): string[] { - const autoGeneratedFiles = allFiles.filter((file) => { + return allFiles.filter((file) => { let isGenerated = false; const { dir, name, ext } = path.parse(file); if (generatedFileExts.indexOf(ext) > -1) { @@ -900,44 +905,114 @@ export class MigrateController return isGenerated; }); + } + + private isOutdatedVersion( + current: string, + target: IDependencyVersion, + loose: boolean + ): boolean { + // in loose mode, a falsy version is not considered outdated + if (!current && loose) { + return false; + } + + const installed = semver.coerce(current); + const min = semver.coerce(target.minVersion); + const desired = semver.coerce(target.desiredVersion); + + // in loose mode we check if we satisfy the min version + if (loose) { + return semver.lt(installed, min); + } + + // otherwise we compare with the desired version + return semver.lt(installed, desired); + } + + private detectAppPath(projectDir: string, configData: INsConfig) { + if (configData.appPath) { + return configData.appPath; + } - return autoGeneratedFiles; + const possibleAppPaths = [ + path.resolve(projectDir, constants.SRC_DIR), + path.resolve(projectDir, constants.APP_FOLDER_NAME), + ]; + + const appPath = possibleAppPaths.find((possiblePath) => + this.$fs.exists(possiblePath) + ); + if (appPath) { + const relativeAppPath = path + .relative(projectDir, appPath) + .replace(path.sep, "/"); + this.$logger.trace(`Found app source at '${appPath}'.`); + return relativeAppPath.toString(); + } + } + + private detectAppResourcesPath(projectDir: string, configData: INsConfig) { + if (configData.appResourcesPath) { + return configData.appResourcesPath; + } + + const possibleAppResourcesPaths = [ + path.resolve( + projectDir, + configData.appPath, + constants.APP_RESOURCES_FOLDER_NAME + ), + path.resolve(projectDir, constants.APP_RESOURCES_FOLDER_NAME), + ]; + + const appResourcesPath = possibleAppResourcesPaths.find((possiblePath) => + this.$fs.exists(possiblePath) + ); + if (appResourcesPath) { + const relativeAppResourcesPath = path + .relative(projectDir, appResourcesPath) + .replace(path.sep, "/"); + this.$logger.trace(`Found App_Resources at '${appResourcesPath}'.`); + return relativeAppResourcesPath.toString(); + } } private async migrateDependencies( projectData: IProjectData, platforms: string[], - allowInvalidVersions: boolean + loose: boolean ): Promise { for (let i = 0; i < this.migrationDependencies.length; i++) { const dependency = this.migrationDependencies[i]; const hasDependency = this.hasDependency(dependency, projectData); - if ( - hasDependency && - dependency.migrateAction && - (await dependency.shouldMigrateAction( - projectData, - allowInvalidVersions - )) - ) { - const newDependencies = await dependency.migrateAction( + if (!hasDependency && !dependency.shouldAddIfMissing) { + continue; + } + + if (dependency.migrateAction) { + const shouldMigrate = await dependency.shouldMigrateAction.bind(this)( + dependency, projectData, - path.join(projectData.projectDir, MigrateController.backupFolderName) + loose ); - for (const newDependency of newDependencies) { - await this.migrateDependency( - newDependency, + + if (shouldMigrate) { + const newDependencies = await dependency.migrateAction( projectData, - allowInvalidVersions + path.join( + projectData.projectDir, + MigrateController.backupFolderName + ) ); + for (const newDependency of newDependencies) { + await this.migrateDependency(newDependency, projectData, loose); + } } } - await this.migrateDependency( - dependency, - projectData, - allowInvalidVersions - ); + + await this.migrateDependency(dependency, projectData, loose); } for (const platform of platforms) { @@ -946,238 +1021,237 @@ export class MigrateController platform, projectData, }); - if ( - hasRuntimeDependency && - (await this.shouldUpdateRuntimeVersion( - this.verifiedPlatformVersions[lowercasePlatform], - platform, - projectData, - allowInvalidVersions - )) - ) { - const verifiedPlatformVersion = this.verifiedPlatformVersions[ - lowercasePlatform - ]; - const platformData = this.$platformsDataService.getPlatformData( - lowercasePlatform, - projectData - ); - this.spinner.info( - `Updating ${platform} platform to version '${verifiedPlatformVersion}'.` - ); - await this.$addPlatformService.setPlatformVersion( - platformData, - projectData, - verifiedPlatformVersion - ); - this.spinner.succeed(); + + if (!hasRuntimeDependency) { + continue; } - } - // this.spinner.info("Installing packages."); - // await this.$packageManager.install( - // projectData.projectDir, - // projectData.projectDir, - // { - // disableNpmInstall: false, - // frameworkPath: null, - // ignoreScripts: false, - // path: projectData.projectDir, - // } - // ); - // this.spinner.text = "Installing packages... Complete"; - // this.spinner.succeed(); - // - // this.spinner.succeed("Migration complete."); + const shouldUpdate = await this.shouldUpdateRuntimeVersion( + this.verifiedPlatformVersions[lowercasePlatform], + platform, + projectData, + loose + ); + + if (!shouldUpdate) { + continue; + } + + const verifiedPlatformVersion = this.verifiedPlatformVersions[ + lowercasePlatform + ]; + const platformData = this.$platformsDataService.getPlatformData( + lowercasePlatform, + projectData + ); + + this.spinner.info( + `Updating ${platform} platform to version '${verifiedPlatformVersion}'.` + ); + + await this.$addPlatformService.setPlatformVersion( + platformData, + projectData, + verifiedPlatformVersion.desiredVersion + ); + + this.spinner.succeed(); + } } private async migrateDependency( dependency: IMigrationDependency, projectData: IProjectData, - allowInvalidVersions: boolean + loose: boolean ): Promise { const hasDependency = this.hasDependency(dependency, projectData); + + // show warning if needed if (hasDependency && dependency.warning) { this.$logger.warn(dependency.warning); } - if (hasDependency && (dependency.replaceWith || dependency.shouldRemove)) { - this.$pluginsService.removeFromPackageJson( - dependency.packageName, - projectData.projectDir - ); - if (dependency.replaceWith) { - const replacementDep = _.find( - this.migrationDependencies, - (migrationPackage) => - migrationPackage.packageName === dependency.replaceWith - ); - if (!replacementDep) { - this.$errors.fail("Failed to find replacement dependency."); - } - - this.$pluginsService.addToPackageJson( - replacementDep.packageName, - replacementDep.verifiedVersion, - replacementDep.isDev, - projectData.projectDir - ); - - this.spinner.clear(); - this.$logger.info( - ` - ${dependency.packageName.yellow} has been replaced with ${ - replacementDep.packageName.cyan - } ${`v${replacementDep.verifiedVersion}`.green}` - ); - this.spinner.render(); + if (!hasDependency) { + if (!dependency.shouldAddIfMissing) { + return; } + const version = dependency.desiredVersion ?? dependency.minVersion; - return; - } - - if ( - hasDependency && - (await this.shouldMigrateDependencyVersion( - dependency, - projectData, - allowInvalidVersions - )) - ) { this.$pluginsService.addToPackageJson( dependency.packageName, - dependency.verifiedVersion, + version, dependency.isDev, projectData.projectDir ); this.spinner.clear(); this.$logger.info( - ` - ${dependency.packageName.yellow} has been updated to ${ - `v${dependency.verifiedVersion}`.green - }` + ` - ${dependency.packageName.yellow} ${ + `${version}`.green + } has been added` ); this.spinner.render(); return; } - if (!hasDependency && dependency.shouldAddIfMissing) { - this.$pluginsService.addToPackageJson( + if (dependency.replaceWith || dependency.shouldRemove) { + // remove + this.$pluginsService.removeFromPackageJson( dependency.packageName, - dependency.verifiedVersion, - dependency.isDev, + projectData.projectDir + ); + + // no replacement required - we're done + if (!dependency.replaceWith) { + return; + } + + const replacementDep = _.find( + this.migrationDependencies, + (migrationPackage) => + migrationPackage.packageName === dependency.replaceWith + ); + + if (!replacementDep) { + this.$errors.fail("Failed to find replacement dependency."); + } + + const version = dependency.desiredVersion ?? dependency.minVersion; + + // add replacement dependency + this.$pluginsService.addToPackageJson( + replacementDep.packageName, + version, + replacementDep.isDev, projectData.projectDir ); this.spinner.clear(); this.$logger.info( - ` - ${dependency.packageName.yellow} ${ - `v${dependency.verifiedVersion}`.green - } has been added` + ` - ${dependency.packageName.yellow} has been replaced with ${ + replacementDep.packageName.cyan + } ${`${version}`.green}` ); this.spinner.render(); + + return; } - } - private async shouldMigrateDependencyVersion( - dependency: IMigrationDependency, - projectData: IProjectData, - allowInvalidVersions: boolean - ): Promise { - const installedVersion = await this.$packageInstallationManager.getInstalledDependencyVersion( - dependency.packageName, - projectData.projectDir + const shouldMigrateVersion = await this.shouldMigrateDependencyVersion( + dependency, + projectData, + loose ); - const requiredVersion = dependency.verifiedVersion; - if ( - dependency.shouldUseExactVersion && - installedVersion !== requiredVersion - ) { - return true; + if (!shouldMigrateVersion) { + return; } - return this.isOutdatedVersion( - installedVersion, - requiredVersion, - allowInvalidVersions - ); - } + const version = dependency.desiredVersion ?? dependency.minVersion; - private async shouldUpdateRuntimeVersion( - targetVersion: string, - platform: string, - projectData: IProjectData, - allowInvalidVersions: boolean - ): Promise { - const installedVersion = await this.getMaxRuntimeVersion({ - platform, - projectData, - }); + this.$pluginsService.addToPackageJson( + dependency.packageName, + version, + dependency.isDev, + projectData.projectDir + ); - return this.isOutdatedVersion( - installedVersion, - targetVersion, - allowInvalidVersions + this.spinner.clear(); + this.$logger.info( + ` - ${dependency.packageName.yellow} has been updated to ${ + `${version}`.green + }` ); + this.spinner.render(); } - private isOutdatedVersion( - version: string, - targetVersion: string, - allowInvalidVersions: boolean - ): boolean { - return !!version - ? semver.lt(semver.coerce(version), targetVersion) - : !allowInvalidVersions; - } + private async migrateConfigs(projectDir: string): Promise { + const projectData = this.$projectDataService.getProjectData(projectDir); - private detectAppPath(projectDir: string, configData: INsConfig) { - if (configData.appPath) { - return configData.appPath; + // package.json + const rootPackageJsonPath: any = path.resolve( + projectDir, + constants.PACKAGE_JSON_FILE_NAME + ); + // nested package.json + const embeddedPackageJsonPath = path.resolve( + projectData.projectDir, + projectData.getAppDirectoryRelativePath(), + constants.PACKAGE_JSON_FILE_NAME + ); + // nsconfig.json + const legacyNsConfigPath = path.resolve( + projectData.projectDir, + constants.CONFIG_NS_FILE_NAME + ); + + let rootPackageJsonData: any = {}; + if (this.$fs.exists(rootPackageJsonPath)) { + rootPackageJsonData = this.$fs.readJson(rootPackageJsonPath); } - const possibleAppPaths = [ - path.resolve(projectDir, constants.SRC_DIR), - path.resolve(projectDir, constants.APP_FOLDER_NAME), - ]; + // write the default config unless it already exists + const newConfigPath = this.$projectConfigService.writeDefaultConfig( + projectData.projectDir + ); - const appPath = possibleAppPaths.find((possiblePath) => - this.$fs.exists(possiblePath) + // force legacy config mode + this.$projectConfigService.setForceUsingLegacyConfig(true); + + // all different sources are combined into configData (nested package.json, nsconfig and root package.json[nativescript]) + const configData = this.$projectConfigService.readConfig( + projectData.projectDir ); - if (appPath) { - const relativeAppPath = path - .relative(projectDir, appPath) - .replace(path.sep, "/"); - this.$logger.trace(`Found app source at '${appPath}'.`); - return relativeAppPath.toString(); - } - } - private detectAppResourcesPath(projectDir: string, configData: INsConfig) { - if (configData.appResourcesPath) { - return configData.appResourcesPath; + // we no longer want to force legacy config mode + this.$projectConfigService.setForceUsingLegacyConfig(false); + + // move main key into root package.json + if (configData.main) { + rootPackageJsonData.main = configData.main; + delete configData.main; } - const possibleAppResourcesPaths = [ - path.resolve( - projectDir, - configData.appPath, - constants.APP_RESOURCES_FOLDER_NAME - ), - path.resolve(projectDir, constants.APP_RESOURCES_FOLDER_NAME), - ]; + // detect appPath and App_Resources path + configData.appPath = this.detectAppPath(projectDir, configData); + configData.appResourcesPath = this.detectAppResourcesPath( + projectDir, + configData + ); - const appResourcesPath = possibleAppResourcesPaths.find((possiblePath) => - this.$fs.exists(possiblePath) + // delete nativescript key from root package.json + if (rootPackageJsonData.nativescript) { + delete rootPackageJsonData.nativescript; + } + + // force the config service to use nativescript.config.ts + this.$projectConfigService.setForceUsingNewConfig(true); + // migrate data into nativescript.config.ts + const hasUpdatedConfigSuccessfully = await this.$projectConfigService.setValue( + "", // root + configData as { [key: string]: SupportedConfigValues } ); - if (appResourcesPath) { - const relativeAppResourcesPath = path - .relative(projectDir, appResourcesPath) - .replace(path.sep, "/"); - this.$logger.trace(`Found App_Resources at '${appResourcesPath}'.`); - return relativeAppResourcesPath.toString(); + + if (!hasUpdatedConfigSuccessfully) { + if (typeof newConfigPath === "string") { + // only clean the config if it was created by the migration script + await this.$projectCleanupService.cleanPath(newConfigPath); + } + + this.$errors.fail( + `Failed to migrate project to use ${constants.CONFIG_FILE_NAME_TS}. One or more values could not be updated.` + ); } + + // save root package.json + this.$fs.writeJson(rootPackageJsonPath, rootPackageJsonData); + + // delete migrated files + await this.$projectCleanupService.cleanPath(embeddedPackageJsonPath); + await this.$projectCleanupService.cleanPath(legacyNsConfigPath); + + return true; } private async migrateUnitTestRunner( @@ -1215,18 +1289,44 @@ export class MigrateController } // Dependencies to migrate - const dependencies = [ + const dependencies: IMigrationDependency[] = [ { packageName: "karma-webpack", - verifiedVersion: "3.0.5", + minVersion: "3.0.5", + desiredVersion: "~5.0.0", isDev: true, shouldAddIfMissing: true, }, - { packageName: "karma-jasmine", verifiedVersion: "2.0.1", isDev: true }, - { packageName: "karma-mocha", verifiedVersion: "1.3.0", isDev: true }, - { packageName: "karma-chai", verifiedVersion: "0.1.0", isDev: true }, - { packageName: "karma-qunit", verifiedVersion: "3.1.2", isDev: true }, - { packageName: "karma", verifiedVersion: "4.1.0", isDev: true }, + { + packageName: "karma-jasmine", + minVersion: "2.0.1", + desiredVersion: "~4.0.1", + isDev: true, + }, + { + packageName: "karma-mocha", + minVersion: "1.3.0", + desiredVersion: "~2.0.1", + isDev: true, + }, + { + packageName: "karma-chai", + minVersion: "0.1.0", + desiredVersion: "~0.1.0", + isDev: true, + }, + { + packageName: "karma-qunit", + minVersion: "3.1.2", + desiredVersion: "~4.1.2", + isDev: true, + }, + { + packageName: "karma", + minVersion: "4.1.0", + desiredVersion: "~6.3.2", + isDev: true, + }, ]; return dependencies; @@ -1258,71 +1358,93 @@ export class MigrateController } private async migrateNativeScriptAngular(): Promise { - const dependencies = [ + const minVersion = "10.0.0"; + const desiredVersion = "~11.2.7"; + + /* + "@angular/router": "~11.2.7", + */ + + const dependencies: IMigrationDependency[] = [ { - packageName: "@angular/platform-browser-dynamic", - verifiedVersion: "10.0.0", + packageName: "@angular/animations", + minVersion, + desiredVersion, shouldAddIfMissing: true, }, { packageName: "@angular/common", - verifiedVersion: "10.0.0", + minVersion, + desiredVersion, shouldAddIfMissing: true, }, { packageName: "@angular/compiler", - verifiedVersion: "10.0.0", + minVersion, + desiredVersion, shouldAddIfMissing: true, }, { packageName: "@angular/core", - verifiedVersion: "10.0.0", + minVersion, + desiredVersion, shouldAddIfMissing: true, }, { packageName: "@angular/forms", - verifiedVersion: "10.0.0", + minVersion, + desiredVersion, shouldAddIfMissing: true, }, { packageName: "@angular/platform-browser", - verifiedVersion: "10.0.0", + minVersion, + desiredVersion, shouldAddIfMissing: true, }, { - packageName: "@angular/router", - verifiedVersion: "10.0.0", + packageName: "@angular/platform-browser-dynamic", + minVersion, + desiredVersion, shouldAddIfMissing: true, }, { - packageName: "rxjs", - verifiedVersion: "6.6.0", + packageName: "@angular/router", + minVersion, + desiredVersion, shouldAddIfMissing: true, }, { - packageName: "zone.js", - verifiedVersion: "0.11.1", + packageName: "rxjs", + minVersion: "6.6.0", + desiredVersion: "~6.6.7", shouldAddIfMissing: true, }, { - packageName: "@angular/animations", - verifiedVersion: "10.0.0", + packageName: "zone.js", + minVersion: "0.11.1", + desiredVersion: "~0.11.1", shouldAddIfMissing: true, }, + + // devDependencies { packageName: "@angular/compiler-cli", - verifiedVersion: "10.0.0", + minVersion, + desiredVersion, isDev: true, }, { packageName: "@ngtools/webpack", - verifiedVersion: "10.0.0", + minVersion, + desiredVersion: "~11.2.6", isDev: true, }, + + // obsolete { packageName: "@angular-devkit/build-angular", - verifiedVersion: "0.1000.8", - isDev: true, + shouldRemove: true, }, ]; @@ -1333,26 +1455,24 @@ export class MigrateController const dependencies: IMigrationDependency[] = [ { packageName: "nativescript-vue-template-compiler", - verifiedVersion: "2.8.0", - shouldUseExactVersion: true, + minVersion: "2.7.0", + desiredVersion: "~2.8.4", isDev: true, shouldAddIfMissing: true, }, { packageName: "vue-loader", - verifiedVersion: "15.9.3", - shouldUseExactVersion: true, - isDev: true, - shouldAddIfMissing: true, + shouldRemove: true, }, + // remove any version of vue { packageName: "vue", shouldRemove: true, }, + // add latest { packageName: "vue", - verifiedVersion: "2.6.12", - shouldUseExactVersion: true, + desiredVersion: "2.6.12", isDev: true, }, ]; @@ -1364,15 +1484,15 @@ export class MigrateController const dependencies: IMigrationDependency[] = [ { packageName: "svelte-native-nativescript-ui", - verifiedVersion: "0.9.0", - shouldUseExactVersion: true, + minVersion: "0.9.0", + desiredVersion: "~0.9.0", isDev: true, shouldAddIfMissing: true, }, { packageName: "svelte-native-preprocessor", - verifiedVersion: "0.2.0", - shouldUseExactVersion: true, + minVersion: "0.2.0", + desiredVersion: "~0.2.0", isDev: true, shouldAddIfMissing: true, }, @@ -1382,10 +1502,7 @@ export class MigrateController }, { packageName: "svelte-loader-hot", - verifiedVersion: "0.3.1", - shouldUseExactVersion: true, - isDev: true, - shouldAddIfMissing: true, + shouldRemove: true, }, { packageName: "svelte", @@ -1393,7 +1510,8 @@ export class MigrateController }, { packageName: "svelte", - verifiedVersion: "3.24.1", + minVersion: "3.24.1", + desiredVersion: "3.24.1", shouldUseExactVersion: true, isDev: true, }, @@ -1403,7 +1521,7 @@ export class MigrateController } private async migrateWebpack(): Promise { - const scopedWebpackDeps = [ + const webpackDependencies = [ "@angular-devkit/core", "clean-webpack-plugin", "copy-webpack-plugin", @@ -1435,14 +1553,87 @@ export class MigrateController "webpack-sources", ]; - const dependencies = scopedWebpackDeps.map((dep) => { + return webpackDependencies.map((dep) => { return { packageName: dep, shouldRemove: true, }; }); + } - return dependencies; + private async migrateWebpack5(projectDir: string, projectData: IProjectData) { + this.spinner.start(`Initializing new ${"webpack.config.js".yellow}`); + const { desiredVersion: webpackVersion } = this.migrationDependencies.find( + (dep) => dep.packageName === constants.WEBPACK_PLUGIN_NAME + ); + + try { + await this.$childProcess.spawnFromEvent( + "npx", + [ + "--package", + `@nativescript/webpack@${webpackVersion}`, + "nativescript-webpack", + "init", + ], + "close", + { + cwd: projectDir, + stdio: "ignore", + } + ); + } catch (err) { + this.$logger.printMarkdown( + `Failed to initialize \`webpack.config.js\`, you can try again by running \`npm install\` (or yarn, pnpm) and then \`npx @nativescript/webpack init\`.` + ); + } + this.spinner.succeed(`Initialized new ${"webpack.config.js".yellow}`); + + const packageJSON = this.$fs.readJson(projectData.projectFilePath); + const currentMain = packageJSON.main; + const currentMainTS = currentMain.replace(/.js$/, ".ts"); + + const appPath = projectData.appDirectoryPath; + + const possibleMains = [ + `./${appPath}/${currentMain}`, + `./${appPath}/${currentMainTS}`, + `./app/${currentMain}`, + `./app/${currentMainTS}`, + `./src/${currentMain}`, + `./src/${currentMainTS}`, + ]; + const replacedMain = possibleMains.find((possibleMain) => { + return this.$fs.exists(path.resolve(projectDir, possibleMain)); + }); + + if (replacedMain) { + packageJSON.main = replacedMain; + this.$fs.writeJson(projectData.projectFilePath, packageJSON); + + this.spinner.info( + `Updated ${"package.json".yellow} main field to ${replacedMain.green}` + ); + } else { + this.$logger.warn(); + this.$logger.warn("Note:\n-----"); + this.$logger.printMarkdown( + `Could not determine the correct \`main\` field for \`package.json\`. Make sure to update it manually, pointing to the actual entry file relative to the \`package.json\`.\n` + ); + } + } + + private async runESLint(projectDir: string) { + // todo: run @nativescript/eslint-plugin on project folder to update imports + // const childProcess = injector.resolve('childProcess') as IChildProcess; + // const args = [ + // 'npx', + // '--package typescript', + // '--package @nativescript/eslint-plugin', + // '-c eslint-plugin', + // projectDir + // ] + // await childProcess.exec(args.join(' ')) } } diff --git a/lib/controllers/update-controller-base.ts b/lib/controllers/update-controller-base.ts index 4d014be539..5919ebc3bd 100644 --- a/lib/controllers/update-controller-base.ts +++ b/lib/controllers/update-controller-base.ts @@ -64,13 +64,12 @@ export class UpdateControllerBase { dependency: IDependency, projectData: IProjectData ): boolean { - const devDependencies = projectData.devDependencies; - const dependencies = projectData.dependencies; + const devDependencies = Object.keys(projectData.devDependencies); + const dependencies = Object.keys(projectData.dependencies); - return ( - (dependencies && dependencies[dependency.packageName]) || - (devDependencies && devDependencies[dependency.packageName]) - ); + return [...devDependencies, ...dependencies].some((packageName) => { + return packageName === dependency.packageName; + }); } protected hasRuntimeDependency({ diff --git a/lib/definitions/migrate.d.ts b/lib/definitions/migrate.d.ts index dcb34d8341..c4a9f5e83e 100644 --- a/lib/definitions/migrate.d.ts +++ b/lib/definitions/migrate.d.ts @@ -10,15 +10,9 @@ interface IMigrateController { interface IMigrationData extends IProjectDir { platforms: string[]; - allowInvalidVersions?: boolean; + loose?: boolean; } -// declare const enum ShouldMigrate { -// NO, -// YES , -// ADVISED -// } - interface IMigrationShouldMigrate { shouldMigrate: ShouldMigrate; reasons: string[]; @@ -29,16 +23,21 @@ interface IDependency { isDev?: boolean; } -interface IMigrationDependency extends IDependency { +interface IDependencyVersion { + minVersion?: string; + desiredVersion?: string; +} + +interface IMigrationDependency extends IDependency, IDependencyVersion { shouldRemove?: boolean; replaceWith?: string; warning?: string; - verifiedVersion?: string; shouldUseExactVersion?: boolean; shouldAddIfMissing?: boolean; shouldMigrateAction?: ( + dependency: IMigrationDependency, projectData: IProjectData, - allowInvalidVersions: boolean + loose: boolean ) => Promise; migrateAction?: ( projectData: IProjectData, diff --git a/lib/services/project-cleanup-service.ts b/lib/services/project-cleanup-service.ts index e5ea46a652..ba62535471 100644 --- a/lib/services/project-cleanup-service.ts +++ b/lib/services/project-cleanup-service.ts @@ -27,6 +27,8 @@ export class ProjectCleanupService implements IProjectCleanupService { } public async cleanPath(pathToClean: string): Promise { + this.spinner.clear(); + if (!pathToClean || pathToClean.trim().length === 0) { this.$logger.trace("cleanPath called with no pathToClean."); return; @@ -38,7 +40,6 @@ export class ProjectCleanupService implements IProjectCleanupService { filePath )}`.yellow; - this.spinner.start(`Cleaning ${displayPath}`); this.$logger.trace(`Trying to clean '${filePath}'`); if (this.$fs.exists(filePath)) { @@ -46,25 +47,18 @@ export class ProjectCleanupService implements IProjectCleanupService { if (stat.isDirectory()) { this.$logger.trace(`Path '${filePath}' is a directory, deleting.`); - this.$fs.deleteDirectorySafe(filePath); - - this.spinner.text = `Cleaned directory ${displayPath}`; - this.spinner.succeed(); + this.spinner.succeed(`Cleaned directory ${displayPath}`); } else { this.$logger.trace(`Path '${filePath}' is a file, deleting.`); - this.$fs.deleteFile(filePath); - - this.spinner.text = `Cleaned file ${displayPath}`; - this.spinner.succeed(); + this.spinner.succeed(`Cleaned file ${displayPath}`); } return; } - this.$logger.trace(`Path '${filePath}' not found, skipping.`); - this.spinner.text = `Skipping ${displayPath} because it doesn't exist.`; - this.spinner.info(); + // this.spinner.text = `Skipping ${displayPath} because it doesn't exist.`; + // this.spinner.info(); } } diff --git a/tslint.json b/tslint.json index d394803d07..8211d1c9c6 100644 --- a/tslint.json +++ b/tslint.json @@ -18,7 +18,7 @@ "no-construct": true, "no-debugger": true, "no-duplicate-variable": true, - "no-shadowed-variable": true, + "no-shadowed-variable": false, "no-empty": true, "no-eval": true, "no-switch-case-fall-through": true, From 598f66d34d96ea762c85129144405fbffe1f2954 Mon Sep 17 00:00:00 2001 From: Igor Randjelovic Date: Wed, 31 Mar 2021 01:46:44 +0200 Subject: [PATCH 15/18] chore: remove console.logs --- lib/services/cocoapods-service.ts | 2 +- lib/services/webpack/webpack-compiler-service.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/services/cocoapods-service.ts b/lib/services/cocoapods-service.ts index 4e4a6dcad3..7c3e922026 100644 --- a/lib/services/cocoapods-service.ts +++ b/lib/services/cocoapods-service.ts @@ -61,7 +61,7 @@ export class CocoaPodsService implements ICocoaPodsService { const args = ["install"]; if (process.platform === "darwin" && process.arch === "arm64") { - console.log("Running on arm64 - running pod through rosetta2."); + this.$logger.trace("Running on arm64 - running pod through rosetta2."); args.unshift(podTool); args.unshift("-x86_64"); podTool = "arch"; diff --git a/lib/services/webpack/webpack-compiler-service.ts b/lib/services/webpack/webpack-compiler-service.ts index 5b309dffcd..737a4d8957 100644 --- a/lib/services/webpack/webpack-compiler-service.ts +++ b/lib/services/webpack/webpack-compiler-service.ts @@ -445,7 +445,7 @@ export class WebpackCompilerService envValue.map((value: any) => args.push(`--env.${item}=${value}`)); } }); - // console.log(args) + return args; } From b955be4015d1e765e8806114525cb25948fe3b79 Mon Sep 17 00:00:00 2001 From: Igor Randjelovic Date: Wed, 31 Mar 2021 18:46:32 +0200 Subject: [PATCH 16/18] chore: bump ios-device-lib --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index f1178ad0e6..d7f626b3e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5151,9 +5151,9 @@ "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" }, "ios-device-lib": { - "version": "0.9.0-alpha.0", - "resolved": "https://registry.npmjs.org/ios-device-lib/-/ios-device-lib-0.9.0-alpha.0.tgz", - "integrity": "sha512-y9PhpY2eJeNc/5uKdjFrvSJJPiQ9/RlvcsB/D1u0q2Kj9vueMnP2bhMvIXf+F+pvW1sv36ja0DMl8cV+kKDnkg==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/ios-device-lib/-/ios-device-lib-0.9.0.tgz", + "integrity": "sha512-cCY1zU7M7Vkq77FsFzXBDuWhlOu4RYWMko8ZJUcsp+S/lr3PeEbfKdKcl8a1j1RYokEQBhCrbBikd4pyn4chIw==", "requires": { "bufferpack": "0.0.6", "node-uuid": "1.4.7" diff --git a/package.json b/package.json index 62f1ec82f4..af5608d74a 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "esprima": "4.0.1", "font-finder": "1.1.0", "glob": "7.1.6", - "ios-device-lib": "0.9.0-alpha.0", + "ios-device-lib": "0.9.0", "ios-mobileprovision-finder": "1.0.11", "ios-sim-portable": "4.2.1", "istextorbinary": "5.9.0", From 74e192e2bce4870d73f23f97fa8d1d571b85a0c6 Mon Sep 17 00:00:00 2001 From: Igor Randjelovic Date: Wed, 31 Mar 2021 21:34:21 +0200 Subject: [PATCH 17/18] feat: allow defaultValue fallback in config getValue --- lib/services/project-config-service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/services/project-config-service.ts b/lib/services/project-config-service.ts index 47735f06e2..586f4df867 100644 --- a/lib/services/project-config-service.ts +++ b/lib/services/project-config-service.ts @@ -167,8 +167,8 @@ export default { } @exported("projectConfigService") - public getValue(key: string): any { - return _.get(this.readConfig(), key); + public getValue(key: string, defaultValue?: any): any { + return _.get(this.readConfig(), key, defaultValue); } @exported("projectConfigService") From 60c0f56887f22717433d907d243978ba14786d4e Mon Sep 17 00:00:00 2001 From: Igor Randjelovic Date: Wed, 31 Mar 2021 21:37:57 +0200 Subject: [PATCH 18/18] chore: adjust migration desiredVersions --- lib/controllers/migrate-controller.ts | 30 ++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/lib/controllers/migrate-controller.ts b/lib/controllers/migrate-controller.ts index f49ff72b92..37247d473b 100644 --- a/lib/controllers/migrate-controller.ts +++ b/lib/controllers/migrate-controller.ts @@ -129,7 +129,7 @@ export class MigrateController { packageName: constants.SCOPED_TNS_CORE_MODULES, minVersion: "6.5.0", - desiredVersion: "~8.0.0-alpha.9", + desiredVersion: "~8.0.0", shouldAddIfMissing: true, }, { @@ -139,7 +139,7 @@ export class MigrateController { packageName: "@nativescript/types", minVersion: "7.0.0", - desiredVersion: "~7.3.0", + desiredVersion: "~8.0.0", isDev: true, }, { @@ -165,14 +165,14 @@ export class MigrateController { packageName: constants.WEBPACK_PLUGIN_NAME, minVersion: "3.0.0", - desiredVersion: "~5.0.0-alpha.7", + desiredVersion: "~5.0.0-beta.0", shouldAddIfMissing: true, isDev: true, }, { packageName: "nativescript-vue", minVersion: "2.7.0", - desiredVersion: "~2.8.0", + desiredVersion: "~2.9.0", async shouldMigrateAction( dependency: IMigrationDependency, projectData: IProjectData, @@ -267,11 +267,11 @@ export class MigrateController return { [this.$devicePlatformsConstants.Android.toLowerCase()]: { minVersion: "6.5.3", - desiredVersion: "7.2.0", + desiredVersion: "8.0.0", }, [this.$devicePlatformsConstants.iOS.toLowerCase()]: { minVersion: "6.5.4", - desiredVersion: "7.2.0", + desiredVersion: "8.0.0", }, }; } @@ -923,9 +923,15 @@ export class MigrateController // in loose mode we check if we satisfy the min version if (loose) { + if (!installed || !min) { + return false; + } return semver.lt(installed, min); } + if (!installed || !desired) { + return true; + } // otherwise we compare with the desired version return semver.lt(installed, desired); } @@ -1046,7 +1052,7 @@ export class MigrateController ); this.spinner.info( - `Updating ${platform} platform to version '${verifiedPlatformVersion}'.` + `Updating ${platform} platform to version ${verifiedPlatformVersion.desiredVersion.green}.` ); await this.$addPlatformService.setPlatformVersion( @@ -1460,6 +1466,12 @@ export class MigrateController isDev: true, shouldAddIfMissing: true, }, + { + packageName: "nativescript-vue-devtools", + minVersion: "1.4.0", + desiredVersion: "~1.5.0", + isDev: true, + }, { packageName: "vue-loader", shouldRemove: true, @@ -1583,6 +1595,10 @@ export class MigrateController } ); } catch (err) { + this.$logger.trace( + "Failed to initialize webpack.config.js. Error is: ", + err + ); this.$logger.printMarkdown( `Failed to initialize \`webpack.config.js\`, you can try again by running \`npm install\` (or yarn, pnpm) and then \`npx @nativescript/webpack init\`.` );