clean-webpack-plugin.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.CleanWebpackPlugin = void 0;
  6. var _del = require("del");
  7. var _path = _interopRequireDefault(require("path"));
  8. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  9. // Copied from https://github.com/sindresorhus/is-plain-obj/blob/97480673cf12145b32ec2ee924980d66572e8a86/index.js
  10. function isPlainObject(value) {
  11. if (Object.prototype.toString.call(value) !== '[object Object]') {
  12. return false;
  13. }
  14. const prototype = Object.getPrototypeOf(value);
  15. return prototype === null || prototype === Object.getPrototypeOf({});
  16. }
  17. class CleanWebpackPlugin {
  18. constructor(options = {}) {
  19. if (isPlainObject(options) === false) {
  20. throw new Error(`clean-webpack-plugin only accepts an options object. See:
  21. https://github.com/johnagan/clean-webpack-plugin#options-and-defaults-optional`);
  22. } // @ts-ignore
  23. if (options.allowExternal) {
  24. throw new Error('clean-webpack-plugin: `allowExternal` option no longer supported. Use `dangerouslyAllowCleanPatternsOutsideProject`');
  25. }
  26. if (options.dangerouslyAllowCleanPatternsOutsideProject === true && options.dry !== true && options.dry !== false) {
  27. // eslint-disable-next-line no-console
  28. console.warn('clean-webpack-plugin: dangerouslyAllowCleanPatternsOutsideProject requires dry: false to be explicitly set. Enabling dry mode');
  29. }
  30. this.dangerouslyAllowCleanPatternsOutsideProject = options.dangerouslyAllowCleanPatternsOutsideProject === true || false;
  31. this.dry = options.dry === true || options.dry === false ? options.dry : this.dangerouslyAllowCleanPatternsOutsideProject === true || false;
  32. this.verbose = this.dry === true || options.verbose === true || false;
  33. this.cleanStaleWebpackAssets = options.cleanStaleWebpackAssets === true || options.cleanStaleWebpackAssets === false ? options.cleanStaleWebpackAssets : true;
  34. this.protectWebpackAssets = options.protectWebpackAssets === true || options.protectWebpackAssets === false ? options.protectWebpackAssets : true;
  35. this.cleanAfterEveryBuildPatterns = Array.isArray(options.cleanAfterEveryBuildPatterns) ? options.cleanAfterEveryBuildPatterns : [];
  36. this.cleanOnceBeforeBuildPatterns = Array.isArray(options.cleanOnceBeforeBuildPatterns) ? options.cleanOnceBeforeBuildPatterns : ['**/*'];
  37. /**
  38. * Store webpack build assets
  39. */
  40. this.currentAssets = [];
  41. /**
  42. * Only used with cleanOnceBeforeBuildPatterns
  43. */
  44. this.initialClean = false;
  45. this.outputPath = '';
  46. this.apply = this.apply.bind(this);
  47. this.handleInitial = this.handleInitial.bind(this);
  48. this.handleDone = this.handleDone.bind(this);
  49. this.removeFiles = this.removeFiles.bind(this);
  50. }
  51. apply(compiler) {
  52. if (!compiler.options.output || !compiler.options.output.path) {
  53. // eslint-disable-next-line no-console
  54. console.warn('clean-webpack-plugin: options.output.path not defined. Plugin disabled...');
  55. return;
  56. }
  57. this.outputPath = compiler.options.output.path;
  58. /**
  59. * webpack 4+ comes with a new plugin system.
  60. *
  61. * Check for hooks in-order to support old plugin system
  62. * webpack 5+ removed the old system, the check now breaks
  63. */
  64. const hooks = compiler.hooks;
  65. if (this.cleanOnceBeforeBuildPatterns.length !== 0) {
  66. hooks.emit.tap('clean-webpack-plugin', compilation => {
  67. this.handleInitial(compilation);
  68. });
  69. }
  70. hooks.done.tap('clean-webpack-plugin', stats => {
  71. this.handleDone(stats);
  72. });
  73. }
  74. /**
  75. * Initially remove files from output directory prior to build.
  76. *
  77. * Only happens once.
  78. *
  79. * Warning: It is recommended to initially clean your build directory outside of webpack to minimize unexpected behavior.
  80. */
  81. handleInitial(compilation) {
  82. if (this.initialClean) {
  83. return;
  84. }
  85. /**
  86. * Do not remove files if there are compilation errors
  87. *
  88. * Handle logging inside this.handleDone
  89. */
  90. const stats = compilation.getStats();
  91. if (stats.hasErrors()) {
  92. return;
  93. }
  94. this.initialClean = true;
  95. this.removeFiles(this.cleanOnceBeforeBuildPatterns);
  96. }
  97. handleDone(stats) {
  98. /**
  99. * Do nothing if there is a webpack error
  100. */
  101. if (stats.hasErrors()) {
  102. if (this.verbose) {
  103. // eslint-disable-next-line no-console
  104. console.warn('clean-webpack-plugin: pausing due to webpack errors');
  105. }
  106. return;
  107. }
  108. /**
  109. * Fetch Webpack's output asset files
  110. */
  111. const assetList = Object.keys(stats.compilation.assets);
  112. /**
  113. * Get all files that were in the previous build but not the current
  114. *
  115. * (relies on del's cwd: outputPath option)
  116. */
  117. const staleFiles = this.currentAssets.filter(previousAsset => {
  118. const assetCurrent = assetList.includes(previousAsset) === false;
  119. return assetCurrent;
  120. });
  121. /**
  122. * Save assets for next compilation
  123. */
  124. this.currentAssets = assetList.sort();
  125. const removePatterns = [];
  126. /**
  127. * Remove unused webpack assets
  128. */
  129. if (this.cleanStaleWebpackAssets === true && staleFiles.length !== 0) {
  130. removePatterns.push(...staleFiles);
  131. }
  132. /**
  133. * Remove cleanAfterEveryBuildPatterns
  134. */
  135. if (this.cleanAfterEveryBuildPatterns.length !== 0) {
  136. removePatterns.push(...this.cleanAfterEveryBuildPatterns);
  137. }
  138. if (removePatterns.length !== 0) {
  139. this.removeFiles(removePatterns);
  140. }
  141. }
  142. removeFiles(patterns) {
  143. try {
  144. const deleted = (0, _del.sync)(patterns, {
  145. force: this.dangerouslyAllowCleanPatternsOutsideProject,
  146. // Change context to build directory
  147. cwd: this.outputPath,
  148. dryRun: this.dry,
  149. dot: true,
  150. ignore: this.protectWebpackAssets ? this.currentAssets : []
  151. });
  152. /**
  153. * Log if verbose is enabled
  154. */
  155. if (this.verbose) {
  156. deleted.forEach(file => {
  157. const filename = _path.default.relative(process.cwd(), file);
  158. const message = this.dry ? 'dry' : 'removed';
  159. /**
  160. * Use console.warn over .log
  161. * https://github.com/webpack/webpack/issues/1904
  162. * https://github.com/johnagan/clean-webpack-plugin/issues/11
  163. */
  164. // eslint-disable-next-line no-console
  165. console.warn(`clean-webpack-plugin: ${message} ${filename}`);
  166. });
  167. }
  168. } catch (error) {
  169. const needsForce = /Cannot delete files\/folders outside the current working directory\./.test(error.message);
  170. if (needsForce) {
  171. const message = 'clean-webpack-plugin: Cannot delete files/folders outside the current working directory. Can be overridden with the `dangerouslyAllowCleanPatternsOutsideProject` option.';
  172. throw new Error(message);
  173. }
  174. /* istanbul ignore next */
  175. throw error;
  176. }
  177. }
  178. }
  179. exports.CleanWebpackPlugin = CleanWebpackPlugin;
  180. //# sourceMappingURL=clean-webpack-plugin.js.map