AssetModulesPlugin.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Yuta Hiroto @hiroppy
  4. */
  5. "use strict";
  6. const {
  7. ASSET_MODULE_TYPE_RESOURCE,
  8. ASSET_MODULE_TYPE_INLINE,
  9. ASSET_MODULE_TYPE,
  10. ASSET_MODULE_TYPE_SOURCE
  11. } = require("../ModuleTypeConstants");
  12. const { cleverMerge } = require("../util/cleverMerge");
  13. const { compareModulesByIdentifier } = require("../util/comparators");
  14. const createSchemaValidation = require("../util/create-schema-validation");
  15. const memoize = require("../util/memoize");
  16. /** @typedef {import("webpack-sources").Source} Source */
  17. /** @typedef {import("../../declarations/WebpackOptions").AssetParserOptions} AssetParserOptions */
  18. /** @typedef {import("../Chunk")} Chunk */
  19. /** @typedef {import("../Compiler")} Compiler */
  20. /** @typedef {import("../Module")} Module */
  21. /** @typedef {import("../Module").BuildInfo} BuildInfo */
  22. /** @typedef {import("../Module").CodeGenerationResult} CodeGenerationResult */
  23. /**
  24. * @param {string} name name of definitions
  25. * @returns {TODO} definition
  26. */
  27. const getSchema = name => {
  28. const { definitions } = require("../../schemas/WebpackOptions.json");
  29. return {
  30. definitions,
  31. oneOf: [{ $ref: `#/definitions/${name}` }]
  32. };
  33. };
  34. const generatorValidationOptions = {
  35. name: "Asset Modules Plugin",
  36. baseDataPath: "generator"
  37. };
  38. const validateGeneratorOptions = {
  39. asset: createSchemaValidation(
  40. require("../../schemas/plugins/asset/AssetGeneratorOptions.check.js"),
  41. () => getSchema("AssetGeneratorOptions"),
  42. generatorValidationOptions
  43. ),
  44. "asset/resource": createSchemaValidation(
  45. require("../../schemas/plugins/asset/AssetResourceGeneratorOptions.check.js"),
  46. () => getSchema("AssetResourceGeneratorOptions"),
  47. generatorValidationOptions
  48. ),
  49. "asset/inline": createSchemaValidation(
  50. require("../../schemas/plugins/asset/AssetInlineGeneratorOptions.check.js"),
  51. () => getSchema("AssetInlineGeneratorOptions"),
  52. generatorValidationOptions
  53. )
  54. };
  55. const validateParserOptions = createSchemaValidation(
  56. require("../../schemas/plugins/asset/AssetParserOptions.check.js"),
  57. () => getSchema("AssetParserOptions"),
  58. {
  59. name: "Asset Modules Plugin",
  60. baseDataPath: "parser"
  61. }
  62. );
  63. const getAssetGenerator = memoize(() => require("./AssetGenerator"));
  64. const getAssetParser = memoize(() => require("./AssetParser"));
  65. const getAssetSourceParser = memoize(() => require("./AssetSourceParser"));
  66. const getAssetSourceGenerator = memoize(() =>
  67. require("./AssetSourceGenerator")
  68. );
  69. const type = ASSET_MODULE_TYPE;
  70. const plugin = "AssetModulesPlugin";
  71. class AssetModulesPlugin {
  72. /**
  73. * Apply the plugin
  74. * @param {Compiler} compiler the compiler instance
  75. * @returns {void}
  76. */
  77. apply(compiler) {
  78. compiler.hooks.compilation.tap(
  79. plugin,
  80. (compilation, { normalModuleFactory }) => {
  81. normalModuleFactory.hooks.createParser
  82. .for(ASSET_MODULE_TYPE)
  83. .tap(plugin, parserOptions => {
  84. validateParserOptions(parserOptions);
  85. parserOptions = cleverMerge(
  86. /** @type {AssetParserOptions} */
  87. (compiler.options.module.parser.asset),
  88. parserOptions
  89. );
  90. let dataUrlCondition = parserOptions.dataUrlCondition;
  91. if (!dataUrlCondition || typeof dataUrlCondition === "object") {
  92. dataUrlCondition = {
  93. maxSize: 8096,
  94. ...dataUrlCondition
  95. };
  96. }
  97. const AssetParser = getAssetParser();
  98. return new AssetParser(dataUrlCondition);
  99. });
  100. normalModuleFactory.hooks.createParser
  101. .for(ASSET_MODULE_TYPE_INLINE)
  102. .tap(plugin, _parserOptions => {
  103. const AssetParser = getAssetParser();
  104. return new AssetParser(true);
  105. });
  106. normalModuleFactory.hooks.createParser
  107. .for(ASSET_MODULE_TYPE_RESOURCE)
  108. .tap(plugin, _parserOptions => {
  109. const AssetParser = getAssetParser();
  110. return new AssetParser(false);
  111. });
  112. normalModuleFactory.hooks.createParser
  113. .for(ASSET_MODULE_TYPE_SOURCE)
  114. .tap(plugin, _parserOptions => {
  115. const AssetSourceParser = getAssetSourceParser();
  116. return new AssetSourceParser();
  117. });
  118. for (const type of [
  119. ASSET_MODULE_TYPE,
  120. ASSET_MODULE_TYPE_INLINE,
  121. ASSET_MODULE_TYPE_RESOURCE
  122. ]) {
  123. normalModuleFactory.hooks.createGenerator
  124. .for(type)
  125. .tap(plugin, generatorOptions => {
  126. validateGeneratorOptions[type](generatorOptions);
  127. let dataUrl;
  128. if (type !== ASSET_MODULE_TYPE_RESOURCE) {
  129. dataUrl = generatorOptions.dataUrl;
  130. if (!dataUrl || typeof dataUrl === "object") {
  131. dataUrl = {
  132. encoding: undefined,
  133. mimetype: undefined,
  134. ...dataUrl
  135. };
  136. }
  137. }
  138. let filename;
  139. let publicPath;
  140. let outputPath;
  141. if (type !== ASSET_MODULE_TYPE_INLINE) {
  142. filename = generatorOptions.filename;
  143. publicPath = generatorOptions.publicPath;
  144. outputPath = generatorOptions.outputPath;
  145. }
  146. const AssetGenerator = getAssetGenerator();
  147. return new AssetGenerator(
  148. compilation.moduleGraph,
  149. dataUrl,
  150. filename,
  151. publicPath,
  152. outputPath,
  153. generatorOptions.emit !== false
  154. );
  155. });
  156. }
  157. normalModuleFactory.hooks.createGenerator
  158. .for(ASSET_MODULE_TYPE_SOURCE)
  159. .tap(plugin, () => {
  160. const AssetSourceGenerator = getAssetSourceGenerator();
  161. return new AssetSourceGenerator(compilation.moduleGraph);
  162. });
  163. compilation.hooks.renderManifest.tap(plugin, (result, options) => {
  164. const { chunkGraph } = compilation;
  165. const { chunk, codeGenerationResults } = options;
  166. const modules = chunkGraph.getOrderedChunkModulesIterableBySourceType(
  167. chunk,
  168. ASSET_MODULE_TYPE,
  169. compareModulesByIdentifier
  170. );
  171. if (modules) {
  172. for (const module of modules) {
  173. try {
  174. const codeGenResult = codeGenerationResults.get(
  175. module,
  176. chunk.runtime
  177. );
  178. const buildInfo = /** @type {BuildInfo} */ (module.buildInfo);
  179. const data =
  180. /** @type {NonNullable<CodeGenerationResult["data"]>} */
  181. (codeGenResult.data);
  182. const errored = module.getNumberOfErrors() > 0;
  183. result.push({
  184. render: () =>
  185. /** @type {Source} */ (codeGenResult.sources.get(type)),
  186. filename: errored
  187. ? module.nameForCondition()
  188. : buildInfo.filename || data.get("filename"),
  189. info: buildInfo.assetInfo || data.get("assetInfo"),
  190. auxiliary: true,
  191. identifier: `assetModule${chunkGraph.getModuleId(module)}`,
  192. hash: errored
  193. ? chunkGraph.getModuleHash(module, chunk.runtime)
  194. : buildInfo.fullContentHash || data.get("fullContentHash")
  195. });
  196. } catch (err) {
  197. /** @type {Error} */ (err).message +=
  198. `\nduring rendering of asset ${module.identifier()}`;
  199. throw err;
  200. }
  201. }
  202. }
  203. return result;
  204. });
  205. compilation.hooks.prepareModuleExecution.tap(
  206. "AssetModulesPlugin",
  207. (options, context) => {
  208. const { codeGenerationResult } = options;
  209. const source = codeGenerationResult.sources.get(ASSET_MODULE_TYPE);
  210. if (source === undefined) return;
  211. const data =
  212. /** @type {NonNullable<CodeGenerationResult["data"]>} */
  213. (codeGenerationResult.data);
  214. context.assets.set(data.get("filename"), {
  215. source,
  216. info: data.get("assetInfo")
  217. });
  218. }
  219. );
  220. }
  221. );
  222. }
  223. }
  224. module.exports = AssetModulesPlugin;