Intellectual Webpack cache management (#5295)

* Intellectual Webpack cache management

* Check if webpackRoot exists.

* Wrap webpack directory reading in try-catch

* Enhance cache pruning console output
This commit is contained in:
Cohee
2026-03-15 23:30:57 +02:00
committed by GitHub
parent f868e9ed68
commit 645b8063e1
3 changed files with 72 additions and 20 deletions
+7 -5
View File
@@ -26,16 +26,18 @@ export default function getWebpackServeMiddleware() {
/** /**
* Wait until Webpack is done compiling. * Wait until Webpack is done compiling.
* @param {object} param Parameters. * @param {object} param Parameters.
* @param {boolean} [param.forceDist] Whether to force the use the /dist folder. * @param {boolean} [param.forceDist=false] Whether to force the use the /dist folder.
* @param {boolean} [param.pruneCache=false] Whether to prune old cache directories before compiling.
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
devMiddleware.runWebpackCompiler = ({ forceDist = false } = {}) => { devMiddleware.runWebpackCompiler = ({ forceDist = false, pruneCache = false } = {}) => {
const publicLibConfig = getPublicLibConfig(forceDist); console.log();
console.log('Compiling frontend libraries...');
const publicLibConfig = getPublicLibConfig({ forceDist, pruneCache });
const compiler = webpack(publicLibConfig); const compiler = webpack(publicLibConfig);
return new Promise((resolve) => { return new Promise((resolve) => {
console.log();
console.log('Compiling frontend libraries...');
compiler.run((_error, stats) => { compiler.run((_error, stats) => {
const output = stats?.toString(publicLibConfig.stats); const output = stats?.toString(publicLibConfig.stats);
if (output) { if (output) {
+1 -1
View File
@@ -328,7 +328,7 @@ async function preSetupTasks() {
initRequestProxy({ enabled: cliArgs.requestProxyEnabled, url: cliArgs.requestProxyUrl, bypass: cliArgs.requestProxyBypass }); initRequestProxy({ enabled: cliArgs.requestProxyEnabled, url: cliArgs.requestProxyUrl, bypass: cliArgs.requestProxyBypass });
// Wait for frontend libs to compile // Wait for frontend libs to compile
await webpackMiddleware.runWebpackCompiler(); await webpackMiddleware.runWebpackCompiler({ pruneCache: true });
} }
/** /**
+64 -14
View File
@@ -1,45 +1,95 @@
import process from 'node:process'; import process from 'node:process';
import path from 'node:path'; import path from 'node:path';
import fs from 'node:fs';
import crypto from 'node:crypto';
import isDocker from 'is-docker'; import isDocker from 'is-docker';
import webpack from 'webpack'; import webpack from 'webpack';
import { serverDirectory } from './src/server-directory.js'; import { serverDirectory } from './src/server-directory.js';
import { getVersion, color } from './src/util.js';
/**
* Generate a cache version string based on the application version, Git revision, and Webpack version.
* @returns {string} The cache version string.
*/
function getWebpackCacheVersion() {
return crypto.createHash('shake256', { outputLength: 8 })
.update(JSON.stringify([appVersion.pkgVersion, appVersion.gitRevision, webpack.version]))
.digest('hex');
}
/**
* Prune old Webpack cache directories that do not match the current cache version.
* @param {string} webpackRoot The root directory where Webpack caches are stored.
* @param {string} currentCacheVersion The current cache version to keep.
*/
function pruneWebpackCache(webpackRoot, currentCacheVersion) {
try {
if (!fs.existsSync(webpackRoot)) {
return;
}
const cacheDirectories = fs.readdirSync(webpackRoot, { withFileTypes: true })
.filter(dirent => dirent.isDirectory())
.map(dirent => dirent.name);
for (const dir of cacheDirectories) {
const dirPath = path.join(webpackRoot, dir);
if (dir !== currentCacheVersion) {
try {
fs.rmSync(dirPath, { recursive: true, force: true });
console.debug(`Removed outdated cache directory: ${color.yellow(dir)}`);
} catch (error) {
console.error(`Failed to remove Webpack cache directory: ${color.red(dir)}`, error);
}
}
}
} catch (error) {
console.error('Failed to read Webpack cache directories for pruning.', error);
}
}
const appVersion = await getVersion();
/** /**
* Get the Webpack configuration for the public/lib.js file. * Get the Webpack configuration for the public/lib.js file.
* 1. Docker has got cache and the output file pre-baked. * 1. Docker has got cache and the output file pre-baked.
* 2. Non-Docker environments use the global DATA_ROOT variable to determine the cache and output directories. * 2. Non-Docker environments use the global DATA_ROOT variable to determine the cache and output directories.
* @param {boolean} forceDist Whether to force the use the /dist folder. * @param {object} options Configuration options.
* @param {boolean} [options.forceDist=false] Whether to force the use the /dist folder.
* @param {boolean} [options.pruneCache=false] Whether to prune old cache directories.
* @returns {import('webpack').Configuration} * @returns {import('webpack').Configuration}
* @throws {Error} If the DATA_ROOT variable is not set. * @throws {Error} If the DATA_ROOT variable is not set.
* */ * */
export default function getPublicLibConfig(forceDist = false) { export default function getPublicLibConfig({ forceDist = false, pruneCache = false } = {}) {
function getCacheDirectory() { function getWebpackRoot() {
if (forceDist || isDocker()) { if (forceDist || isDocker()) {
return path.resolve(process.cwd(), 'dist', '_webpack', webpack.version, 'cache'); return path.resolve(process.cwd(), 'dist', '_webpack');
} }
if (typeof globalThis.DATA_ROOT === 'string') { if (typeof globalThis.DATA_ROOT === 'string') {
return path.resolve(globalThis.DATA_ROOT, '_webpack', webpack.version, 'cache'); return path.resolve(globalThis.DATA_ROOT, '_webpack');
} }
throw new Error('DATA_ROOT variable is not set.'); throw new Error('DATA_ROOT variable is not set.');
} }
function getCacheDirectory() {
return path.join(webpackRoot, cacheVersion, 'cache');
}
function getOutputDirectory() { function getOutputDirectory() {
if (forceDist || isDocker()) { return path.join(webpackRoot, cacheVersion, 'output');
return path.resolve(process.cwd(), 'dist', '_webpack', webpack.version, 'output');
}
if (typeof globalThis.DATA_ROOT === 'string') {
return path.resolve(globalThis.DATA_ROOT, '_webpack', webpack.version, 'output');
}
throw new Error('DATA_ROOT variable is not set.');
} }
const webpackRoot = getWebpackRoot();
const cacheVersion = getWebpackCacheVersion();
const cacheDirectory = getCacheDirectory(); const cacheDirectory = getCacheDirectory();
const outputDirectory = getOutputDirectory(); const outputDirectory = getOutputDirectory();
if (pruneCache) {
pruneWebpackCache(webpackRoot, cacheVersion);
}
return { return {
mode: 'production', mode: 'production',
entry: path.join(serverDirectory, 'public/lib.js'), entry: path.join(serverDirectory, 'public/lib.js'),