refactor(core): remove clean-webpack-plugin (#11037)

* remove clean-webpack-plugin

* refactor: apply lint autofix

* remove clean-webpack-plugin

---------

Co-authored-by: slorber <749374+slorber@users.noreply.github.com>
This commit is contained in:
Sébastien Lorber 2025-03-27 18:44:48 +01:00 committed by GitHub
parent 37d8844506
commit dae29a2782
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 31 additions and 270 deletions

View file

@ -47,7 +47,6 @@
"combine-promises": "^1.1.0",
"commander": "^5.1.0",
"core-js": "^3.31.1",
"del": "^6.1.1",
"detect-port": "^1.5.1",
"escape-html": "^1.0.3",
"eta": "^2.2.0",

View file

@ -26,6 +26,7 @@ import type {
} from '@docusaurus/types';
import type {SiteCollectedData} from '../../common';
import {BuildCLIOptions} from './build';
import clearPath from '../utils/clearPath';
export type BuildLocaleParams = {
siteDir: string;
@ -77,6 +78,11 @@ export async function buildLocale({
props,
configureWebpackUtils,
}),
// We also clear website/build dir
// returns void, no useful result needed before compilation
// See also https://github.com/facebook/docusaurus/pull/11037
clearPath(outDir),
]),
);

View file

