webpack plugin源码解析(一) clean-webpack-plugin

文章目录

  • 作用
  • 实现

作用

  • 可以配置构建前后删除某个文件夹内容,默认为 webpack.options.output.path
new CleanWebpackPlugin({
	dry: true,  // true 不真实删除,只输出删除的文件信息,默认为false
	cleanOnceBeforeBuildPatterns: [ // 构建前删除,glob 匹配规则
        '**/*',  // 默认值
        '!static-files*',
        '!directoryToExclude/**',
    ],
    cleanOnceBeforeBuildPatterns: [] // 构建后删除
	// ...
})

实现

constructor

  • 配置项格式化
class CleanWebpackPlugin {
  constructor(options = {}) {
    if (isPlainObject(options) === false) {
      throw new Error(`clean-webpack-plugin only accepts an options object. See:
            https://github.com/johnagan/clean-webpack-plugin#options-and-defaults-optional`);
    } // @ts-ignore


    if (options.allowExternal) {
      throw new Error('clean-webpack-plugin: `allowExternal` option no longer supported. Use `dangerouslyAllowCleanPatternsOutsideProject`');
    }

    if (options.dangerouslyAllowCleanPatternsOutsideProject === true && options.dry !== true && options.dry !== false) {
      // eslint-disable-next-line no-console
      console.warn('clean-webpack-plugin: dangerouslyAllowCleanPatternsOutsideProject requires dry: false to be explicitly set. Enabling dry mode');
    }

    this.dangerouslyAllowCleanPatternsOutsideProject = options.dangerouslyAllowCleanPatternsOutsideProject === true || false;
    this.dry = options.dry === true || options.dry === false ? options.dry : this.dangerouslyAllowCleanPatternsOutsideProject === true || false;
    this.verbose = this.dry === true || 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.cleanAfterEveryBuildPatterns = Array.isArray(options.cleanAfterEveryBuildPatterns) ? options.cleanAfterEveryBuildPatterns : [];
    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

  • emit hook:资源构建前删除
  • done hook:资源构建后删除
apply(compiler) {
    if (!compiler.options.output || !compiler.options.output.path) {
      // eslint-disable-next-line no-console
      console.warn('clean-webpack-plugin: options.output.path not defined. Plugin disabled...');
      return;
    }

    this.outputPath = compiler.options.output.path; // "/xxx/Desktop/webpack/wb/dist"
    /**
     * webpack 4+ comes with a new plugin system.
     *
     * Check for hooks in-order to support old plugin system
     * webpack 5+ removed the old system, the check now breaks
     */

    const hooks = compiler.hooks;

    if (this.cleanOnceBeforeBuildPatterns.length !== 0) {
      hooks.emit.tap('clean-webpack-plugin', compilation => {
        this.handleInitial(compilation);
      });
    }

    hooks.done.tap('clean-webpack-plugin', stats => {
      this.handleDone(stats);
    });
  }

handleInitial

  • 资源输出前守卫,如果编译过程报错,停止删除
handleInitial(compilation) {
   if (this.initialClean) {
     return;
   }
   /**
    * Do not remove files if there are compilation errors
    *
    * Handle logging inside this.handleDone
    */


   const stats = compilation.getStats();

   if (stats.hasErrors()) {  // 如果编译过程报错,停止删除
     return;
   }

   this.initialClean = true;
   this.removeFiles(this.cleanOnceBeforeBuildPatterns);
 }

handleDone

  • 资源输出后守卫,保存上一次编译资源文件列表,根据开发者传入配置进行清空
handleDone(stats) {
  /**
 * Do nothing if there is a webpack error
   */
  if (stats.hasErrors()) {
    if (this.verbose) {
      // eslint-disable-next-line no-console
      console.warn('clean-webpack-plugin: pausing due to webpack errors');
    }

    return;
  }
  /**
 * Fetch Webpack's output asset files
   */


  const assetList = Object.keys(stats.compilation.assets); // 生成的资源文件名
  // ["main.css","main.js", ...]
  
  /**
 * 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 => {
    const assetCurrent = assetList.includes(previousAsset) === false;
    return assetCurrent;
  });
  /**
 * Save assets for next compilation
   */

  this.currentAssets = assetList.sort();
  const removePatterns = [];
  /**
 * Remove unused webpack assets
   */

  if (this.cleanStaleWebpackAssets === true && staleFiles.length !== 0) {
    removePatterns.push(...staleFiles);
  }
  /**
 * Remove cleanAfterEveryBuildPatterns
   */


  if (this.cleanAfterEveryBuildPatterns.length !== 0) {
    removePatterns.push(...this.cleanAfterEveryBuildPatterns);
  }

  if (removePatterns.length !== 0) {
    this.removeFiles(removePatterns);
  }
}

removeFiles

  • 通过 del 库进行删除,删除根据 glob 格式匹配
removeFiles(patterns) {
  try {
    const deleted = (0, _del.sync)(patterns, {
      force: this.dangerouslyAllowCleanPatternsOutsideProject,
      // Change context to build directory
      cwd: this.outputPath,
      dryRun: this.dry,
      dot: true,
      ignore: this.protectWebpackAssets ? this.currentAssets : []
    });
    /**
     * Log if verbose is enabled
     */

    if (this.verbose) { // 如果配置了true,会提示删除了的文件
      deleted.forEach(file => {
        const filename = _path.default.relative(process.cwd(), file);

        const message = this.dry ? 'dry' : 'removed';
        /**
         * Use console.warn over .log
         * https://github.com/webpack/webpack/issues/1904
         * https://github.com/johnagan/clean-webpack-plugin/issues/11
         */
        // eslint-disable-next-line no-console

        console.warn(`clean-webpack-plugin: ${message} ${filename}`);
      });
    }
  } catch (error) {
    const needsForce = /Cannot delete files\/folders outside the current working directory\./.test(error.message);

    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);
    }
    /* istanbul ignore next */


    throw error;
  }
}

你可能感兴趣的:(javascript,前端,webpack)