optimize webpack config for faster build website

- switch to modern html-bundler-webpack-plugin
- replace deprecated `file-loader` and `url-loader` with Webpack 5 assets module
- create single webpack config instead of two configs
This commit is contained in:
biodiscus 2023-09-02 22:20:55 +02:00
parent 3732d59249
commit 818f752714
12 changed files with 1731 additions and 1293 deletions

View file

@ -111,17 +111,14 @@
"eslint-plugin-optimize-regex": "1.2.1",
"eslint-plugin-promise": "6.1.1",
"eslint-plugin-sonarjs": "0.20.0",
"file-loader": "6.2.0",
"handlebars": "4.7.8",
"handlebars-loader": "1.7.3",
"html-webpack-plugin": "5.5.3",
"html-bundler-webpack-plugin": "2.10.1",
"husky": "^8.0.1",
"image-webpack-loader": "8.1.0",
"is-ci-cli": "2.2.0",
"jest": "29.6.2",
"lint-staged": "13.2.3",
"markdown-toc": "^1.2.0",
"mini-css-extract-plugin": "2.7.6",
"mkdirp": "3.0.1",
"nopt": "7.2.0",
"postcss": "8.4.27",
@ -134,7 +131,6 @@
"ts-loader": "9.4.4",
"ts-node": "10.9.1",
"typescript": "5.1.6",
"url-loader": "4.1.1",
"webpack": "5.88.2",
"webpack-cli": "5.1.4",
"webpack-dev-server": "4.15.1",

View file

@ -1,28 +1,40 @@
import path from 'path';
import webpack from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import HtmlBundlerPlugin from 'html-bundler-webpack-plugin';
// eslint-disable-next-line import/default
import CopyPlugin from 'copy-webpack-plugin';
const pages = ['index', 'demo'];
type Plugin = ((this: webpack.Compiler, compiler: webpack.Compiler) => void) | webpack.WebpackPluginInstance;
function plugins(page: string): Plugin[] {
return [
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[id].css',
}),
new HtmlWebpackPlugin({
hash: true,
inject: true,
title: `${page} page`,
filename: `${page}.html`,
template: `./website/templates/pages/${page}/${page}.handlebars`,
minify: {
const config = {
output: {
path: path.resolve(__dirname, './docs'),
},
resolve: {
extensions: ['.tsx', '.ts', '.jsx', '.js'],
},
plugins: [
new HtmlBundlerPlugin({
entry: {
// define templates here
index: './website/templates/pages/index/index.handlebars', // => docs/index.html,
demo: './website/templates/pages/demo/demo.handlebars', // => docs/demo.html
},
js: {
// output filename of compiled JavaScript, used if `inline` option is false (defaults)
filename: '[name].[contenthash:8].js',
//inline: true, // inlines JS into HTML
},
css: {
// output filename of extracted CSS, used if `inline` option is false (defaults)
filename: '[name].[contenthash:8].css',
//inline: true, // inlines CSS into HTML
},
preprocessor: 'handlebars', // use the handlebars compiler
preprocessorOptions: {
knownHelpersOnly: false,
helpers: [path.join(__dirname, 'website/templates/helpers')],
partials: [path.join(__dirname, 'website/templates/partials'), path.join(__dirname, 'website/templates/pages')],
},
minify: 'auto', // minify in production mode only
minifyOptions: {
html5: true,
collapseWhitespace: true,
caseSensitive: true,
@ -45,107 +57,74 @@ function plugins(page: string): Plugin[] {
{ from: 'website/sitemap.xml', to: 'sitemap.xml' },
],
}),
];
}
const config: webpack.Configuration[] = pages.map(page => {
return {
entry: {
[page]: `./website/templates/pages/${page}/${page}.ts`,
},
output: {
path: path.resolve(__dirname, './docs'),
},
resolve: {
extensions: ['.tsx', '.ts', '.jsx', '.js'],
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
],
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
{
test: /\.(gif|png|jpe?g|webp)$/i,
type: 'asset/resource',
generator: {
filename: 'images/[name][ext]?[hash]',
},
{
test: /\.(html)$/,
use: {
loader: 'html-loader',
use: [
{
loader: 'image-webpack-loader',
options: {
attrs: ['img:src'],
mozjpeg: {
progressive: true,
quality: 65,
},
optipng: {
enabled: true,
},
pngquant: {
quality: [0.65, 0.9],
speed: 4,
},
gifsicle: {
interlaced: false,
},
webp: {
quality: 75,
},
},
},
},
{
test: /\.handlebars$/,
loader: 'handlebars-loader',
options: {
inlineRequires: '/images/',
precompileOptions: {
knownHelpersOnly: false,
},
helperDirs: [path.join(__dirname, 'website/templates/helpers')],
partialDirs: [path.join(__dirname, 'website/templates')],
],
},
{
test: /\.(css)$/,
use: [{ loader: 'css-loader', options: { importLoaders: 1 } }, 'postcss-loader'],
},
{
test: /\.woff(2)?(\?v=\d\.\d\.\d)?$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 1000,
},
},
{
test: /\.(gif|png|jpe?g|webp)$/i,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]',
outputPath: 'images',
esModule: false,
},
},
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
quality: 65,
},
optipng: {
enabled: true,
},
pngquant: {
quality: [0.65, 0.9],
speed: 4,
},
gifsicle: {
interlaced: false,
},
webp: {
quality: 75,
},
},
},
],
},
{
test: /\.(css)$/,
use: [MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { importLoaders: 1 } }, 'postcss-loader'],
},
{
test: /\.woff(2)?(\?v=\d\.\d\.\d)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 1000,
mimetype: 'application/font-woff',
},
},
],
},
{
test: /\.(ttf|eot|svg)(\?v=\d\.\d\.\d)?$/,
loader: 'file-loader',
},
],
},
{
test: /\.(ttf|eot|svg)(\?v=\d\.\d\.\d)?$/,
type: 'asset/resource',
},
],
},
// enable live reload after changes
devServer: {
static: path.resolve(__dirname, './docs'),
watchFiles: {
paths: ['website/**/*.*'],
options: {
usePolling: true,
},
},
plugins: plugins(page),
};
});
},
};
export default config;

