From 603eb9d3e465e478c6e4b024374738efb0d2d60c Mon Sep 17 00:00:00 2001 From: Dimitar Kerezov Date: Thu, 1 Feb 2018 14:03:34 +0200 Subject: [PATCH 1/5] feat(livesync): enable webpack with watch Plug into NativeScript CLI's LiveSync pipeline in order to enable incremental webpacking with watch. Includes: * Instruct CLI to watch `App_Resources` directory only * Launch `webpack` with `--watch` on before-watch hook * Do not launch multiple `webpack` processes --- lib/before-prepareJS.js | 2 +- lib/before-watch.js | 21 +++++++++++++++++++++ lib/before-watchPatterns.js | 20 ++++++++++++++++++++ lib/compiler.js | 27 ++++++++++++++++++++------- package.json | 10 ++++++++++ plugins/WatchStateLoggerPlugin.ts | 11 +++++++++-- templates/webpack.angular.js | 3 +++ templates/webpack.javascript.js | 3 +++ templates/webpack.typescript.js | 3 +++ 9 files changed, 90 insertions(+), 10 deletions(-) create mode 100644 lib/before-watch.js create mode 100644 lib/before-watchPatterns.js diff --git a/lib/before-prepareJS.js b/lib/before-prepareJS.js index d7b69a61..55f9d94f 100644 --- a/lib/before-prepareJS.js +++ b/lib/before-prepareJS.js @@ -8,7 +8,7 @@ module.exports = function ($mobileHelper, $projectData, hookArgs) { env, platform, bundle: appFilesUpdaterOptions.bundle, - watch: false // TODO: Read from CLI options... + watch: false }; const result = config.bundle && runWebpackCompiler.bind(runWebpackCompiler, config, $mobileHelper, $projectData, hookArgs); return result; diff --git a/lib/before-watch.js b/lib/before-watch.js new file mode 100644 index 00000000..d64acb59 --- /dev/null +++ b/lib/before-watch.js @@ -0,0 +1,21 @@ +const { runWebpackCompiler } = require("./compiler"); + +module.exports = function ($mobileHelper, $projectData, hookArgs) { + if (hookArgs.config) { + const appFilesUpdaterOptions = hookArgs.config.appFilesUpdaterOptions; + if (appFilesUpdaterOptions.bundle) { + const platforms = hookArgs.config.platforms; + platforms.forEach(platform => { + const env = hookArgs.config.env || {}; + const config = { + env, + platform, + bundle: appFilesUpdaterOptions.bundle, + watch: true + }; + + runWebpackCompiler(config, $mobileHelper, $projectData, hookArgs); + }); + } + } +} diff --git a/lib/before-watchPatterns.js b/lib/before-watchPatterns.js new file mode 100644 index 00000000..bc1c2f03 --- /dev/null +++ b/lib/before-watchPatterns.js @@ -0,0 +1,20 @@ +module.exports = function (hookArgs) { + if (hookArgs.liveSyncData && hookArgs.liveSyncData.bundle) { + const appDirectoryLocation = "app"; // Might need to get this from config file in the future + const appResourcesDirectoryLocation = "app/App_Resources"; // Might need to get this from config file in the future + return (args, originalMethod) => { + return originalMethod().then(originalPatterns => { + const appDirectoryLocationIndex = originalPatterns.indexOf(appDirectoryLocation); + if (appDirectoryLocationIndex !== -1) { + originalPatterns.splice(appDirectoryLocationIndex, 1); + } + + if (originalPatterns.indexOf(appResourcesDirectoryLocation) === -1) { + originalPatterns.push(appResourcesDirectoryLocation); + } + + return originalPatterns; + }); + }; + } +} diff --git a/lib/compiler.js b/lib/compiler.js index f30173d8..cdf097da 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -4,6 +4,7 @@ const { join, resolve: pathResolve } = require("path"); const { existsSync } = require("fs"); const readline = require("readline"); const { messages } = require("../plugins/WatchStateLoggerPlugin"); +const ProjectSnapshotGenerator = require("../snapshot/android/project-snapshot-generator"); let hasBeenInvoked = false; @@ -16,6 +17,10 @@ exports.getWebpackProcess = function getWebpackProcess() { exports.runWebpackCompiler = function runWebpackCompiler(config, $mobileHelper, $projectData, hookArgs, originalArgs, originalMethod) { if (config.bundle) { return new Promise(function (resolveBase, rejectBase) { + if (webpackProcess) { + return resolveBase(); + } + if (hookArgs && hookArgs.config && hookArgs.config.changesInfo) { hookArgs.config.changesInfo.nativeChanged = true; } @@ -24,17 +29,11 @@ exports.runWebpackCompiler = function runWebpackCompiler(config, $mobileHelper, function resolve() { if (isResolved) return; isResolved = true; - if (childProcess) { - childProcess.removeListener("message", resolveOnWebpackCompilationComplete); - } resolveBase(); } function reject(error) { if (isResolved) return; isResolved = true; - if (childProcess) { - childProcess.removeListener("message", resolveOnWebpackCompilationComplete); - } rejectBase(error); } @@ -75,11 +74,25 @@ exports.runWebpackCompiler = function runWebpackCompiler(config, $mobileHelper, cwd: $projectData.projectDir }); + let isFirstWebpackWatchCompilation = true; function resolveOnWebpackCompilationComplete(message) { if (message === messages.compilationComplete) { - console.log("Initial webpack build done!"); + console.log("Webpack build done!"); resolve(); } + + if (message.emittedFiles) { + if (isFirstWebpackWatchCompilation) { + isFirstWebpackWatchCompilation = false; + return; + } + + if (hookArgs.filesToSync && hookArgs.startSyncFilesTimeout) { + // TODO: Might need to get the "app" constant from config file in the future + hookArgs.filesToSync.push(...message.emittedFiles.map(emittedFile => join("app", emittedFile))); + hookArgs.startSyncFilesTimeout(); + } + } } if (config.watch) { diff --git a/package.json b/package.json index e4a07c5d..69b41d5b 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,16 @@ "script": "lib/before-cleanApp.js", "inject": true }, + { + "type": "before-watch", + "script": "lib/before-watch.js", + "inject": true + }, + { + "type": "before-watchPatterns", + "script": "lib/before-watchPatterns.js", + "inject": true + }, { "type": "after-prepare", "script": "lib/after-prepare.js", diff --git a/plugins/WatchStateLoggerPlugin.ts b/plugins/WatchStateLoggerPlugin.ts index 9bda54a0..e7264f45 100644 --- a/plugins/WatchStateLoggerPlugin.ts +++ b/plugins/WatchStateLoggerPlugin.ts @@ -13,7 +13,7 @@ export class WatchStateLoggerPlugin { isRunningWatching: boolean; apply(compiler) { const plugin = this; - compiler.plugin("watch-run", function(compiler, callback) { + compiler.plugin("watch-run", function (compiler, callback) { plugin.isRunningWatching = true; if (plugin.isRunningWatching) { console.log(messages.changeDetected); @@ -21,14 +21,21 @@ export class WatchStateLoggerPlugin { process.send && process.send(messages.changeDetected, error => null); callback(); }); - compiler.plugin("after-emit", function(compilation, callback) { + compiler.plugin("after-emit", function (compilation, callback) { callback(); if (plugin.isRunningWatching) { console.log(messages.startWatching); } else { console.log(messages.compilationComplete); } + + const emittedFiles = Object + .keys(compilation.assets) + .filter(assetKey => compilation.assets[assetKey].emitted); + process.send && process.send(messages.compilationComplete, error => null); + // Send emitted files so they can be LiveSynced if need be + process.send && process.send({ emittedFiles }, error => null); }); } } diff --git a/templates/webpack.angular.js b/templates/webpack.angular.js index 56434bcb..d280a5c0 100644 --- a/templates/webpack.angular.js +++ b/templates/webpack.angular.js @@ -20,6 +20,9 @@ module.exports = env => { const config = { context: resolve("./app"), + watchOptions: { + ignored: resolve("./app/App_Resources") + }, target: nativescriptTarget, entry: { bundle: aot ? "./main.aot.ts" : "./main.ts", diff --git a/templates/webpack.javascript.js b/templates/webpack.javascript.js index 73127aa4..aa290210 100644 --- a/templates/webpack.javascript.js +++ b/templates/webpack.javascript.js @@ -18,6 +18,9 @@ module.exports = env => { const config = { context: resolve("./app"), + watchOptions: { + ignored: resolve("./app/App_Resources") + }, target: nativescriptTarget, entry: { bundle: `./${nsWebpack.getEntryModule()}`, diff --git a/templates/webpack.typescript.js b/templates/webpack.typescript.js index c0129d7b..ec62f1a5 100644 --- a/templates/webpack.typescript.js +++ b/templates/webpack.typescript.js @@ -18,6 +18,9 @@ module.exports = env => { const config = { context: resolve("./app"), + watchOptions: { + ignored: resolve("./app/App_Resources") + }, target: nativescriptTarget, entry: { bundle: `./${nsWebpack.getEntryModule()}`, From e56de67cbaaca96fdbaf90d45d85de77ef9ae7c4 Mon Sep 17 00:00:00 2001 From: Dimitar Kerezov Date: Fri, 9 Feb 2018 12:01:12 +0200 Subject: [PATCH 2/5] refactor(livesync): extract app as constant --- lib/before-prepareJS.js | 3 +-- lib/before-watchPatterns.js | 10 +++------- lib/compiler.js | 5 ++--- lib/constants.js | 3 +++ 4 files changed, 9 insertions(+), 12 deletions(-) create mode 100644 lib/constants.js diff --git a/lib/before-prepareJS.js b/lib/before-prepareJS.js index 55f9d94f..c490955a 100644 --- a/lib/before-prepareJS.js +++ b/lib/before-prepareJS.js @@ -7,8 +7,7 @@ module.exports = function ($mobileHelper, $projectData, hookArgs) { const config = { env, platform, - bundle: appFilesUpdaterOptions.bundle, - watch: false + bundle: appFilesUpdaterOptions.bundle }; const result = config.bundle && runWebpackCompiler.bind(runWebpackCompiler, config, $mobileHelper, $projectData, hookArgs); return result; diff --git a/lib/before-watchPatterns.js b/lib/before-watchPatterns.js index bc1c2f03..0f3d89cf 100644 --- a/lib/before-watchPatterns.js +++ b/lib/before-watchPatterns.js @@ -1,18 +1,14 @@ +const { AppDirectoryLocation } = require("./constants"); + module.exports = function (hookArgs) { if (hookArgs.liveSyncData && hookArgs.liveSyncData.bundle) { - const appDirectoryLocation = "app"; // Might need to get this from config file in the future - const appResourcesDirectoryLocation = "app/App_Resources"; // Might need to get this from config file in the future return (args, originalMethod) => { return originalMethod().then(originalPatterns => { - const appDirectoryLocationIndex = originalPatterns.indexOf(appDirectoryLocation); + const appDirectoryLocationIndex = originalPatterns.indexOf(AppDirectoryLocation); if (appDirectoryLocationIndex !== -1) { originalPatterns.splice(appDirectoryLocationIndex, 1); } - if (originalPatterns.indexOf(appResourcesDirectoryLocation) === -1) { - originalPatterns.push(appResourcesDirectoryLocation); - } - return originalPatterns; }); }; diff --git a/lib/compiler.js b/lib/compiler.js index cdf097da..dd35416b 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -4,7 +4,7 @@ const { join, resolve: pathResolve } = require("path"); const { existsSync } = require("fs"); const readline = require("readline"); const { messages } = require("../plugins/WatchStateLoggerPlugin"); -const ProjectSnapshotGenerator = require("../snapshot/android/project-snapshot-generator"); +const { AppDirectoryLocation } = require("./constants"); let hasBeenInvoked = false; @@ -88,8 +88,7 @@ exports.runWebpackCompiler = function runWebpackCompiler(config, $mobileHelper, } if (hookArgs.filesToSync && hookArgs.startSyncFilesTimeout) { - // TODO: Might need to get the "app" constant from config file in the future - hookArgs.filesToSync.push(...message.emittedFiles.map(emittedFile => join("app", emittedFile))); + hookArgs.filesToSync.push(...message.emittedFiles.map(emittedFile => join(AppDirectoryLocation, emittedFile))); hookArgs.startSyncFilesTimeout(); } } diff --git a/lib/constants.js b/lib/constants.js new file mode 100644 index 00000000..3efcf965 --- /dev/null +++ b/lib/constants.js @@ -0,0 +1,3 @@ +module.exports = { + AppDirectoryLocation: "app" +}; \ No newline at end of file From b7263884cce2de9da1d637496e55aa79baf00c71 Mon Sep 17 00:00:00 2001 From: Dimitar Kerezov Date: Thu, 15 Feb 2018 10:41:10 +0200 Subject: [PATCH 3/5] chore(livesync): make code synchronous --- lib/before-watch.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/before-watch.js b/lib/before-watch.js index d64acb59..e554ac28 100644 --- a/lib/before-watch.js +++ b/lib/before-watch.js @@ -5,7 +5,7 @@ module.exports = function ($mobileHelper, $projectData, hookArgs) { const appFilesUpdaterOptions = hookArgs.config.appFilesUpdaterOptions; if (appFilesUpdaterOptions.bundle) { const platforms = hookArgs.config.platforms; - platforms.forEach(platform => { + return Promise.all(platforms.map(platform => { const env = hookArgs.config.env || {}; const config = { env, @@ -14,8 +14,8 @@ module.exports = function ($mobileHelper, $projectData, hookArgs) { watch: true }; - runWebpackCompiler(config, $mobileHelper, $projectData, hookArgs); - }); + return runWebpackCompiler(config, $mobileHelper, $projectData, hookArgs); + })); } } } From 4fc6deb4972bf4c495e3f3ee0d564cb5bf9bfa9a Mon Sep 17 00:00:00 2001 From: Dimitar Kerezov Date: Thu, 15 Feb 2018 14:39:10 +0200 Subject: [PATCH 4/5] fix: do not rebuild every time --- lib/compiler.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/compiler.js b/lib/compiler.js index dd35416b..b966644b 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -21,10 +21,6 @@ exports.runWebpackCompiler = function runWebpackCompiler(config, $mobileHelper, return resolveBase(); } - if (hookArgs && hookArgs.config && hookArgs.config.changesInfo) { - hookArgs.config.changesInfo.nativeChanged = true; - } - let isResolved = false; function resolve() { if (isResolved) return; From 3919f617b459545e001968c9571bc7beeb44c44f Mon Sep 17 00:00:00 2001 From: Dimitar Kerezov Date: Fri, 16 Feb 2018 17:50:42 +0200 Subject: [PATCH 5/5] fix: emit absolute paths --- lib/compiler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compiler.js b/lib/compiler.js index b966644b..04b14c8c 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -84,7 +84,7 @@ exports.runWebpackCompiler = function runWebpackCompiler(config, $mobileHelper, } if (hookArgs.filesToSync && hookArgs.startSyncFilesTimeout) { - hookArgs.filesToSync.push(...message.emittedFiles.map(emittedFile => join(AppDirectoryLocation, emittedFile))); + hookArgs.filesToSync.push(...message.emittedFiles.map(emittedFile => join($projectData.projectDir, AppDirectoryLocation, emittedFile))); hookArgs.startSyncFilesTimeout(); } }