Skip to content
This repository was archived by the owner on Aug 7, 2021. It is now read-only.

Commit fe4abfb

Browse files
authored
feat(Angular): apply changes in application styles at runtime with HMR (#748)
* feat(Angular): apply changes in application styles at runtime with HMR * refactor(HMR): unify snippet for different flavors `global.__hmrInitialSync` is no more used `global.__hmrNeedReload` is removed: - on changes in multiple files (app.css, main-page.css, main-page.xml, main-page.ts) both *reapply styles* and *navigate* logic need to execute - this is important for the bootstrap of Angular apps * refactor(HMR): unify snippet for different flavors Remove `hmrUpdate()` from `main.ts` as a duplication of `global.__onLiveSync()`. `global.__onLiveSync()` ensures all patches get to the bundle. * fix(HMR): initial updates on app restart
1 parent a6c23da commit fe4abfb

File tree

6 files changed

+71
-46
lines changed

6 files changed

+71
-46
lines changed

Diff for: bundle-config-loader.js

+31-27
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,43 @@ module.exports = function (source) {
22
this.cacheable();
33
const { angular = false, loadCss = true, registerModules = /(root|page)\.(xml|css|js|ts|scss)$/ } = this.query;
44

5+
const hmr = `
6+
if (module.hot) {
7+
const hmrUpdate = require("nativescript-dev-webpack/hmr").hmrUpdate;
8+
let initialHmrUpdate = true;
9+
global.__hmrSyncBackup = global.__onLiveSync;
10+
11+
global.__onLiveSync = function () {
12+
hmrUpdate();
13+
};
14+
15+
global.__hmrRefresh = function({ type, module }) {
16+
if (initialHmrUpdate) {
17+
return;
18+
}
19+
20+
setTimeout(() => {
21+
global.__hmrSyncBackup({ type, module });
22+
});
23+
};
24+
25+
hmrUpdate().then(() =>{
26+
initialHmrUpdate = false;
27+
})
28+
}
29+
`;
30+
531
source = `
632
require("tns-core-modules/bundle-entry-points");
733
${source}
834
`;
935

10-
if (!angular && registerModules) {
11-
const hmr = `
12-
if (module.hot) {
13-
const fileSystemModule = require("tns-core-modules/file-system");
14-
const applicationFiles = fileSystemModule.knownFolders.currentApp();
15-
16-
global.__hmrLivesyncBackup = global.__onLiveSync;
17-
global.__onLiveSync = function () {
18-
console.log("HMR: Sync...");
19-
require("nativescript-dev-webpack/hot")(__webpack_require__.h(), (fileName) => applicationFiles.getFile(fileName));
20-
};
21-
22-
global.__hmrRefresh = function({ type, module }) {
23-
global.__hmrNeedReload = true;
24-
setTimeout(() => {
25-
if(global.__hmrNeedReload) {
26-
global.__hmrNeedReload = false;
27-
global.__hmrLivesyncBackup({ type, module });
28-
}
29-
});
30-
}
31-
32-
global.__hmrInitialSync = true; // needed to determine if we are performing initial sync
33-
global.__onLiveSync();
34-
}
36+
if (angular) {
37+
source = `
38+
${hmr}
39+
${source}
3540
`;
36-
41+
} else if (registerModules) {
3742
source = `
3843
${hmr}
3944
const context = require.context("~/", true, ${registerModules});
@@ -55,4 +60,3 @@ module.exports = function (source) {
5560

5661
this.callback(null, source);
5762
};
58-

Diff for: demo/AngularApp/app/main.ts

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,26 @@
11
// this import should be first in order to load some required settings (like globals and reflect-metadata)
22
import { platformNativeScriptDynamic } from "nativescript-angular/platform";
3+
import { AppOptions } from "nativescript-angular/platform-common";
34

45
import { AppModule } from "./app.module";
56

7+
let options: AppOptions = {};
8+
9+
if (module["hot"]) {
10+
options.hmrOptions = {
11+
moduleTypeFactory: () => AppModule,
12+
livesyncCallback: (platformReboot) => {
13+
setTimeout(platformReboot, 0);
14+
},
15+
}
16+
17+
// Path to your app module.
18+
// You might have to change it if your module is in a different place.
19+
module["hot"].accept(["./app.module"], global["__hmrRefresh"]);
20+
}
21+
622
// A traditional NativeScript application starts by initializing global objects, setting up global CSS rules, creating, and navigating to the main page.
723
// Angular applications need to take care of their own initialization: modules, components, directives, routes, DI providers.
824
// A NativeScript Angular app needs to make both paradigms work together, so we provide a wrapper platform object, platformNativeScriptDynamic,
925
// that sets up a NativeScript application and can bootstrap the Angular framework.
10-
platformNativeScriptDynamic().bootstrapModule(AppModule);
26+
platformNativeScriptDynamic(options).bootstrapModule(AppModule);

Diff for: demo/AngularApp/webpack.config.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -173,14 +173,15 @@ module.exports = env => {
173173
// tns-core-modules reads the app.css and its imports using css-loader
174174
{
175175
test: /[\/|\\]app\.css$/,
176-
use: {
177-
loader: "css-loader",
178-
options: { minimize: false, url: false },
179-
}
176+
use: [
177+
"nativescript-dev-webpack/style-hot-loader",
178+
{ loader: "css-loader", options: { minimize: false, url: false } }
179+
]
180180
},
181181
{
182182
test: /[\/|\\]app\.scss$/,
183183
use: [
184+
"nativescript-dev-webpack/style-hot-loader",
184185
{ loader: "css-loader", options: { minimize: false, url: false } },
185186
"sass-loader"
186187
]

Diff for: hmr/hmr-update.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ module.exports = () => {
22
const update = require("../hot");
33
const fileSystemModule = require("tns-core-modules/file-system");
44
const applicationFiles = fileSystemModule.knownFolders.currentApp();
5-
update(__webpack_require__["h"](), filename => applicationFiles.getFile(filename));
5+
return update(__webpack_require__["h"](), filename => applicationFiles.getFile(filename));
66
}

Diff for: hot.js

+12-9
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ function result(modules, appliedModules) {
7373
}
7474

7575
function check(options) {
76-
module.hot
76+
return module.hot
7777
.check()
7878
.then((modules) => {
7979
if (!modules) {
@@ -86,8 +86,9 @@ function check(options) {
8686
return module.hot
8787
.apply(hotOptions)
8888
.then((appliedModules) => {
89+
let nextCheck;
8990
if (!upToDate()) {
90-
check(options);
91+
nextCheck = check(options);
9192
}
9293

9394
result(modules, appliedModules);
@@ -96,6 +97,8 @@ function check(options) {
9697
//Do not modify message - CLI depends on this exact content to determine hmr operation status.
9798
log.info(`Successfully applied update with hmr hash ${currentHash}. App is up to date.`);
9899
}
100+
101+
return nextCheck || null;
99102
})
100103
.catch((err) => {
101104
const status = module.hot.status();
@@ -114,7 +117,7 @@ function check(options) {
114117
log.warn(`Cannot check for update. ${refresh}`);
115118
log.warn(err.stack || err.message);
116119
} else {
117-
log.warn(`Update check failed: ${err.stack|| err.message}`);
120+
log.warn(`Update check failed: ${err.stack || err.message}`);
118121
}
119122
});
120123
}
@@ -133,7 +136,7 @@ function update(latestHash, options) {
133136
if (status === 'idle') {
134137
//Do not modify message - CLI depends on this exact content to determine hmr operation status.
135138
log.info(`Checking for updates to the bundle with hmr hash ${currentHash}.`);
136-
check(options);
139+
return check(options);
137140
} else if (['abort', 'fail'].indexOf(status) >= 0) {
138141
log.warn(
139142
`Cannot apply update. A previous update ${status}ed. ${refresh}`
@@ -145,7 +148,7 @@ function update(latestHash, options) {
145148
function getNextHash(hash, getFileContent) {
146149
const file = getFileContent(`${hash}.hot-update.json`);
147150
return file.readText().then(hotUpdateContent => {
148-
if(hotUpdateContent) {
151+
if (hotUpdateContent) {
149152
const manifest = JSON.parse(hotUpdateContent);
150153
const newHash = manifest.h;
151154
return getNextHash(newHash, getFileContent);
@@ -157,9 +160,9 @@ function getNextHash(hash, getFileContent) {
157160

158161
module.exports = function checkState(initialHash, getFileContent) {
159162
currentHash = initialHash;
160-
getNextHash(initialHash, getFileContent).then(nextHash => {
161-
if(nextHash != initialHash) {
162-
update(nextHash, {});
163+
return getNextHash(initialHash, getFileContent).then(nextHash => {
164+
if (nextHash != initialHash) {
165+
return update(nextHash, {});
163166
}
164167
})
165-
}
168+
}

Diff for: templates/webpack.angular.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -194,14 +194,15 @@ module.exports = env => {
194194
// tns-core-modules reads the app.css and its imports using css-loader
195195
{
196196
test: /[\/|\\]app\.css$/,
197-
use: {
198-
loader: "css-loader",
199-
options: { minimize: false, url: false },
200-
}
197+
use: [
198+
"nativescript-dev-webpack/style-hot-loader",
199+
{ loader: "css-loader", options: { minimize: false, url: false } }
200+
]
201201
},
202202
{
203203
test: /[\/|\\]app\.scss$/,
204204
use: [
205+
"nativescript-dev-webpack/style-hot-loader",
205206
{ loader: "css-loader", options: { minimize: false, url: false } },
206207
"sass-loader"
207208
]

0 commit comments

Comments
 (0)