diff --git a/packages/webpack5/__tests__/configuration/__snapshots__/angular.spec.ts.snap b/packages/webpack5/__tests__/configuration/__snapshots__/angular.spec.ts.snap index abe92c9f6..75d9d4337 100644 --- a/packages/webpack5/__tests__/configuration/__snapshots__/angular.spec.ts.snap +++ b/packages/webpack5/__tests__/configuration/__snapshots__/angular.spec.ts.snap @@ -9,6 +9,12 @@ exports[`angular configuration for android 1`] = ` ], devtool: 'inline-source-map', target: 'node', + watchOptions: { + ignored: [ + '__jest__/platforms/platforms/**', + '__jest__/App_Resources/**' + ] + }, output: { path: '__jest__/platforms/android/app/src/main/assets/app', pathinfo: false, @@ -282,6 +288,12 @@ exports[`angular configuration for ios 1`] = ` ], devtool: 'inline-source-map', target: 'node', + watchOptions: { + ignored: [ + '__jest__/platforms/platforms/**', + '__jest__/App_Resources/**' + ] + }, output: { path: '__jest__/platforms/ios/jest/app', pathinfo: false, diff --git a/packages/webpack5/__tests__/configuration/__snapshots__/base.spec.ts.snap b/packages/webpack5/__tests__/configuration/__snapshots__/base.spec.ts.snap index cd5cfa0ae..13d0dcee2 100644 --- a/packages/webpack5/__tests__/configuration/__snapshots__/base.spec.ts.snap +++ b/packages/webpack5/__tests__/configuration/__snapshots__/base.spec.ts.snap @@ -9,6 +9,12 @@ exports[`base configuration for android 1`] = ` ], devtool: 'inline-source-map', target: 'node', + watchOptions: { + ignored: [ + '__jest__/platforms/platforms/**', + '__jest__/App_Resources/**' + ] + }, output: { path: '__jest__/platforms/android/app/src/main/assets/app', pathinfo: false, @@ -270,6 +276,12 @@ exports[`base configuration for ios 1`] = ` ], devtool: 'inline-source-map', target: 'node', + watchOptions: { + ignored: [ + '__jest__/platforms/platforms/**', + '__jest__/App_Resources/**' + ] + }, output: { path: '__jest__/platforms/ios/jest/app', pathinfo: false, diff --git a/packages/webpack5/__tests__/configuration/__snapshots__/javascript.spec.ts.snap b/packages/webpack5/__tests__/configuration/__snapshots__/javascript.spec.ts.snap index 9fc956b69..3a5395cf2 100644 --- a/packages/webpack5/__tests__/configuration/__snapshots__/javascript.spec.ts.snap +++ b/packages/webpack5/__tests__/configuration/__snapshots__/javascript.spec.ts.snap @@ -9,6 +9,12 @@ exports[`javascript configuration for android 1`] = ` ], devtool: 'inline-source-map', target: 'node', + watchOptions: { + ignored: [ + '__jest__/platforms/platforms/**', + '__jest__/App_Resources/**' + ] + }, output: { path: '__jest__/platforms/android/app/src/main/assets/app', pathinfo: false, @@ -159,6 +165,22 @@ exports[`javascript configuration for android 1`] = ` loader: 'xml-namespace-loader' } ] + }, + /* config.module.rule('hmr-core') */ + { + test: /\\\\.js$/, + exclude: [ + /node_modules/ + ], + use: [ + /* config.module.rule('hmr-core').use('nativescript-hot-loader') */ + { + loader: 'nativescript-hot-loader', + options: { + appPath: '__jest__/src' + } + } + ] } ] }, @@ -291,6 +313,12 @@ exports[`javascript configuration for ios 1`] = ` ], devtool: 'inline-source-map', target: 'node', + watchOptions: { + ignored: [ + '__jest__/platforms/platforms/**', + '__jest__/App_Resources/**' + ] + }, output: { path: '__jest__/platforms/ios/jest/app', pathinfo: false, @@ -441,6 +469,22 @@ exports[`javascript configuration for ios 1`] = ` loader: 'xml-namespace-loader' } ] + }, + /* config.module.rule('hmr-core') */ + { + test: /\\\\.js$/, + exclude: [ + /node_modules/ + ], + use: [ + /* config.module.rule('hmr-core').use('nativescript-hot-loader') */ + { + loader: 'nativescript-hot-loader', + options: { + appPath: '__jest__/src' + } + } + ] } ] }, diff --git a/packages/webpack5/__tests__/configuration/__snapshots__/react.spec.ts.snap b/packages/webpack5/__tests__/configuration/__snapshots__/react.spec.ts.snap index a162eec11..2d3bcbb98 100644 --- a/packages/webpack5/__tests__/configuration/__snapshots__/react.spec.ts.snap +++ b/packages/webpack5/__tests__/configuration/__snapshots__/react.spec.ts.snap @@ -9,6 +9,12 @@ exports[`react configuration > android > adds ReactRefreshWebpackPlugin when HMR ], devtool: 'inline-source-map', target: 'node', + watchOptions: { + ignored: [ + '__jest__/platforms/platforms/**', + '__jest__/App_Resources/**' + ] + }, output: { path: '__jest__/platforms/android/app/src/main/assets/app', pathinfo: false, @@ -298,6 +304,12 @@ exports[`react configuration > android > base config 1`] = ` ], devtool: 'inline-source-map', target: 'node', + watchOptions: { + ignored: [ + '__jest__/platforms/platforms/**', + '__jest__/App_Resources/**' + ] + }, output: { path: '__jest__/platforms/android/app/src/main/assets/app', pathinfo: false, @@ -565,6 +577,12 @@ exports[`react configuration > ios > adds ReactRefreshWebpackPlugin when HMR ena ], devtool: 'inline-source-map', target: 'node', + watchOptions: { + ignored: [ + '__jest__/platforms/platforms/**', + '__jest__/App_Resources/**' + ] + }, output: { path: '__jest__/platforms/ios/jest/app', pathinfo: false, @@ -855,6 +873,12 @@ exports[`react configuration > ios > base config 1`] = ` ], devtool: 'inline-source-map', target: 'node', + watchOptions: { + ignored: [ + '__jest__/platforms/platforms/**', + '__jest__/App_Resources/**' + ] + }, output: { path: '__jest__/platforms/ios/jest/app', pathinfo: false, diff --git a/packages/webpack5/__tests__/configuration/__snapshots__/svelte.spec.ts.snap b/packages/webpack5/__tests__/configuration/__snapshots__/svelte.spec.ts.snap index e899bd504..c70e0b169 100644 --- a/packages/webpack5/__tests__/configuration/__snapshots__/svelte.spec.ts.snap +++ b/packages/webpack5/__tests__/configuration/__snapshots__/svelte.spec.ts.snap @@ -9,6 +9,12 @@ exports[`svelte configuration for android 1`] = ` ], devtool: 'inline-source-map', target: 'node', + watchOptions: { + ignored: [ + '__jest__/platforms/platforms/**', + '__jest__/App_Resources/**' + ] + }, output: { path: '__jest__/platforms/android/app/src/main/assets/app', pathinfo: false, @@ -295,6 +301,12 @@ exports[`svelte configuration for ios 1`] = ` ], devtool: 'inline-source-map', target: 'node', + watchOptions: { + ignored: [ + '__jest__/platforms/platforms/**', + '__jest__/App_Resources/**' + ] + }, output: { path: '__jest__/platforms/ios/jest/app', pathinfo: false, diff --git a/packages/webpack5/__tests__/configuration/__snapshots__/typescript.spec.ts.snap b/packages/webpack5/__tests__/configuration/__snapshots__/typescript.spec.ts.snap index fe876098e..cf0437589 100644 --- a/packages/webpack5/__tests__/configuration/__snapshots__/typescript.spec.ts.snap +++ b/packages/webpack5/__tests__/configuration/__snapshots__/typescript.spec.ts.snap @@ -9,6 +9,12 @@ exports[`typescript configuration for android 1`] = ` ], devtool: 'inline-source-map', target: 'node', + watchOptions: { + ignored: [ + '__jest__/platforms/platforms/**', + '__jest__/App_Resources/**' + ] + }, output: { path: '__jest__/platforms/android/app/src/main/assets/app', pathinfo: false, @@ -159,6 +165,22 @@ exports[`typescript configuration for android 1`] = ` loader: 'xml-namespace-loader' } ] + }, + /* config.module.rule('hmr-core') */ + { + test: /\\\\.(js|ts)$/, + exclude: [ + /node_modules/ + ], + use: [ + /* config.module.rule('hmr-core').use('nativescript-hot-loader') */ + { + loader: 'nativescript-hot-loader', + options: { + appPath: '__jest__/src' + } + } + ] } ] }, @@ -291,6 +313,12 @@ exports[`typescript configuration for ios 1`] = ` ], devtool: 'inline-source-map', target: 'node', + watchOptions: { + ignored: [ + '__jest__/platforms/platforms/**', + '__jest__/App_Resources/**' + ] + }, output: { path: '__jest__/platforms/ios/jest/app', pathinfo: false, @@ -441,6 +469,22 @@ exports[`typescript configuration for ios 1`] = ` loader: 'xml-namespace-loader' } ] + }, + /* config.module.rule('hmr-core') */ + { + test: /\\\\.(js|ts)$/, + exclude: [ + /node_modules/ + ], + use: [ + /* config.module.rule('hmr-core').use('nativescript-hot-loader') */ + { + loader: 'nativescript-hot-loader', + options: { + appPath: '__jest__/src' + } + } + ] } ] }, diff --git a/packages/webpack5/__tests__/configuration/__snapshots__/vue.spec.ts.snap b/packages/webpack5/__tests__/configuration/__snapshots__/vue.spec.ts.snap index 5f4ae6b00..3c6ef3bb2 100644 --- a/packages/webpack5/__tests__/configuration/__snapshots__/vue.spec.ts.snap +++ b/packages/webpack5/__tests__/configuration/__snapshots__/vue.spec.ts.snap @@ -9,6 +9,12 @@ exports[`vue configuration for android 1`] = ` ], devtool: 'inline-source-map', target: 'node', + watchOptions: { + ignored: [ + '__jest__/platforms/platforms/**', + '__jest__/App_Resources/**' + ] + }, output: { path: '__jest__/platforms/android/app/src/main/assets/app', pathinfo: false, @@ -302,6 +308,12 @@ exports[`vue configuration for ios 1`] = ` ], devtool: 'inline-source-map', target: 'node', + watchOptions: { + ignored: [ + '__jest__/platforms/platforms/**', + '__jest__/App_Resources/**' + ] + }, output: { path: '__jest__/platforms/ios/jest/app', pathinfo: false, diff --git a/packages/webpack5/src/configuration/base.ts b/packages/webpack5/src/configuration/base.ts index e64e24351..c87869d18 100644 --- a/packages/webpack5/src/configuration/base.ts +++ b/packages/webpack5/src/configuration/base.ts @@ -1,7 +1,6 @@ import { DefinePlugin, HotModuleReplacementPlugin } from 'webpack'; import Config from 'webpack-chain'; import { resolve } from 'path'; -import os from 'os'; import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'; import FilterWarningsPlugin from 'webpack-filter-warnings-plugin'; @@ -9,10 +8,10 @@ import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; import TerserPlugin from 'terser-webpack-plugin'; // import { WatchStateLoggerPlugin } from '../plugins/WatchStateLoggerPlugin'; +import { getProjectFilePath, getProjectRootPath } from '../helpers/project'; import { PlatformSuffixPlugin } from '../plugins/PlatformSuffixPlugin'; import { addCopyRule, applyCopyRules } from '../helpers/copyRules'; import { WatchStatePlugin } from '../plugins/WatchStatePlugin'; -import { getProjectRootPath } from '../helpers/project'; import { hasDependency } from '../helpers/dependencies'; import { applyDotEnvPlugin } from '../helpers/dotEnv'; import { env as _env, IWebpackEnv } from '../index'; @@ -80,6 +79,13 @@ export default function (config: Config, env: IWebpackEnv = _env): Config { .globalObject('global') .set('clean', true); + config.watchOptions({ + ignored: [ + `${getProjectFilePath('platforms')}/platforms/**`, + `${env.appResourcesPath ?? getProjectFilePath('App_Resources')}/**` + ] + }) + // Set up Terser options config.optimization.minimizer('TerserPlugin').use(TerserPlugin, [ { diff --git a/packages/webpack5/src/configuration/javascript.ts b/packages/webpack5/src/configuration/javascript.ts index 2ded22963..53f7a0cd5 100644 --- a/packages/webpack5/src/configuration/javascript.ts +++ b/packages/webpack5/src/configuration/javascript.ts @@ -1,6 +1,7 @@ import Config from 'webpack-chain'; import { addVirtualEntry } from '../helpers/virtualModules'; +import { getEntryDirPath } from "../helpers/platform"; import { env as _env, IWebpackEnv } from '../index'; import base from './base'; @@ -31,5 +32,18 @@ export default function (config: Config, env: IWebpackEnv = _env): Config { .use('xml-namespace-loader') .loader('xml-namespace-loader'); + // set up core HMR + config.module + .rule('hmr-core') + .test(/\.js$/) + .exclude + .add(/node_modules/) + .end() + .use('nativescript-hot-loader') + .loader('nativescript-hot-loader') + .options({ + appPath: getEntryDirPath() + }) + return config; } diff --git a/packages/webpack5/src/configuration/typescript.ts b/packages/webpack5/src/configuration/typescript.ts index 7cfb974b4..de0c28d9f 100644 --- a/packages/webpack5/src/configuration/typescript.ts +++ b/packages/webpack5/src/configuration/typescript.ts @@ -1,6 +1,7 @@ import Config from 'webpack-chain'; import { addVirtualEntry } from '../helpers/virtualModules'; +import { getEntryDirPath } from "../helpers/platform"; import { env as _env, IWebpackEnv } from '../index'; import base from './base'; @@ -31,5 +32,18 @@ export default function (config: Config, env: IWebpackEnv = _env): Config { .use('xml-namespace-loader') .loader('xml-namespace-loader'); + // set up core HMR + config.module + .rule('hmr-core') + .test(/\.(js|ts)$/) + .exclude + .add(/node_modules/) + .end() + .use('nativescript-hot-loader') + .loader('nativescript-hot-loader') + .options({ + appPath: getEntryDirPath() + }) + return config; } diff --git a/packages/webpack5/src/loaders/nativescript-hot-loader/index.ts b/packages/webpack5/src/loaders/nativescript-hot-loader/index.ts new file mode 100644 index 000000000..13d4c12bd --- /dev/null +++ b/packages/webpack5/src/loaders/nativescript-hot-loader/index.ts @@ -0,0 +1,32 @@ +import { relative } from "path"; +import dedent from "ts-dedent"; + +// note: this will bail even if module.hot appears in a comment +const MODULE_HOT_RE = /module\.hot/ + +export default function loader(content: string, map: any) { + if (MODULE_HOT_RE.test(content)) { + // Code already handles HMR - we don't need to do anything + return this.callback(null, content, map) + } + const opts = this.getOptions(); + + const relativePath = relative( + opts.appPath ?? this.rootContext, + this.resourcePath + ).replace(/\\/g, '/') + + const hmrCode = this.hot + ? dedent` + /* NATIVESCRIPT-HOT-LOADER */ + if(module.hot && global._isModuleLoadedForUI && global._isModuleLoadedForUI("./${relativePath}")) { + module.hot.accept() + } + ` + : ``; + + const source = `${content}\n${hmrCode}` + + this.callback(null, source, map) +} + diff --git a/packages/webpack5/src/loaders/xml-namespace-loader/index.ts b/packages/webpack5/src/loaders/xml-namespace-loader/index.ts index d0966993b..f3bff1a83 100644 --- a/packages/webpack5/src/loaders/xml-namespace-loader/index.ts +++ b/packages/webpack5/src/loaders/xml-namespace-loader/index.ts @@ -178,11 +178,21 @@ async function parseXML(content: string): Promise { .replace(/\u2028/g, '\\u2028') .replace(/\u2029/g, '\\u2029'); + const hmrCode = this.hot + ? dedent` + if(module.hot) { + module.hot.accept() + // module.hot.dispose(() => {}) + } + ` + : ``; + const code = dedent` ${moduleRegisters.join('\n')} /* XML-NAMESPACE-LOADER */ const ___XML_NAMESPACE_LOADER_EXPORT___ = ${xml} export default ___XML_NAMESPACE_LOADER_EXPORT___ + ${hmrCode} `; if (errors.length) {