MultiCompiler.js 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const Tapable = require("tapable");
  7. const asyncLib = require("async");
  8. const MultiWatching = require("./MultiWatching");
  9. const MultiStats = require("./MultiStats");
  10. module.exports = class MultiCompiler extends Tapable {
  11. constructor(compilers) {
  12. super();
  13. if(!Array.isArray(compilers)) {
  14. compilers = Object.keys(compilers).map((name) => {
  15. compilers[name].name = name;
  16. return compilers[name];
  17. });
  18. }
  19. this.compilers = compilers;
  20. let doneCompilers = 0;
  21. let compilerStats = [];
  22. this.compilers.forEach((compiler, idx) => {
  23. let compilerDone = false;
  24. compiler.plugin("done", stats => {
  25. if(!compilerDone) {
  26. compilerDone = true;
  27. doneCompilers++;
  28. }
  29. compilerStats[idx] = stats;
  30. if(doneCompilers === this.compilers.length) {
  31. this.applyPlugins("done", new MultiStats(compilerStats));
  32. }
  33. });
  34. compiler.plugin("invalid", () => {
  35. if(compilerDone) {
  36. compilerDone = false;
  37. doneCompilers--;
  38. }
  39. this.applyPlugins("invalid");
  40. });
  41. }, this);
  42. }
  43. get outputPath() {
  44. let commonPath = this.compilers[0].outputPath;
  45. for(const compiler of this.compilers) {
  46. while(compiler.outputPath.indexOf(commonPath) !== 0 && /[/\\]/.test(commonPath)) {
  47. commonPath = commonPath.replace(/[/\\][^/\\]*$/, "");
  48. }
  49. }
  50. if(!commonPath && this.compilers[0].outputPath[0] === "/") return "/";
  51. return commonPath;
  52. }
  53. get inputFileSystem() {
  54. throw new Error("Cannot read inputFileSystem of a MultiCompiler");
  55. }
  56. get outputFileSystem() {
  57. throw new Error("Cannot read outputFileSystem of a MultiCompiler");
  58. }
  59. set inputFileSystem(value) {
  60. this.compilers.forEach(compiler => {
  61. compiler.inputFileSystem = value;
  62. });
  63. }
  64. set outputFileSystem(value) {
  65. this.compilers.forEach(compiler => {
  66. compiler.outputFileSystem = value;
  67. });
  68. }
  69. runWithDependencies(compilers, fn, callback) {
  70. let fulfilledNames = {};
  71. let remainingCompilers = compilers;
  72. const isDependencyFulfilled = (d) => fulfilledNames[d];
  73. const getReadyCompilers = () => {
  74. let readyCompilers = [];
  75. let list = remainingCompilers;
  76. remainingCompilers = [];
  77. for(const c of list) {
  78. const ready = !c.dependencies || c.dependencies.every(isDependencyFulfilled);
  79. if(ready)
  80. readyCompilers.push(c);
  81. else
  82. remainingCompilers.push(c);
  83. }
  84. return readyCompilers;
  85. };
  86. const runCompilers = (callback) => {
  87. if(remainingCompilers.length === 0) return callback();
  88. asyncLib.map(getReadyCompilers(), (compiler, callback) => {
  89. fn(compiler, (err) => {
  90. if(err) return callback(err);
  91. fulfilledNames[compiler.name] = true;
  92. runCompilers(callback);
  93. });
  94. }, callback);
  95. };
  96. runCompilers(callback);
  97. }
  98. watch(watchOptions, handler) {
  99. let watchings = [];
  100. let allStats = this.compilers.map(() => null);
  101. let compilerStatus = this.compilers.map(() => false);
  102. this.runWithDependencies(this.compilers, (compiler, callback) => {
  103. const compilerIdx = this.compilers.indexOf(compiler);
  104. let firstRun = true;
  105. let watching = compiler.watch(Array.isArray(watchOptions) ? watchOptions[compilerIdx] : watchOptions, (err, stats) => {
  106. if(err)
  107. handler(err);
  108. if(stats) {
  109. allStats[compilerIdx] = stats;
  110. compilerStatus[compilerIdx] = "new";
  111. if(compilerStatus.every(Boolean)) {
  112. const freshStats = allStats.filter((s, idx) => {
  113. return compilerStatus[idx] === "new";
  114. });
  115. compilerStatus.fill(true);
  116. const multiStats = new MultiStats(freshStats);
  117. handler(null, multiStats);
  118. }
  119. }
  120. if(firstRun && !err) {
  121. firstRun = false;
  122. callback();
  123. }
  124. });
  125. watchings.push(watching);
  126. }, () => {
  127. // ignore
  128. });
  129. return new MultiWatching(watchings, this);
  130. }
  131. run(callback) {
  132. const allStats = this.compilers.map(() => null);
  133. this.runWithDependencies(this.compilers, ((compiler, callback) => {
  134. const compilerIdx = this.compilers.indexOf(compiler);
  135. compiler.run((err, stats) => {
  136. if(err) return callback(err);
  137. allStats[compilerIdx] = stats;
  138. callback();
  139. });
  140. }), (err) => {
  141. if(err) return callback(err);
  142. callback(null, new MultiStats(allStats));
  143. });
  144. }
  145. purgeInputFileSystem() {
  146. this.compilers.forEach((compiler) => {
  147. if(compiler.inputFileSystem && compiler.inputFileSystem.purge)
  148. compiler.inputFileSystem.purge();
  149. });
  150. }
  151. };