const path = require('path') const TerserPlugin = require('terser-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const PrerenderSPAPlugin = require('prerender-spa-plugin') const Renderer = PrerenderSPAPlugin.PuppeteerRenderer const CopyPlugin = require('copy-webpack-plugin') const { InjectManifest } = require('workbox-webpack-plugin') const ImageminPlugin = require('imagemin-webpack-plugin').default const FaviconsWebpackPlugin = require('favicons-webpack-plugin') const { JSDOM } = require('jsdom') const tempy = require('tempy') const fs = require('fs') const magicFile = (text) => { const p = tempy.file() fs.writeFileSync(p, text) return p } // const magicAppend = (orig, text, sep = '\n') => magicFile(fs.readFileSync(orig) + sep + text) 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, } prerenderOpts = (width, height, name) => ({ outputDir: path.join(__dirname, 'dist', 'prerender', name), staticDir: path.join(__dirname, 'dist'), routes: ROUTES, minify: htmlMin, renderer: new Renderer({ renderAfterTime: 300, viewport: { width: width, height: height, deviceScaleFactor: 1, } }), postProcess(renderedRoute) { const dom = new JSDOM(renderedRoute.html, { runScripts: 'outside-only' }) dom.window.eval(` const wandTarget = document.createElement('script') wandTarget.src = '/wand.js' document.body.appendChild(wandTarget) `) return { ...renderedRoute, html: dom.serialize(), } } }) module.exports = { 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/], loader: 'elm-webpack-loader', options: { optimize: true } }, { test: /\.html$/, loader: 'html-loader', options: { minimize: true, ...htmlMin }, } ], }, optimization: { minimize: true, minimizer: [ new TerserPlugin({ terserOptions: jsMin }), new TerserPlugin({ terserOptions: jsMin }), ], }, plugins: [ new CopyPlugin([{ from: 'data', to: 'data' }, { from: 'wand.js', to: 'wand.js' }]), new ImageminPlugin({ jpegtran: { progressive: true }, svgo: null, // svgo: { // full: true, // plugins: [ // { 'removeDoctype': { className: 'removeDoctype', attributes: [] } }, // { 'removeXMLProcInst': { className: 'removeXMLProcInst', attributes: [] } }, // { 'removeComments': { className: 'removeComments', attributes: [] } }, // { 'removeMetadata': { className: 'removeMetadata', attributes: [] } }, // { 'removeXMLNS': { className: 'removeXMLNS', attributes: [] } }, // { 'removeEditorsNSData': { className: 'removeEditorsNSData', attributes: [] } }, // { 'cleanupAttrs': { className: 'cleanupAttrs', attributes: [] } }, // { 'inlineStyles': { className: 'inlineStyles', attributes: [] } }, // { 'minifyStyles': { className: 'minifyStyles', attributes: [] } }, // { 'convertStyleToAttrs': { className: 'convertStyleToAttrs', attributes: [] } }, // { 'cleanupIDs': { className: 'cleanupIDs', attributes: [] } }, // { 'prefixIds': { className: 'prefixIds', attributes: [] } }, // { 'removeRasterImages': { className: 'removeRasterImages', attributes: [] } }, // { 'removeUselessDefs': { className: 'removeUselessDefs', attributes: [] } }, // { 'cleanupNumericValues': { className: 'cleanupNumericValues', attributes: [] } }, // { 'cleanupListOfValues': { className: 'cleanupListOfValues', attributes: [] } }, // { 'convertColors': { className: 'convertColors', attributes: [] } }, // { 'removeUnknownsAndDefaults': { className: 'removeUnknownsAndDefaults', attributes: [] } }, // { 'removeNonInheritableGroupAttrs': { className: 'removeNonInheritableGroupAttrs', attributes: [] } }, // { 'removeUselessStrokeAndFill': { className: 'removeUselessStrokeAndFill', attributes: [] } }, // { 'removeViewBox': { className: 'removeViewBox', attributes: [] } }, // { 'cleanupEnableBackground': { className: 'cleanupEnableBackground', attributes: [] } }, // { 'removeHiddenElems': { className: 'removeHiddenElems', attributes: [] } }, // { 'removeEmptyText': { className: 'removeEmptyText', attributes: [] } }, // { 'convertShapeToPath': { className: 'convertShapeToPath', attributes: [] } }, // { 'convertEllipseToCircle': { className: 'convertEllipseToCircle', attributes: [] } }, // { 'moveElemsAttrsToGroup': { className: 'moveElemsAttrsToGroup', attributes: [] } }, // { 'moveGroupAttrsToElems': { className: 'moveGroupAttrsToElems', attributes: [] } }, // { 'collapseGroups': { className: 'collapseGroups', attributes: [] } }, // { 'convertPathData': { className: 'convertPathData', attributes: [] } }, // { 'convertTransform': { className: 'convertTransform', attributes: [] } }, // { 'removeEmptyAttrs': { className: 'removeEmptyAttrs', attributes: [] } }, // { 'removeEmptyContainers': { className: 'removeEmptyContainers', attributes: [] } }, // { 'mergePaths': { className: 'mergePaths', attributes: [] } }, // { 'removeUnusedNS': { className: 'removeUnusedNS', attributes: [] } }, // { 'sortAttrs': { className: 'sortAttrs', attributes: [] } }, // { 'sortDefsChildren': { className: 'sortDefsChildren', attributes: [] } }, // { 'removeTitle': { className: 'removeTitle', attributes: [] } }, // { 'removeDesc': { className: 'removeDesc', attributes: [] } }, // { 'removeDimensions': { className: 'removeDimensions', attributes: [] } }, // { 'removeAttrs': { className: 'removeAttrs', attributes: [] } }, // { 'removeAttributesBySelector': { className: 'removeAttributesBySelector', attributes: [] } }, // { 'removeElementsByAttr': { className: 'removeElementsByAttr', attributes: [] } }, // { 'addClassesToSVGElement': { className: 'addClassesToSVGElement', attributes: [] } }, // { 'removeStyleElement': { className: 'removeStyleElement', attributes: [] } }, // { 'removeScriptElement': { className: 'removeScriptElement', attributes: [] } }, // { 'addAttributesToSVGElement': { className: 'addAttributesToSVGElement', attributes: [] } }, // { 'removeOffCanvasPaths': { className: 'removeOffCanvasPaths', attributes: [] } }, // { 'reusePaths': { className: 'reusePaths', attributes: [] } }, // ] // }, }), new HtmlWebpackPlugin({ hash: true, inject: true, minify: htmlMin, template: './template.html', meta: { viewport: 'width=360, initial-scale=1, maximum-scale=1' } }), 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, } }), new PrerenderSPAPlugin(prerenderOpts(1920, 1080, 'desktop')), new PrerenderSPAPlugin(prerenderOpts(375, 667, 'phone')), new PrerenderSPAPlugin(prerenderOpts(768, 1024, 'tablet')), new InjectManifest({ importWorkboxFrom: 'local', swSrc: magicFile(` workbox.core.skipWaiting(); workbox.core.clientsClaim(); workbox.precaching.cleanupOutdatedCaches() workbox.routing.registerNavigationRoute('/index.html'); workbox.precaching.precacheAndRoute(self.__precacheManifest); `), swDest: 'sw.js', }), ] }