diff --git a/packages/webpack5/__tests__/configuration/__snapshots__/react.spec.ts.snap b/packages/webpack5/__tests__/configuration/__snapshots__/react.spec.ts.snap index 9f4fd1d32..b5dc376bc 100644 --- a/packages/webpack5/__tests__/configuration/__snapshots__/react.spec.ts.snap +++ b/packages/webpack5/__tests__/configuration/__snapshots__/react.spec.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`react configuration for android 1`] = ` +exports[`react configuration > android > adds ReactRefreshWebpackPlugin when HMR enabled 1`] = ` "{ mode: 'development', resolve: { @@ -142,7 +142,142 @@ exports[`react configuration for android 1`] = ` }" `; -exports[`react configuration for ios 1`] = ` +exports[`react configuration > android > base config 1`] = ` +"{ + mode: 'development', + resolve: { + symlinks: true, + alias: { + '~/package.json': 'package.json', + '~': 'appFullPath', + '@': 'appFullPath', + 'react-dom': 'react-nativescript' + }, + extensions: [ + '.tsx' + ] + }, + resolveLoader: { + modules: [ + '@nativescript/webpack/loaders', + 'node_modules' + ] + }, + module: { + rules: [ + /* config.module.rule('ts') */ + { + test: [ + /\\\\.ts$/, + /\\\\.tsx$/ + ], + use: [ + /* config.module.rule('ts').use('babel-loader|react-refresh') */ + { + loader: 'babel-loader', + options: { + sourceMaps: 'inline', + babelrc: false, + plugins: [ + 'react-refresh/babel' + ] + } + }, + /* config.module.rule('ts').use('ts-loader') */ + { + loader: 'ts-loader', + options: { + transpileOnly: true, + allowTsInNodeModules: true, + compilerOptions: { + sourceMap: true, + declaration: false + }, + getCustomTransformers: function () { /* omitted long function */ } + } + } + ] + }, + /* config.module.rule('js') */ + { + test: /\\\\.js$/, + use: [ + /* config.module.rule('js').use('babel-loader') */ + { + loader: 'babel-loader' + } + ] + }, + /* config.module.rule('css') */ + { + test: /\\\\.css$/, + use: [ + /* config.module.rule('css').use('css2json-loader') */ + { + loader: 'css2json-loader' + }, + /* config.module.rule('css').use('css-loader') */ + { + loader: 'css-loader' + } + ] + }, + /* config.module.rule('scss') */ + { + test: /\\\\.scss$/, + use: [ + /* config.module.rule('scss').use('css2json-loader') */ + { + loader: 'css2json-loader' + }, + /* config.module.rule('scss').use('scss-loader') */ + { + loader: 'scss-loader' + } + ] + } + ] + }, + plugins: [ + /* config.plugin('CleanWebpackPlugin') */ + new CleanWebpackPlugin( + { + cleanOnceBeforeBuildPatterns: [ + 'platforms/android/app/src/main/assets/app/**/*' + ], + verbose: true + } + ), + /* config.plugin('DefinePlugin') */ + new DefinePlugin( + { + 'global.NS_WEBPACK': true, + 'global.isAndroid': true, + 'global.isIOS': false, + process: 'global.process', + __DEV__: 'true', + __TEST__: 'false', + 'process.env.NODE_ENV': '\\"development\\"' + } + ), + /* config.plugin('CopyWebpackPlugin') */ + new CopyPluginTemp( + { + patterns: [] + } + ), + /* config.plugin('WatchStateLoggerPlugin') */ + new WatchStateLoggerPlugin() + ], + entry: { + bundle: [ + 'todo/main' + ] + } +}" +`; + +exports[`react configuration > ios > adds ReactRefreshWebpackPlugin when HMR enabled 1`] = ` "{ mode: 'development', resolve: { @@ -286,3 +421,141 @@ exports[`react configuration for ios 1`] = ` } }" `; + +exports[`react configuration > ios > base config 1`] = ` +"{ + mode: 'development', + resolve: { + symlinks: true, + alias: { + '~/package.json': 'package.json', + '~': 'appFullPath', + '@': 'appFullPath', + 'react-dom': 'react-nativescript' + }, + extensions: [ + '.tsx' + ] + }, + resolveLoader: { + modules: [ + '@nativescript/webpack/loaders', + 'node_modules' + ] + }, + module: { + rules: [ + /* config.module.rule('ts') */ + { + test: [ + /\\\\.ts$/, + /\\\\.tsx$/ + ], + use: [ + /* config.module.rule('ts').use('babel-loader|react-refresh') */ + { + loader: 'babel-loader', + options: { + sourceMaps: 'inline', + babelrc: false, + plugins: [ + 'react-refresh/babel' + ] + } + }, + /* config.module.rule('ts').use('ts-loader') */ + { + loader: 'ts-loader', + options: { + transpileOnly: true, + allowTsInNodeModules: true, + compilerOptions: { + sourceMap: true, + declaration: false + }, + getCustomTransformers: function () { /* omitted long function */ } + } + } + ] + }, + /* config.module.rule('js') */ + { + test: /\\\\.js$/, + use: [ + /* config.module.rule('js').use('babel-loader') */ + { + loader: 'babel-loader' + } + ] + }, + /* config.module.rule('css') */ + { + test: /\\\\.css$/, + use: [ + /* config.module.rule('css').use('css2json-loader') */ + { + loader: 'css2json-loader' + }, + /* config.module.rule('css').use('css-loader') */ + { + loader: 'css-loader' + } + ] + }, + /* config.module.rule('scss') */ + { + test: /\\\\.scss$/, + use: [ + /* config.module.rule('scss').use('css2json-loader') */ + { + loader: 'css2json-loader' + }, + /* config.module.rule('scss').use('scss-loader') */ + { + loader: 'scss-loader' + } + ] + } + ] + }, + plugins: [ + /* config.plugin('CleanWebpackPlugin') */ + new CleanWebpackPlugin( + { + cleanOnceBeforeBuildPatterns: [ + 'platforms/ios/[todo]/app/**/*' + ], + verbose: true + } + ), + /* config.plugin('DefinePlugin') */ + new DefinePlugin( + { + 'global.NS_WEBPACK': true, + 'global.isAndroid': false, + 'global.isIOS': true, + process: 'global.process', + __DEV__: 'true', + __TEST__: 'false', + 'process.env.NODE_ENV': '\\"development\\"' + } + ), + /* config.plugin('CopyWebpackPlugin') */ + new CopyPluginTemp( + { + patterns: [] + } + ), + /* config.plugin('WatchStateLoggerPlugin') */ + new WatchStateLoggerPlugin() + ], + entry: { + inspector_modules: [ + 'tns_modules/@nativescript/core/inspector_modules' + ], + bundle: [ + 'todo/main' + ] + } +}" +`; diff --git a/packages/webpack5/__tests__/configuration/react.spec.ts b/packages/webpack5/__tests__/configuration/react.spec.ts index d6e86ba71..366f71a62 100644 --- a/packages/webpack5/__tests__/configuration/react.spec.ts +++ b/packages/webpack5/__tests__/configuration/react.spec.ts @@ -10,12 +10,23 @@ describe('react configuration', () => { const platforms = ['ios', 'android']; for (let platform of platforms) { - it(`for ${platform}`, () => { - expect( - __react({ - [platform]: true, - }).toString() - ).toMatchSnapshot(); + describe(`> ${platform} >`, () => { + it(`base config`, () => { + expect( + __react({ + [platform]: true, + }).toString() + ).toMatchSnapshot(); + }); + + it(`adds ReactRefreshWebpackPlugin when HMR enabled`, () => { + expect( + __react({ + [platform]: true, + hmr: true, + }).toString() + ).toMatchSnapshot(); + }); }); } }); diff --git a/packages/webpack5/src/configuration/react.ts b/packages/webpack5/src/configuration/react.ts index 3ca8d959c..27341e3f0 100644 --- a/packages/webpack5/src/configuration/react.ts +++ b/packages/webpack5/src/configuration/react.ts @@ -41,22 +41,24 @@ export default function (env: IWebpackEnv): Config { return args; }); - // todo: conditional + env flag to forceEnable? - config.plugin('ReactRefreshWebpackPlugin').use(function ReactRefreshWebpackPlugin() {}, [ - { - /** - * Maybe one day we'll implement an Error Overlay, but the work involved is too daunting for now. - * @see https://github.com/pmmmwh/react-refresh-webpack-plugin/issues/79#issuecomment-644324557 - */ - overlay: false, - /** - * If you (temporarily) want to enable HMR on a production build: - * 1) Set `forceEnable` to `true` - * 2) Remove the `!production` condition on `tsxRule` to ensure that babel-loader gets used. - */ - forceEnable: false, - }, - ]); + // todo: env flag to forceEnable? + config.when(env.hmr && !production, (config) => { + config.plugin('ReactRefreshWebpackPlugin').use(function ReactRefreshWebpackPlugin() {}, [ + { + /** + * Maybe one day we'll implement an Error Overlay, but the work involved is too daunting for now. + * @see https://github.com/pmmmwh/react-refresh-webpack-plugin/issues/79#issuecomment-644324557 + */ + overlay: false, + /** + * If you (temporarily) want to enable HMR on a production build: + * 1) Set `forceEnable` to `true` + * 2) Remove the `!production` condition on `tsxRule` to ensure that babel-loader gets used. + */ + forceEnable: false, + }, + ]); + }); return config; }