Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 

233 řádky
6.4 KiB

  1. var path = require('path');
  2. var fse = require('fs-extra');
  3. var _ = require('lodash');
  4. const emitCountMap = new Map();
  5. function ManifestPlugin(opts) {
  6. this.opts = _.assign({
  7. publicPath: null,
  8. basePath: '',
  9. fileName: 'manifest.json',
  10. transformExtensions: /^(gz|map)$/i,
  11. writeToFileEmit: false,
  12. seed: null,
  13. filter: null,
  14. map: null,
  15. generate: null,
  16. sort: null,
  17. serialize: function(manifest) {
  18. return JSON.stringify(manifest, null, 2);
  19. },
  20. }, opts || {});
  21. }
  22. ManifestPlugin.prototype.getFileType = function(str) {
  23. str = str.replace(/\?.*/, '');
  24. var split = str.split('.');
  25. var ext = split.pop();
  26. if (this.opts.transformExtensions.test(ext)) {
  27. ext = split.pop() + '.' + ext;
  28. }
  29. return ext;
  30. };
  31. ManifestPlugin.prototype.apply = function(compiler) {
  32. var moduleAssets = {};
  33. var outputFolder = compiler.options.output.path;
  34. var outputFile = path.resolve(outputFolder, this.opts.fileName);
  35. var outputName = path.relative(outputFolder, outputFile);
  36. var moduleAsset = function (module, file) {
  37. if (module.userRequest) {
  38. moduleAssets[file] = path.join(
  39. path.dirname(file),
  40. path.basename(module.userRequest)
  41. );
  42. }
  43. };
  44. var emit = function(compilation, compileCallback) {
  45. const emitCount = emitCountMap.get(outputFile) - 1
  46. emitCountMap.set(outputFile, emitCount);
  47. var seed = this.opts.seed || {};
  48. var publicPath = this.opts.publicPath != null ? this.opts.publicPath : compilation.options.output.publicPath;
  49. var stats = compilation.getStats().toJson();
  50. var files = compilation.chunks.reduce(function(files, chunk) {
  51. return chunk.files.reduce(function (files, path) {
  52. var name = chunk.name ? chunk.name : null;
  53. if (name) {
  54. name = name + '.' + this.getFileType(path);
  55. } else {
  56. // For nameless chunks, just map the files directly.
  57. name = path;
  58. }
  59. // Webpack 4: .isOnlyInitial()
  60. // Webpack 3: .isInitial()
  61. // Webpack 1/2: .initial
  62. return files.concat({
  63. path: path,
  64. chunk: chunk,
  65. name: name,
  66. isInitial: chunk.isOnlyInitial ? chunk.isOnlyInitial() : (chunk.isInitial ? chunk.isInitial() : chunk.initial),
  67. isChunk: true,
  68. isAsset: false,
  69. isModuleAsset: false
  70. });
  71. }.bind(this), files);
  72. }.bind(this), []);
  73. // module assets don't show up in assetsByChunkName.
  74. // we're getting them this way;
  75. files = stats.assets.reduce(function (files, asset) {
  76. var name = moduleAssets[asset.name];
  77. if (name) {
  78. return files.concat({
  79. path: asset.name,
  80. name: name,
  81. isInitial: false,
  82. isChunk: false,
  83. isAsset: true,
  84. isModuleAsset: true
  85. });
  86. }
  87. var isEntryAsset = asset.chunks.length > 0;
  88. if (isEntryAsset) {
  89. return files;
  90. }
  91. return files.concat({
  92. path: asset.name,
  93. name: asset.name,
  94. isInitial: false,
  95. isChunk: false,
  96. isAsset: true,
  97. isModuleAsset: false
  98. });
  99. }, files);
  100. files = files.filter(function (file) {
  101. // Don't add hot updates to manifest
  102. var isUpdateChunk = file.path.indexOf('hot-update') >= 0;
  103. // Don't add manifest from another instance
  104. var isManifest = emitCountMap.get(path.join(outputFolder, file.name)) !== undefined;
  105. return !isUpdateChunk && !isManifest;
  106. });
  107. // Append optional basepath onto all references.
  108. // This allows output path to be reflected in the manifest.
  109. if (this.opts.basePath) {
  110. files = files.map(function(file) {
  111. file.name = this.opts.basePath + file.name;
  112. return file;
  113. }.bind(this));
  114. }
  115. if (publicPath) {
  116. // Similar to basePath but only affects the value (similar to how
  117. // output.publicPath turns require('foo/bar') into '/public/foo/bar', see
  118. // https://github.com/webpack/docs/wiki/configuration#outputpublicpath
  119. files = files.map(function(file) {
  120. file.path = publicPath + file.path;
  121. return file;
  122. }.bind(this));
  123. }
  124. files = files.map(file => {
  125. file.name = file.name.replace(/\\/g, '/');
  126. file.path = file.path.replace(/\\/g, '/');
  127. return file;
  128. });
  129. if (this.opts.filter) {
  130. files = files.filter(this.opts.filter);
  131. }
  132. if (this.opts.map) {
  133. files = files.map(this.opts.map);
  134. }
  135. if (this.opts.sort) {
  136. files = files.sort(this.opts.sort);
  137. }
  138. var manifest;
  139. if (this.opts.generate) {
  140. manifest = this.opts.generate(seed, files);
  141. } else {
  142. manifest = files.reduce(function (manifest, file) {
  143. manifest[file.name] = file.path;
  144. return manifest;
  145. }, seed);
  146. }
  147. const isLastEmit = emitCount === 0
  148. if (isLastEmit) {
  149. var output = this.opts.serialize(manifest);
  150. compilation.assets[outputName] = {
  151. source: function() {
  152. return output;
  153. },
  154. size: function() {
  155. return output.length;
  156. }
  157. };
  158. if (this.opts.writeToFileEmit) {
  159. fse.outputFileSync(outputFile, output);
  160. }
  161. }
  162. if (compiler.hooks) {
  163. compiler.hooks.webpackManifestPluginAfterEmit.call(manifest);
  164. } else {
  165. compilation.applyPluginsAsync('webpack-manifest-plugin-after-emit', manifest, compileCallback);
  166. }
  167. }.bind(this);
  168. function beforeRun (compiler, callback) {
  169. let emitCount = emitCountMap.get(outputFile) || 0;
  170. emitCountMap.set(outputFile, emitCount + 1);
  171. if (callback) {
  172. callback();
  173. }
  174. }
  175. if (compiler.hooks) {
  176. const SyncWaterfallHook = require('tapable').SyncWaterfallHook;
  177. const pluginOptions = {
  178. name: 'ManifestPlugin',
  179. stage: Infinity
  180. };
  181. compiler.hooks.webpackManifestPluginAfterEmit = new SyncWaterfallHook(['manifest']);
  182. compiler.hooks.compilation.tap(pluginOptions, function (compilation) {
  183. compilation.hooks.moduleAsset.tap(pluginOptions, moduleAsset);
  184. });
  185. compiler.hooks.emit.tap(pluginOptions, emit);
  186. compiler.hooks.run.tap(pluginOptions, beforeRun);
  187. compiler.hooks.watchRun.tap(pluginOptions, beforeRun);
  188. } else {
  189. compiler.plugin('compilation', function (compilation) {
  190. compilation.plugin('module-asset', moduleAsset);
  191. });
  192. compiler.plugin('emit', emit);
  193. compiler.plugin('before-run', beforeRun);
  194. compiler.plugin('watch-run', beforeRun);
  195. }
  196. };
  197. module.exports = ManifestPlugin;