import CopyWebpackPlugin from 'copy-webpack-plugin' ; import glob from 'glob' ; import HtmlWebpackPlugin from 'html-webpack-plugin' ; import HtmlWebpackTagsPlugin from 'html-webpack-tags-plugin' ; import MiniCssExtractPlugin from 'mini-css-extract-plugin' ; import { execSync } from 'node:child_process' ; import path from 'node:path' ; import PurgecssPlugin from 'purgecss-webpack-plugin' ; import webpack from 'webpack' ; import { getPackageNameFromPath } from './src/utils/getPackageNameFromPath' ; import myPackage from './package.json' ; export default ( _webpackEnv , argv ) => { const isProd = argv . mode === 'production' ; const output = `[name].${ isProd ? 'production.min' : 'development' }` ; const config = { entry : './src/index.tsx' , output : { path : path . join ( __dirname , 'build' ) , filename : `${ output }.js` , publicPath : '/' } , devtool : isProd ? 'source-map' : 'cheap-module-source-map' , resolve : { extensions : [ '.js' , '.ts' , '.tsx' ] } , module : { rules : [ { test : /\.(js|tsx?)$/ , exclude : /\/core-js/ , loader : 'babel-loader' } , { test : /\.scss$/ , use : [ isProd ? MiniCssExtractPlugin . loader : { loader : 'style-loader' } , 'css-loader' , { loader : 'postcss-loader' , options : { postcssOptions : { plugins : [ [ 'postcss-preset-env' ] ] } } } , 'sass-loader' ] } ] } , plugins : [ new CopyWebpackPlugin ( { patterns : [ { from : './src/assets/favicons' , to : 'favicons' } ] } ) , isProd && new MiniCssExtractPlugin ( { filename : `${ output }.css` } ) , isProd && new PurgecssPlugin ( { paths : glob . sync ( 'src/**/*' , { nodir : true } ) , safelist : [ ] } ) , new HtmlWebpackPlugin ( { description : myPackage . description , version : myPackage . version , buildMode : isProd ? 'production' : 'development' , date : new Date ( ) . toISOString ( ) , rev : execSync ( 'git rev-parse HEAD' ) . toString ( ) . trim ( ) , template : './src/index.html' , hash : isProd } ) , new HtmlWebpackTagsPlugin ( { links : [ { path : 'favicons/apple-touch-icon.png' , attributes : { rel : 'apple-touch-icon' , sizes : '180x180' } } , { path : 'favicons/favicon-32x32.png' , attributes : { rel : 'icon' , type : 'image/png' , sizes : '32x32' } } , { path : 'favicons/favicon-16x16.png' , attributes : { rel : 'icon' , type : 'image/png' , sizes : '16x16' } } , { path : 'favicons/site.webmanifest' , attributes : { rel : 'manifest' } } , { path : 'favicons/safari-pinned-tab.svg' , attributes : { rel : 'mask-icon' , color : '#ed1d24' } } , { path : 'favicons/favicon.ico' , attributes : { rel : 'shortcut icon' } } ] , metas : [ { attributes : { name : 'msapplication-TileColor' , content : '#ed1d24' } } , { path : 'favicons/browserconfig.xml' , attributes : { name : 'msapplication-config' } } , { attributes : { name : 'theme-color' , content : '#ffffff' } } ] , append : false , hash : true } ) ] as webpack . WebpackPluginInstance [ ] , devServer : { historyApiFallback : true } , optimization : { runtimeChunk : 'single' , splitChunks : { chunks : 'all' , minSize : 0 , maxInitialRequests : Number . POSITIVE_INFINITY , cacheGroups : { vendors : { test : /\/node_modules\// , name ( module , chunks ) { const packageName = getPackageNameFromPath ( module . context ) . replace ( '/' , '-' ) ; return `${ packageName }~${ chunks . map ( chunk => chunk . name ) . join ( '~' ) }` ; } } } } } } ; config . plugins = config . plugins ! . filter ( plugin => plugin ) ; return config ; } ;