Skip to content

Commit 65278fa

Browse files
author
Dimitar Tachev
authored
Merge pull request #5163 from NativeScript/tachev/fix-hmr-restart
fix: sync all files when a HMR update is not possible in order to avoid multiple HMR syncs after restart
2 parents 38e7dd7 + 875f1d2 commit 65278fa

File tree

12 files changed

+375
-491
lines changed

12 files changed

+375
-491
lines changed

Gruntfile.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,6 @@ module.exports = function (grunt) {
138138
"isWindows": true,
139139
"isMacOS": true,
140140
"isLinux": true,
141-
"formatListOfNames": () => { },
142141
"constants": ""
143142
}
144143
},

docs/man_pages/project/testing/test-init.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ General | `$ tns test init [--framework <Framework>]`
1717

1818
### Options
1919

20-
* `--framework <Framework>` - Sets the unit testing framework to install. The following frameworks are available: <%= formatListOfNames(getUnitTestingFrameworkNames(), 'and') %>.
20+
* `--framework <Framework>` - Sets the unit testing framework to install. The following frameworks are available: mocha, jasmine and qunit.
2121

2222
<% if(isHtml) { %>
2323

lib/common/services/micro-templating-service.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
import * as util from "util";
22
import * as os from "os";
33
import * as constants from "../../constants";
4-
import { formatListOfNames } from '../helpers';
54

65
export class MicroTemplateService implements IMicroTemplateService {
76
private dynamicCallRegex: RegExp;
87

9-
constructor(private $injector: IInjector,
10-
private $testInitializationService: ITestInitializationService) {
8+
constructor(private $injector: IInjector) {
119
// Injector's dynamicCallRegex doesn't have 'g' option, which we need here.
1210
// Use ( ) in order to use $1 to get whole expression later
1311
this.dynamicCallRegex = new RegExp(util.format("(%s)", this.$injector.dynamicCallRegex.source), "g");
@@ -37,8 +35,6 @@ export class MicroTemplateService implements IMicroTemplateService {
3735
localVariables["isMacOS"] = isHtml || this.isPlatform("darwin");
3836
localVariables["isConsole"] = !isHtml;
3937
localVariables["isHtml"] = isHtml;
40-
localVariables["formatListOfNames"] = formatListOfNames;
41-
localVariables["getUnitTestingFrameworkNames"] = this.$testInitializationService.getFrameworkNames;
4238
localVariables["isJekyll"] = false;
4339

4440
return localVariables;

lib/controllers/preview-app-controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ export class PreviewAppController extends EventEmitter implements IPreviewAppCon
141141
const platformHmrData = _.cloneDeep(hmrData) || <IPlatformHmrData>{};
142142
const connectedDevices = this.$previewDevicesService.getDevicesForPlatform(platform);
143143
if (!connectedDevices || !connectedDevices.length) {
144-
this.$logger.warn(`Unable to find any connected devices for platform '${platform}'. In order to execute live sync, open your Preview app and optionally re-scan the QR code using the Playground app.`);
144+
this.$logger.warn(`Unable to find any connected devices for platform '${platform}'. In order to execute livesync, open your Preview app and optionally re-scan the QR code using the Playground app.`);
145145
return;
146146
}
147147

lib/controllers/run-controller.ts

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,10 @@ export class RunController extends EventEmitter implements IRunController {
144144
return this.$liveSyncProcessDataService.getDeviceDescriptors(data.projectDir);
145145
}
146146

147-
protected async refreshApplication(projectData: IProjectData, liveSyncResultInfo: ILiveSyncResultInfo, filesChangeEventData: IFilesChangeEventData, deviceDescriptor: ILiveSyncDeviceDescriptor): Promise<IRestartApplicationInfo> {
147+
protected async refreshApplication(projectData: IProjectData, liveSyncResultInfo: ILiveSyncResultInfo, filesChangeEventData: IFilesChangeEventData, deviceDescriptor: ILiveSyncDeviceDescriptor, fullSyncAction?: () => Promise<void>): Promise<IRestartApplicationInfo> {
148148
const result = deviceDescriptor.debuggingEnabled ?
149149
await this.refreshApplicationWithDebug(projectData, liveSyncResultInfo, filesChangeEventData, deviceDescriptor) :
150-
await this.refreshApplicationWithoutDebug(projectData, liveSyncResultInfo, filesChangeEventData, deviceDescriptor);
150+
await this.refreshApplicationWithoutDebug(projectData, liveSyncResultInfo, filesChangeEventData, deviceDescriptor, undefined, fullSyncAction);
151151

152152
const device = liveSyncResultInfo.deviceAppData.device;
153153

@@ -181,14 +181,15 @@ export class RunController extends EventEmitter implements IRunController {
181181
}
182182

183183
@performanceLog()
184-
protected async refreshApplicationWithoutDebug(projectData: IProjectData, liveSyncResultInfo: ILiveSyncResultInfo, filesChangeEventData: IFilesChangeEventData, deviceDescriptor: ILiveSyncDeviceDescriptor, settings?: IRefreshApplicationSettings): Promise<IRestartApplicationInfo> {
184+
protected async refreshApplicationWithoutDebug(projectData: IProjectData, liveSyncResultInfo: ILiveSyncResultInfo, filesChangeEventData: IFilesChangeEventData, deviceDescriptor: ILiveSyncDeviceDescriptor, settings?: IRefreshApplicationSettings, fullSyncAction?: () => Promise<void>): Promise<IRestartApplicationInfo> {
185185
const result = { didRestart: false };
186186
const platform = liveSyncResultInfo.deviceAppData.platform;
187187
const applicationIdentifier = projectData.projectIdentifiers[platform.toLowerCase()];
188188
const platformLiveSyncService = this.$liveSyncServiceResolver.resolveLiveSyncService(platform);
189189

190190
try {
191-
let shouldRestart = filesChangeEventData && (filesChangeEventData.hasNativeChanges || !filesChangeEventData.hasOnlyHotUpdateFiles);
191+
const isFullSync = filesChangeEventData && (filesChangeEventData.hasNativeChanges || !filesChangeEventData.hasOnlyHotUpdateFiles);
192+
let shouldRestart = isFullSync;
192193
if (!shouldRestart) {
193194
shouldRestart = await platformLiveSyncService.shouldRestart(projectData, liveSyncResultInfo);
194195
}
@@ -197,6 +198,12 @@ export class RunController extends EventEmitter implements IRunController {
197198
shouldRestart = !await platformLiveSyncService.tryRefreshApplication(projectData, liveSyncResultInfo);
198199
}
199200

201+
if (!isFullSync && shouldRestart && fullSyncAction) {
202+
this.$logger.trace(`Syncing all files as the current app state does not support hot updates.`);
203+
liveSyncResultInfo.didRecover = true;
204+
await fullSyncAction();
205+
}
206+
200207
if (shouldRestart) {
201208
this.emit(DEBUGGER_DETACHED_EVENT_NAME, { deviceIdentifier: liveSyncResultInfo.deviceAppData.device.deviceInfo.identifier });
202209
await platformLiveSyncService.restartApplication(projectData, liveSyncResultInfo);
@@ -360,11 +367,14 @@ export class RunController extends EventEmitter implements IRunController {
360367

361368
try {
362369
const platformLiveSyncService = this.$liveSyncServiceResolver.resolveLiveSyncService(device.deviceInfo.platform);
370+
const allAppFiles = (data.hmrData && data.hmrData.fallbackFiles && data.hmrData.fallbackFiles.length) ?
371+
data.hmrData.fallbackFiles : data.files;
372+
const filesToSync = data.hasOnlyHotUpdateFiles ? data.files : allAppFiles;
363373
const watchInfo = {
364374
liveSyncDeviceData: deviceDescriptor,
365375
projectData,
366376
filesToRemove: <any>[],
367-
filesToSync: data.files,
377+
filesToSync,
368378
hmrData: data.hmrData,
369379
useHotModuleReload: liveSyncInfo.useHotModuleReload,
370380
force: liveSyncInfo.force,
@@ -391,16 +401,21 @@ export class RunController extends EventEmitter implements IRunController {
391401
}
392402

393403
const watchAction = async (): Promise<void> => {
394-
let liveSyncResultInfo = await platformLiveSyncService.liveSyncWatchAction(device, watchInfo);
395-
await this.refreshApplication(projectData, liveSyncResultInfo, data, deviceDescriptor);
404+
const liveSyncResultInfo = await platformLiveSyncService.liveSyncWatchAction(device, watchInfo);
405+
const fullSyncAction = async () => {
406+
watchInfo.filesToSync = allAppFiles;
407+
const fullLiveSyncResultInfo = await platformLiveSyncService.liveSyncWatchAction(device, watchInfo);
408+
// IMPORTANT: keep the same instance as we rely on side effects
409+
_.assign(liveSyncResultInfo, fullLiveSyncResultInfo);
410+
};
411+
412+
await this.refreshApplication(projectData, liveSyncResultInfo, data, deviceDescriptor, fullSyncAction);
396413

397414
if (!liveSyncResultInfo.didRecover && isInHMRMode) {
398415
const status = await this.$hmrStatusService.getHmrStatus(device.deviceInfo.identifier, data.hmrData.hash);
399-
// error or timeout
400-
if (status !== HmrConstants.HMR_SUCCESS_STATUS) {
401-
watchInfo.filesToSync = data.hmrData.fallbackFiles;
402-
liveSyncResultInfo = await platformLiveSyncService.liveSyncWatchAction(device, watchInfo);
403-
// We want to force a restart of the application.
416+
// the timeout is assumed OK as the app could be blocked on a breakpoint
417+
if (status === HmrConstants.HMR_ERROR_STATUS) {
418+
await fullSyncAction();
404419
liveSyncResultInfo.isFullSync = true;
405420
await this.refreshApplication(projectData, liveSyncResultInfo, data, deviceDescriptor);
406421
}

lib/options.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ export class Options {
4444
// on each livesync in order to stop and allow debugging on app start
4545
this.argv.hmr = false;
4646
}
47+
48+
if (this.argv.justlaunch) {
49+
this.argv.hmr = false;
50+
}
4751
}
4852

4953
constructor(private $errors: IErrors,

lib/services/livesync/android-livesync-service.ts

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,6 @@ export class AndroidLiveSyncService extends PlatformLiveSyncServiceBase implemen
2525

2626
@performanceLog()
2727
public async liveSyncWatchAction(device: Mobile.IDevice, liveSyncInfo: ILiveSyncWatchInfo): Promise<IAndroidLiveSyncResultInfo> {
28-
let result = await this.liveSyncWatchActionCore(device, liveSyncInfo);
29-
30-
// When we use hmr, there is only one case when result.didRefresh is false.
31-
// This is the case when the app has crashed and is in ErrorActivity.
32-
// As the app might not have time to apply the patches, we will send the whole bundle.js(fallbackFiles)
33-
if (liveSyncInfo.useHotModuleReload && !result.didRefresh && liveSyncInfo.hmrData && liveSyncInfo.hmrData.hash) {
34-
liveSyncInfo.filesToSync = liveSyncInfo.hmrData.fallbackFiles;
35-
result = await this.liveSyncWatchActionCore(device, liveSyncInfo);
36-
result.didRecover = true;
37-
}
38-
39-
return result;
40-
}
41-
42-
private async liveSyncWatchActionCore(device: Mobile.IDevice, liveSyncInfo: ILiveSyncWatchInfo): Promise<IAndroidLiveSyncResultInfo> {
4328
const liveSyncResult = await super.liveSyncWatchAction(device, liveSyncInfo);
4429
const result = await this.finalizeSync(device, liveSyncInfo.projectData, liveSyncResult);
4530

lib/services/livesync/ios-device-livesync-service.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export class IOSDeviceLiveSyncService extends DeviceLiveSyncServiceBase implemen
7676
shouldRestart = true;
7777
} else {
7878
const canExecuteFastSync = this.canExecuteFastSyncForPaths(liveSyncInfo, localToDevicePaths, projectData, deviceAppData.platform);
79-
const isRefreshConnectionSetup = this.canRefreshWithNotification(projectData, liveSyncInfo) || (!this.device.isOnlyWiFiConnected && await this.setupSocketIfNeeded(projectData));
79+
const isRefreshConnectionSetup = this.canRefreshWithNotification(projectData, liveSyncInfo) || (!this.device.isOnlyWiFiConnected && this.socket);
8080
if (!canExecuteFastSync || !isRefreshConnectionSetup) {
8181
shouldRestart = true;
8282
}
@@ -137,6 +137,11 @@ export class IOSDeviceLiveSyncService extends DeviceLiveSyncServiceBase implemen
137137
waitForDebugger: liveSyncInfo.waitForDebugger,
138138
projectDir: projectData.projectDir
139139
});
140+
141+
if (liveSyncInfo.useHotModuleReload) {
142+
// enable HOT updates
143+
await this.setupSocketIfNeeded(projectData);
144+
}
140145
}
141146

142147
private async reloadPage(): Promise<void> {

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,8 +272,9 @@ export class WebpackCompilerService extends EventEmitter implements IWebpackComp
272272
this.expectedHashes[platform] = nextHash;
273273

274274
const emittedHotUpdatesAndAssets = isHashValid ? _.difference(allEmittedFiles, chunkFiles) : allEmittedFiles;
275+
const fallbackFiles = chunkFiles.concat(emittedHotUpdatesAndAssets.filter(f => f.indexOf("hot-update") === -1));
275276

276-
return { emittedFiles: emittedHotUpdatesAndAssets, fallbackFiles: chunkFiles, hash: currentHash };
277+
return { emittedFiles: emittedHotUpdatesAndAssets, fallbackFiles, hash: currentHash };
277278
}
278279

279280
private getCurrentHotUpdateHash(emittedFiles: string[]) {

0 commit comments

Comments
 (0)