ProgressPlugin.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const createDefaultHandler = profile => {
  7. let lineCaretPosition = 0;
  8. let lastState;
  9. let lastStateTime;
  10. const defaultHandler = (percentage, msg, ...args) => {
  11. let state = msg;
  12. const details = args;
  13. if (percentage < 1) {
  14. percentage = Math.floor(percentage * 100);
  15. msg = `${percentage}% ${msg}`;
  16. if (percentage < 100) {
  17. msg = ` ${msg}`;
  18. }
  19. if (percentage < 10) {
  20. msg = ` ${msg}`;
  21. }
  22. for (let detail of details) {
  23. if (!detail) continue;
  24. if (detail.length > 40) {
  25. detail = `…${detail.substr(detail.length - 39)}`;
  26. }
  27. msg += ` ${detail}`;
  28. }
  29. }
  30. if (profile) {
  31. state = state.replace(/^\d+\/\d+\s+/, "");
  32. if (percentage === 0) {
  33. lastState = null;
  34. lastStateTime = Date.now();
  35. } else if (state !== lastState || percentage === 1) {
  36. const now = Date.now();
  37. if (lastState) {
  38. const stateMsg = `${now - lastStateTime}ms ${lastState}`;
  39. goToLineStart(stateMsg);
  40. process.stderr.write(stateMsg + "\n");
  41. lineCaretPosition = 0;
  42. }
  43. lastState = state;
  44. lastStateTime = now;
  45. }
  46. }
  47. goToLineStart(msg);
  48. process.stderr.write(msg);
  49. };
  50. const goToLineStart = nextMessage => {
  51. let str = "";
  52. for (; lineCaretPosition > nextMessage.length; lineCaretPosition--) {
  53. str += "\b \b";
  54. }
  55. for (var i = 0; i < lineCaretPosition; i++) {
  56. str += "\b";
  57. }
  58. lineCaretPosition = nextMessage.length;
  59. if (str) process.stderr.write(str);
  60. };
  61. return defaultHandler;
  62. };
  63. class ProgressPlugin {
  64. constructor(options) {
  65. if (typeof options === "function") {
  66. options = {
  67. handler: options
  68. };
  69. }
  70. options = options || {};
  71. this.profile = options.profile;
  72. this.handler = options.handler;
  73. }
  74. apply(compiler) {
  75. const handler = this.handler || createDefaultHandler(this.profile);
  76. if (compiler.compilers) {
  77. const states = new Array(compiler.compilers.length);
  78. compiler.compilers.forEach((compiler, idx) => {
  79. new ProgressPlugin((p, msg, ...args) => {
  80. states[idx] = [p, msg, ...args];
  81. handler(
  82. states
  83. .map(state => (state && state[0]) || 0)
  84. .reduce((a, b) => a + b) / states.length,
  85. `[${idx}] ${msg}`,
  86. ...args
  87. );
  88. }).apply(compiler);
  89. });
  90. } else {
  91. let lastModulesCount = 0;
  92. let moduleCount = 500;
  93. let doneModules = 0;
  94. const activeModules = [];
  95. const update = module => {
  96. handler(
  97. 0.1 + (doneModules / Math.max(lastModulesCount, moduleCount)) * 0.6,
  98. "building modules",
  99. `${doneModules}/${moduleCount} modules`,
  100. `${activeModules.length} active`,
  101. activeModules[activeModules.length - 1]
  102. );
  103. };
  104. const moduleDone = module => {
  105. doneModules++;
  106. const ident = module.identifier();
  107. if (ident) {
  108. const idx = activeModules.indexOf(ident);
  109. if (idx >= 0) activeModules.splice(idx, 1);
  110. }
  111. update();
  112. };
  113. compiler.hooks.compilation.tap("ProgressPlugin", compilation => {
  114. if (compilation.compiler.isChild()) return;
  115. lastModulesCount = moduleCount;
  116. moduleCount = 0;
  117. doneModules = 0;
  118. handler(0, "compiling");
  119. compilation.hooks.buildModule.tap("ProgressPlugin", module => {
  120. moduleCount++;
  121. const ident = module.identifier();
  122. if (ident) {
  123. activeModules.push(ident);
  124. }
  125. update();
  126. });
  127. compilation.hooks.failedModule.tap("ProgressPlugin", moduleDone);
  128. compilation.hooks.succeedModule.tap("ProgressPlugin", moduleDone);
  129. const hooks = {
  130. finishModules: "finish module graph",
  131. seal: "sealing",
  132. optimizeDependenciesBasic: "basic dependencies optimization",
  133. optimizeDependencies: "dependencies optimization",
  134. optimizeDependenciesAdvanced: "advanced dependencies optimization",
  135. afterOptimizeDependencies: "after dependencies optimization",
  136. optimize: "optimizing",
  137. optimizeModulesBasic: "basic module optimization",
  138. optimizeModules: "module optimization",
  139. optimizeModulesAdvanced: "advanced module optimization",
  140. afterOptimizeModules: "after module optimization",
  141. optimizeChunksBasic: "basic chunk optimization",
  142. optimizeChunks: "chunk optimization",
  143. optimizeChunksAdvanced: "advanced chunk optimization",
  144. afterOptimizeChunks: "after chunk optimization",
  145. optimizeTree: "module and chunk tree optimization",
  146. afterOptimizeTree: "after module and chunk tree optimization",
  147. optimizeChunkModulesBasic: "basic chunk modules optimization",
  148. optimizeChunkModules: "chunk modules optimization",
  149. optimizeChunkModulesAdvanced: "advanced chunk modules optimization",
  150. afterOptimizeChunkModules: "after chunk modules optimization",
  151. reviveModules: "module reviving",
  152. optimizeModuleOrder: "module order optimization",
  153. advancedOptimizeModuleOrder: "advanced module order optimization",
  154. beforeModuleIds: "before module ids",
  155. moduleIds: "module ids",
  156. optimizeModuleIds: "module id optimization",
  157. afterOptimizeModuleIds: "module id optimization",
  158. reviveChunks: "chunk reviving",
  159. optimizeChunkOrder: "chunk order optimization",
  160. beforeChunkIds: "before chunk ids",
  161. optimizeChunkIds: "chunk id optimization",
  162. afterOptimizeChunkIds: "after chunk id optimization",
  163. recordModules: "record modules",
  164. recordChunks: "record chunks",
  165. beforeHash: "hashing",
  166. afterHash: "after hashing",
  167. recordHash: "record hash",
  168. beforeModuleAssets: "module assets processing",
  169. beforeChunkAssets: "chunk assets processing",
  170. additionalChunkAssets: "additional chunk assets processing",
  171. record: "recording",
  172. additionalAssets: "additional asset processing",
  173. optimizeChunkAssets: "chunk asset optimization",
  174. afterOptimizeChunkAssets: "after chunk asset optimization",
  175. optimizeAssets: "asset optimization",
  176. afterOptimizeAssets: "after asset optimization",
  177. afterSeal: "after seal"
  178. };
  179. const numberOfHooks = Object.keys(hooks).length;
  180. Object.keys(hooks).forEach((name, idx) => {
  181. const title = hooks[name];
  182. const percentage = (idx / numberOfHooks) * 0.25 + 0.7;
  183. compilation.hooks[name].intercept({
  184. name: "ProgressPlugin",
  185. context: true,
  186. call: () => {
  187. handler(percentage, title);
  188. },
  189. tap: (context, tap) => {
  190. if (context) {
  191. // p is percentage from 0 to 1
  192. // args is any number of messages in a hierarchical matter
  193. context.reportProgress = (p, ...args) => {
  194. handler(percentage, title, tap.name, ...args);
  195. };
  196. }
  197. handler(percentage, title, tap.name);
  198. }
  199. });
  200. });
  201. });
  202. compiler.hooks.emit.intercept({
  203. name: "ProgressPlugin",
  204. context: true,
  205. call: () => {
  206. handler(0.95, "emitting");
  207. },
  208. tap: (context, tap) => {
  209. if (context) {
  210. context.reportProgress = (p, ...args) => {
  211. handler(0.95, "emitting", tap.name, ...args);
  212. };
  213. }
  214. handler(0.95, "emitting", tap.name);
  215. }
  216. });
  217. compiler.hooks.afterEmit.intercept({
  218. name: "ProgressPlugin",
  219. context: true,
  220. call: () => {
  221. handler(0.98, "after emitting");
  222. },
  223. tap: (context, tap) => {
  224. if (context) {
  225. context.reportProgress = (p, ...args) => {
  226. handler(0.98, "after emitting", tap.name, ...args);
  227. };
  228. }
  229. handler(0.98, "after emitting", tap.name);
  230. }
  231. });
  232. compiler.hooks.done.tap("ProgressPlugin", () => {
  233. handler(1, "");
  234. });
  235. }
  236. }
  237. }
  238. module.exports = ProgressPlugin;