MultiCompiler.js 4.9 KB

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