MultiStats.js 5.7 KB

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