UNPKG

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