@ -12,13 +12,14 @@ import {
DEFAULT_BUILD_DIR_NAME,
GENERATED_FILES_DIR_NAME,
} from '@docusaurus/utils';
import clearPath from './utils/clearPath';
async function removePath(entry: {path: string; description: string}) {
if (!(await fs.pathExists(entry.path))) {
return;
}
try {
await fs.remove(entry.path);
await clearPath(entry.path);
logger.success`Removed the ${entry.description} at path=${path.relative(
process.cwd(),
entry.path,

View file

@ -0,0 +1,22 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import path from 'path';
import {rm} from 'fs/promises';
import {PerfLogger} from '@docusaurus/logger';
/**
* @param pathToClear
*/
export default async function clearPath(pathToClear: string): Promise<void> {
return PerfLogger.async(
`clearPath ${path.relative(process.cwd(), pathToClear)}`,
async () => {
await rm(pathToClear, {recursive: true, force: true});
},
);
}

View file

@ -13,7 +13,6 @@ import HtmlWebpackPlugin from 'html-webpack-plugin';
import {getProgressBarPlugin} from '@docusaurus/bundler';
import {createBaseConfig} from './base';
import ChunkAssetPlugin from './plugins/ChunkAssetPlugin';
import CleanWebpackPlugin from './plugins/CleanWebpackPlugin';
import ForceTerminatePlugin from './plugins/ForceTerminatePlugin';
import {createStaticDirectoriesCopyPlugin} from './plugins/StaticDirectoriesCopyPlugin';
import type {
@ -164,8 +163,6 @@ export async function createBuildClientConfig({
{
plugins: [
new ForceTerminatePlugin(),
// Remove/clean build folders before building bundles.
new CleanWebpackPlugin({verbose: false}),
// Visualize size of webpack output files with an interactive zoomable
// tree map.
bundleAnalyzer && new BundleAnalyzerPlugin(),

View file

@ -1,263 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* The MIT License (MIT)
* Copyright (c) 2015 John Agan
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// Forked from https://github.com/johnagan/clean-webpack-plugin
// Modified to optimize performance for Docusaurus specific use case
// More context: https://github.com/facebook/docusaurus/pull/1839
import path from 'path';
import fs from 'fs-extra';
import {sync as delSync} from 'del';
import type {Compiler, Stats} from 'webpack';
export type Options = {
/**
* Write Logs to Console
* (Always enabled when dry is true)
*
* default: false
*/
verbose?: boolean;
/**
* Automatically remove all unused webpack assets on rebuild
*
* default: true
*/
cleanStaleWebpackAssets?: boolean;
/**
* Do not allow removal of current webpack assets
*
* default: true
*/
protectWebpackAssets?: boolean;
/**
* Removes files once prior to Webpack compilation
* Not included in rebuilds (watch mode)
*
* Use !negative patterns to exclude files
*
* default: ['**\/*']
*/
cleanOnceBeforeBuildPatterns?: string[];
};
export default class CleanWebpackPlugin {
private readonly verbose: boolean;
private readonly cleanStaleWebpackAssets: boolean;
private readonly protectWebpackAssets: boolean;
private readonly cleanOnceBeforeBuildPatterns: string[];
private currentAssets: string[];
private initialClean: boolean;
private outputPath: string;
constructor(options: Options = {}) {
this.verbose = options.verbose === true || false;
this.cleanStaleWebpackAssets =
options.cleanStaleWebpackAssets === true ||
options.cleanStaleWebpackAssets === false
? options.cleanStaleWebpackAssets
: true;
this.protectWebpackAssets =
options.protectWebpackAssets === true ||
options.protectWebpackAssets === false
? options.protectWebpackAssets
: true;
this.cleanOnceBeforeBuildPatterns = Array.isArray(
options.cleanOnceBeforeBuildPatterns,
)
? options.cleanOnceBeforeBuildPatterns
: ['**/*'];
/**
* Store webpack build assets
*/
this.currentAssets = [];
/**
* Only used with cleanOnceBeforeBuildPatterns
*/
this.initialClean = false;
this.outputPath = '';
this.apply = this.apply.bind(this);
this.handleInitial = this.handleInitial.bind(this);
this.handleDone = this.handleDone.bind(this);
this.removeFiles = this.removeFiles.bind(this);
}
apply(compiler: Compiler): void {
if (!compiler.options.output.path) {
console.warn(
'clean-webpack-plugin: options.output.path not defined. Plugin disabled...',
);
return;
}
this.outputPath = compiler.options.output.path;
const {hooks} = compiler;
if (this.cleanOnceBeforeBuildPatterns.length !== 0) {
hooks.compile.tap('clean-webpack-plugin', () => {
this.handleInitial();
});
}
hooks.done.tap('clean-webpack-plugin', (stats) => {
this.handleDone(stats);
});
}
/**
* Initially remove files from output directory prior to build.
*
* Only happens once.
*
* Warning: It is recommended to initially clean your build directory outside
* of webpack to minimize unexpected behavior.
*/
handleInitial(): void {
if (this.initialClean) {
return;
}
if (
// eslint-disable-next-line no-restricted-properties
fs.pathExistsSync(this.outputPath) &&
// eslint-disable-next-line no-restricted-properties
fs.statSync(this.outputPath).isFile()
) {
throw new Error(
`A file '${this.outputPath}' already exists. Docusaurus needs this directory to save the build output. Either remove/change the file or choose a different build directory via '--out-dir'.`,
);
}
this.initialClean = true;
this.removeFiles(this.cleanOnceBeforeBuildPatterns);
}
handleDone(stats: Stats): void {
/**
* Do nothing if there is a webpack error
*/
if (stats.hasErrors()) {
if (this.verbose) {
console.warn('clean-webpack-plugin: pausing due to webpack errors');
}
return;
}
/**
* Fetch Webpack's output asset files
*/
const statsAssets =
stats.toJson({
all: false,
assets: true,
}).assets ?? [];
const assets = statsAssets.map((asset: {name: string}) => asset.name);
/**
* Get all files that were in the previous build but not the current
*
* (relies on del's cwd: outputPath option)
*/
const staleFiles = this.currentAssets.filter(
(previousAsset) => !assets.includes(previousAsset),
);
/**
* Save assets for next compilation
*/
this.currentAssets = assets.sort();
const removePatterns: string[] = [];
/**
* Remove unused webpack assets
*/
if (this.cleanStaleWebpackAssets && staleFiles.length !== 0) {
removePatterns.push(...staleFiles);
}
if (removePatterns.length !== 0) {
this.removeFiles(removePatterns);
}
}
removeFiles(patterns: string[]): void {
try {
const deleted = delSync(patterns, {
force: false,
// Change context to build directory
cwd: this.outputPath,
dryRun: false,
dot: true,
ignore: this.protectWebpackAssets ? this.currentAssets : [],
});
/**
* Log if verbose is enabled
*/
if (this.verbose) {
deleted.forEach((file) => {
const filename = path.relative(process.cwd(), file);
/**
* Use console.warn over .log
* https://github.com/webpack/webpack/issues/1904
* https://github.com/johnagan/clean-webpack-plugin/issues/11
*/
console.warn(`clean-webpack-plugin: removed ${filename}`);
});
}
} catch (err) {
const needsForce = (err as Error).message.includes(
'Cannot delete files/folders outside the current working directory.',
);
if (needsForce) {
const message =
'clean-webpack-plugin: Cannot delete files/folders outside the current working directory. Can be overridden with the "dangerouslyAllowCleanPatternsOutsideProject" option.';
throw new Error(message);
}
throw err;
}
}
}

View file

@ -1,7 +1,6 @@
# Project Words - DO NOT TOUCH - This is updated through CI
abernathyca
Adriaan
Agan
alexbdebrie
Alexey
algoliasearch

View file

@ -7339,7 +7339,7 @@ define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0, de
has-property-descriptors "^1.0.0"
object-keys "^1.1.1"
del@^6.0.0, del@^6.1.1:
del@^6.0.0:
version "6.1.1"
resolved "https://registry.yarnpkg.com/del/-/del-6.1.1.tgz#3b70314f1ec0aa325c6b14eb36b95786671edb7a"
integrity sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==