const path = require('path') const TerserPlugin = require('terser-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const CopyPlugin = require('copy-webpack-plugin') const { GenerateSW } = require('workbox-webpack-plugin') const ImageminPlugin = require('imagemin-webpack-plugin').default const ImageminWebpWebpackPlugin = require('imagemin-webp-webpack-plugin') const ImageminJpegifyWebpackPlugin = require('./.imagemin-jpgify-webpack-plugin.js') const FaviconsWebpackPlugin = require('favicons-webpack-plugin') const { JSDOM } = require('jsdom') const tempy = require('tempy') const fs = require('fs') const webpack = require('webpack') const magicFile = (text) => { const p = tempy.file() fs.writeFileSync(p, text) return p } const jsMin = { mangle: true, compress: { keep_fargs: false, pure_getters: true, pure_funcs: ['F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9'], unsafe: true, ecma: 6, unsafe_comps: true, unsafe_arrows: true, } } const htmlMin = { minifyCSS: { compatibility: 'ie9', level: 2 }, minifyJS: jsMin, collapseBooleanAttributes: true, removeRedundantAttributes: true, removeScriptTypeAttributes: true, removeStyleLinkTypeAttributes: true, removeComments: true, sortAttributes: true, sortClassName: true, } const productionPlugins = [ new GenerateSW({ navigateFallback: '/index.html', clientsClaim: true, skipWaiting: true, swDest: 'sw.js', }), // smallen images new ImageminPlugin({ jpegtran: { progressive: true }, svgo: null, }), // make the webps (.l.png is for lossy) new ImageminWebpWebpackPlugin({ config: [{ test: /\.jpe?g$/, options: { force: true, quality: 75 } }, { test: /\.l\.png$/, options: { force: true, quality: 75 } }, { test: /\.png$/, options: { force: true, lossless: true } }], overrideExtension: false, detailedLogs: false, silent: true, strict: true, }), // make jpgs out of .l.pngs too new ImageminJpegifyWebpackPlugin({ config: [{ test: /\.l\.png$/, options: { force: true, quality: 50 } }], overrideExtension: false, detailedLogs: false, silent: true, strict: true, }), new FaviconsWebpackPlugin({ logo: path.join(__dirname, 'data', 'images', 'icon.png'), inject: false, // manually in our template for now cos its fuckd mode: 'webapp', prefix: 'appdata', favicons: { appName: PRETTY_NAME, appDescription: DESCRIPTION, background: BACKGROUND_COLOR, theme_color: THEME_COLOR, } }), ]; let plugins = (env, argv) => [ new webpack.HotModuleReplacementPlugin(), new CopyPlugin({ patterns: [{ from: 'data', to: 'data' }, { from: 'wand.js', to: 'wand.js' }] }), new HtmlWebpackPlugin({ hash: true, inject: true, minify: argv.mode == 'production' ? htmlMin : false, template: './template.html', meta: { viewport: 'width=320, initial-scale=1, maximum-scale=5', description: DESCRIPTION, } }), ]; module.exports = (env, argv) => ({ target: 'web', externals: { svgo: 'svgo' }, entry: magicFile(` if ('serviceWorker' in navigator) { window.addEventListener('load', () => navigator.serviceWorker.register('/sw.js')); } require('${process.env.PWD}/src/index.js') `), output: { filename: 'main.js', path: path.join(__dirname, 'dist'), publicPath: '/' }, module: { rules: [ { test: /\.elm$/, exclude: [/elm-stuff/, /node_modules/], use: [ { loader: 'elm-hot-webpack-loader', options: { debug: argv.mode != 'production', } }, { loader: 'elm-webpack-loader', options: { optimize: argv.mode == 'production', debug: argv.mode != 'production', }, }, ], }, { test: /\.html$/, loader: 'html-loader', options: { minimize: true, ...htmlMin }, }, { test: /\.css$/i, use: ['style-loader', 'css-loader'], }, ], }, devServer: { inline: true, hot: true, stats: 'errors-only' }, optimization: { minimize: argv.mode == 'production', minimizer: [ new TerserPlugin({ terserOptions: jsMin }), new TerserPlugin({ terserOptions: jsMin }), ], }, plugins: [...plugins(env, argv), ...(argv.mode == 'production' ? productionPlugins : [])], })