| | |
| | | const asyncLib = require("neo-async"); |
| | | const { SyncBailHook } = require("tapable"); |
| | | const Compilation = require("./Compilation"); |
| | | const createSchemaValidation = require("./util/create-schema-validation"); |
| | | const { join } = require("./util/fs"); |
| | | const processAsyncTree = require("./util/processAsyncTree"); |
| | | |
| | |
| | | /** @typedef {Map<string, number>} Assets */ |
| | | |
| | | /** |
| | | * Defines the clean plugin compilation hooks type used by this module. |
| | | * @typedef {object} CleanPluginCompilationHooks |
| | | * @property {SyncBailHook<[string], boolean | void>} keep when returning true the file/directory will be kept during cleaning, returning false will clean it and ignore the following plugins and config |
| | | */ |
| | | |
| | | /** |
| | | * Defines the keep fn callback. |
| | | * @callback KeepFn |
| | | * @param {string} path path |
| | | * @returns {boolean | undefined} true, if the path should be kept |
| | | */ |
| | | |
| | | const validate = createSchemaValidation( |
| | | undefined, |
| | | () => { |
| | | const { definitions } = require("../schemas/WebpackOptions.json"); |
| | | |
| | | return { |
| | | definitions, |
| | | oneOf: [{ $ref: "#/definitions/CleanOptions" }] |
| | | }; |
| | | }, |
| | | { |
| | | name: "Clean Plugin", |
| | | baseDataPath: "options" |
| | | } |
| | | ); |
| | | const _10sec = 10 * 1000; |
| | | |
| | | /** |
| | |
| | | } |
| | | }; |
| | | |
| | | /** @typedef {Map<string, number>} CurrentAssets */ |
| | | |
| | | /** |
| | | * @param {Map<string, number>} assets current assets |
| | | * Returns set of directory paths. |
| | | * @param {CurrentAssets} assets current assets |
| | | * @returns {Set<string>} Set of directory paths |
| | | */ |
| | | function getDirectories(assets) { |
| | | /** @type {Set<string>} */ |
| | | const directories = new Set(); |
| | | /** |
| | | * Adds the provided filename to this object. |
| | | * @param {string} filename asset filename |
| | | */ |
| | | const addDirectory = (filename) => { |
| | |
| | | /** @typedef {Set<string>} Diff */ |
| | | |
| | | /** |
| | | * Returns diff to fs. |
| | | * @param {OutputFileSystem} fs filesystem |
| | | * @param {string} outputPath output path |
| | | * @param {Map<string, number>} currentAssets filename of the current assets (must not start with .. or ., must only use / as path separator) |
| | | * @param {CurrentAssets} currentAssets filename of the current assets (must not start with .. or ., must only use / as path separator) |
| | | * @param {(err?: Error | null, set?: Diff) => void} callback returns the filenames of the assets that shouldn't be there |
| | | * @returns {void} |
| | | */ |
| | | const getDiffToFs = (fs, outputPath, currentAssets, callback) => { |
| | | const directories = getDirectories(currentAssets); |
| | | /** @type {Diff} */ |
| | | const diff = new Set(); |
| | | asyncLib.forEachLimit( |
| | | directories, |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Gets diff to old assets. |
| | | * @param {Assets} currentAssets assets list |
| | | * @param {Assets} oldAssets old assets list |
| | | * @returns {Diff} diff |
| | | */ |
| | | const getDiffToOldAssets = (currentAssets, oldAssets) => { |
| | | /** @type {Diff} */ |
| | | const diff = new Set(); |
| | | const now = Date.now(); |
| | | for (const [asset, ts] of oldAssets) { |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Processes the provided f. |
| | | * @param {OutputFileSystem} fs filesystem |
| | | * @param {string} filename path to file |
| | | * @param {StatsCallback} callback callback for provided filename |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Processes the provided f. |
| | | * @param {OutputFileSystem} fs filesystem |
| | | * @param {string} outputPath output path |
| | | * @param {boolean} dry only log instead of fs modification |
| | |
| | | */ |
| | | const applyDiff = (fs, outputPath, dry, logger, diff, isKept, callback) => { |
| | | /** |
| | | * Processes the provided msg. |
| | | * @param {string} msg message |
| | | */ |
| | | const log = (msg) => { |
| | |
| | | ({ type, filename, parent }, push, callback) => { |
| | | const path = join(fs, outputPath, filename); |
| | | /** |
| | | * Describes how this handle error operation behaves. |
| | | * @param {Error & { code?: string }} err error |
| | | * @returns {void} |
| | | */ |
| | |
| | | |
| | | class CleanPlugin { |
| | | /** |
| | | * Returns the attached hooks. |
| | | * @param {Compilation} compilation the compilation |
| | | * @returns {CleanPluginCompilationHooks} the attached hooks |
| | | */ |
| | |
| | | |
| | | /** @param {CleanOptions} options options */ |
| | | constructor(options = {}) { |
| | | validate(options); |
| | | this.options = { dry: false, ...options }; |
| | | /** @type {CleanOptions} */ |
| | | this.options = options; |
| | | } |
| | | |
| | | /** |
| | | * Apply the plugin |
| | | * Applies the plugin by registering its hooks on the compiler. |
| | | * @param {Compiler} compiler the compiler instance |
| | | * @returns {void} |
| | | */ |
| | | apply(compiler) { |
| | | const { dry, keep } = this.options; |
| | | compiler.hooks.validate.tap(PLUGIN_NAME, () => { |
| | | compiler.validate( |
| | | () => { |
| | | const { definitions } = require("../schemas/WebpackOptions.json"); |
| | | |
| | | return { |
| | | definitions, |
| | | oneOf: [{ $ref: "#/definitions/CleanOptions" }] |
| | | }; |
| | | }, |
| | | this.options, |
| | | { |
| | | name: "Clean Plugin", |
| | | baseDataPath: "options" |
| | | } |
| | | ); |
| | | }); |
| | | |
| | | const { keep } = this.options; |
| | | |
| | | /** @type {boolean} */ |
| | | const dry = this.options.dry || false; |
| | | /** @type {KeepFn} */ |
| | | const keepFn = |
| | | typeof keep === "function" |
| | |
| | | // We assume that no external modification happens while the compiler is active |
| | | // So we can store the old assets and only diff to them to avoid fs access on |
| | | // incremental builds |
| | | /** @type {undefined|Assets} */ |
| | | /** @type {undefined | Assets} */ |
| | | let oldAssets; |
| | | |
| | | compiler.hooks.emit.tapAsync( |
| | |
| | | const currentAssets = new Map(); |
| | | const now = Date.now(); |
| | | for (const asset of Object.keys(compilation.assets)) { |
| | | if (/^[A-Za-z]:\\|^\/|^\\\\/.test(asset)) continue; |
| | | if (/^[a-z]:\\|^\/|^\\\\/i.test(asset)) continue; |
| | | /** @type {string} */ |
| | | let normalizedAsset; |
| | | let newNormalizedAsset = asset.replace(/\\/g, "/"); |
| | | do { |
| | |
| | | const outputPath = compilation.getPath(compiler.outputPath, {}); |
| | | |
| | | /** |
| | | * Checks whether this clean plugin is kept. |
| | | * @param {string} path path |
| | | * @returns {boolean | undefined} true, if needs to be kept |
| | | */ |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Processes the provided err. |
| | | * @param {(Error | null)=} err err |
| | | * @param {Diff=} diff diff |
| | | */ |