diff --git a/packages/webpack/package.json b/packages/webpack/package.json index 7c94b8ef9..e674ab6f6 100644 --- a/packages/webpack/package.json +++ b/packages/webpack/package.json @@ -1,6 +1,6 @@ { "name": "@nativescript/webpack", - "version": "3.0.1", + "version": "3.0.2", "main": "index", "description": "Webpack plugin for NativeScript", "homepage": "https://nativescript.org", diff --git a/packages/webpack/templates/webpack.react.js b/packages/webpack/templates/webpack.react.js index 27d5912cf..d9c3f9062 100644 --- a/packages/webpack/templates/webpack.react.js +++ b/packages/webpack/templates/webpack.react.js @@ -1,3 +1,7 @@ +/** + * @see https://github.com/NativeScript/NativeScript/tree/feat/ns7-finishing-touches/packages/webpack/templates + * @see https://github.com/NativeScript/NativeScript/pull/8801/files + */ const webpackConfig = require("./webpack.typescript"); const webpack = require("webpack"); const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); @@ -8,105 +12,77 @@ module.exports = (env) => { const production = env.production; const isAnySourceMapEnabled = !!env.sourceMap || !!env.hiddenSourceMap; - const babelOptions = { - sourceMaps: isAnySourceMapEnabled ? "inline" : false, - babelrc: false, - presets: [ - // https://github.com/Microsoft/TypeScript-Babel-Starter - "@babel/env", - "@babel/typescript", - "@babel/react" - ], - plugins: [ - ...( - hmr && !production ? - [ - require.resolve('react-refresh/babel') - ] : - [] - ), - ["@babel/plugin-proposal-class-properties", { loose: true }] - ] - }; - const baseConfig = webpackConfig(env); - // Remove ts-loader as we'll be using Babel to transpile the TypeScript instead. - baseConfig.module.rules = baseConfig.module.rules.filter((rule) => { - const isTsLoader = rule.use && rule.use.loader === "ts-loader"; - return !isTsLoader; - }); + /** Find the rule for transpiling ts files ("ts-loader"), and modify it to test for .tsx files too. */ + const tsxRule = baseConfig.module.rules.find(rule => rule.use && rule.use.loader === "ts-loader"); + tsxRule.test = /\.(ts|tsx)$/; + tsxRule.use = [ + /** + * Add React Refresh HMR support. + * @see https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/55028c6355b31e697e21bf3e9a48613a7b94bee7/examples/typescript-without-babel/webpack.config.js#L18-L21 + */ + hmr && !production && { + loader: "babel-loader", + options: { + sourceMaps: isAnySourceMapEnabled ? "inline" : false, + babelrc: false, + plugins: ['react-refresh/babel'] + } + }, + tsxRule.use, + ].filter(Boolean); - // Modify "nativescript-dev-webpack/hmr/hot-loader" to test for .tsx files - // (and also js(x) files, which it should have been doing to begin with!) - baseConfig.module.rules.some(rule => { - const isNativeScriptDevWebpackHotLoader = rule.use === "nativescript-dev-webpack/hmr/hot-loader"; - - if(isNativeScriptDevWebpackHotLoader){ - rule.test = /\.(ts|tsx|js|jsx|css|scss|html|xml)$/; - } - - return isNativeScriptDevWebpackHotLoader; // Break loop once we've found the one. - }); - - baseConfig.module.rules.push( - { - test: /\.[jt]s(x?)$/, - exclude: /node_modules/, - use: [ - { - loader: "babel-loader", - options: babelOptions - } - ], - } + /** + * Modify "nativescript-dev-webpack/hmr/hot-loader" to test for .tsx files + * (and also js files, which it should have been doing to begin with!) + */ + const nativeScriptDevWebpackHotLoader = baseConfig.module.rules.find(rule => + rule.use === "@nativescript/webpack/hmr/hot-loader" ); + nativeScriptDevWebpackHotLoader.test = /\.(ts|tsx|js|css|scss|html|xml)$/; - baseConfig.resolve.extensions = [".ts", ".tsx", ".js", ".jsx", ".scss", ".css"]; + /** We don't officially support JSX. Makes the webpack config rather more complicated to set up. */ + baseConfig.resolve.extensions = [".tsx", ...baseConfig.resolve.extensions]; baseConfig.resolve.alias["react-dom"] = "react-nativescript"; - // Remove ForkTsCheckerWebpackPlugin because, now that we're using Babel, we'll leave type-checking to the IDE instead. - baseConfig.plugins = baseConfig.plugins.filter(plugin => { - const isForkTsCheckerWebpackPlugin = plugin && plugin.constructor && plugin.constructor.name === "ForkTsCheckerWebpackPlugin"; - return !isForkTsCheckerWebpackPlugin; - }); + /** Augment NativeScript's existing DefinePlugin definitions with a few more of our own. */ + const existingDefinePlugin = baseConfig.plugins.find(plugin => + plugin && plugin.constructor && plugin.constructor.name === "DefinePlugin" + ); + baseConfig.plugins.splice( + baseConfig.plugins.indexOf(existingDefinePlugin), + 1, + new webpack.DefinePlugin({ + ...existingDefinePlugin.definitions, + /** For various libraries in the React ecosystem. */ + "__DEV__": production ? "false" : "true", + "__TEST__": "false", + /** + * Primarily for React Fast Refresh plugin, but technically the allowHmrInProduction option could be used instead. + * Worth including anyway, as there are plenty of Node libraries that use this flag. + */ + "process.env.NODE_ENV": JSON.stringify(production ? "production" : "development"), + }), + ); - // Augment NativeScript's existing DefinePlugin definitions with a few more of our own. - let existingDefinePlugin; - baseConfig.plugins = baseConfig.plugins.filter(plugin => { - const isDefinePlugin = plugin && plugin.constructor && plugin.constructor.name === "DefinePlugin"; - existingDefinePlugin = plugin; - return !isDefinePlugin; - }); - const newDefinitions = { - ...existingDefinePlugin.definitions, - /* For various libraries in the React ecosystem. */ - "__DEV__": production ? "false" : "true", - "__TEST__": "false", - /* - * Primarily for React Fast Refresh plugin, but technically the forceEnable option could be used instead. - * Worth including anyway, as there are plenty of Node libraries that use this flag. - */ - "process.env.NODE_ENV": JSON.stringify(production ? "production" : "development"), - }; - baseConfig.plugins.unshift(new webpack.DefinePlugin(newDefinitions)); - - /** - * Set forceEnable to `true` if you want to use HMR on a production build. - */ - const forceEnable = false; - if(hmr && (!production || forceEnable)){ + if(hmr && !production){ baseConfig.plugins.push(new 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, - forceEnable, + /** + * 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, })); } else { baseConfig.plugins = baseConfig.plugins.filter(p => !(p && p.constructor && p.constructor.name === "HotModuleReplacementPlugin")); } return baseConfig; -}; +}; \ No newline at end of file