I'm sure there a hundreds of variants of these out there in the wild, but I thought I'd throw my aging hat into the ring. Or keys into the hat. What's the saying?
This is something I've boiled down to be as light as I could possibly make it, and as straightforward to work with as I could get. Although critique is always welcome. Constructive critique. Don't just wade in here, call me a muppet, and then ride off into the sunset.
I've put a link to the Github repository at the end, so you can skip past everything if you just want the raw goods.
Node version management
First thing to talk about - Node.js. I expect many of you out there working on front-end development need to switch node versions between different projects. It's a given. Of course, we need a way to manage that, and I used to use NVM.
However, the past few months I've been trying out something called FNM, or Fast Node Manager, and I really like it. It's built in Rust, and if you want to give it a go, there's a very descriptive Github repo here https://github.com/Schniz/fnm that explains how to configure it.
What's in the box?
Firstly, the packaging
This is my package.json
. I'm running it in Node.js 22.14.0, and there is a .node-version
file in the project that should automatically install the correct node variant if you're running FNM with --use-on-cd
. (I won't explain that here, it's in the repo referenced above).
{
"name": "frontend-build",
"version": "1.0.0",
"description": "Webpack build for the Bootstrap and VueJS",
"author": "davidisnotnull",
"license": "MIT",
"keywords": [
"webpack",
"boilerplate",
"template",
"setup"
],
"dependencies": {
"@fortawesome/fontawesome-free": "^6.2.1",
"@fortawesome/fontawesome-svg-core": "^6.2.1",
"@fortawesome/free-solid-svg-icons": "^6.2.1",
"@fortawesome/vue-fontawesome": "^3.0.2",
"@popperjs/core": "^2.11.6",
"axios": "^1.1.3",
"bootstrap": "^5.2.2",
"jquery": "^3.6.1",
"jquery-validation": "^1.19.5",
"jquery-validation-unobtrusive": "^4.0.0",
"mitt": "^3.0.0",
"vue": "^3.2.41",
"vue-axios": "^3.5.2"
},
"devDependencies": {
"@babel/core": "^7.21.0",
"@babel/preset-env": "^7.19.4",
"assets-webpack-plugin": "^7.1.1",
"autoprefixer": "^10.4.12",
"babel-loader": "^8.2.5",
"clean-webpack-plugin": "^4.0.0",
"css-loader": "^6.7.1",
"file-loader": "^6.2.0",
"mini-css-extract-plugin": "^2.7.5",
"postcss-loader": "^7.0.1",
"sass": "^1.55.0",
"sass-loader": "^13.1.0",
"style-loader": "^3.3.1",
"url-loader": "^4.1.1",
"vue-loader": "^17.0.0",
"webpack": "^5.78.0",
"webpack-bundle-analyzer": "^4.6.1",
"webpack-cli": "^4.10.0",
"webpack-merge": "^5.8.0"
},
"engines": {
"node": ">=22"
},
"scripts": {
"build": "webpack --config ./configuration/webpack.dev.config.js --mode=development",
"watch": "webpack --config ./configuration/webpack.dev.config.js --mode=development --watch",
"bundle": "npm install && npm run watch",
"production": "webpack --config ./configuration/webpack.production.config.js --mode=production",
"stats": "webpack --config ./configuration/webpack.production.config.js --mode=production --json > dist/stats.json && webpack-bundle-analyzer dist/stats.json"
}
}
There's a dev
and production
version of the build. That's kind of self-explanatory, but use dev
when you're dev'ing so you can debug things easier.
Enter webpack
I've used webpack-merge
so that I can create config variants for development and production. You'll see these in the configuration
folder in the repo.
The main bare-bones webpack config looks like this:
const path = require('path');
const environment = require('./configuration/environment');
const webpack = require('webpack')
const AssetsPlugin = require('assets-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader')
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: [
path.resolve(__dirname, 'js/app.js'),
path.resolve(__dirname, 'scss/app.scss')
],
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
'babel-loader'
]
},
{
test: /\.css$/,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader',
},
],
},
{
test: /\.(scss)$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
},
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: function () {
return [
require('autoprefixer')
];
}
}
}
},
{
loader: 'sass-loader'
},
]
},
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/'
}
}
]
}
]
},
output: {
filename: 'js/[name].[contenthash].js',
path: environment.paths.output,
publicPath: '/dist/'
},
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
}),
new CleanWebpackPlugin({
verbose: true,
cleanOnceBeforeBuildPatterns: ['**/*', '!stats.json'],
}),
new MiniCssExtractPlugin({
filename: "css/[name].[contenthash].css",
}),
new VueLoaderPlugin(),
new AssetsPlugin({
filename: 'webpack.assets.json',
path: environment.paths.output,
prettyPrint: true,
}),
],
optimization: {
minimize: false,
},
resolve: {
fallback: {
"fs": false
},
extensions: ['.js', '.jsx', '.json', '.css', '.scss'],
modules: [
path.resolve(__dirname, 'node_modules'),
]
}
}
This includes a compiler for Vuejs as well, as I've been using that for some simple interactive functionality. I like React, but I find it a bit too heavy for a lot of the simpler stuff that clients tend to ask for. Unless someone wants an SPA, I find myself leaning into Vue a lot more.
The great babel fish
I've got babel configured to only target modern browsers. No IE11 fixes required these days. I am so glad that old Trident engine is dead.
module.exports = {
presets: [
[
"@babel/preset-env",
{
// Target modern browsers (>0.25% market share and not obsolete)
targets: ">0.25%, not dead",
// Enables polyfill injection based on your code usage (and your browser targets)
useBuiltIns: "usage",
corejs: "3.28",
// Apply bug fixes from Babel's plugin layer
bugfixes: true
}
]
]
};
Scripting and styling
There's an app.js
and an app.scss
file in their respective folders in the repo. These, again, are barebones and just import the components that you need to get going.
Extend these as you need to.
That's pretty much it. PostCSS and Sass Linting are both light-touch configured, so you've got scope to modify those as you like as well.
Now go grab the repo
I've structured all of this into a public repo on Github. Please use it to your heart's content. Fork it. Recommend changes.
https://github.com/davidisnotnull/webpack-build-framework
David
Top comments (0)