View file

@ -0,0 +1,24 @@
'use strict';
/* eslint-disable @typescript-eslint/no-var-requires */
const Handlebars = require('handlebars');
/** @typedef {import('handlebars').HelperOptions} HelperOptions */
/**
* @param {string} name
* @param {HelperOptions}
* @return {string}
*/
module.exports = function (name, options) {
// eslint-disable-next-line
const context = this;
let partial = context._blocks[name] || options.fn;
if (typeof partial === 'string') {
partial = Handlebars.compile(partial);
context._blocks[name] = partial;
}
return partial(context, { data: options.hash });
};

View file

@ -1,15 +0,0 @@
import handlebars, { HelperOptions } from 'handlebars';
const loadPartial = <T>(name: string): handlebars.Template<T> => {
let partial = handlebars.partials[name];
if (typeof partial === 'string') {
partial = handlebars.compile(partial);
handlebars.partials[name] = partial;
}
return partial;
};
export default (name: string, options: HelperOptions): string => {
const partial = loadPartial(name) || options.fn;
return partial(this, { data: options.hash });
};

View file

@ -0,0 +1,20 @@
'use strict';
/** @typedef {import('handlebars').HelperOptions} HelperOptions */
/**
* @param {string} name
* @param {HelperOptions} options
* @return {void}
*/
module.exports = function (name, options) {
// don't modify `this` in code directly, because it will be compiled in `exports` as an immutable object
// eslint-disable-next-line
const context = this;
if (!context._blocks) {
context._blocks = {};
}
context._blocks[name] = options.fn;
};

View file

@ -1,5 +0,0 @@
import handlebars, { HelperOptions } from 'handlebars';
export default (name: string, options: HelperOptions): void => {
handlebars.registerPartial(name, options.fn);
};

View file

@ -1,4 +1,8 @@
{{#partial "content"}}
{{> content}}
{{#partial 'scripts'}}
{{! define here page-specific source script files }}
<script src="./demo.ts" defer="defer"></script>
{{/partial}}
{{> ../../template}}
{{#partial "content"}}
{{> demo/content}}
{{/partial}}
{{> template}}

View file

@ -1,6 +1,6 @@
import { Diff2HtmlUI, defaultDiff2HtmlUIConfig, Diff2HtmlUIConfig } from '../../../../src/ui/js/diff2html-ui-slim';
import '../../../main.ts';
import '../../../main';
import '../../../main.css';
import 'highlight.js/styles/github.css';
import '../../../../src/ui/css/diff2html.css';

View file

@ -1,4 +1,8 @@
{{#partial "content"}}
{{> content}}
{{#partial 'scripts'}}
{{! define here page-specific source script files }}
<script src="./index.ts" defer="defer"></script>
{{/partial}}
{{> ../../template}}
{{#partial "content"}}
{{> index/content}}
{{/partial}}
{{> template}}

View file

@ -1,6 +1,6 @@
import Clipboard from 'clipboard';
import '../../../main.ts';
import '../../../main';
import '../../../main.css';
import './index.css';

View file

@ -40,6 +40,8 @@
ga('create', 'UA-78351861-2', 'auto');
ga('send', 'pageview');
</script>
{{#block 'scripts'}}{{/block}}
</head>
<body>

2717
yarn.lock

File diff suppressed because it is too large Load diff