clean-webpack-plugin.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.CleanWebpackPlugin = void 0;
  6. var _path = _interopRequireDefault(require("path"));
  7. var _del = require("del");
  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. */
  63. const hooks = compiler.hooks;
  64. if (this.cleanOnceBeforeBuildPatterns.length !== 0) {
  65. if (hooks) {
  66. hooks.emit.tap('clean-webpack-plugin', compilation => {
  67. this.handleInitial(compilation);
  68. });
  69. } else {
  70. compiler.plugin('emit', (compilation, callback) => {
  71. try {
  72. this.handleInitial(compilation);
  73. callback();
  74. } catch (error) {
  75. callback(error);
  76. }
  77. });
  78. }
  79. }
  80. if (hooks) {
  81. hooks.done.tap('clean-webpack-plugin', stats => {
  82. this.handleDone(stats);
  83. });
  84. } else {
  85. compiler.plugin('done', stats => {
  86. this.handleDone(stats);
  87. });
  88. }
  89. }
  90. /**
  91. * Initially remove files from output directory prior to build.
  92. *
  93. * Only happens once.
  94. *
  95. * Warning: It is recommended to initially clean your build directory outside of webpack to minimize unexpected behavior.
  96. */
  97. handleInitial(compilation) {
  98. if (this.initialClean) {
  99. return;
  100. }
  101. /**
  102. * Do not remove files if there are compilation errors
  103. *
  104. * Handle logging inside this.handleDone
  105. */
  106. const stats = compilation.getStats();
  107. if (stats.hasErrors()) {
  108. return;
  109. }
  110. this.initialClean = true;
  111. this.removeFiles(this.cleanOnceBeforeBuildPatterns);
  112. }
  113. handleDone(stats) {
  114. /**
  115. * Do nothing if there is a webpack error
  116. */
  117. if (stats.hasErrors()) {
  118. if (this.verbose) {
  119. // eslint-disable-next-line no-console
  120. console.warn('clean-webpack-plugin: pausing due to webpack errors');
  121. }
  122. return;
  123. }
  124. /**
  125. * Fetch Webpack's output asset files
  126. */
  127. const assets = stats.toJson().assets || [];
  128. const assetList = assets.map(asset => {
  129. return asset.name;
  130. });
  131. /**
  132. * Get all files that were in the previous build but not the current
  133. *
  134. * (relies on del's cwd: outputPath option)
  135. */
  136. const staleFiles = this.currentAssets.filter(previousAsset => {
  137. const assetCurrent = assetList.includes(previousAsset) === false;
  138. return assetCurrent;
  139. });
  140. /**
  141. * Save assets for next compilation
  142. */
  143. this.currentAssets = assetList.sort();
  144. const removePatterns = [];
  145. /**
  146. * Remove unused webpack assets
  147. */
  148. if (this.cleanStaleWebpackAssets === true && staleFiles.length !== 0) {
  149. removePatterns.push(...staleFiles);
  150. }
  151. /**
  152. * Remove cleanAfterEveryBuildPatterns
  153. */
  154. if (this.cleanAfterEveryBuildPatterns.length !== 0) {
  155. removePatterns.push(...this.cleanAfterEveryBuildPatterns);
  156. }
  157. if (removePatterns.length !== 0) {
  158. this.removeFiles(removePatterns);
  159. }
  160. }
  161. removeFiles(patterns) {
  162. try {
  163. const deleted = (0, _del.sync)(patterns, {
  164. force: this.dangerouslyAllowCleanPatternsOutsideProject,
  165. // Change context to build directory
  166. cwd: this.outputPath,
  167. dryRun: this.dry,
  168. dot: true,
  169. ignore: this.protectWebpackAssets ? this.currentAssets : []
  170. });
  171. /**
  172. * Log if verbose is enabled
  173. */
  174. if (this.verbose) {
  175. deleted.forEach(file => {
  176. const filename = _path.default.relative(process.cwd(), file);
  177. const message = this.dry ? 'dry' : 'removed';
  178. /**
  179. * Use console.warn over .log
  180. * https://github.com/webpack/webpack/issues/1904
  181. * https://github.com/johnagan/clean-webpack-plugin/issues/11
  182. */
  183. // eslint-disable-next-line no-console
  184. console.warn(`clean-webpack-plugin: ${message} ${filename}`);
  185. });
  186. }
  187. } catch (error) {
  188. const needsForce = /Cannot delete files\/folders outside the current working directory\./.test(error.message);
  189. if (needsForce) {
  190. const message = 'clean-webpack-plugin: Cannot delete files/folders outside the current working directory. Can be overridden with the `dangerouslyAllowCleanPatternsOutsideProject` option.';
  191. throw new Error(message);
  192. }
  193. throw error;
  194. }
  195. }
  196. }
  197. exports.CleanWebpackPlugin = CleanWebpackPlugin;
  198. //# sourceMappingURL=clean-webpack-plugin.js.map