You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

185 lines
5.3 KiB

4 years ago
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const Parser = require("./Parser");
  7. const Template = require("./Template");
  8. const { ConcatSource } = require("webpack-sources");
  9. const JavascriptGenerator = require("./JavascriptGenerator");
  10. const createHash = require("./util/createHash");
  11. class JavascriptModulesPlugin {
  12. apply(compiler) {
  13. compiler.hooks.compilation.tap(
  14. "JavascriptModulesPlugin",
  15. (compilation, { normalModuleFactory }) => {
  16. normalModuleFactory.hooks.createParser
  17. .for("javascript/auto")
  18. .tap("JavascriptModulesPlugin", options => {
  19. return new Parser(options, "auto");
  20. });
  21. normalModuleFactory.hooks.createParser
  22. .for("javascript/dynamic")
  23. .tap("JavascriptModulesPlugin", options => {
  24. return new Parser(options, "script");
  25. });
  26. normalModuleFactory.hooks.createParser
  27. .for("javascript/esm")
  28. .tap("JavascriptModulesPlugin", options => {
  29. return new Parser(options, "module");
  30. });
  31. normalModuleFactory.hooks.createGenerator
  32. .for("javascript/auto")
  33. .tap("JavascriptModulesPlugin", () => {
  34. return new JavascriptGenerator();
  35. });
  36. normalModuleFactory.hooks.createGenerator
  37. .for("javascript/dynamic")
  38. .tap("JavascriptModulesPlugin", () => {
  39. return new JavascriptGenerator();
  40. });
  41. normalModuleFactory.hooks.createGenerator
  42. .for("javascript/esm")
  43. .tap("JavascriptModulesPlugin", () => {
  44. return new JavascriptGenerator();
  45. });
  46. compilation.mainTemplate.hooks.renderManifest.tap(
  47. "JavascriptModulesPlugin",
  48. (result, options) => {
  49. const chunk = options.chunk;
  50. const hash = options.hash;
  51. const fullHash = options.fullHash;
  52. const outputOptions = options.outputOptions;
  53. const moduleTemplates = options.moduleTemplates;
  54. const dependencyTemplates = options.dependencyTemplates;
  55. const filenameTemplate =
  56. chunk.filenameTemplate || outputOptions.filename;
  57. const useChunkHash = compilation.mainTemplate.useChunkHash(chunk);
  58. result.push({
  59. render: () =>
  60. compilation.mainTemplate.render(
  61. hash,
  62. chunk,
  63. moduleTemplates.javascript,
  64. dependencyTemplates
  65. ),
  66. filenameTemplate,
  67. pathOptions: {
  68. noChunkHash: !useChunkHash,
  69. contentHashType: "javascript",
  70. chunk
  71. },
  72. identifier: `chunk${chunk.id}`,
  73. hash: useChunkHash ? chunk.hash : fullHash
  74. });
  75. return result;
  76. }
  77. );
  78. compilation.mainTemplate.hooks.modules.tap(
  79. "JavascriptModulesPlugin",
  80. (source, chunk, hash, moduleTemplate, dependencyTemplates) => {
  81. return Template.renderChunkModules(
  82. chunk,
  83. m => typeof m.source === "function",
  84. moduleTemplate,
  85. dependencyTemplates,
  86. "/******/ "
  87. );
  88. }
  89. );
  90. compilation.chunkTemplate.hooks.renderManifest.tap(
  91. "JavascriptModulesPlugin",
  92. (result, options) => {
  93. const chunk = options.chunk;
  94. const outputOptions = options.outputOptions;
  95. const moduleTemplates = options.moduleTemplates;
  96. const dependencyTemplates = options.dependencyTemplates;
  97. const filenameTemplate =
  98. chunk.filenameTemplate || outputOptions.chunkFilename;
  99. result.push({
  100. render: () =>
  101. this.renderJavascript(
  102. compilation.chunkTemplate,
  103. chunk,
  104. moduleTemplates.javascript,
  105. dependencyTemplates
  106. ),
  107. filenameTemplate,
  108. pathOptions: {
  109. chunk,
  110. contentHashType: "javascript"
  111. },
  112. identifier: `chunk${chunk.id}`,
  113. hash: chunk.hash
  114. });
  115. return result;
  116. }
  117. );
  118. compilation.hooks.contentHash.tap("JavascriptModulesPlugin", chunk => {
  119. const outputOptions = compilation.outputOptions;
  120. const {
  121. hashSalt,
  122. hashDigest,
  123. hashDigestLength,
  124. hashFunction
  125. } = outputOptions;
  126. const hash = createHash(hashFunction);
  127. if (hashSalt) hash.update(hashSalt);
  128. const template = chunk.hasRuntime()
  129. ? compilation.mainTemplate
  130. : compilation.chunkTemplate;
  131. hash.update(`${chunk.id} `);
  132. hash.update(chunk.ids ? chunk.ids.join(",") : "");
  133. template.updateHashForChunk(
  134. hash,
  135. chunk,
  136. compilation.moduleTemplates.javascript,
  137. compilation.dependencyTemplates
  138. );
  139. for (const m of chunk.modulesIterable) {
  140. if (typeof m.source === "function") {
  141. hash.update(m.hash);
  142. }
  143. }
  144. const digest = /** @type {string} */ (hash.digest(hashDigest));
  145. chunk.contentHash.javascript = digest.substr(0, hashDigestLength);
  146. });
  147. }
  148. );
  149. }
  150. renderJavascript(chunkTemplate, chunk, moduleTemplate, dependencyTemplates) {
  151. const moduleSources = Template.renderChunkModules(
  152. chunk,
  153. m => typeof m.source === "function",
  154. moduleTemplate,
  155. dependencyTemplates
  156. );
  157. const core = chunkTemplate.hooks.modules.call(
  158. moduleSources,
  159. chunk,
  160. moduleTemplate,
  161. dependencyTemplates
  162. );
  163. let source = chunkTemplate.hooks.render.call(
  164. core,
  165. chunk,
  166. moduleTemplate,
  167. dependencyTemplates
  168. );
  169. if (chunk.hasEntryModule()) {
  170. source = chunkTemplate.hooks.renderWithEntry.call(source, chunk);
  171. }
  172. chunk.rendered = true;
  173. return new ConcatSource(source, ";");
  174. }
  175. }
  176. module.exports = JavascriptModulesPlugin;