AssetModulesPlugin.js 6.7 KB

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