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

Commit

Permalink
feat(Angular): apply changes in application styles at runtime with HMR (
Browse files Browse the repository at this point in the history
#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
  • Loading branch information
vchimev authored Dec 27, 2018
1 parent a6c23da commit fe4abfb
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 46 deletions.
58 changes: 31 additions & 27 deletions bundle-config-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,43 @@ module.exports = function (source) {
this.cacheable();
const { angular = false, loadCss = true, registerModules = /(root|page)\.(xml|css|js|ts|scss)$/ } = this.query;

const hmr = `
if (module.hot) {
const hmrUpdate = require("nativescript-dev-webpack/hmr").hmrUpdate;
let initialHmrUpdate = true;
global.__hmrSyncBackup = global.__onLiveSync;
global.__onLiveSync = function () {
hmrUpdate();
};
global.__hmrRefresh = function({ type, module }) {
if (initialHmrUpdate) {
return;
}
setTimeout(() => {
global.__hmrSyncBackup({ type, module });
});
};
hmrUpdate().then(() =>{
initialHmrUpdate = false;
})
}
`;

source = `
require("tns-core-modules/bundle-entry-points");
${source}
`;

if (!angular && registerModules) {
const hmr = `
if (module.hot) {
const fileSystemModule = require("tns-core-modules/file-system");
const applicationFiles = fileSystemModule.knownFolders.currentApp();
global.__hmrLivesyncBackup = global.__onLiveSync;
global.__onLiveSync = function () {
console.log("HMR: Sync...");
require("nativescript-dev-webpack/hot")(__webpack_require__.h(), (fileName) => applicationFiles.getFile(fileName));
};
global.__hmrRefresh = function({ type, module }) {
global.__hmrNeedReload = true;
setTimeout(() => {
if(global.__hmrNeedReload) {
global.__hmrNeedReload = false;
global.__hmrLivesyncBackup({ type, module });
}
});
}
global.__hmrInitialSync = true; // needed to determine if we are performing initial sync
global.__onLiveSync();
}
if (angular) {
source = `
${hmr}
${source}
`;

} else if (registerModules) {
source = `
${hmr}
const context = require.context("~/", true, ${registerModules});
Expand All @@ -55,4 +60,3 @@ module.exports = function (source) {

this.callback(null, source);
};

18 changes: 17 additions & 1 deletion demo/AngularApp/app/main.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
// this import should be first in order to load some required settings (like globals and reflect-metadata)
import { platformNativeScriptDynamic } from "nativescript-angular/platform";
import { AppOptions } from "nativescript-angular/platform-common";

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

let options: AppOptions = {};

if (module["hot"]) {
options.hmrOptions = {
moduleTypeFactory: () => AppModule,
livesyncCallback: (platformReboot) => {
setTimeout(platformReboot, 0);
},
}

// Path to your app module.
// You might have to change it if your module is in a different place.
module["hot"].accept(["./app.module"], global["__hmrRefresh"]);
}

// A traditional NativeScript application starts by initializing global objects, setting up global CSS rules, creating, and navigating to the main page.
// Angular applications need to take care of their own initialization: modules, components, directives, routes, DI providers.
// A NativeScript Angular app needs to make both paradigms work together, so we provide a wrapper platform object, platformNativeScriptDynamic,
// that sets up a NativeScript application and can bootstrap the Angular framework.
platformNativeScriptDynamic().bootstrapModule(AppModule);
platformNativeScriptDynamic(options).bootstrapModule(AppModule);
9 changes: 5 additions & 4 deletions demo/AngularApp/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,14 +173,15 @@ module.exports = env => {
// tns-core-modules reads the app.css and its imports using css-loader
{
test: /[\/|\\]app\.css$/,
use: {
loader: "css-loader",
options: { minimize: false, url: false },
}
use: [
"nativescript-dev-webpack/style-hot-loader",
{ loader: "css-loader", options: { minimize: false, url: false } }
]
},
{
test: /[\/|\\]app\.scss$/,
use: [
"nativescript-dev-webpack/style-hot-loader",
{ loader: "css-loader", options: { minimize: false, url: false } },
"sass-loader"
]
Expand Down
2 changes: 1 addition & 1 deletion hmr/hmr-update.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ module.exports = () => {
const update = require("../hot");
const fileSystemModule = require("tns-core-modules/file-system");
const applicationFiles = fileSystemModule.knownFolders.currentApp();
update(__webpack_require__["h"](), filename => applicationFiles.getFile(filename));
return update(__webpack_require__["h"](), filename => applicationFiles.getFile(filename));
}
21 changes: 12 additions & 9 deletions hot.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ function result(modules, appliedModules) {
}

function check(options) {
module.hot
return module.hot
.check()
.then((modules) => {
if (!modules) {
Expand All @@ -86,8 +86,9 @@ function check(options) {
return module.hot
.apply(hotOptions)
.then((appliedModules) => {
let nextCheck;
if (!upToDate()) {
check(options);
nextCheck = check(options);
}

result(modules, appliedModules);
Expand All @@ -96,6 +97,8 @@ function check(options) {
//Do not modify message - CLI depends on this exact content to determine hmr operation status.
log.info(`Successfully applied update with hmr hash ${currentHash}. App is up to date.`);
}

return nextCheck || null;
})
.catch((err) => {
const status = module.hot.status();
Expand All @@ -114,7 +117,7 @@ function check(options) {
log.warn(`Cannot check for update. ${refresh}`);
log.warn(err.stack || err.message);
} else {
log.warn(`Update check failed: ${err.stack|| err.message}`);
log.warn(`Update check failed: ${err.stack || err.message}`);
}
});
}
Expand All @@ -133,7 +136,7 @@ function update(latestHash, options) {
if (status === 'idle') {
//Do not modify message - CLI depends on this exact content to determine hmr operation status.
log.info(`Checking for updates to the bundle with hmr hash ${currentHash}.`);
check(options);
return check(options);
} else if (['abort', 'fail'].indexOf(status) >= 0) {
log.warn(
`Cannot apply update. A previous update ${status}ed. ${refresh}`
Expand All @@ -145,7 +148,7 @@ function update(latestHash, options) {
function getNextHash(hash, getFileContent) {
const file = getFileContent(`${hash}.hot-update.json`);
return file.readText().then(hotUpdateContent => {
if(hotUpdateContent) {
if (hotUpdateContent) {
const manifest = JSON.parse(hotUpdateContent);
const newHash = manifest.h;
return getNextHash(newHash, getFileContent);
Expand All @@ -157,9 +160,9 @@ function getNextHash(hash, getFileContent) {

module.exports = function checkState(initialHash, getFileContent) {
currentHash = initialHash;
getNextHash(initialHash, getFileContent).then(nextHash => {
if(nextHash != initialHash) {
update(nextHash, {});
return getNextHash(initialHash, getFileContent).then(nextHash => {
if (nextHash != initialHash) {
return update(nextHash, {});
}
})
}
}
9 changes: 5 additions & 4 deletions templates/webpack.angular.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,14 +194,15 @@ module.exports = env => {
// tns-core-modules reads the app.css and its imports using css-loader
{
test: /[\/|\\]app\.css$/,
use: {
loader: "css-loader",
options: { minimize: false, url: false },
}
use: [
"nativescript-dev-webpack/style-hot-loader",
{ loader: "css-loader", options: { minimize: false, url: false } }
]
},
{
test: /[\/|\\]app\.scss$/,
use: [
"nativescript-dev-webpack/style-hot-loader",
{ loader: "css-loader", options: { minimize: false, url: false } },
"sass-loader"
]
Expand Down

0 comments on commit fe4abfb

Please sign in to comment.