diff --git a/packages/webpack5/__tests__/configuration/__snapshots__/javascript.spec.ts.snap b/packages/webpack5/__tests__/configuration/__snapshots__/javascript.spec.ts.snap index a72f3b8de..d52f8d5b1 100644 --- a/packages/webpack5/__tests__/configuration/__snapshots__/javascript.spec.ts.snap +++ b/packages/webpack5/__tests__/configuration/__snapshots__/javascript.spec.ts.snap @@ -302,16 +302,6 @@ exports[`javascript configuration for android 1`] = ` ), /* config.plugin('WatchStatePlugin') */ new WatchStatePlugin(), - /* config.plugin('ContextExclusionPlugin|__@nativescript_webpack_virtual_entry_javascript__') */ - new ContextExclusionPlugin( - /__@nativescript_webpack_virtual_entry_javascript__.js$/ - ), - /* config.plugin('VirtualModulesPlugin') */ - new VirtualModulesPlugin( - { - '__jest__/src/__@nativescript_webpack_virtual_entry_javascript__': '// VIRTUAL ENTRY START\\\\nrequire(\\\\'@nativescript/core/bundle-entry-points\\\\')\\\\nconst context = require.context(\\"~/\\", /* deep: */ true, /* filter: */ /.(xml|js|s?css)$/);\\\\nglobal.registerWebpackModules(context);\\\\n// VIRTUAL ENTRY END' - } - ), /* config.plugin('ContextExclusionPlugin|exclude_files') */ new ContextExclusionPlugin( /\\\\b_.+\\\\./ @@ -320,7 +310,7 @@ exports[`javascript configuration for android 1`] = ` entry: { bundle: [ '@nativescript/core/globals/index', - '__jest__/src/__@nativescript_webpack_virtual_entry_javascript__', + '__jest__/node_modules/@nativescript/webpack/dist/stubs/virtual-entry-javascript', '@nativescript/core/bundle-entry-points', '__jest__/src/app.js', '@nativescript/core/ui/frame', @@ -632,16 +622,6 @@ exports[`javascript configuration for ios 1`] = ` ), /* config.plugin('WatchStatePlugin') */ new WatchStatePlugin(), - /* config.plugin('ContextExclusionPlugin|__@nativescript_webpack_virtual_entry_javascript__') */ - new ContextExclusionPlugin( - /__@nativescript_webpack_virtual_entry_javascript__.js$/ - ), - /* config.plugin('VirtualModulesPlugin') */ - new VirtualModulesPlugin( - { - '__jest__/src/__@nativescript_webpack_virtual_entry_javascript__': '// VIRTUAL ENTRY START\\\\nrequire(\\\\'@nativescript/core/bundle-entry-points\\\\')\\\\nconst context = require.context(\\"~/\\", /* deep: */ true, /* filter: */ /.(xml|js|s?css)$/);\\\\nglobal.registerWebpackModules(context);\\\\n// VIRTUAL ENTRY END' - } - ), /* config.plugin('ContextExclusionPlugin|exclude_files') */ new ContextExclusionPlugin( /\\\\b_.+\\\\./ @@ -650,7 +630,7 @@ exports[`javascript configuration for ios 1`] = ` entry: { bundle: [ '@nativescript/core/globals/index', - '__jest__/src/__@nativescript_webpack_virtual_entry_javascript__', + '__jest__/node_modules/@nativescript/webpack/dist/stubs/virtual-entry-javascript', '@nativescript/core/bundle-entry-points', '__jest__/src/app.js' ], diff --git a/packages/webpack5/__tests__/configuration/__snapshots__/typescript.spec.ts.snap b/packages/webpack5/__tests__/configuration/__snapshots__/typescript.spec.ts.snap index 5831fac29..39af4794d 100644 --- a/packages/webpack5/__tests__/configuration/__snapshots__/typescript.spec.ts.snap +++ b/packages/webpack5/__tests__/configuration/__snapshots__/typescript.spec.ts.snap @@ -302,16 +302,6 @@ exports[`typescript configuration for android 1`] = ` ), /* config.plugin('WatchStatePlugin') */ new WatchStatePlugin(), - /* config.plugin('ContextExclusionPlugin|__@nativescript_webpack_virtual_entry_typescript__') */ - new ContextExclusionPlugin( - /__@nativescript_webpack_virtual_entry_typescript__.js$/ - ), - /* config.plugin('VirtualModulesPlugin') */ - new VirtualModulesPlugin( - { - '__jest__/src/__@nativescript_webpack_virtual_entry_typescript__': '// VIRTUAL ENTRY START\\\\nrequire(\\\\'@nativescript/core/bundle-entry-points\\\\')\\\\nconst context = require.context(\\"~/\\", /* deep: */ true, /* filter: */ /\\\\\\\\.(xml|js|(? { }); } - it('filter typescript declaration files', () => { + it('filters typescript declaration files', () => { init({ ios: true, }); - const tsConfig = typescript(new Config()); + // const tsConfig = typescript(new Config()); let regex: RegExp; - // Get the filterRE from the typescript configuration - tsConfig.plugin('VirtualModulesPlugin').tap((args) => { - const options = args[0]; - const virtualConfig: string = options[Object.keys(options)[0]]; - const filterLine = virtualConfig - .split('\n') - .find((v) => v.includes('filter')); - const matches = filterLine.match(/\/(?\S+)\//); + const virtualEntryPath = path.join( + __dirname, + '../../src/stubs/virtual-entry-typescript.js' + ); + const virtualEntry = fs.readFileSync(virtualEntryPath); - if (matches) { - regex = new RegExp(matches.groups.filter); - } + const filterLine = virtualEntry + .toString() + .split('\n') + .find((v) => v.includes('filter')); - return args; - }); + const matches = filterLine.match(/\/(?\S+)\//); + + if (matches) { + regex = new RegExp(matches.groups.filter); + } expect(regex).toBeDefined(); diff --git a/packages/webpack5/package.json b/packages/webpack5/package.json index d813a25eb..9d168dd33 100644 --- a/packages/webpack5/package.json +++ b/packages/webpack5/package.json @@ -1,6 +1,6 @@ { "name": "@nativescript/webpack", - "version": "5.0.7", + "version": "5.0.8-alpha.3", "private": false, "main": "dist/index.js", "files": [ @@ -29,17 +29,17 @@ "css": "^3.0.0", "css-loader": "^6.0.0", "dotenv-webpack": "^7.0.0", - "fork-ts-checker-webpack-plugin": "^6.0.0", - "loader-utils": "^2.0.0", + "fork-ts-checker-webpack-plugin": "^7.0.0", + "loader-utils": "^2.0.0 || ^3.0.0", "lodash.get": "^4.0.0", "micromatch": "^4.0.0", "postcss": "^8.0.0", "postcss-import": "^14.0.0", - "postcss-loader": "^6.0.0", + "postcss-loader": "^7.0.0", "raw-loader": "^4.0.0", "react-refresh": "~0.11.0", "sass": "^1.0.0", - "sass-loader": "^12.0.0", + "sass-loader": "^13.0.0", "sax": "^1.0.0", "source-map": "^0.7.0", "terser-webpack-plugin": "^5.0.0", @@ -56,17 +56,17 @@ "devDependencies": { "@angular-devkit/build-angular": "^13.1.2", "@types/css": "0.0.33", - "@types/jest": "27.0.1", + "@types/jest": "27.5.1", "@types/loader-utils": "2.0.3", - "@types/lodash.get": "4.4.6", + "@types/lodash.get": "4.4.7", "@types/micromatch": "4.0.2", - "@types/sax": "1.2.3", + "@types/sax": "1.2.4", "@types/terser-webpack-plugin": "5.2.0", "@types/webpack-virtual-modules": "0.1.1", - "jest": "27.1.1", - "jest-matcher-utils": "27.1.1", - "nativescript-vue-template-compiler": "2.9.0", - "ts-jest": "27.0.5", + "jest": "28.1.0", + "jest-matcher-utils": "28.1.0", + "nativescript-vue-template-compiler": "2.9.2", + "ts-jest": "28.0.3", "typescript": "4.7.3" }, "peerDependencies": { diff --git a/packages/webpack5/src/bin/index.ts b/packages/webpack5/src/bin/index.ts index 87ceed551..c9dddb315 100644 --- a/packages/webpack5/src/bin/index.ts +++ b/packages/webpack5/src/bin/index.ts @@ -56,6 +56,7 @@ program env['env'] = options.env; } + env['stats'] ??= true; env['watch'] ??= options.watch; // if --env.config is passed, we'll set an environment @@ -108,13 +109,15 @@ program // Set the process exit code depending on errors process.exitCode = stats.hasErrors() ? 1 : 0; - console.log( - stats.toString({ - chunks: false, - colors: true, - errorDetails: env.verbose, - }) - ); + if (env.stats) { + console.log( + stats.toString({ + chunks: false, + colors: true, + errorDetails: env.verbose, + }) + ); + } // if webpack profile is enabled we write the stats to a JSON file if (configuration.profile || env.profile) { @@ -141,7 +144,7 @@ program }; if (options.watch) { - console.log('webpack is watching the files...'); + env.stats && console.log('webpack is watching the files...'); compiler.watch( configuration.watchOptions ?? {}, webpackCompilationCallback diff --git a/packages/webpack5/src/configuration/javascript.ts b/packages/webpack5/src/configuration/javascript.ts index 914036eb2..d0af14695 100644 --- a/packages/webpack5/src/configuration/javascript.ts +++ b/packages/webpack5/src/configuration/javascript.ts @@ -1,26 +1,18 @@ import Config from 'webpack-chain'; import { getEntryPath, getEntryDirPath } from '../helpers/platform'; -import { addVirtualEntry } from '../helpers/virtualModules'; import { chainedSetAddAfter } from '../helpers/chain'; import { env as _env, IWebpackEnv } from '../index'; import { ContextExclusionPlugin } from 'webpack'; import base from './base'; +import path from 'path'; export default function (config: Config, env: IWebpackEnv = _env): Config { base(config, env); const entryPath = getEntryPath(); - const filterRE = '/.(xml|js|s?css)$/'; - const virtualEntryPath = addVirtualEntry( - config, - 'javascript', - ` - // VIRTUAL ENTRY START - require('@nativescript/core/bundle-entry-points') - const context = require.context("~/", /* deep: */ true, /* filter: */ ${filterRE}); - global.registerWebpackModules(context); - // VIRTUAL ENTRY END - ` + const virtualEntryPath = path.resolve( + __dirname, + '../stubs/virtual-entry-javascript' ); // exclude files starting with _ from require.context diff --git a/packages/webpack5/src/configuration/typescript.ts b/packages/webpack5/src/configuration/typescript.ts index 4513af52d..da0409319 100644 --- a/packages/webpack5/src/configuration/typescript.ts +++ b/packages/webpack5/src/configuration/typescript.ts @@ -1,26 +1,18 @@ import Config from 'webpack-chain'; import { getEntryDirPath, getEntryPath } from '../helpers/platform'; -import { addVirtualEntry } from '../helpers/virtualModules'; import { chainedSetAddAfter } from '../helpers/chain'; import { env as _env, IWebpackEnv } from '../index'; import { ContextExclusionPlugin } from 'webpack'; import base from './base'; +import path from 'path'; export default function (config: Config, env: IWebpackEnv = _env): Config { base(config, env); const entryPath = getEntryPath(); - const filterRE = '/\\.(xml|js|(? { + // if (process.env.NODE_ENV === 'test') { + // return true; + // } + // // remove rules that do not match any paths + // // prevents webpack watch mode from firing + // // due to "removed" paths. + // return globbySync(glob).length > 0; + // }) .map((glob) => ({ from: glob, context: entryDir, diff --git a/packages/webpack5/src/helpers/virtualModules.ts b/packages/webpack5/src/helpers/virtualModules.ts index 4e8a79b93..b03990111 100644 --- a/packages/webpack5/src/helpers/virtualModules.ts +++ b/packages/webpack5/src/helpers/virtualModules.ts @@ -8,6 +8,9 @@ import { getEntryDirPath } from './platform'; import dedent from 'ts-dedent'; import { getProjectFilePath } from './project'; +/** + * @deprecated Virtual entries are not recommended by the webpack team, use real files instead. For example, resolve a path in node_modules if necessary. + */ export function addVirtualEntry( config: Config, name: string, @@ -20,6 +23,9 @@ export function addVirtualEntry( ); } +/** + * @deprecated Virtual modules are not recommended by the webpack team, use real files instead. For example, resolve a path in node_modules if necessary. + */ export function addVirtualModule( config: Config, name: string, diff --git a/packages/webpack5/src/index.ts b/packages/webpack5/src/index.ts index 39c2e1c8e..058c74b68 100644 --- a/packages/webpack5/src/index.ts +++ b/packages/webpack5/src/index.ts @@ -28,7 +28,7 @@ export interface IWebpackEnv { appResourcesPath?: string; appComponents?: string[]; - nativescriptLibPath?: string; + nativescriptLibPath?: string | boolean; android?: boolean; ios?: boolean; @@ -46,6 +46,9 @@ export interface IWebpackEnv { // enable webpack profiling profile?: boolean; + // print webpack stats (default: true) + stats?: boolean; + // misc replace?: string[] | string; watchNodeModules?: boolean; diff --git a/packages/webpack5/src/loaders/nativescript-hot-loader/hmr.runtime.ts b/packages/webpack5/src/loaders/nativescript-hot-loader/hmr.runtime.ts index 5434a0034..b456a1fdc 100644 --- a/packages/webpack5/src/loaders/nativescript-hot-loader/hmr.runtime.ts +++ b/packages/webpack5/src/loaders/nativescript-hot-loader/hmr.runtime.ts @@ -5,6 +5,7 @@ if (module.hot) { let hash = __webpack_require__.h(); + let hmrBootEmittedSymbol = Symbol.for('HMRBootEmitted'); const logVerbose = (title: string, ...info: any) => { if (__NS_ENV_VERBOSE__) { @@ -19,7 +20,7 @@ if (module.hot) { const setStatus = ( hash: string, - status: 'success' | 'failure', + status: 'success' | 'failure' | 'boot', message?: string, ...info: any ): boolean => { @@ -93,7 +94,7 @@ if (module.hot) { const requireExists = (path) => { try { - __non_webpack_require__(path); + global['require'](path); return true; } catch (err) { return false; @@ -112,10 +113,18 @@ if (module.hot) { logVerbose('LiveSync'); if (!hasUpdate()) { - return; + return false; } - await checkAndApply(); - originalOnLiveSync(); + if (!(await checkAndApply())) { + return false; + } + + await originalOnLiveSync(); }; + + if (!global[hmrBootEmittedSymbol]) { + global[hmrBootEmittedSymbol] = true; + setStatus(hash, 'boot', 'HMR Enabled - waiting for changes...'); + } } diff --git a/packages/webpack5/src/plugins/WatchStatePlugin.ts b/packages/webpack5/src/plugins/WatchStatePlugin.ts index 9adf2c6b0..559f2e610 100644 --- a/packages/webpack5/src/plugins/WatchStatePlugin.ts +++ b/packages/webpack5/src/plugins/WatchStatePlugin.ts @@ -22,7 +22,7 @@ export class WatchStatePlugin { callback(); if (isWatchMode) { - console.log(messages.changeDetected); + env.stats && console.log(messages.changeDetected); if (env.verbose) { if (compiler.modifiedFiles) { @@ -44,9 +44,10 @@ export class WatchStatePlugin { compiler.hooks.afterEmit.tapAsync(id, function (compilation, callback) { callback(); - console.log( - isWatchMode ? messages.startWatching : messages.compilationComplete - ); + env.stats && + console.log( + isWatchMode ? messages.startWatching : messages.compilationComplete + ); // Do not notify the CLI if the compilation failed const stats = compilation.getStats(); @@ -55,16 +56,24 @@ export class WatchStatePlugin { } // logic taken from CleanWebpackPlugin - const assets = - stats.toJson( - { - assets: true, - }, - true - ).assets || []; - const assetList = assets.map((asset) => asset.name); + // const assets = + // stats.toJson( + // { + // assets: true, + // }, + // true + // ).assets || []; + // const assetList = assets.map((asset) => asset.name); + // const emittedAssets = Array.from(compilation.emittedAssets); + + const assetList = Object.keys(compilation.assets); const emittedAssets = Array.from(compilation.emittedAssets); + + if (!prevAssets.length && emittedAssets.length < assetList.length) { + emittedAssets.push(...assetList); + } + const staleAssets = prevAssets.filter((asset) => { return assetList.includes(asset) === false; }); diff --git a/packages/webpack5/src/stubs/virtual-entry-javascript.js b/packages/webpack5/src/stubs/virtual-entry-javascript.js new file mode 100644 index 000000000..86e778c00 --- /dev/null +++ b/packages/webpack5/src/stubs/virtual-entry-javascript.js @@ -0,0 +1,5 @@ +// VIRTUAL ENTRY START +require('@nativescript/core/bundle-entry-points') +const context = require.context("~/", /* deep: */ true, /* filter: */ /.(xml|js|s?css)$/); +global.registerWebpackModules(context); +// VIRTUAL ENTRY END \ No newline at end of file diff --git a/packages/webpack5/src/stubs/virtual-entry-typescript.js b/packages/webpack5/src/stubs/virtual-entry-typescript.js new file mode 100644 index 000000000..083813806 --- /dev/null +++ b/packages/webpack5/src/stubs/virtual-entry-typescript.js @@ -0,0 +1,5 @@ +// VIRTUAL ENTRY START +require('@nativescript/core/bundle-entry-points') +const context = require.context("~/", /* deep: */ true, /* filter: */ /\.(xml|js|(?