const path = require('path'); const webpack = require('webpack'); const CleanPlugin = require('clean-webpack-plugin'); const TerserPlugin = require('terser-webpack-plugin'); const ProgressBarPlugin = require('progress-bar-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const HtmlPlugin = require('html-webpack-plugin'); const ScriptExtHtmlPlugin = require('script-ext-html-webpack-plugin'); const CopyPlugin = require('copy-webpack-plugin'); const WatchTimestampsPlugin = require('./config/watch-timestamps-plugin'); const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; const WorkerPlugin = require('worker-plugin'); const AutoSWPlugin = require('./config/auto-sw-plugin'); const CrittersPlugin = require('critters-webpack-plugin'); const AssetTemplatePlugin = require('./config/asset-template-plugin'); const addCssTypes = require('./config/add-css-types'); const VERSION = require('./package.json').version; /** @returns {Promise} */ module.exports = async function(_, env) { const isProd = env.mode === 'production'; const nodeModules = path.join(__dirname, 'node_modules'); const componentStyleDirs = [ path.join(__dirname, 'src', 'components'), path.join(__dirname, 'src', 'codecs'), path.join(__dirname, 'src', 'custom-els'), path.join(__dirname, 'src', 'lib') ]; await addCssTypes(componentStyleDirs, { watch: !isProd }); return { mode: isProd ? 'production' : 'development', entry: { 'first-interaction': './src/index' }, devtool: isProd ? 'source-map' : 'inline-source-map', stats: 'minimal', output: { filename: isProd ? '[name].[chunkhash:5].js' : '[name].js', chunkFilename: '[name].[chunkhash:5].js', path: path.join(__dirname, 'build'), publicPath: '/', globalObject: 'self' }, resolve: { extensions: ['.ts', '.tsx', '.mjs', '.js', '.scss', '.css'], alias: { style: path.join(__dirname, 'src/style') } }, resolveLoader: { alias: { // async-component-loader returns a wrapper component that waits for the import to load before rendering: async: path.join(__dirname, 'config', 'async-component-loader') } }, module: { rules: [ { oneOf: [ { test: /(\.mjs|\.esm\.js)$/i, // don't use strict esm resolution for mjs: type: 'javascript/auto', resolve: {}, parser: { harmony: true, amd: false, commonjs: false, system: false, requireInclude: false, requireEnsure: false, requireContext: false, browserify: false, requireJs: false, node: false } }, { type: 'javascript/auto', resolve: {}, parser: { system: false, requireJs: false } } ] }, { test: /\.(scss|sass)$/, loader: 'sass-loader', // SCSS gets preprocessed, then treated like any other CSS: enforce: 'pre', options: { sourceMap: true, includePaths: [nodeModules] } }, { test: /\.(scss|sass|css)$/, // Only enable CSS Modules within `src/components/*` include: componentStyleDirs, use: [ // In production, CSS is extracted to files on disk. In development, it's inlined into JS: isProd ? MiniCssExtractPlugin.loader : 'style-loader', { loader: 'css-loader', options: { modules: true, localIdentName: isProd ? '[hash:base64:5]' : '[local]__[hash:base64:5]', namedExport: true, camelCase: true, importLoaders: 1, sourceMap: isProd, sass: true } } ] }, { test: /\.(scss|sass|css)$/, // Process non-modular CSS everywhere *except* `src/components/*` exclude: componentStyleDirs, use: [ isProd ? MiniCssExtractPlugin.loader : 'style-loader', { loader: 'css-loader', options: { importLoaders: 1, sourceMap: isProd } } ] }, { test: /\.tsx?$/, exclude: nodeModules, loader: 'ts-loader' }, { // All the codec files define a global with the same name as their file name. `exports-loader` attaches those to `module.exports`. test: /\.js$/, include: path.join(__dirname, 'src', 'codecs'), loader: 'exports-loader' }, { // Emscripten modules don't work with Webpack's Wasm loader. test: /\.wasm$/, exclude: /(? hack to load async css: preload: 'media', // inline all styles from any stylesheet below this size: inlineThreshold: 2000, // don't bother lazy-loading non-critical stylesheets below this size, just inline the non-critical styles too: minimumExternalSize: 4000, // don't emit