LoaderPlugin.js 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const NormalModule = require("../NormalModule");
  7. const { markNotCacheable } = require("../loaders/LoaderRunner");
  8. const LazySet = require("../util/LazySet");
  9. const LoaderDependency = require("./LoaderDependency");
  10. const LoaderImportDependency = require("./LoaderImportDependency");
  11. /** @import { RawSourceMap } from "webpack-sources" */
  12. /**
  13. * @import {
  14. * DependencyConstructor,
  15. * ExecuteModuleExports,
  16. * ExecuteModuleResult,
  17. * FileSystemDependencies
  18. * } from "../Compilation"
  19. */
  20. /** @import Compiler from "../Compiler" */
  21. /** @import { BuildInfo } from "../Module" */
  22. /**
  23. * Defines the import module callback callback.
  24. * @callback ImportModuleCallback
  25. * @param {(Error | null)=} err error object
  26. * @param {ExecuteModuleExports=} exports exports of the evaluated module
  27. * @returns {void}
  28. */
  29. /**
  30. * Defines the import module options type used by this module.
  31. * @typedef {object} ImportModuleOptions
  32. * @property {string=} layer the target layer
  33. * @property {string=} publicPath the target public path
  34. * @property {string=} baseUri target base uri
  35. */
  36. const PLUGIN_NAME = "LoaderPlugin";
  37. class LoaderPlugin {
  38. /**
  39. * Applies the plugin by registering its hooks on the compiler.
  40. * @param {Compiler} compiler the compiler instance
  41. * @returns {void}
  42. */
  43. apply(compiler) {
  44. compiler.hooks.compilation.tap(
  45. PLUGIN_NAME,
  46. (compilation, { normalModuleFactory }) => {
  47. compilation.dependencyFactories.set(
  48. LoaderDependency,
  49. normalModuleFactory
  50. );
  51. compilation.dependencyFactories.set(
  52. LoaderImportDependency,
  53. normalModuleFactory
  54. );
  55. }
  56. );
  57. compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
  58. const moduleGraph = compilation.moduleGraph;
  59. NormalModule.getCompilationHooks(compilation).loader.tap(
  60. PLUGIN_NAME,
  61. (loaderContext) => {
  62. loaderContext.loadModule = (request, callback) => {
  63. const dep = new LoaderDependency(request);
  64. dep.loc = {
  65. name: request
  66. };
  67. const factory = compilation.dependencyFactories.get(
  68. /** @type {DependencyConstructor} */
  69. (dep.constructor)
  70. );
  71. if (factory === undefined) {
  72. return callback(
  73. new Error(
  74. `No module factory available for dependency type: ${dep.constructor.name}`
  75. )
  76. );
  77. }
  78. const oldFactorizeQueueContext =
  79. compilation.factorizeQueue.getContext();
  80. compilation.factorizeQueue.setContext("load-module");
  81. const oldAddModuleQueueContext =
  82. compilation.addModuleQueue.getContext();
  83. compilation.addModuleQueue.setContext("load-module");
  84. compilation.buildQueue.increaseParallelism();
  85. compilation.handleModuleCreation(
  86. {
  87. factory,
  88. dependencies: [dep],
  89. originModule:
  90. /** @type {NormalModule} */
  91. (loaderContext._module),
  92. context: loaderContext.context,
  93. recursive: false
  94. },
  95. (err) => {
  96. compilation.factorizeQueue.setContext(oldFactorizeQueueContext);
  97. compilation.addModuleQueue.setContext(oldAddModuleQueueContext);
  98. compilation.buildQueue.decreaseParallelism();
  99. if (err) {
  100. return callback(err);
  101. }
  102. const referencedModule = moduleGraph.getModule(dep);
  103. if (!referencedModule) {
  104. return callback(new Error("Cannot load the module"));
  105. }
  106. if (referencedModule.getNumberOfErrors() > 0) {
  107. return callback(
  108. new Error("The loaded module contains errors")
  109. );
  110. }
  111. const moduleSource = referencedModule.originalSource();
  112. if (!moduleSource) {
  113. return callback(
  114. new Error(
  115. "The module created for a LoaderDependency must have an original source"
  116. )
  117. );
  118. }
  119. /** @type {null | RawSourceMap} */
  120. let map;
  121. /** @type {string | Buffer | undefined} */
  122. let source;
  123. if (moduleSource.sourceAndMap) {
  124. const sourceAndMap = moduleSource.sourceAndMap();
  125. map = sourceAndMap.map;
  126. source = sourceAndMap.source;
  127. } else {
  128. map = moduleSource.map();
  129. source = moduleSource.source();
  130. }
  131. /** @type {FileSystemDependencies} */
  132. const fileDependencies = new LazySet();
  133. /** @type {FileSystemDependencies} */
  134. const contextDependencies = new LazySet();
  135. /** @type {FileSystemDependencies} */
  136. const missingDependencies = new LazySet();
  137. /** @type {FileSystemDependencies} */
  138. const buildDependencies = new LazySet();
  139. referencedModule.addCacheDependencies(
  140. fileDependencies,
  141. contextDependencies,
  142. missingDependencies,
  143. buildDependencies
  144. );
  145. for (const d of fileDependencies) {
  146. loaderContext.addDependency(d);
  147. }
  148. for (const d of contextDependencies) {
  149. loaderContext.addContextDependency(d);
  150. }
  151. for (const d of missingDependencies) {
  152. loaderContext.addMissingDependency(d);
  153. }
  154. for (const d of buildDependencies) {
  155. loaderContext.addBuildDependency(d);
  156. }
  157. return callback(null, source, map, referencedModule);
  158. }
  159. );
  160. };
  161. /**
  162. * Processes the provided request.
  163. * @param {string} request the request string to load the module from
  164. * @param {ImportModuleOptions} options options
  165. * @param {ImportModuleCallback} callback callback returning the exports
  166. * @returns {void}
  167. */
  168. const importModule = (request, options, callback) => {
  169. const dep = new LoaderImportDependency(request);
  170. dep.loc = {
  171. name: request
  172. };
  173. const factory = compilation.dependencyFactories.get(
  174. /** @type {DependencyConstructor} */
  175. (dep.constructor)
  176. );
  177. if (factory === undefined) {
  178. return callback(
  179. new Error(
  180. `No module factory available for dependency type: ${dep.constructor.name}`
  181. )
  182. );
  183. }
  184. const oldFactorizeQueueContext =
  185. compilation.factorizeQueue.getContext();
  186. compilation.factorizeQueue.setContext("import-module");
  187. const oldAddModuleQueueContext =
  188. compilation.addModuleQueue.getContext();
  189. compilation.addModuleQueue.setContext("import-module");
  190. compilation.buildQueue.increaseParallelism();
  191. compilation.handleModuleCreation(
  192. {
  193. factory,
  194. dependencies: [dep],
  195. originModule:
  196. /** @type {NormalModule} */
  197. (loaderContext._module),
  198. contextInfo: {
  199. issuerLayer: options.layer
  200. },
  201. context: loaderContext.context,
  202. connectOrigin: false,
  203. checkCycle: true
  204. },
  205. (err) => {
  206. compilation.factorizeQueue.setContext(oldFactorizeQueueContext);
  207. compilation.addModuleQueue.setContext(oldAddModuleQueueContext);
  208. compilation.buildQueue.decreaseParallelism();
  209. if (err) {
  210. return callback(err);
  211. }
  212. const referencedModule = moduleGraph.getModule(dep);
  213. if (!referencedModule) {
  214. return callback(new Error("Cannot load the module"));
  215. }
  216. compilation.buildQueue.increaseParallelism();
  217. compilation.executeModule(
  218. referencedModule,
  219. {
  220. entryOptions: {
  221. baseUri: options.baseUri,
  222. publicPath: options.publicPath
  223. }
  224. },
  225. (err, result) => {
  226. compilation.buildQueue.decreaseParallelism();
  227. if (err) return callback(err);
  228. const {
  229. fileDependencies,
  230. contextDependencies,
  231. missingDependencies,
  232. buildDependencies,
  233. cacheable,
  234. notCacheableReasons,
  235. assets,
  236. exports
  237. } = /** @type {ExecuteModuleResult} */ (result);
  238. for (const d of fileDependencies) {
  239. loaderContext.addDependency(d);
  240. }
  241. for (const d of contextDependencies) {
  242. loaderContext.addContextDependency(d);
  243. }
  244. for (const d of missingDependencies) {
  245. loaderContext.addMissingDependency(d);
  246. }
  247. for (const d of buildDependencies) {
  248. loaderContext.addBuildDependency(d);
  249. }
  250. if (cacheable === false) {
  251. markNotCacheable(loaderContext, notCacheableReasons);
  252. }
  253. for (const [name, { source, info }] of assets) {
  254. const buildInfo =
  255. /** @type {BuildInfo} */
  256. (
  257. /** @type {NormalModule} */ (loaderContext._module)
  258. .buildInfo
  259. );
  260. if (!buildInfo.assets) {
  261. buildInfo.assets = Object.create(null);
  262. buildInfo.assetsInfo = new Map();
  263. }
  264. /** @type {NonNullable<BuildInfo["assets"]>} */
  265. (buildInfo.assets)[name] = source;
  266. /** @type {NonNullable<BuildInfo["assetsInfo"]>} */
  267. (buildInfo.assetsInfo).set(name, info);
  268. }
  269. callback(null, exports);
  270. }
  271. );
  272. }
  273. );
  274. };
  275. // @ts-expect-error overloading doesn't work
  276. loaderContext.importModule = (request, options, callback) => {
  277. if (!callback) {
  278. return new Promise((resolve, reject) => {
  279. importModule(request, options || {}, (err, result) => {
  280. if (err) reject(err);
  281. else resolve(result);
  282. });
  283. });
  284. }
  285. return importModule(request, options || {}, callback);
  286. };
  287. }
  288. );
  289. });
  290. }
  291. }
  292. module.exports = LoaderPlugin;