From 647b0f62286af28a9ad5af3ef22f5ea2af7323d1 Mon Sep 17 00:00:00 2001 From: fatme Date: Fri, 18 Oct 2019 08:49:18 +0300 Subject: [PATCH] fix: persist the next hmr hash per platform The NativeScript CLI persists the hash of the next hmr compilation locally and based on that decides if the reported hash is valid or not. However, in case when `tns run` command is executed, the application is randomly restarted on some devices. This happens as in this situation we have 2 webpack processes but the hmr hash is not persisted per platform. Rel to: https://github.com/NativeScript/nativescript-cli/issues/5080 --- .../webpack/webpack-compiler-service.ts | 12 +++--- .../webpack/webpack-compiler-service.ts | 37 ++++++++++++++----- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/lib/services/webpack/webpack-compiler-service.ts b/lib/services/webpack/webpack-compiler-service.ts index 2603f5a762..4f0dd015da 100644 --- a/lib/services/webpack/webpack-compiler-service.ts +++ b/lib/services/webpack/webpack-compiler-service.ts @@ -7,7 +7,7 @@ import { WEBPACK_COMPILATION_COMPLETE, WEBPACK_PLUGIN_NAME } from "../../constan export class WebpackCompilerService extends EventEmitter implements IWebpackCompilerService { private webpackProcesses: IDictionary = {}; - private expectedHash: string = null; + private expectedHashes: IStringDictionary = {}; constructor( private $errors: IErrors, @@ -42,14 +42,14 @@ export class WebpackCompilerService extends EventEmitter implements IWebpackComp if (message.emittedFiles) { if (isFirstWebpackWatchCompilation) { isFirstWebpackWatchCompilation = false; - this.expectedHash = message.hash; + this.expectedHashes[platformData.platformNameLowerCase] = message.hash; return; } let result; if (prepareData.hmr) { - result = this.getUpdatedEmittedFiles(message.emittedFiles, message.chunkFiles, message.hash); + result = this.getUpdatedEmittedFiles(message.emittedFiles, message.chunkFiles, message.hash, platformData.platformNameLowerCase); } else { result = { emittedFiles: message.emittedFiles, fallbackFiles: [], hash: "" }; } @@ -248,7 +248,7 @@ export class WebpackCompilerService extends EventEmitter implements IWebpackComp return args; } - public getUpdatedEmittedFiles(allEmittedFiles: string[], chunkFiles: string[], nextHash: string) { + public getUpdatedEmittedFiles(allEmittedFiles: string[], chunkFiles: string[], nextHash: string, platform: string) { const currentHash = this.getCurrentHotUpdateHash(allEmittedFiles); // This logic is needed as there are already cases when webpack doesn't emit any files physically. @@ -260,8 +260,8 @@ export class WebpackCompilerService extends EventEmitter implements IWebpackComp // if files will be emitted or not. This way, the first successful compilation after fixing the compilation error generates // a hash that is not the same as the one expected in the latest emitted hot-update.json file. // As a result, the hmr chain is broken and the changes are not applied. - const isHashValid = nextHash ? this.expectedHash === currentHash : true; - this.expectedHash = nextHash; + const isHashValid = nextHash ? this.expectedHashes[platform] === currentHash : true; + this.expectedHashes[platform] = nextHash; const emittedHotUpdatesAndAssets = isHashValid ? _.difference(allEmittedFiles, chunkFiles) : allEmittedFiles; diff --git a/test/services/webpack/webpack-compiler-service.ts b/test/services/webpack/webpack-compiler-service.ts index e23a701fa4..22fb85686d 100644 --- a/test/services/webpack/webpack-compiler-service.ts +++ b/test/services/webpack/webpack-compiler-service.ts @@ -2,6 +2,8 @@ import { Yok } from "../../../lib/common/yok"; import { WebpackCompilerService } from "../../../lib/services/webpack/webpack-compiler-service"; import { assert } from "chai"; +const iOSPlatformName = "ios"; +const androidPlatformName = "android"; const chunkFiles = ["bundle.js", "runtime.js", "vendor.js"]; function getAllEmittedFiles(hash: string) { @@ -35,39 +37,54 @@ describe("WebpackCompilerService", () => { describe("getUpdatedEmittedFiles", () => { // backwards compatibility with old versions of nativescript-dev-webpack it("should return only hot updates when nextHash is not provided", async () => { - const result = webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash1"), chunkFiles, null); + const result = webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash1"), chunkFiles, null, iOSPlatformName); const expectedEmittedFiles = ['bundle.hash1.hot-update.js', 'hash1.hot-update.json']; assert.deepEqual(result.emittedFiles, expectedEmittedFiles); }); // 2 successful webpack compilations it("should return only hot updates when nextHash is provided", async () => { - webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash1"), chunkFiles, "hash2"); - const result = webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash2"), chunkFiles, "hash3"); + webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash1"), chunkFiles, "hash2", iOSPlatformName); + const result = webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash2"), chunkFiles, "hash3", iOSPlatformName); assert.deepEqual(result.emittedFiles, ['bundle.hash2.hot-update.js', 'hash2.hot-update.json']); }); // 1 successful webpack compilation, n compilations with no emitted files it("should return all files when there is a webpack compilation with no emitted files", () => { - webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash1"), chunkFiles, "hash2"); - const result = webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash4"), chunkFiles, "hash5"); + webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash1"), chunkFiles, "hash2", iOSPlatformName); + const result = webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash4"), chunkFiles, "hash5", iOSPlatformName); assert.deepEqual(result.emittedFiles, ['bundle.js', 'runtime.js', 'bundle.hash4.hot-update.js', 'hash4.hot-update.json']); }); // 1 successful webpack compilation, n compilations with no emitted files, 1 successful webpack compilation it("should return only hot updates after fixing the compilation error", () => { - webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash1"), chunkFiles, "hash2"); - webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash5"), chunkFiles, "hash6"); - const result = webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash6"), chunkFiles, "hash7"); + webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash1"), chunkFiles, "hash2", iOSPlatformName); + webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash5"), chunkFiles, "hash6", iOSPlatformName); + const result = webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash6"), chunkFiles, "hash7", iOSPlatformName); assert.deepEqual(result.emittedFiles, ['bundle.hash6.hot-update.js', 'hash6.hot-update.json']); }); // 1 webpack compilation with no emitted files it("should return all files when first compilation on livesync change is not successful", () => { - (webpackCompilerService).expectedHash = "hash1"; - const result = webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash1"), chunkFiles, "hash2"); + (webpackCompilerService).expectedHashes = { + "ios": "hash1" + }; + const result = webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash1"), chunkFiles, "hash2", iOSPlatformName); assert.deepEqual(result.emittedFiles, ["bundle.hash1.hot-update.js", "hash1.hot-update.json"]); }); + it("should return correct hashes when there are more than one platform", () => { + webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash1"), chunkFiles, "hash2", iOSPlatformName); + webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash3"), chunkFiles, "hash4", androidPlatformName); + + webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash2"), chunkFiles, "hash5", iOSPlatformName); + webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash4"), chunkFiles, "hash6", androidPlatformName); + + const iOSResult = webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash5"), chunkFiles, "hash7", iOSPlatformName); + assert.deepEqual(iOSResult.emittedFiles, ["bundle.hash5.hot-update.js", "hash5.hot-update.json"]); + + const androidResult = webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash6"), chunkFiles, "hash8", androidPlatformName); + assert.deepEqual(androidResult.emittedFiles, ["bundle.hash6.hot-update.js", "hash6.hot-update.json"]); + }); }); });