Skip to content

Commit 72f0d56

Browse files
author
Dimitar Tachev
authored
Merge pull request #5037 from NativeScript/tachev/snapshots-in-docker
feat: support snapshots on Windows
2 parents b48208b + d50563d commit 72f0d56

File tree

8 files changed

+143
-107
lines changed

8 files changed

+143
-107
lines changed

lib/controllers/migrate-controller.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -341,11 +341,7 @@ Running this command will ${MigrateController.COMMON_MIGRATE_MESSAGE}`;
341341
}
342342

343343
private async shouldMigrateDependencyVersion(dependency: IMigrationDependency, projectData: IProjectData, allowInvalidVersions: boolean): Promise<boolean> {
344-
const devDependencies = projectData.devDependencies || {};
345-
const dependencies = projectData.dependencies || {};
346-
const packageName = dependency.packageName;
347-
const referencedVersion = dependencies[packageName] || devDependencies[packageName];
348-
const installedVersion = await this.getMaxDependencyVersion(dependency.packageName, referencedVersion);
344+
const installedVersion = await this.$packageInstallationManager.getInstalledDependencyVersion(dependency.packageName, projectData.projectDir);
349345
const requiredVersion = dependency.verifiedVersion;
350346

351347
return this.isOutdatedVersion(installedVersion, requiredVersion, allowInvalidVersions);

lib/controllers/update-controller-base.ts

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -56,23 +56,10 @@ export class UpdateControllerBase {
5656
const currentPlatformVersion = this.$platformCommandHelper.getCurrentPlatformVersion(lowercasePlatform, projectData);
5757
const platformData = this.$platformsDataService.getPlatformData(lowercasePlatform, projectData);
5858
if (currentPlatformVersion) {
59-
return await this.getMaxDependencyVersion(platformData.frameworkPackageName, currentPlatformVersion) || currentPlatformVersion;
59+
return await this.$packageInstallationManager.getMaxSatisfyingVersionSafe(platformData.frameworkPackageName, currentPlatformVersion) || currentPlatformVersion;
6060
}
6161
}
6262

63-
protected async getMaxDependencyVersion(dependency: string, version: string): Promise<string> {
64-
let maxDependencyVersion;
65-
if (semver.valid(version)) {
66-
maxDependencyVersion = version;
67-
} else if (semver.validRange(version)) {
68-
maxDependencyVersion = await this.$packageInstallationManager.maxSatisfyingVersion(dependency, version);
69-
} else {
70-
maxDependencyVersion = await this.$packageManager.getTagVersion(dependency, version);
71-
}
72-
73-
return maxDependencyVersion;
74-
}
75-
7663
private async _getPackageManifest(templateName: string, version: string): Promise<any> {
7764
const packageVersion = semver.valid(version) || await this.$packageManager.getTagVersion(templateName, version);
7865

lib/controllers/update-controller.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,8 @@ export class UpdateController extends UpdateControllerBase implements IUpdateCon
139139
const devDependencies = projectData.devDependencies || {};
140140
const dependencies = projectData.dependencies || {};
141141
const projectVersion = dependencies[dependency] || devDependencies[dependency];
142-
const maxSatisfyingTargetVersion = await this.getMaxDependencyVersion(dependency, targetVersion);
143-
const maxSatisfyingProjectVersion = await this.getMaxDependencyVersion(dependency, projectVersion);
142+
const maxSatisfyingTargetVersion = await this.$packageInstallationManager.getMaxSatisfyingVersionSafe(dependency, targetVersion);
143+
const maxSatisfyingProjectVersion = await this.$packageInstallationManager.getMaxSatisfyingVersionSafe(dependency, projectVersion);
144144
return maxSatisfyingProjectVersion && maxSatisfyingTargetVersion && semver.gt(maxSatisfyingTargetVersion, maxSatisfyingProjectVersion);
145145
}
146146

@@ -177,7 +177,7 @@ export class UpdateController extends UpdateControllerBase implements IUpdateCon
177177
return false;
178178
}
179179

180-
const maxTemplateRuntimeVersion = await this.getMaxDependencyVersion(frameworkPackageName, templateRuntimeVersion);
180+
const maxTemplateRuntimeVersion = await this.$packageInstallationManager.getMaxSatisfyingVersionSafe(frameworkPackageName, templateRuntimeVersion);
181181
const maxRuntimeVersion = await this.getMaxRuntimeVersion({ platform, projectData });
182182

183183
return maxTemplateRuntimeVersion && maxRuntimeVersion && semver.gt(maxTemplateRuntimeVersion, maxRuntimeVersion);

lib/declarations.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,12 @@ interface IPackageInstallationManager {
9898
getLatestVersion(packageName: string): Promise<string>;
9999
getNextVersion(packageName: string): Promise<string>;
100100
getLatestCompatibleVersion(packageName: string, referenceVersion?: string): Promise<string>;
101-
maxSatisfyingVersion(packageName: string, versionRange: string): Promise<string>;
101+
getMaxSatisfyingVersion(packageName: string, versionRange: string): Promise<string>;
102102
getLatestCompatibleVersionSafe(packageName: string, referenceVersion?: string): Promise<string>;
103103
getInspectorFromCache(inspectorNpmPackageName: string, projectDir: string): Promise<string>;
104104
clearInspectorCache(): void;
105+
getInstalledDependencyVersion(packageName: string, projectDir?: string): Promise<string>;
106+
getMaxSatisfyingVersionSafe(packageName: string, versionIdentifier: string): Promise<string>;
105107
}
106108

107109
/**

lib/package-installation-manager.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as constants from "./constants";
44

55
export class PackageInstallationManager implements IPackageInstallationManager {
66
constructor(
7-
private $packageManager: INodePackageManager,
7+
private $packageManager: IPackageManager,
88
private $childProcess: IChildProcess,
99
private $logger: ILogger,
1010
private $settingsService: ISettingsService,
@@ -33,15 +33,38 @@ export class PackageInstallationManager implements IPackageInstallationManager {
3333
return latestVersion;
3434
}
3535

36-
return await this.maxSatisfyingVersion(packageName, compatibleVersionRange) || latestVersion;
36+
return await this.getMaxSatisfyingVersion(packageName, compatibleVersionRange) || latestVersion;
3737
}
3838

39-
public async maxSatisfyingVersion(packageName: string, versionRange: string): Promise<string> {
39+
public async getMaxSatisfyingVersion(packageName: string, versionRange: string): Promise<string> {
4040
const data = await this.$packageManager.view(packageName, { "versions": true });
4141

4242
return semver.maxSatisfying(data, versionRange);
4343
}
4444

45+
public async getMaxSatisfyingVersionSafe(packageName: string, versionIdentifier: string): Promise<string> {
46+
let maxDependencyVersion;
47+
if (semver.valid(versionIdentifier)) {
48+
maxDependencyVersion = versionIdentifier;
49+
} else if (semver.validRange(versionIdentifier)) {
50+
maxDependencyVersion = await this.getMaxSatisfyingVersion(packageName, versionIdentifier);
51+
} else {
52+
maxDependencyVersion = await this.$packageManager.getTagVersion(packageName, versionIdentifier);
53+
}
54+
55+
return maxDependencyVersion;
56+
}
57+
58+
public async getInstalledDependencyVersion(packageName: string, projectDir?: string): Promise<string> {
59+
const projectData = this.$projectDataService.getProjectData(projectDir);
60+
const devDependencies = projectData.devDependencies || {};
61+
const dependencies = projectData.dependencies || {};
62+
const referencedVersion = dependencies[packageName] || devDependencies[packageName];
63+
const installedVersion = await this.getMaxSatisfyingVersionSafe(packageName, referencedVersion);
64+
65+
return installedVersion;
66+
}
67+
4568
public async getLatestCompatibleVersionSafe(packageName: string, referenceVersion?: string): Promise<string> {
4669
let version = "";
4770
const canGetVersionFromNpm = await this.$packageManager.isRegistered(packageName);

lib/services/webpack/webpack-compiler-service.ts

Lines changed: 98 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,21 @@ import * as child_process from "child_process";
33
import * as semver from "semver";
44
import { EventEmitter } from "events";
55
import { performanceLog } from "../../common/decorators";
6-
import { WEBPACK_COMPILATION_COMPLETE } from "../../constants";
6+
import { WEBPACK_COMPILATION_COMPLETE, WEBPACK_PLUGIN_NAME } from "../../constants";
77

88
export class WebpackCompilerService extends EventEmitter implements IWebpackCompilerService {
99
private webpackProcesses: IDictionary<child_process.ChildProcess> = {};
1010
private expectedHash: string = null;
1111

1212
constructor(
13+
private $errors: IErrors,
1314
private $childProcess: IChildProcess,
1415
public $hooksService: IHooksService,
1516
public $hostInfo: IHostInfo,
1617
private $logger: ILogger,
1718
private $mobileHelper: Mobile.IMobileHelper,
18-
private $cleanupService: ICleanupService
19+
private $cleanupService: ICleanupService,
20+
private $packageInstallationManager: IPackageInstallationManager
1921
) { super(); }
2022

2123
public async compileWithWatch(platformData: IPlatformData, projectData: IProjectData, prepareData: IPrepareData): Promise<any> {
@@ -27,67 +29,71 @@ export class WebpackCompilerService extends EventEmitter implements IWebpackComp
2729

2830
let isFirstWebpackWatchCompilation = true;
2931
prepareData.watch = true;
30-
const childProcess = await this.startWebpackProcess(platformData, projectData, prepareData);
32+
try {
33+
const childProcess = await this.startWebpackProcess(platformData, projectData, prepareData);
3134

32-
childProcess.on("message", (message: string | IWebpackEmitMessage) => {
33-
if (message === "Webpack compilation complete.") {
34-
this.$logger.info("Webpack build done!");
35-
resolve(childProcess);
36-
}
35+
childProcess.on("message", (message: string | IWebpackEmitMessage) => {
36+
if (message === "Webpack compilation complete.") {
37+
this.$logger.info("Webpack build done!");
38+
resolve(childProcess);
39+
}
3740

38-
message = message as IWebpackEmitMessage;
39-
if (message.emittedFiles) {
40-
if (isFirstWebpackWatchCompilation) {
41-
isFirstWebpackWatchCompilation = false;
41+
message = message as IWebpackEmitMessage;
42+
if (message.emittedFiles) {
43+
if (isFirstWebpackWatchCompilation) {
44+
isFirstWebpackWatchCompilation = false;
4245
this.expectedHash = message.hash;
43-
return;
44-
}
46+
return;
47+
}
4548

46-
let result;
49+
let result;
4750

48-
if (prepareData.hmr) {
51+
if (prepareData.hmr) {
4952
result = this.getUpdatedEmittedFiles(message.emittedFiles, message.chunkFiles, message.hash);
50-
} else {
51-
result = { emittedFiles: message.emittedFiles, fallbackFiles: <string[]>[], hash: "" };
53+
} else {
54+
result = { emittedFiles: message.emittedFiles, fallbackFiles: <string[]>[], hash: "" };
55+
}
56+
57+
const files = result.emittedFiles
58+
.map((file: string) => path.join(platformData.appDestinationDirectoryPath, "app", file));
59+
const fallbackFiles = result.fallbackFiles
60+
.map((file: string) => path.join(platformData.appDestinationDirectoryPath, "app", file));
61+
62+
const data = {
63+
files,
64+
hasOnlyHotUpdateFiles: files.every(f => f.indexOf("hot-update") > -1),
65+
hmrData: {
66+
hash: result.hash,
67+
fallbackFiles
68+
},
69+
platform: platformData.platformNameLowerCase
70+
};
71+
72+
if (data.files.length) {
73+
this.emit(WEBPACK_COMPILATION_COMPLETE, data);
74+
}
5275
}
76+
});
5377

54-
const files = result.emittedFiles
55-
.map((file: string) => path.join(platformData.appDestinationDirectoryPath, "app", file));
56-
const fallbackFiles = result.fallbackFiles
57-
.map((file: string) => path.join(platformData.appDestinationDirectoryPath, "app", file));
58-
59-
const data = {
60-
files,
61-
hasOnlyHotUpdateFiles: files.every(f => f.indexOf("hot-update") > -1),
62-
hmrData: {
63-
hash: result.hash,
64-
fallbackFiles
65-
},
66-
platform: platformData.platformNameLowerCase
67-
};
68-
69-
if (data.files.length) {
70-
this.emit(WEBPACK_COMPILATION_COMPLETE, data);
71-
}
72-
}
73-
});
78+
childProcess.on("error", (err) => {
79+
this.$logger.trace(`Unable to start webpack process in watch mode. Error is: ${err}`);
80+
delete this.webpackProcesses[platformData.platformNameLowerCase];
81+
reject(err);
82+
});
7483

75-
childProcess.on("error", (err) => {
76-
this.$logger.trace(`Unable to start webpack process in watch mode. Error is: ${err}`);
77-
delete this.webpackProcesses[platformData.platformNameLowerCase];
84+
childProcess.on("close", async (arg: any) => {
85+
await this.$cleanupService.removeKillProcess(childProcess.pid.toString());
86+
87+
const exitCode = typeof arg === "number" ? arg : arg && arg.code;
88+
this.$logger.trace(`Webpack process exited with code ${exitCode} when we expected it to be long living with watch.`);
89+
const error = new Error(`Executing webpack failed with exit code ${exitCode}.`);
90+
error.code = exitCode;
91+
delete this.webpackProcesses[platformData.platformNameLowerCase];
92+
reject(error);
93+
});
94+
} catch (err) {
7895
reject(err);
79-
});
80-
81-
childProcess.on("close", async (arg: any) => {
82-
await this.$cleanupService.removeKillProcess(childProcess.pid.toString());
83-
84-
const exitCode = typeof arg === "number" ? arg : arg && arg.code;
85-
this.$logger.trace(`Webpack process exited with code ${exitCode} when we expected it to be long living with watch.`);
86-
const error = new Error(`Executing webpack failed with exit code ${exitCode}.`);
87-
error.code = exitCode;
88-
delete this.webpackProcesses[platformData.platformNameLowerCase];
89-
reject(error);
90-
});
96+
}
9197
});
9298
}
9399

@@ -98,26 +104,30 @@ export class WebpackCompilerService extends EventEmitter implements IWebpackComp
98104
return;
99105
}
100106

101-
const childProcess = await this.startWebpackProcess(platformData, projectData, prepareData);
102-
childProcess.on("error", (err) => {
103-
this.$logger.trace(`Unable to start webpack process in non-watch mode. Error is: ${err}`);
104-
delete this.webpackProcesses[platformData.platformNameLowerCase];
107+
try {
108+
const childProcess = await this.startWebpackProcess(platformData, projectData, prepareData);
109+
childProcess.on("error", (err) => {
110+
this.$logger.trace(`Unable to start webpack process in non-watch mode. Error is: ${err}`);
111+
delete this.webpackProcesses[platformData.platformNameLowerCase];
112+
reject(err);
113+
});
114+
115+
childProcess.on("close", async (arg: any) => {
116+
await this.$cleanupService.removeKillProcess(childProcess.pid.toString());
117+
118+
delete this.webpackProcesses[platformData.platformNameLowerCase];
119+
const exitCode = typeof arg === "number" ? arg : arg && arg.code;
120+
if (exitCode === 0) {
121+
resolve();
122+
} else {
123+
const error = new Error(`Executing webpack failed with exit code ${exitCode}.`);
124+
error.code = exitCode;
125+
reject(error);
126+
}
127+
});
128+
} catch (err) {
105129
reject(err);
106-
});
107-
108-
childProcess.on("close", async (arg: any) => {
109-
await this.$cleanupService.removeKillProcess(childProcess.pid.toString());
110-
111-
delete this.webpackProcesses[platformData.platformNameLowerCase];
112-
const exitCode = typeof arg === "number" ? arg : arg && arg.code;
113-
if (exitCode === 0) {
114-
resolve();
115-
} else {
116-
const error = new Error(`Executing webpack failed with exit code ${exitCode}.`);
117-
error.code = exitCode;
118-
reject(error);
119-
}
120-
});
130+
}
121131
});
122132
}
123133

@@ -136,7 +146,7 @@ export class WebpackCompilerService extends EventEmitter implements IWebpackComp
136146
@performanceLog()
137147
private async startWebpackProcess(platformData: IPlatformData, projectData: IProjectData, prepareData: IPrepareData): Promise<child_process.ChildProcess> {
138148
const envData = this.buildEnvData(platformData.platformNameLowerCase, projectData, prepareData);
139-
const envParams = this.buildEnvCommandLineParams(envData, platformData, prepareData);
149+
const envParams = await this.buildEnvCommandLineParams(envData, platformData, projectData, prepareData);
140150
const additionalNodeArgs = semver.major(process.version) <= 8 ? ["--harmony"] : [];
141151

142152
const args = [
@@ -198,14 +208,22 @@ export class WebpackCompilerService extends EventEmitter implements IWebpackComp
198208
return envData;
199209
}
200210

201-
private buildEnvCommandLineParams(envData: any, platformData: IPlatformData, prepareData: IPrepareData) {
211+
private async buildEnvCommandLineParams(envData: any, platformData: IPlatformData, projectData: IProjectData, prepareData: IPrepareData) {
202212
const envFlagNames = Object.keys(envData);
203-
const shouldSnapshot = prepareData.release && !this.$hostInfo.isWindows && this.$mobileHelper.isAndroidPlatform(platformData.normalizedPlatformName);
204-
if (envData && envData.snapshot && !shouldSnapshot) {
205-
this.$logger.warn("Stripping the snapshot flag. " +
206-
"Bear in mind that snapshot is only available in release builds and " +
207-
"is NOT available on Windows systems.");
208-
envFlagNames.splice(envFlagNames.indexOf("snapshot"), 1);
213+
const canSnapshot = prepareData.release && this.$mobileHelper.isAndroidPlatform(platformData.normalizedPlatformName);
214+
if (envData && envData.snapshot) {
215+
if (!canSnapshot) {
216+
this.$logger.warn("Stripping the snapshot flag. " +
217+
"Bear in mind that snapshot is only available in Android release builds.");
218+
envFlagNames.splice(envFlagNames.indexOf("snapshot"), 1);
219+
} else if (this.$hostInfo.isWindows) {
220+
const minWebpackPluginWithWinSnapshotsVersion = "1.3.0";
221+
const installedWebpackPluginVersion = await this.$packageInstallationManager.getInstalledDependencyVersion(WEBPACK_PLUGIN_NAME, projectData.projectDir);
222+
const hasWebpackPluginWithWinSnapshotsSupport = !!installedWebpackPluginVersion ? semver.gte(installedWebpackPluginVersion, minWebpackPluginWithWinSnapshotsVersion) : true;
223+
if (!hasWebpackPluginWithWinSnapshotsSupport) {
224+
this.$errors.fail(`In order to generate Snapshots on Windows, please upgrade your Webpack plugin version (npm i nativescript-dev-webpack@latest).`);
225+
}
226+
}
209227
}
210228

211229
const args: any[] = [];

test/services/webpack/webpack-compiler-service.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ function createTestInjector(): IInjector {
1515
testInjector.register("hooksService", {});
1616
testInjector.register("hostInfo", {});
1717
testInjector.register("logger", {});
18+
testInjector.register("errors", {});
19+
testInjector.register("packageInstallationManager", {});
1820
testInjector.register("mobileHelper", {});
1921
testInjector.register("cleanupService", {});
2022

test/stubs.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,9 +249,17 @@ export class PackageInstallationManagerStub implements IPackageInstallationManag
249249
return Promise.resolve("");
250250
}
251251

252-
async maxSatisfyingVersion(): Promise<string> {
252+
async getMaxSatisfyingVersion(): Promise<string> {
253253
return "";
254254
}
255+
256+
async getInstalledDependencyVersion(packageName: string, projectDir?: string): Promise<string> {
257+
return Promise.resolve("");
258+
}
259+
260+
async getMaxSatisfyingVersionSafe(packageName: string, versionIdentifier: string): Promise<string> {
261+
return Promise.resolve(versionIdentifier);
262+
}
255263
}
256264

257265
export class NodePackageManagerStub implements INodePackageManager {

0 commit comments

Comments
 (0)