From dae29a27825f75aaa2d08843360da0684f8282ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Lorber?= Date: Thu, 27 Mar 2025 18:44:48 +0100 Subject: [PATCH] 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> --- packages/docusaurus/package.json | 1 - .../src/commands/build/buildLocale.ts | 6 + packages/docusaurus/src/commands/clear.ts | 3 +- .../src/commands/utils/clearPath.ts | 22 ++ packages/docusaurus/src/webpack/client.ts | 3 - .../src/webpack/plugins/CleanWebpackPlugin.ts | 263 ------------------ project-words.txt | 1 - yarn.lock | 2 +- 8 files changed, 31 insertions(+), 270 deletions(-) create mode 100644 packages/docusaurus/src/commands/utils/clearPath.ts delete mode 100644 packages/docusaurus/src/webpack/plugins/CleanWebpackPlugin.ts diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json index 93b05d06de..50e8b84477 100644 --- a/packages/docusaurus/package.json +++ b/packages/docusaurus/package.json @@ -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", diff --git a/packages/docusaurus/src/commands/build/buildLocale.ts b/packages/docusaurus/src/commands/build/buildLocale.ts index 46e44a5215..84f65a63d3 100644 --- a/packages/docusaurus/src/commands/build/buildLocale.ts +++ b/packages/docusaurus/src/commands/build/buildLocale.ts @@ -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), ]), ); diff --git a/packages/docusaurus/src/commands/clear.ts b/packages/docusaurus/src/commands/clear.ts index 0be7f40700..5dc28aba2d 100644 --- a/packages/docusaurus/src/commands/clear.ts +++ b/packages/docusaurus/src/commands/clear.ts @@ -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, diff --git a/packages/docusaurus/src/commands/utils/clearPath.ts b/packages/docusaurus/src/commands/utils/clearPath.ts new file mode 100644 index 0000000000..b26d18b639 --- /dev/null +++ b/packages/docusaurus/src/commands/utils/clearPath.ts @@ -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 { + return PerfLogger.async( + `clearPath ${path.relative(process.cwd(), pathToClear)}`, + async () => { + await rm(pathToClear, {recursive: true, force: true}); + }, + ); +} diff --git a/packages/docusaurus/src/webpack/client.ts b/packages/docusaurus/src/webpack/client.ts index c5ec4dbdd9..56bbb669d4 100644 --- a/packages/docusaurus/src/webpack/client.ts +++ b/packages/docusaurus/src/webpack/client.ts @@ -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(), diff --git a/packages/docusaurus/src/webpack/plugins/CleanWebpackPlugin.ts b/packages/docusaurus/src/webpack/plugins/CleanWebpackPlugin.ts deleted file mode 100644 index a619b4b0e0..0000000000 --- a/packages/docusaurus/src/webpack/plugins/CleanWebpackPlugin.ts +++ /dev/null @@ -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; - } - } -} diff --git a/project-words.txt b/project-words.txt index a12674f547..73c418bb1f 100644 --- a/project-words.txt +++ b/project-words.txt @@ -1,7 +1,6 @@ # Project Words - DO NOT TOUCH - This is updated through CI abernathyca Adriaan -Agan alexbdebrie Alexey algoliasearch diff --git a/yarn.lock b/yarn.lock index cf02d68882..d393b5906c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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==