diff --git a/.changeset/sweet-snakes-cheat.md b/.changeset/sweet-snakes-cheat.md
new file mode 100644
index 000000000..4f87f1b9e
--- /dev/null
+++ b/.changeset/sweet-snakes-cheat.md
@@ -0,0 +1,12 @@
+---
+'preact-cli': major
+---
+
+- Upgrades to Webpack v5
+ - Any custom configuration you do in your `preact.config.js` may need to be altered to account for this. Plugins may need replacements or different option formats.
+
+- `--esm` flag has been removed
+ - Dual output is now enabled by default in production builds.
+
+- `.babelrc` no longer overwrites matching keys
+ - Instead, the config will be merged in to the default. The default still takes precedence when there are conflicts, so you will still need to use your `preact.config.js` if you want to edit or remove default plugins or presets.
diff --git a/.eslintignore b/.eslintignore
index 66e817fc8..4ec2f1666 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,3 +1,4 @@
**/node_modules
**/tests/output
+**/tests/subjects/*/preact.config.js
**/*.d.ts
diff --git a/README.md b/README.md
index a1fac105c..084651231 100644
--- a/README.md
+++ b/README.md
@@ -106,7 +106,7 @@ Note: If you don't specify enough data to the `npx preact-cli create` command, i
Create a production build
-You can disable `default: true` flags by prefixing them with `--no-`; for example, `--no-sw`, `--no-esm`, and `--no-inline-css`.
+You can disable `default: true` flags by prefixing them with `--no- `; for example, `--no-sw`, `--no-prerender`, and `--no-inline-css`.
```
$ preact build
@@ -114,7 +114,6 @@ $ preact build
--src Specify source directory (default src)
--dest Specify output directory (default build)
--cwd A directory to use instead of $PWD (default .)
- --esm Builds ES-2015 bundles for your code (default true)
--sw Generate and attach a Service Worker (default true)
--babelConfig Path to custom Babel config (default .babelrc)
--json Generate build stats for bundle analysis
@@ -139,7 +138,6 @@ $ preact watch
--src Specify source directory (default src)
--cwd A directory to use instead of $PWD (default .)
- --esm Builds ES-2015 bundles for your code (default false)
--clear Clear the console (default true)
--sw Generate and attach a Service Worker (default false)
--babelConfig Path to custom Babel config (default .babelrc)
@@ -192,7 +190,7 @@ To make customizing your configuration easier, preact-cli supports plugins. Visi
#### Browserslist
-You may customize your list of supported browser versions by declaring a [`"browserslist"`] key within your `package.json`. Changing these values will modify your JavaScript (via [`@babel/preset-env`]) and your CSS (via [`autoprefixer`](https://github.com/postcss/autoprefixer)) output.
+You may customize your list of supported browser versions by declaring a [`"browserslist"`] key within your `package.json`. Changing these values will modify your legacy JavaScript (via [`@babel/preset-env`]) and your CSS (via [`autoprefixer`](https://github.com/postcss/autoprefixer)) output.
By default, `preact-cli` emulates the following config:
@@ -200,7 +198,7 @@ By default, `preact-cli` emulates the following config:
```json
{
- "browserslist": ["> 0.25%", "IE >= 9"]
+ "browserslist": ["> 0.5%", "last 2 versions", "Firefox ESR", "not dead"]
}
```
@@ -208,9 +206,9 @@ By default, `preact-cli` emulates the following config:
To customize Babel, you have two options:
-1. You may create a [`.babelrc`] file in your project's root directory. Any settings you define here will overwrite matching config-keys within [Preact CLI preset]. For example, if you pass a `"plugins"` object, it will replace & reset all Babel plugins that Preact CLI defaults to.
+1. You may create a [`.babelrc`] file in your project's root directory, or use the `--babelConfig` path to point at any valid [Babel config file]. Any settings you define here will be merged into the [Preact CLI preset]. For example, if you pass a `"plugins"` object containing different plugins from those already in use, they will simply be added on top of the existing config. If there are conflicts, you'll want to look into option 2, as the default will take precedence.
-2. If you'd like to modify or add to the existing Babel config, you must use a `preact.config.js` file. Visit the [Webpack](#webpack) section for more info, or check out the [Customize Babel] example!
+2. If you'd like to modify the existing Babel config you must use a `preact.config.js` file. Visit the [Webpack](#webpack) section for more info, or check out the [Customize Babel] example!
#### Webpack
@@ -400,9 +398,9 @@ Automatic code splitting is applied to all JavaScript and TypeScript files in th
[preact]: https://github.com/preactjs/preact
[webpackconfighelpers]: docs/webpack-helpers.md
[`.babelrc`]: https://babeljs.io/docs/usage/babelrc
+[babel config file]: https://babeljs.io/docs/en/config-files
[simple]: https://github.com/preactjs-templates/simple
[`"browserslist"`]: https://github.com/ai/browserslist
-[```.babelrc```]: https://babeljs.io/docs/usage/babelrc
[default]: https://github.com/preactjs-templates/default
[workbox]: https://developers.google.com/web/tools/workbox
[preact-router]: https://github.com/preactjs/preact-router
diff --git a/packages/cli/babel/index.js b/packages/cli/babel/index.js
deleted file mode 100644
index 6fff82da7..000000000
--- a/packages/cli/babel/index.js
+++ /dev/null
@@ -1,52 +0,0 @@
-var isProd = (process.env.BABEL_ENV || process.env.NODE_ENV) === 'production';
-/**
- * test env detection is used to default mode for
- * preset-env modules to "commonjs" otherwise, testing framework
- * will struggle
- */
-var isTest = (process.env.BABEL_ENV || process.env.NODE_ENV) === 'test';
-
-// default supported browsers for prod nomodule bundles:
-var defaultBrowserList = ['> 0.25%', 'IE >= 9'];
-
-// default supported browsers for all dev bundles (module/nomodule is not used):
-// see https://github.com/babel/babel/blob/master/packages/babel-compat-data/data/native-modules.json
-var defaultBrowserListDev = [
- 'chrome >= 61',
- 'and_chr >= 61',
- 'android >= 61',
- 'firefox >= 60',
- 'and_ff >= 60',
- 'safari >= 10.1',
- 'ios_saf >= 10.3',
- 'edge >= 16',
- 'opera >= 48',
- 'samsung >= 8.2',
-];
-
-// preact-cli babel configs
-var babelConfigs = require('../lib/lib/babel-config');
-
-/**
- * preset as a function means allow users to override some options
- * like env, modules for environment
- */
-module.exports = function preactCli(ctx, userOptions = {}) {
- // set default configs based on user environment
- var presetOptions = {
- env: isProd ? 'production' : 'development',
- modules: isTest ? 'commonjs' : false,
- browsers: isProd ? defaultBrowserList : defaultBrowserListDev,
- };
-
- // user specified options always the strongest
- Object.keys(presetOptions).forEach(function (key) {
- presetOptions[key] = userOptions[key] || presetOptions[key];
- });
-
- // yay! return the configs
- return babelConfigs(
- { env: { production: presetOptions.env === 'production' } },
- { modules: presetOptions.modules, browsers: presetOptions.browsers }
- );
-};
diff --git a/packages/cli/lib/commands/build.js b/packages/cli/lib/commands/build.js
index 411b39baa..6722fe340 100644
--- a/packages/cli/lib/commands/build.js
+++ b/packages/cli/lib/commands/build.js
@@ -21,11 +21,6 @@ const options = [
description: 'A directory to use instead of $PWD',
default: '.',
},
- {
- name: '--esm',
- description: 'Builds ES-2015 bundles for your code',
- default: true,
- },
{
name: '--sw',
description: 'Generate and attach a Service Worker',
diff --git a/packages/cli/lib/commands/watch.js b/packages/cli/lib/commands/watch.js
index 20c78f7da..8fbbeae1c 100644
--- a/packages/cli/lib/commands/watch.js
+++ b/packages/cli/lib/commands/watch.js
@@ -15,11 +15,6 @@ const options = [
description: 'A directory to use instead of $PWD',
default: '.',
},
- {
- name: '--esm',
- description: 'Builds ES-2015 bundles for your code',
- default: false,
- },
{
name: '--clear',
description: 'Clear the console',
diff --git a/packages/cli/lib/lib/babel-config.js b/packages/cli/lib/lib/babel-config.js
index df2bb1a90..288de4f2c 100644
--- a/packages/cli/lib/lib/babel-config.js
+++ b/packages/cli/lib/lib/babel-config.js
@@ -1,26 +1,31 @@
-module.exports = function (env, options = {}) {
- const { production: isProd, refresh } = env || {};
+const { tryResolveConfig } = require('../util');
+
+module.exports = function (env) {
+ const { babelConfig, cwd, isProd, refresh } = env;
+
+ const resolvedConfig =
+ babelConfig &&
+ tryResolveConfig(cwd, babelConfig, babelConfig === '.babelrc');
return {
+ babelrc: false,
+ configFile: resolvedConfig,
presets: [
- [
+ !isProd && [
require.resolve('@babel/preset-env'),
{
+ loose: true,
+ modules: false,
bugfixes: true,
- modules: options.modules || false,
targets: {
- browsers: options.browsers,
+ esmodules: true,
},
exclude: ['transform-regenerator'],
},
],
- ],
+ ].filter(Boolean),
plugins: [
- require.resolve('@babel/plugin-syntax-dynamic-import'),
- require.resolve('@babel/plugin-transform-object-assign'),
[require.resolve('@babel/plugin-proposal-decorators'), { legacy: true }],
- require.resolve('@babel/plugin-proposal-class-properties'),
- require.resolve('@babel/plugin-proposal-object-rest-spread'),
isProd &&
require.resolve('babel-plugin-transform-react-remove-prop-types'),
require.resolve('babel-plugin-macros'),
diff --git a/packages/cli/lib/lib/entry.js b/packages/cli/lib/lib/entry.js
index 5be51642e..910ea9965 100644
--- a/packages/cli/lib/lib/entry.js
+++ b/packages/cli/lib/lib/entry.js
@@ -18,14 +18,12 @@ if (process.env.NODE_ENV === 'development') {
);
} else if (process.env.ADD_SW && 'serviceWorker' in navigator) {
navigator.serviceWorker.register(
- normalizeURL(__webpack_public_path__) +
- (process.env.ES_BUILD ? 'sw-esm.js' : 'sw.js')
+ normalizeURL(__webpack_public_path__) + 'sw.js'
);
}
} else if (process.env.ADD_SW && 'serviceWorker' in navigator) {
navigator.serviceWorker.register(
- normalizeURL(__webpack_public_path__) +
- (process.env.ES_BUILD ? 'sw-esm.js' : 'sw.js')
+ normalizeURL(__webpack_public_path__) + 'sw.js'
);
}
diff --git a/packages/cli/lib/lib/webpack/proxy-loader.js b/packages/cli/lib/lib/webpack/proxy-loader.js
index 27c7b56fb..f02648b8d 100644
--- a/packages/cli/lib/lib/webpack/proxy-loader.js
+++ b/packages/cli/lib/lib/webpack/proxy-loader.js
@@ -1,7 +1,5 @@
-var utils = require('loader-utils');
-
function proxyLoader(source, map) {
- var options = utils.getOptions(this);
+ var options = this.getOptions();
// First run proxy-loader run
if (!this.query.__proxy_loader__) {
diff --git a/packages/cli/lib/lib/webpack/push-manifest.js b/packages/cli/lib/lib/webpack/push-manifest.js
index d45c9a675..0c97941d4 100644
--- a/packages/cli/lib/lib/webpack/push-manifest.js
+++ b/packages/cli/lib/lib/webpack/push-manifest.js
@@ -1,33 +1,25 @@
-const webpack = require('webpack');
+const { Compilation, sources } = require('webpack');
const createLoadManifest = require('./create-load-manifest');
module.exports = class PushManifestPlugin {
apply(compiler) {
- compiler.hooks.emit.tap(
- {
- name: 'PushManifestPlugin',
- stage: webpack.Compiler.PROCESS_ASSETS_STAGE_REPORT,
- },
- compilation => {
- const manifest = createLoadManifest(
- compilation.assets,
- compilation.namedChunkGroups
- );
+ compiler.hooks.thisCompilation.tap('PushManifestPlugin', compilation => {
+ compilation.hooks.processAssets.tap(
+ {
+ name: 'PushManifestPlugin',
+ stage: Compilation.PROCESS_ASSETS_STAGE_REPORT,
+ },
+ () => {
+ const manifest = JSON.stringify(
+ createLoadManifest(compilation.assets, compilation.namedChunkGroups)
+ );
- let output = JSON.stringify(manifest);
- compilation.assets['push-manifest.json'] = {
- source() {
- return output;
- },
- size() {
- return output.length;
- },
- };
-
- return compilation;
-
- // callback();
- }
- );
+ compilation.emitAsset(
+ 'push-manifest.json',
+ new sources.RawSource(manifest)
+ );
+ }
+ );
+ });
}
};
diff --git a/packages/cli/lib/lib/webpack/render-html-plugin.js b/packages/cli/lib/lib/webpack/render-html-plugin.js
index bae6ee996..7ec0b3f26 100644
--- a/packages/cli/lib/lib/webpack/render-html-plugin.js
+++ b/packages/cli/lib/lib/webpack/render-html-plugin.js
@@ -1,6 +1,7 @@
const { resolve, join } = require('path');
const os = require('os');
const { existsSync, readFileSync, writeFileSync, mkdirSync } = require('fs');
+const { Compilation, sources } = require('webpack');
const {
HtmlWebpackSkipAssetsPlugin,
} = require('html-webpack-skip-assets-plugin');
@@ -110,7 +111,6 @@ module.exports = async function renderHTMLPlugin(config) {
};
},
inject: true,
- scriptLoading: 'defer',
favicon: existsSync(resolve(src, 'assets/favicon.ico'))
? 'assets/favicon.ico'
: '',
@@ -183,20 +183,28 @@ class PrerenderDataExtractPlugin {
this.data_ = JSON.stringify(page || {});
}
apply(compiler) {
- compiler.hooks.emit.tap('PrerenderDataExtractPlugin', compilation => {
- if (this.location_ === `${PREACT_FALLBACK_URL}/`) {
- // We dont build prerender data for `200.html`. It can re-use the one for homepage.
- return;
- }
- let path = this.location_ + 'preact_prerender_data.json';
- if (path.startsWith('/')) {
- path = path.substr(1);
+ compiler.hooks.thisCompilation.tap(
+ 'PrerenderDataExtractPlugin',
+ compilation => {
+ compilation.hooks.processAssets.tap(
+ {
+ name: 'PrerenderDataExtractPlugin',
+ stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
+ },
+ () => {
+ if (this.location_ === `${PREACT_FALLBACK_URL}/`) {
+ // We dont build prerender data for `200.html`. It can re-use the one for homepage.
+ return;
+ }
+ let path = this.location_ + 'preact_prerender_data.json';
+ if (path.startsWith('/')) {
+ path = path.substr(1);
+ }
+ compilation.emitAsset(path, new sources.RawSource(this.data_));
+ }
+ );
}
- compilation.assets[path] = {
- source: () => this.data_,
- size: () => this.data_.length,
- };
- });
+ );
}
}
diff --git a/packages/cli/lib/lib/webpack/run-webpack.js b/packages/cli/lib/lib/webpack/run-webpack.js
index abc1544f5..3b40eab1d 100644
--- a/packages/cli/lib/lib/webpack/run-webpack.js
+++ b/packages/cli/lib/lib/webpack/run-webpack.js
@@ -96,26 +96,13 @@ function showStats(stats, isProd) {
if (stats.hasErrors()) {
allFields(stats, 'errors')
.map(stripLoaderPrefix)
- .forEach(msg => error(msg, isProd ? 1 : 0));
+ .forEach(({ message }) => error(message, isProd ? 1 : 0));
}
if (stats.hasWarnings()) {
allFields(stats, 'warnings')
.map(stripLoaderPrefix)
- .forEach(msg => {
- if (
- msg.match(
- /Conflict: Multiple assets emit different content to the same filename .*\.(css|map)/
- )
- ) {
- /**
- * This particular warning is expected due to `babel-esm-plugin`.
- * This can be removed when upgrading to webpack5 with https://webpack.js.org/configuration/output/#outputcomparebeforeemit
- */
- return;
- }
- warn(msg);
- });
+ .forEach(({ message }) => warn(message));
}
}
diff --git a/packages/cli/lib/lib/webpack/webpack-base-config.js b/packages/cli/lib/lib/webpack/webpack-base-config.js
index 67063aa3e..bcc803a07 100644
--- a/packages/cli/lib/lib/webpack/webpack-base-config.js
+++ b/packages/cli/lib/lib/webpack/webpack-base-config.js
@@ -6,13 +6,12 @@ const { isInstalledVersionPreactXOrAbove } = require('./utils');
const autoprefixer = require('autoprefixer');
const browserslist = require('browserslist');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
-const FixStyleOnlyEntriesPlugin = require('webpack-fix-style-only-entries');
+const RemoveEmptyScriptsPlugin = require('webpack-remove-empty-scripts');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
const ReplacePlugin = require('webpack-plugin-replace');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const createBabelConfig = require('../babel-config');
const loadPostcssConfig = require('postcss-load-config');
-const PnpWebpackPlugin = require('pnp-webpack-plugin');
const { WebpackManifestPlugin } = require('webpack-manifest-plugin');
function readJson(file) {
@@ -65,23 +64,19 @@ function getSassConfiguration(...includePaths) {
*/
module.exports = function createBaseConfig(env) {
const { cwd, isProd, isWatch, src, source } = env;
- const babelConfigFile = env.babelConfig || '.babelrc';
const IS_SOURCE_PREACT_X_OR_ABOVE = isInstalledVersionPreactXOrAbove(cwd);
// Apply base-level `env` values
env.dest = resolve(cwd, env.dest || 'build');
env.manifest = readJson(source('manifest.json')) || {};
env.pkg = readJson(resolve(cwd, 'package.json')) || {};
- let babelrc = readJson(resolve(cwd, babelConfigFile)) || {};
-
// use browserslist config environment, config default, or default browsers
- // default browsers are > 0.25% global market share or Internet Explorer >= 9
- const browserslistDefaults = ['> 0.25%', 'IE >= 9'];
+ // default browsers are '> 0.5%, last 2 versions, Firefox ESR, not dead'
const browserlistConfig = Object(browserslist.findConfig(cwd));
const browsers =
(isProd ? browserlistConfig.production : browserlistConfig.development) ||
browserlistConfig.defaults ||
- browserslistDefaults;
+ 'defaults';
let userNodeModules = findAllNodeModules(cwd);
let cliNodeModules = findAllNodeModules(__dirname);
@@ -142,7 +137,7 @@ module.exports = function createBaseConfig(env) {
style: source('style'),
'preact-cli-entrypoint': source('index'),
url: dirname(require.resolve('native-url/package.json')),
- // preact-compat aliases for supporting React dependencies:
+ // preact/compat aliases for supporting React dependencies:
react: compat,
'react-dom': compat,
'preact-compat': compat,
@@ -151,10 +146,6 @@ module.exports = function createBaseConfig(env) {
? require.resolve('@preact/async-loader/async')
: require.resolve('@preact/async-loader/async-legacy'),
},
- plugins: [
- // TODO: Remove when upgrading to webpack 5
- PnpWebpackPlugin,
- ],
},
resolveLoader: {
@@ -167,17 +158,12 @@ module.exports = function createBaseConfig(env) {
module: {
rules: [
{
- // ES2015
enforce: 'pre',
test: /\.m?[jt]sx?$/,
resolve: { mainFields: ['module', 'jsnext:main', 'browser', 'main'] },
type: 'javascript/auto',
loader: require.resolve('babel-loader'),
- options: Object.assign(
- { babelrc: false },
- createBabelConfig(env, { browsers }),
- babelrc // intentionally overwrite our settings
- ),
+ options: createBabelConfig(env),
},
{
// LESS
@@ -293,13 +279,11 @@ module.exports = function createBaseConfig(env) {
},
{
test: /\.(xml|html|txt|md)$/,
- loader: require.resolve('raw-loader'),
+ type: 'asset/source',
},
{
test: /\.(svg|woff2?|ttf|eot|jpe?g|png|webp|gif|mp4|mov|ogg|webm)(\?.*)?$/i,
- loader: isProd
- ? require.resolve('file-loader')
- : require.resolve('url-loader'),
+ type: isProd ? 'asset/resource' : 'asset/inline',
},
],
},
@@ -326,8 +310,7 @@ module.exports = function createBaseConfig(env) {
Fragment: ['preact', 'Fragment'],
}),
// Fix for https://github.com/webpack-contrib/mini-css-extract-plugin/issues/151
- new FixStyleOnlyEntriesPlugin(),
- // Extract CSS
+ new RemoveEmptyScriptsPlugin(),
new MiniCssExtractPlugin({
filename: isProd ? '[name].[contenthash:5].css' : '[name].css',
chunkFilename: isProd
@@ -350,37 +333,32 @@ module.exports = function createBaseConfig(env) {
// This is just to avoid any potentially breaking changes for right now.
publicPath: '',
}),
- ...(tsconfig
- ? [
- new ForkTsCheckerWebpackPlugin({
- checkSyntacticErrors: true,
- async: !isProd,
- tsconfig: tsconfig,
- silent: !isWatch,
- }),
- ]
- : []),
- ...(isProd
- ? [
- new webpack.HashedModuleIdsPlugin(),
- new webpack.LoaderOptionsPlugin({ minimize: true }),
- new webpack.optimize.ModuleConcatenationPlugin(),
+ tsconfig &&
+ new ForkTsCheckerWebpackPlugin({
+ typescript: {
+ configFile: tsconfig,
+ diagnosticOptions: {
+ syntactic: true,
+ },
+ },
+ }),
+ isProd && new webpack.LoaderOptionsPlugin({ minimize: true }),
+ new webpack.optimize.ModuleConcatenationPlugin(),
- // strip out babel-helper invariant checks
- new ReplacePlugin({
- include: /babel-helper$/,
- patterns: [
- {
- regex: /throw\s+(new\s+)?(Type|Reference)?Error\s*\(/g,
- value: s => `return;${Array(s.length - 7).join(' ')}(`,
- },
- ],
- }),
- ]
- : []),
- ],
+ // strip out babel-helper invariant checks
+ new ReplacePlugin({
+ include: /babel-helper$/,
+ patterns: [
+ {
+ regex: /throw\s+(new\s+)?(Type|Reference)?Error\s*\(/g,
+ value: s => `return;${Array(s.length - 7).join(' ')}(`,
+ },
+ ],
+ }),
+ ].filter(Boolean),
optimization: {
+ ...(isProd && { moduleIds: 'deterministic' }),
splitChunks: {
minChunks: 3,
},
@@ -388,15 +366,11 @@ module.exports = function createBaseConfig(env) {
mode: isProd ? 'production' : 'development',
- devtool: isWatch ? 'cheap-module-eval-source-map' : 'source-map',
+ devtool: isWatch ? 'eval-cheap-module-source-map' : 'source-map',
node: {
- console: false,
- process: false,
- Buffer: false,
__filename: false,
__dirname: false,
- setImmediate: false,
},
};
};
diff --git a/packages/cli/lib/lib/webpack/webpack-client-config.js b/packages/cli/lib/lib/webpack/webpack-client-config.js
index dc3f26b61..30489de3e 100644
--- a/packages/cli/lib/lib/webpack/webpack-client-config.js
+++ b/packages/cli/lib/lib/webpack/webpack-client-config.js
@@ -7,17 +7,17 @@ const { filter } = require('minimatch');
const SizePlugin = require('size-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
-const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
+const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const CrittersPlugin = require('critters-webpack-plugin');
const renderHTMLPlugin = require('./render-html-plugin');
const PushManifestPlugin = require('./push-manifest');
const baseConfig = require('./webpack-base-config');
-const BabelEsmPlugin = require('babel-esm-plugin');
const { InjectManifest } = require('workbox-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const RefreshPlugin = require('@prefresh/webpack');
const { normalizePath, warn } = require('../../util');
+const OptimizePlugin = require('optimize-plugin');
const cleanFilename = name =>
name.replace(
@@ -37,7 +37,7 @@ async function clientConfig(env) {
let entry = {
bundle: resolve(__dirname, './../entry'),
- polyfills: resolve(__dirname, './polyfills'),
+ 'dom-polyfills': resolve(__dirname, './polyfills'),
};
let swInjectManifest = [];
@@ -50,31 +50,15 @@ async function clientConfig(env) {
warn(`Could not find sw.js in ${src}. Using the default service worker.`);
}
- if (env.esm) {
- swInjectManifest.push(
- new InjectManifest({
- swSrc: swPath,
- swDest: 'sw-esm.js',
- include: [
- /200\.html$/,
- /\.esm.js$/,
- /\.css$/,
- /\.(png|jpg|svg|gif|webp)$/,
- ],
- webpackCompilationPlugins: [
- new webpack.DefinePlugin({
- 'process.env.ESM': true,
- }),
- ],
- })
- );
- }
-
swInjectManifest.push(
new InjectManifest({
swSrc: swPath,
- include: [/200\.html$/, /\.js$/, /\.css$/, /\.(png|jpg|svg|gif|webp)$/],
- exclude: [/\.esm\.js$/],
+ include: [
+ /200\.html$/,
+ /(? {
+ if (pathData.chunk.name === 'dom-polyfills') {
+ return env.isProd
+ ? '[name].[chunkhash:5].legacy.js'
+ : '[name].legacy.js';
+ }
+ return env.isProd ? '[name].[chunkhash:5].js' : '[name].js';
+ },
chunkFilename: '[name].chunk.[chunkhash:5].js',
},
@@ -142,13 +133,11 @@ async function clientConfig(env) {
plugins: [
new webpack.DefinePlugin({
- 'process.env.ES_BUILD': false,
'process.env.ADD_SW': env.sw,
'process.env.PRERENDER': env.prerender,
}),
new PushManifestPlugin(),
...(await renderHTMLPlugin(env)),
- ...getBabelEsmPlugin(env),
copyPatterns.length !== 0 &&
new CopyWebpackPlugin({
patterns: copyPatterns,
@@ -158,41 +147,6 @@ async function clientConfig(env) {
};
}
-function getBabelEsmPlugin(env) {
- const esmPlugins = [];
- if (env.esm) {
- esmPlugins.push(
- new BabelEsmPlugin({
- filename: env.isProd ? '[name].[chunkhash:5].esm.js' : '[name].esm.js',
- chunkFilename: '[name].chunk.[chunkhash:5].esm.js',
- excludedPlugins: ['BabelEsmPlugin', 'InjectManifest'],
- beforeStartExecution: plugins => {
- plugins.forEach(plugin => {
- if (
- plugin.constructor.name === 'DefinePlugin' &&
- plugin.definitions
- ) {
- for (const definition in plugin.definitions) {
- if (definition === 'process.env.ES_BUILD') {
- plugin.definitions[definition] = true;
- }
- }
- } else if (
- plugin.constructor.name === 'DefinePlugin' &&
- !plugin.definitions
- ) {
- throw new Error(
- 'WebpackDefinePlugin found but not `process.env.ES_BUILD`.'
- );
- }
- });
- },
- })
- );
- }
- return esmPlugins;
-}
-
/**
* @returns {import('webpack').Configuration}
*/
@@ -209,17 +163,24 @@ function isProd(env) {
),
plugins: [
- new webpack.DefinePlugin({
- 'process.env.ESM': env.esm,
+ new OptimizePlugin({
+ polyfillsFilename: 'es-polyfills.legacy.js',
+ exclude: [/^sw.*\.js/, /^dom-polyfills.*\.js/],
+ modernize: false,
+ verbose: false,
+ }),
+ new SizePlugin({
+ stripHash: name =>
+ name.replace(/\.[a-z0-9]{5}((\.legacy)?\.(?:js|css)$)/i, '.*****$1'),
}),
- new SizePlugin(),
],
+ cache: true,
optimization: {
minimizer: [
new TerserPlugin({
- cache: true,
- parallel: true,
+ extractComments: false,
+ test: /(sw|dom-polyfills).*\.js$/,
terserOptions: {
output: { comments: false },
mangle: true,
@@ -237,15 +198,8 @@ function isProd(env) {
],
},
},
- extractComments: false,
- sourceMap: true,
- }),
- new OptimizeCssAssetsPlugin({
- cssProcessorOptions: {
- // Fix keyframes in different CSS chunks minifying to colliding names:
- reduceIdents: false,
- },
}),
+ new CssMinimizerPlugin(),
],
},
};
@@ -270,7 +224,7 @@ function isProd(env) {
new CompressionPlugin({
filename: '[path].br[query]',
algorithm: 'brotliCompress',
- test: /\.esm\.js$/,
+ test: /(?
<% } %>
-<% if (htmlWebpackPlugin.files.js.filter(entry => entry.match(/bundle(\.\w{5})?.esm.js$/)).length > 0) { %>
-
- <%
- /*Fetch and Promise polyfills are not needed for browsers that support type=module
- Please re-evaluate below line if adding more polyfills.*/
- %>
-
-
-<% } else { %>
-
-
-<% } %>
+
+<% /*
+ Fetch and Promise polyfills are not needed for browsers that support type=module
+ Please re-evaluate below line if adding more polyfills.
+*/ %>
+
+
+
diff --git a/packages/cli/lib/resources/head-end.ejs b/packages/cli/lib/resources/head-end.ejs
index 8ca472a8b..07ff50c69 100644
--- a/packages/cli/lib/resources/head-end.ejs
+++ b/packages/cli/lib/resources/head-end.ejs
@@ -6,6 +6,6 @@
<% for (const file in cli.loadManifest[cli.url]) { %>
<% if (cli.preload && file && file.match(filesRegexp)) { %>
<% /* crossorigin for main bundle as that is loaded from `
This is an app with custom template
-
-
+
+
+
+