MultiStats.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const identifierUtils = require("./util/identifier");
  7. /** @typedef {import("../declarations/WebpackOptions").StatsOptions} StatsOptions */
  8. /** @typedef {import("./Compilation").CreateStatsOptionsContext} CreateStatsOptionsContext */
  9. /** @typedef {import("./Compilation").NormalizedStatsOptions} NormalizedStatsOptions */
  10. /** @typedef {import("./Stats")} Stats */
  11. /** @typedef {import("./stats/DefaultStatsFactoryPlugin").KnownStatsCompilation} KnownStatsCompilation */
  12. /** @typedef {import("./stats/DefaultStatsFactoryPlugin").StatsCompilation} StatsCompilation */
  13. /** @typedef {import("./stats/DefaultStatsFactoryPlugin").StatsError} StatsError */
  14. /**
  15. * @param {string} str string
  16. * @param {string} prefix pref
  17. * @returns {string} indent
  18. */
  19. const indent = (str, prefix) => {
  20. const rem = str.replace(/\n([^\n])/g, `\n${prefix}$1`);
  21. return prefix + rem;
  22. };
  23. /** @typedef {undefined | string | boolean | StatsOptions} ChildrenStatsOptions */
  24. /** @typedef {Omit<StatsOptions, "children"> & { children?: ChildrenStatsOptions | ChildrenStatsOptions[] }} MultiStatsOptions */
  25. /** @typedef {{ version: boolean, hash: boolean, errorsCount: boolean, warningsCount: boolean, errors: boolean, warnings: boolean, children: NormalizedStatsOptions[] }} ChildOptions */
  26. class MultiStats {
  27. /**
  28. * @param {Stats[]} stats the child stats
  29. */
  30. constructor(stats) {
  31. this.stats = stats;
  32. }
  33. get hash() {
  34. return this.stats.map((stat) => stat.hash).join("");
  35. }
  36. /**
  37. * @returns {boolean} true if a child compilation encountered an error
  38. */
  39. hasErrors() {
  40. return this.stats.some((stat) => stat.hasErrors());
  41. }
  42. /**
  43. * @returns {boolean} true if a child compilation had a warning
  44. */
  45. hasWarnings() {
  46. return this.stats.some((stat) => stat.hasWarnings());
  47. }
  48. /**
  49. * @param {undefined | string | boolean | MultiStatsOptions} options stats options
  50. * @param {CreateStatsOptionsContext} context context
  51. * @returns {ChildOptions} context context
  52. */
  53. _createChildOptions(options, context) {
  54. const getCreateStatsOptions = () => {
  55. if (!options) {
  56. options = {};
  57. }
  58. const { children: childrenOptions = undefined, ...baseOptions } =
  59. typeof options === "string"
  60. ? { preset: options }
  61. : /** @type {StatsOptions} */ (options);
  62. return { childrenOptions, baseOptions };
  63. };
  64. const children = this.stats.map((stat, idx) => {
  65. if (typeof options === "boolean") {
  66. return stat.compilation.createStatsOptions(options, context);
  67. }
  68. const { childrenOptions, baseOptions } = getCreateStatsOptions();
  69. const childOptions = Array.isArray(childrenOptions)
  70. ? childrenOptions[idx]
  71. : childrenOptions;
  72. if (typeof childOptions === "boolean") {
  73. return stat.compilation.createStatsOptions(childOptions, context);
  74. }
  75. return stat.compilation.createStatsOptions(
  76. {
  77. ...baseOptions,
  78. ...(typeof childOptions === "string"
  79. ? { preset: childOptions }
  80. : childOptions && typeof childOptions === "object"
  81. ? childOptions
  82. : undefined)
  83. },
  84. context
  85. );
  86. });
  87. return {
  88. version: children.every((o) => o.version),
  89. hash: children.every((o) => o.hash),
  90. errorsCount: children.every((o) => o.errorsCount),
  91. warningsCount: children.every((o) => o.warningsCount),
  92. errors: children.every((o) => o.errors),
  93. warnings: children.every((o) => o.warnings),
  94. children
  95. };
  96. }
  97. /**
  98. * @param {(string | boolean | MultiStatsOptions)=} options stats options
  99. * @returns {StatsCompilation} json output
  100. */
  101. toJson(options) {
  102. const childOptions = this._createChildOptions(options, {
  103. forToString: false
  104. });
  105. /** @type {KnownStatsCompilation} */
  106. const obj = {};
  107. obj.children = this.stats.map((stat, idx) => {
  108. const obj = stat.toJson(childOptions.children[idx]);
  109. const compilationName = stat.compilation.name;
  110. const name =
  111. compilationName &&
  112. identifierUtils.makePathsRelative(
  113. stat.compilation.compiler.context,
  114. compilationName,
  115. stat.compilation.compiler.root
  116. );
  117. obj.name = name;
  118. return obj;
  119. });
  120. if (childOptions.version) {
  121. obj.version = obj.children[0].version;
  122. }
  123. if (childOptions.hash) {
  124. obj.hash = obj.children.map((j) => j.hash).join("");
  125. }
  126. /**
  127. * @param {StatsCompilation} j stats error
  128. * @param {StatsError} obj Stats error
  129. * @returns {StatsError} result
  130. */
  131. const mapError = (j, obj) => ({
  132. ...obj,
  133. compilerPath: obj.compilerPath ? `${j.name}.${obj.compilerPath}` : j.name
  134. });
  135. if (childOptions.errors) {
  136. obj.errors = [];
  137. for (const j of obj.children) {
  138. const errors =
  139. /** @type {NonNullable<KnownStatsCompilation["errors"]>} */
  140. (j.errors);
  141. for (const i of errors) {
  142. obj.errors.push(mapError(j, i));
  143. }
  144. }
  145. }
  146. if (childOptions.warnings) {
  147. obj.warnings = [];
  148. for (const j of obj.children) {
  149. const warnings =
  150. /** @type {NonNullable<KnownStatsCompilation["warnings"]>} */
  151. (j.warnings);
  152. for (const i of warnings) {
  153. obj.warnings.push(mapError(j, i));
  154. }
  155. }
  156. }
  157. if (childOptions.errorsCount) {
  158. obj.errorsCount = 0;
  159. for (const j of obj.children) {
  160. obj.errorsCount += /** @type {number} */ (j.errorsCount);
  161. }
  162. }
  163. if (childOptions.warningsCount) {
  164. obj.warningsCount = 0;
  165. for (const j of obj.children) {
  166. obj.warningsCount += /** @type {number} */ (j.warningsCount);
  167. }
  168. }
  169. return obj;
  170. }
  171. /**
  172. * @param {(string | boolean | MultiStatsOptions)=} options stats options
  173. * @returns {string} string output
  174. */
  175. toString(options) {
  176. const childOptions = this._createChildOptions(options, {
  177. forToString: true
  178. });
  179. const results = this.stats.map((stat, idx) => {
  180. const str = stat.toString(childOptions.children[idx]);
  181. const compilationName = stat.compilation.name;
  182. const name =
  183. compilationName &&
  184. identifierUtils
  185. .makePathsRelative(
  186. stat.compilation.compiler.context,
  187. compilationName,
  188. stat.compilation.compiler.root
  189. )
  190. .replace(/\|/g, " ");
  191. if (!str) return str;
  192. return name ? `${name}:\n${indent(str, " ")}` : str;
  193. });
  194. return results.filter(Boolean).join("\n\n");
  195. }
  196. }
  197. module.exports = MultiStats;