index.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. var assign = require('lodash/assign');
  2. var each = require('lodash/each');
  3. var find = require('lodash/find');
  4. var isArray = require('lodash/isArray');
  5. var isFunction = require('lodash/isFunction');
  6. var isRegExp = require('lodash/isRegExp');
  7. var keys = require('lodash/keys');
  8. var values = require('lodash/values');
  9. var webpackSources = require('webpack-sources');
  10. function EMPTY_FUNC() {};
  11. var PHASE = {
  12. OPTIMIZE_CHUNK_ASSETS: 'compilation.optimize-chunk-assets',
  13. OPTIMIZE_ASSETS: 'compilation.optimize-assets',
  14. EMIT: 'emit'
  15. };
  16. var PHASES = values(PHASE);
  17. function AssetsManipulation(lastCallWebpackPlugin, compilation) {
  18. this.lastCallWebpackPlugin = lastCallWebpackPlugin;
  19. this.compilation = compilation;
  20. }
  21. AssetsManipulation.prototype.setAsset = function(assetName, assetValue, immediate) {
  22. this.lastCallWebpackPlugin.setAsset(assetName, assetValue, immediate, this.compilation);
  23. };
  24. AssetsManipulation.prototype.getAsset = function(assetName) {
  25. var asset = assetName && this.compilation.assets[assetName] && this.compilation.assets[assetName].source();
  26. return asset || undefined;
  27. };
  28. function LastCallWebpackPlugin(options) {
  29. this.options = assign(
  30. {
  31. assetProcessors: [],
  32. onStart: EMPTY_FUNC,
  33. onEnd: EMPTY_FUNC,
  34. canPrint: true
  35. },
  36. options || {}
  37. );
  38. if (!isArray(this.options.assetProcessors)) {
  39. throw new Error('LastCallWebpackPlugin Error: invalid options.assetProcessors (must be an Array).');
  40. }
  41. each(this.options.assetProcessors, function (processor, index) {
  42. if (!processor) {
  43. throw new Error('LastCallWebpackPlugin Error: invalid options.assetProcessors[' + String(index) + '] (must be an object).');
  44. }
  45. if (!isRegExp(processor.regExp)) {
  46. throw new Error('LastCallWebpackPlugin Error: invalid options.assetProcessors[' + String(index) + '].regExp (must be an regular expression).');
  47. }
  48. if (!isFunction(processor.processor)) {
  49. throw new Error('LastCallWebpackPlugin Error: invalid options.assetProcessors[' + String(index) + '].processor (must be a function).');
  50. }
  51. if (processor.phase === undefined) {
  52. processor.phase = PHASE.OPTIMIZE_ASSETS;
  53. }
  54. if (!find(PHASES, function(p) { return p === processor.phase; })) {
  55. throw new Error('LastCallWebpackPlugin Error: invalid options.assetProcessors[' + String(index) + '].phase (must be on of: ' + PHASES.join(', ') + ').');
  56. }
  57. });
  58. if (!isFunction(this.options.onStart)) {
  59. throw new Error('LastCallWebpackPlugin Error: invalid options.onStart (must be a function).');
  60. }
  61. if (!isFunction(this.options.onEnd)) {
  62. throw new Error('LastCallWebpackPlugin Error: invalid options.onEnd (must be a function).');
  63. }
  64. this.initCompile();
  65. };
  66. LastCallWebpackPlugin.prototype.initCompile = function() {
  67. this.deleteAssetsMap = {};
  68. }
  69. LastCallWebpackPlugin.prototype.print = function() {
  70. if (this.options.canPrint) {
  71. console.log.apply(console, arguments);
  72. }
  73. };
  74. LastCallWebpackPlugin.prototype.onAssetError = function(assetName, asset, err) {
  75. this.print('Error processing file: ' + assetName);
  76. };
  77. LastCallWebpackPlugin.prototype.getAssetsAndProcessors = function(assets, phase) {
  78. var assetProcessors = this.options.assetProcessors;
  79. var assetNames = keys(assets);
  80. var assetsAndProcessors = [];
  81. each(assetNames, function (assetName) {
  82. each(assetProcessors, function(assetProcessor) {
  83. if (assetProcessor.phase === phase) {
  84. var regExpResult = assetProcessor.regExp.exec(assetName);
  85. assetProcessor.regExp.lastIndex = 0;
  86. if (regExpResult) {
  87. var assetAndProcessor = {
  88. assetName: assetName,
  89. regExp: assetProcessor.regExp,
  90. processor: assetProcessor.processor,
  91. regExpResult: regExpResult,
  92. };
  93. assetsAndProcessors.push(assetAndProcessor);
  94. }
  95. }
  96. });
  97. });
  98. return assetsAndProcessors;
  99. };
  100. LastCallWebpackPlugin.prototype.createAsset = function(content, originalAsset) {
  101. return new webpackSources.RawSource(content);
  102. };
  103. LastCallWebpackPlugin.prototype.process = function(compilation, phase, callback) {
  104. var self = this;
  105. var assetsAndProcessors = this.getAssetsAndProcessors(compilation.assets, phase);
  106. if (assetsAndProcessors.length <= 0) {
  107. return callback();
  108. }
  109. this.options.onStart(assetsAndProcessors, compilation, phase);
  110. var hasErrors = false;
  111. var promises = [];
  112. var assetsManipulationObject = new AssetsManipulation(self, compilation);
  113. each(assetsAndProcessors, function(assetAndProcessor) {
  114. var asset = compilation.assets[assetAndProcessor.assetName];
  115. var promise = assetAndProcessor
  116. .processor(assetAndProcessor.assetName, asset, assetsManipulationObject)
  117. .then(function (result) {
  118. if (result !== undefined) {
  119. self.setAsset(assetAndProcessor.assetName, result, false, compilation);
  120. }
  121. })
  122. .catch(function(err) {
  123. hasErrors = true;
  124. self.onAssetError(assetAndProcessor.assetName, asset, err);
  125. throw err;
  126. });
  127. promises.push(promise);
  128. });
  129. return Promise.all(promises)
  130. .then(function () {
  131. self.options.onEnd(assetsAndProcessors, compilation, phase);
  132. callback();
  133. })
  134. .catch(function (err) {
  135. self.options.onEnd(assetsAndProcessors, compilation, phase, err);
  136. callback(err);
  137. });
  138. };
  139. LastCallWebpackPlugin.prototype.setAsset = function(assetName, assetValue, immediate, compilation) {
  140. if (assetName) {
  141. if (assetValue === null) {
  142. this.deleteAssetsMap[assetName] = true;
  143. if (immediate) {
  144. delete compilation.assets[assetName];
  145. }
  146. } else {
  147. if (assetValue !== undefined) {
  148. compilation.assets[assetName] = this.createAsset(assetValue, compilation.assets[assetName]);
  149. }
  150. }
  151. }
  152. };
  153. LastCallWebpackPlugin.prototype.deleteAssets = function(compilation) {
  154. if (this.deleteAssetsMap && compilation) {
  155. each(keys(this.deleteAssetsMap), function(key) {
  156. delete compilation.assets[key];
  157. });
  158. }
  159. };
  160. LastCallWebpackPlugin.prototype.apply = function(compiler) {
  161. var self = this;
  162. compiler.plugin('compilation', function(compilation, params) {
  163. self.initCompile();
  164. compilation.plugin("optimize-chunk-assets", function(chunks, callback) {
  165. self.process(compilation, PHASE.OPTIMIZE_CHUNK_ASSETS, callback);
  166. });
  167. compilation.plugin("optimize-assets", function(chunks, callback) {
  168. self.process(compilation, PHASE.OPTIMIZE_ASSETS, callback);
  169. });
  170. });
  171. compiler.plugin('emit', function(compilation, callback) {
  172. self.process(compilation, PHASE.EMIT, callback);
  173. self.deleteAssets(compilation);
  174. });
  175. };
  176. LastCallWebpackPlugin.prototype.PHASE = PHASE;
  177. LastCallWebpackPlugin.PHASE = PHASE;
  178. module.exports = LastCallWebpackPlugin;