Created
April 17, 2022 03:36
-
-
Save tigt/c0f8344690c306f55426b4e2533895dd to your computer and use it in GitHub Desktop.
Marko + Sass Webpack config
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const path = require('path') | |
const webpack = require('webpack') | |
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer') | |
const { CleanWebpackPlugin } = require('clean-webpack-plugin') | |
const CopyFilesPlugin = require('copy-webpack-plugin') | |
const CSSExtractPlugin = require('mini-css-extract-plugin') | |
const MarkoPlugin = require('@marko/webpack/plugin').default | |
const nodeExternalsPlugin = require('webpack-node-externals') | |
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin') | |
const TerserPlugin = require('terser-webpack-plugin') | |
const WarningsToErrorsPlugin = require('warnings-to-errors-webpack-plugin') | |
const isDev = process.env.NODE_ENV === 'development' | |
const isProd = !isDev | |
if (isDev) { | |
var BrowserSyncPlugin = require('browser-sync-webpack-plugin') | |
var SpawnServerPlugin = require('spawn-server-webpack-plugin') | |
} | |
const SHARED_DEV_PORT = 8080 | |
const ASSETS_BASE_URL = '/_/' | |
const markoPlugin = new MarkoPlugin() | |
const spawnedServer = isDev && new SpawnServerPlugin() | |
const cssExtractor = new CSSExtractPlugin({ | |
filename: '[name]-[contenthash].css', | |
ignoreOrder: true // We can’t guarantee that component CSS will concat in the same order across all chunks (nor should we) | |
}) | |
/** | |
* BEWARE: WebPack does this _hilarious_ thing where it applies plugins and loaders in the **reverse** order of the array. | |
*/ | |
const browserBuild = { | |
name: 'Browser 📱 ', | |
optimization: { | |
splitChunks: { | |
chunks: 'all', | |
maxInitialRequests: 3 | |
} | |
}, | |
output: { | |
filename: '[name]-[contenthash].js', // consider not using [hash]es when isDev: https://webpack.js.org/guides/build-performance/#avoid-production-specific-tooling | |
path: path.join(__dirname, 'dist/browser') | |
}, | |
performance: isDev ? undefined : { // Consider always doing this, even in dev? | |
maxAssetSize: kilobytes(30 * 5), // Multiplying this and the below by 5 because Webpack limits _un_compressed size | |
maxEntrypointSize: kilobytes(50 * 5), | |
hints: 'error' // This should go from error to warn though when isDev | |
}, | |
devServer: isDev ? { | |
overlay: { warnings: true, errors: true }, // open browser tab when build fails | |
noInfo: true, // only show warnings/errors | |
contentBase: false, // I forget why we set this, but I remember it sucks | |
serveIndex: true, // generate a file list in directories without an index.html | |
...spawnedServer.devServerConfig, | |
port: SHARED_DEV_PORT | |
} : undefined, | |
plugins: [ | |
new webpack.DefinePlugin({ 'process.browser': true }), | |
isDev && new BrowserSyncPlugin({ | |
open: 'ui', | |
ui: { port: 9090 }, | |
host: 'localhost', | |
notify: false, | |
proxy: { | |
target: `http://localhost:${SHARED_DEV_PORT}`, | |
ws: true // allow WebSockets so webpack-dev-server’s HMR can work | |
} | |
}, | |
{ | |
injectCss: true, | |
reload: false // prevent BrowserSync from reloading the page; let webpack-dev-server do it | |
}), | |
isProd && new BundleAnalyzerPlugin({ | |
analyzerMode: 'static', // generate an .html file, don’t start up a server | |
defaultSizes: 'gzip', | |
logLevel: 'warn', | |
reportFilename: '../bundle-analysis.html' | |
}), | |
new CopyFilesPlugin([{ | |
from: 'src/static-urls', | |
to: 'static-urls', | |
ignore: ['_README.md'] | |
}]), | |
cssExtractor, | |
isProd && new OptimizeCssAssetsPlugin(), // Consider always doing this? | |
markoPlugin.browser | |
] | |
} | |
const serverBuild = { | |
name: 'Server 🏸 ', | |
target: 'async-node', // Node.js with async/await support | |
node: { | |
__dirname: false // make Webpack stop mocking `__dirname`, since it works fine on the server | |
}, | |
externals: [nodeExternalsPlugin()], | |
optimization: { minimize: false }, // no need to minify on the server | |
output: { | |
libraryTarget: 'commonjs2', | |
path: path.join(__dirname, 'dist/server') | |
}, | |
stats: { | |
assets: false, | |
children: false | |
}, | |
plugins: [ | |
new webpack.DefinePlugin({ | |
'process.browser': undefined, // do we need to explicitly override this? | |
'process.env.BUNDLE': true | |
}), | |
new webpack.BannerPlugin({ // abuse the Webpack banner API to inject JS at the top of files | |
banner: 'require("source-map-support").install();', | |
raw: true | |
}), | |
cssExtractor, | |
isDev && spawnedServer, | |
markoPlugin.server | |
] | |
} | |
// Shared settings for both server and browser compilers | |
function createWebpackCompiler (config) { | |
return { | |
...config, | |
mode: isProd ? 'production' : 'development', | |
devtool: isProd ? 'source-map' : 'inline-source-map', | |
output: { | |
publicPath: ASSETS_BASE_URL, | |
hashDigestLength: 7, | |
...config.output | |
}, | |
optimization: { | |
minimizer: [ | |
new TerserPlugin({ | |
cache: true, // cache transformed JS in node_modules/.cache/ | |
sourceMap: true | |
}) | |
], | |
noEmitOnErrors: true, | |
...config.optimization | |
}, | |
stats: { | |
builtAt: false, | |
chunks: false, | |
entrypoints: false, | |
excludeAssets: /static-urls|\.map$/, // these files don’t matter for the frontend bundle | |
hash: false, | |
modules: false, | |
version: false, | |
...config.stats | |
}, | |
resolve: { | |
extensions: ['.js', '.json', '.marko'] | |
}, | |
module: { | |
rules: [ | |
{ test: /\.marko$/, loader: '@marko/webpack/loader' }, | |
{ test: /\.css$/, use: [CSSExtractPlugin.loader, 'css-loader'] }, | |
{ test: /\.scss$/, use: [CSSExtractPlugin.loader, 'css-loader', 'sass-loader'] }, | |
{ | |
test: /\.(?:jpe?g|gif|png|svg)$/, | |
loader: 'file-loader', | |
options: { | |
// Write assets from server & browser compiler output to browser folder | |
outputPath: '../browser', | |
publicPath: ASSETS_BASE_URL, | |
name: '[name]-[contenthash:base62:5].[ext]' | |
} | |
} | |
] | |
}, | |
plugins: [ | |
...config.plugins, | |
new WarningsToErrorsPlugin(), | |
new webpack.DefinePlugin({ | |
__DEV__: isDev, | |
__PROD__: isProd | |
}), | |
isProd && new CleanWebpackPlugin({ | |
cleanStaleWebpackAssets: false // works around https://github.com/webpack-contrib/copy-webpack-plugin/issues/385 | |
}) | |
].filter(Boolean) | |
} | |
} | |
function kilobytes (x) { | |
return x * 1024 | |
} | |
module.exports = [browserBuild, serverBuild].map(createWebpackCompiler) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment