const path = require('path'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const TerserJSPlugin = require('terser-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const HTMLInlineCSSWebpackPlugin = require('html-inline-css-webpack-plugin').default; // const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; const Sass = require('sass'); module.exports = (env, argv) => { const isProduction = argv.mode !== 'development'; return { entry: { main: path.resolve(__dirname, 'src/index.ts'), }, output: { filename: 'main.js', path: path.resolve(__dirname, 'dist'), }, devServer: { host: '0.0.0.0', allowedHosts: 'all', }, // webpack-dev-server 5 no longer accepts `watchOptions` under `devServer`; // polling is configured on the compiler instead (needed for some FS mounts). watchOptions: { poll: true, }, plugins: [ new CleanWebpackPlugin(), new MiniCssExtractPlugin(), //new BundleAnalyzerPlugin(), new HtmlWebpackPlugin({ template: './src/index.html', }), // The SVG UI icons used to be inlined into the HTML by the (abandoned, // webpack-4-only) html-webpack-inline-svg-plugin. They are now emitted as // static files and referenced via `static/.svg` from index.html. new CopyWebpackPlugin({ patterns: [{ from: 'static/*.svg', to: 'static/[name][ext]' }], }), // Inline the extracted CSS into the HTML for production builds (replaces // the abandoned html-webpack-inline-source-plugin). In development the CSS // stays a separate, linked file for faster rebuilds. ...(isProduction ? [new HTMLInlineCSSWebpackPlugin()] : []), ], optimization: { minimizer: [ new TerserJSPlugin({ exclude: /node_modules/, // The custom serialization protocol keys on class names, so they must // survive minification (see shared/src/serialization). terserOptions: { keep_classnames: true, }, }), ], }, module: { rules: [ { test: /\.js$/, enforce: 'pre', use: ['source-map-loader'], }, { test: /\.ts$/, use: 'ts-loader', exclude: /node_modules/, }, { test: /\.scss$/i, use: [ MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', { loader: 'sass-loader', options: { sourceMap: true, implementation: Sass, }, }, ], }, { // SVGs referenced from CSS (`mask-image`/`background-image`) and JS // must be inlined as real `data:` URIs. svg-url-loader emits a CJS // module (`module.exports = "data:..."`) which webpack 5 + css-loader 7 // write out verbatim as a `.svg` asset file, so the browser fetches JS // instead of an image and the mask/background silently fails. webpack 5's // built-in `asset/inline` produces a proper data URI instead. // (HTML icons are unaffected: they are copied by CopyWebpackPlugin // and referenced by literal `static/*.svg` paths, not through this rule.) test: /\.svg$/, type: 'asset/inline', }, { // Use oneOf so each asset matches exactly one rule (og-image.png would // otherwise match both the generic png rule and its own rule). // The directory is encoded in `name` (not `outputPath`) and has no // leading slash: with the default `publicPath: 'auto'` a leading slash // produces a doubled slash in the emitted URL (e.g. `//static/x.mp3`). oneOf: [ { test: /og-image\.png$/, use: { loader: 'file-loader', options: { name: '[name].[ext]', }, }, }, { test: /\.ico$/, use: { loader: 'file-loader', options: { name: '[name].[ext]', }, }, }, { test: /\.(mp3|png)$/, use: { loader: 'file-loader', options: { name: 'static/[name].[ext]', }, }, }, ], }, ], }, resolve: { extensions: ['.ts', '.js', '.json'], }, }; };