1 | /*
|
---|
2 | MIT License http://www.opensource.org/licenses/mit-license.php
|
---|
3 | Author Tobias Koppers @sokra
|
---|
4 | */
|
---|
5 |
|
---|
6 | "use strict";
|
---|
7 |
|
---|
8 | const { getContext } = require("loader-runner");
|
---|
9 | const asyncLib = require("neo-async");
|
---|
10 | const {
|
---|
11 | AsyncSeriesBailHook,
|
---|
12 | SyncWaterfallHook,
|
---|
13 | SyncBailHook,
|
---|
14 | SyncHook,
|
---|
15 | HookMap
|
---|
16 | } = require("tapable");
|
---|
17 | const ChunkGraph = require("./ChunkGraph");
|
---|
18 | const Module = require("./Module");
|
---|
19 | const ModuleFactory = require("./ModuleFactory");
|
---|
20 | const ModuleGraph = require("./ModuleGraph");
|
---|
21 | const { JAVASCRIPT_MODULE_TYPE_AUTO } = require("./ModuleTypeConstants");
|
---|
22 | const NormalModule = require("./NormalModule");
|
---|
23 | const BasicEffectRulePlugin = require("./rules/BasicEffectRulePlugin");
|
---|
24 | const BasicMatcherRulePlugin = require("./rules/BasicMatcherRulePlugin");
|
---|
25 | const ObjectMatcherRulePlugin = require("./rules/ObjectMatcherRulePlugin");
|
---|
26 | const RuleSetCompiler = require("./rules/RuleSetCompiler");
|
---|
27 | const UseEffectRulePlugin = require("./rules/UseEffectRulePlugin");
|
---|
28 | const LazySet = require("./util/LazySet");
|
---|
29 | const { getScheme } = require("./util/URLAbsoluteSpecifier");
|
---|
30 | const { cachedCleverMerge, cachedSetProperty } = require("./util/cleverMerge");
|
---|
31 | const { join } = require("./util/fs");
|
---|
32 | const {
|
---|
33 | parseResource,
|
---|
34 | parseResourceWithoutFragment
|
---|
35 | } = require("./util/identifier");
|
---|
36 |
|
---|
37 | /** @typedef {import("../declarations/WebpackOptions").ModuleOptionsNormalized} ModuleOptions */
|
---|
38 | /** @typedef {import("../declarations/WebpackOptions").RuleSetRule} RuleSetRule */
|
---|
39 | /** @typedef {import("./Generator")} Generator */
|
---|
40 | /** @typedef {import("./ModuleFactory").ModuleFactoryCreateData} ModuleFactoryCreateData */
|
---|
41 | /** @typedef {import("./ModuleFactory").ModuleFactoryCreateDataContextInfo} ModuleFactoryCreateDataContextInfo */
|
---|
42 | /** @typedef {import("./ModuleFactory").ModuleFactoryResult} ModuleFactoryResult */
|
---|
43 | /** @typedef {import("./NormalModule").GeneratorOptions} GeneratorOptions */
|
---|
44 | /** @typedef {import("./NormalModule").LoaderItem} LoaderItem */
|
---|
45 | /** @typedef {import("./NormalModule").NormalModuleCreateData} NormalModuleCreateData */
|
---|
46 | /** @typedef {import("./NormalModule").ParserOptions} ParserOptions */
|
---|
47 | /** @typedef {import("./Parser")} Parser */
|
---|
48 | /** @typedef {import("./ResolverFactory")} ResolverFactory */
|
---|
49 | /** @typedef {import("./ResolverFactory").ResolveContext} ResolveContext */
|
---|
50 | /** @typedef {import("./ResolverFactory").ResolveRequest} ResolveRequest */
|
---|
51 | /** @typedef {import("./ResolverFactory").ResolverWithOptions} ResolverWithOptions */
|
---|
52 | /** @typedef {import("./dependencies/ModuleDependency")} ModuleDependency */
|
---|
53 | /** @typedef {import("./util/fs").InputFileSystem} InputFileSystem */
|
---|
54 |
|
---|
55 | /** @typedef {Pick<RuleSetRule, 'type' | 'sideEffects' | 'parser' | 'generator' | 'resolve' | 'layer'>} ModuleSettings */
|
---|
56 | /** @typedef {Partial<NormalModuleCreateData & { settings: ModuleSettings }>} CreateData */
|
---|
57 |
|
---|
58 | /**
|
---|
59 | * @typedef {object} ResolveData
|
---|
60 | * @property {ModuleFactoryCreateData["contextInfo"]} contextInfo
|
---|
61 | * @property {ModuleFactoryCreateData["resolveOptions"]} resolveOptions
|
---|
62 | * @property {string} context
|
---|
63 | * @property {string} request
|
---|
64 | * @property {Record<string, any> | undefined} assertions
|
---|
65 | * @property {ModuleDependency[]} dependencies
|
---|
66 | * @property {string} dependencyType
|
---|
67 | * @property {CreateData} createData
|
---|
68 | * @property {LazySet<string>} fileDependencies
|
---|
69 | * @property {LazySet<string>} missingDependencies
|
---|
70 | * @property {LazySet<string>} contextDependencies
|
---|
71 | * @property {Module=} ignoredModule
|
---|
72 | * @property {boolean} cacheable allow to use the unsafe cache
|
---|
73 | */
|
---|
74 |
|
---|
75 | /**
|
---|
76 | * @typedef {object} ResourceData
|
---|
77 | * @property {string} resource
|
---|
78 | * @property {string=} path
|
---|
79 | * @property {string=} query
|
---|
80 | * @property {string=} fragment
|
---|
81 | * @property {string=} context
|
---|
82 | */
|
---|
83 |
|
---|
84 | /** @typedef {ResourceData & { data: Record<string, any> }} ResourceDataWithData */
|
---|
85 |
|
---|
86 | /**
|
---|
87 | * @typedef {object} ParsedLoaderRequest
|
---|
88 | * @property {string} loader loader
|
---|
89 | * @property {string|undefined} options options
|
---|
90 | */
|
---|
91 |
|
---|
92 | /**
|
---|
93 | * @template T
|
---|
94 | * @callback Callback
|
---|
95 | * @param {(Error | null)=} err
|
---|
96 | * @param {T=} stats
|
---|
97 | * @returns {void}
|
---|
98 | */
|
---|
99 |
|
---|
100 | const EMPTY_RESOLVE_OPTIONS = {};
|
---|
101 | /** @type {ParserOptions} */
|
---|
102 | const EMPTY_PARSER_OPTIONS = {};
|
---|
103 | /** @type {GeneratorOptions} */
|
---|
104 | const EMPTY_GENERATOR_OPTIONS = {};
|
---|
105 | /** @type {ParsedLoaderRequest[]} */
|
---|
106 | const EMPTY_ELEMENTS = [];
|
---|
107 |
|
---|
108 | const MATCH_RESOURCE_REGEX = /^([^!]+)!=!/;
|
---|
109 | const LEADING_DOT_EXTENSION_REGEX = /^[^.]/;
|
---|
110 |
|
---|
111 | /**
|
---|
112 | * @param {LoaderItem} data data
|
---|
113 | * @returns {string} ident
|
---|
114 | */
|
---|
115 | const loaderToIdent = data => {
|
---|
116 | if (!data.options) {
|
---|
117 | return data.loader;
|
---|
118 | }
|
---|
119 | if (typeof data.options === "string") {
|
---|
120 | return `${data.loader}?${data.options}`;
|
---|
121 | }
|
---|
122 | if (typeof data.options !== "object") {
|
---|
123 | throw new Error("loader options must be string or object");
|
---|
124 | }
|
---|
125 | if (data.ident) {
|
---|
126 | return `${data.loader}??${data.ident}`;
|
---|
127 | }
|
---|
128 | return `${data.loader}?${JSON.stringify(data.options)}`;
|
---|
129 | };
|
---|
130 |
|
---|
131 | /**
|
---|
132 | * @param {LoaderItem[]} loaders loaders
|
---|
133 | * @param {string} resource resource
|
---|
134 | * @returns {string} stringified loaders and resource
|
---|
135 | */
|
---|
136 | const stringifyLoadersAndResource = (loaders, resource) => {
|
---|
137 | let str = "";
|
---|
138 | for (const loader of loaders) {
|
---|
139 | str += `${loaderToIdent(loader)}!`;
|
---|
140 | }
|
---|
141 | return str + resource;
|
---|
142 | };
|
---|
143 |
|
---|
144 | /**
|
---|
145 | * @param {number} times times
|
---|
146 | * @param {(err?: null | Error) => void} callback callback
|
---|
147 | * @returns {(err?: null | Error) => void} callback
|
---|
148 | */
|
---|
149 | const needCalls = (times, callback) => err => {
|
---|
150 | if (--times === 0) {
|
---|
151 | return callback(err);
|
---|
152 | }
|
---|
153 | if (err && times > 0) {
|
---|
154 | times = Number.NaN;
|
---|
155 | return callback(err);
|
---|
156 | }
|
---|
157 | };
|
---|
158 |
|
---|
159 | /**
|
---|
160 | * @template T
|
---|
161 | * @template O
|
---|
162 | * @param {T} globalOptions global options
|
---|
163 | * @param {string} type type
|
---|
164 | * @param {O} localOptions local options
|
---|
165 | * @returns {T & O | T | O} result
|
---|
166 | */
|
---|
167 | const mergeGlobalOptions = (globalOptions, type, localOptions) => {
|
---|
168 | const parts = type.split("/");
|
---|
169 | let result;
|
---|
170 | let current = "";
|
---|
171 | for (const part of parts) {
|
---|
172 | current = current ? `${current}/${part}` : part;
|
---|
173 | const options =
|
---|
174 | /** @type {T} */
|
---|
175 | (globalOptions[/** @type {keyof T} */ (current)]);
|
---|
176 | if (typeof options === "object") {
|
---|
177 | result =
|
---|
178 | result === undefined ? options : cachedCleverMerge(result, options);
|
---|
179 | }
|
---|
180 | }
|
---|
181 | if (result === undefined) {
|
---|
182 | return localOptions;
|
---|
183 | }
|
---|
184 | return cachedCleverMerge(result, localOptions);
|
---|
185 | };
|
---|
186 |
|
---|
187 | // TODO webpack 6 remove
|
---|
188 | /**
|
---|
189 | * @param {string} name name
|
---|
190 | * @param {TODO} hook hook
|
---|
191 | * @returns {string} result
|
---|
192 | */
|
---|
193 | const deprecationChangedHookMessage = (name, hook) => {
|
---|
194 | const names = hook.taps
|
---|
195 | .map(
|
---|
196 | /**
|
---|
197 | * @param {TODO} tapped tapped
|
---|
198 | * @returns {string} name
|
---|
199 | */
|
---|
200 | tapped => tapped.name
|
---|
201 | )
|
---|
202 | .join(", ");
|
---|
203 |
|
---|
204 | return (
|
---|
205 | `NormalModuleFactory.${name} (${names}) is no longer a waterfall hook, but a bailing hook instead. ` +
|
---|
206 | "Do not return the passed object, but modify it instead. " +
|
---|
207 | "Returning false will ignore the request and results in no module created."
|
---|
208 | );
|
---|
209 | };
|
---|
210 |
|
---|
211 | const ruleSetCompiler = new RuleSetCompiler([
|
---|
212 | new BasicMatcherRulePlugin("test", "resource"),
|
---|
213 | new BasicMatcherRulePlugin("scheme"),
|
---|
214 | new BasicMatcherRulePlugin("mimetype"),
|
---|
215 | new BasicMatcherRulePlugin("dependency"),
|
---|
216 | new BasicMatcherRulePlugin("include", "resource"),
|
---|
217 | new BasicMatcherRulePlugin("exclude", "resource", true),
|
---|
218 | new BasicMatcherRulePlugin("resource"),
|
---|
219 | new BasicMatcherRulePlugin("resourceQuery"),
|
---|
220 | new BasicMatcherRulePlugin("resourceFragment"),
|
---|
221 | new BasicMatcherRulePlugin("realResource"),
|
---|
222 | new BasicMatcherRulePlugin("issuer"),
|
---|
223 | new BasicMatcherRulePlugin("compiler"),
|
---|
224 | new BasicMatcherRulePlugin("issuerLayer"),
|
---|
225 | new ObjectMatcherRulePlugin("assert", "assertions", value => {
|
---|
226 | if (value) {
|
---|
227 | return /** @type {any} */ (value)._isLegacyAssert !== undefined;
|
---|
228 | }
|
---|
229 |
|
---|
230 | return false;
|
---|
231 | }),
|
---|
232 | new ObjectMatcherRulePlugin("with", "assertions", value => {
|
---|
233 | if (value) {
|
---|
234 | return !(/** @type {any} */ (value)._isLegacyAssert);
|
---|
235 | }
|
---|
236 | return false;
|
---|
237 | }),
|
---|
238 | new ObjectMatcherRulePlugin("descriptionData"),
|
---|
239 | new BasicEffectRulePlugin("type"),
|
---|
240 | new BasicEffectRulePlugin("sideEffects"),
|
---|
241 | new BasicEffectRulePlugin("parser"),
|
---|
242 | new BasicEffectRulePlugin("resolve"),
|
---|
243 | new BasicEffectRulePlugin("generator"),
|
---|
244 | new BasicEffectRulePlugin("layer"),
|
---|
245 | new UseEffectRulePlugin()
|
---|
246 | ]);
|
---|
247 |
|
---|
248 | class NormalModuleFactory extends ModuleFactory {
|
---|
249 | /**
|
---|
250 | * @param {object} param params
|
---|
251 | * @param {string=} param.context context
|
---|
252 | * @param {InputFileSystem} param.fs file system
|
---|
253 | * @param {ResolverFactory} param.resolverFactory resolverFactory
|
---|
254 | * @param {ModuleOptions} param.options options
|
---|
255 | * @param {object} param.associatedObjectForCache an object to which the cache will be attached
|
---|
256 | * @param {boolean=} param.layers enable layers
|
---|
257 | */
|
---|
258 | constructor({
|
---|
259 | context,
|
---|
260 | fs,
|
---|
261 | resolverFactory,
|
---|
262 | options,
|
---|
263 | associatedObjectForCache,
|
---|
264 | layers = false
|
---|
265 | }) {
|
---|
266 | super();
|
---|
267 | this.hooks = Object.freeze({
|
---|
268 | /** @type {AsyncSeriesBailHook<[ResolveData], Module | false | void>} */
|
---|
269 | resolve: new AsyncSeriesBailHook(["resolveData"]),
|
---|
270 | /** @type {HookMap<AsyncSeriesBailHook<[ResourceDataWithData, ResolveData], true | void>>} */
|
---|
271 | resolveForScheme: new HookMap(
|
---|
272 | () => new AsyncSeriesBailHook(["resourceData", "resolveData"])
|
---|
273 | ),
|
---|
274 | /** @type {HookMap<AsyncSeriesBailHook<[ResourceDataWithData, ResolveData], true | void>>} */
|
---|
275 | resolveInScheme: new HookMap(
|
---|
276 | () => new AsyncSeriesBailHook(["resourceData", "resolveData"])
|
---|
277 | ),
|
---|
278 | /** @type {AsyncSeriesBailHook<[ResolveData], Module | undefined>} */
|
---|
279 | factorize: new AsyncSeriesBailHook(["resolveData"]),
|
---|
280 | /** @type {AsyncSeriesBailHook<[ResolveData], false | void>} */
|
---|
281 | beforeResolve: new AsyncSeriesBailHook(["resolveData"]),
|
---|
282 | /** @type {AsyncSeriesBailHook<[ResolveData], false | void>} */
|
---|
283 | afterResolve: new AsyncSeriesBailHook(["resolveData"]),
|
---|
284 | /** @type {AsyncSeriesBailHook<[ResolveData["createData"], ResolveData], Module | void>} */
|
---|
285 | createModule: new AsyncSeriesBailHook(["createData", "resolveData"]),
|
---|
286 | /** @type {SyncWaterfallHook<[Module, ResolveData["createData"], ResolveData]>} */
|
---|
287 | module: new SyncWaterfallHook(["module", "createData", "resolveData"]),
|
---|
288 | /** @type {HookMap<SyncBailHook<[ParserOptions], Parser | void>>} */
|
---|
289 | createParser: new HookMap(() => new SyncBailHook(["parserOptions"])),
|
---|
290 | /** @type {HookMap<SyncBailHook<[TODO, ParserOptions], void>>} */
|
---|
291 | parser: new HookMap(() => new SyncHook(["parser", "parserOptions"])),
|
---|
292 | /** @type {HookMap<SyncBailHook<[GeneratorOptions], Generator | void>>} */
|
---|
293 | createGenerator: new HookMap(
|
---|
294 | () => new SyncBailHook(["generatorOptions"])
|
---|
295 | ),
|
---|
296 | /** @type {HookMap<SyncBailHook<[TODO, GeneratorOptions], void>>} */
|
---|
297 | generator: new HookMap(
|
---|
298 | () => new SyncHook(["generator", "generatorOptions"])
|
---|
299 | ),
|
---|
300 | /** @type {HookMap<SyncBailHook<[TODO, ResolveData], Module | void>>} */
|
---|
301 | createModuleClass: new HookMap(
|
---|
302 | () => new SyncBailHook(["createData", "resolveData"])
|
---|
303 | )
|
---|
304 | });
|
---|
305 | this.resolverFactory = resolverFactory;
|
---|
306 | this.ruleSet = ruleSetCompiler.compile([
|
---|
307 | {
|
---|
308 | rules: options.defaultRules
|
---|
309 | },
|
---|
310 | {
|
---|
311 | rules: options.rules
|
---|
312 | }
|
---|
313 | ]);
|
---|
314 | this.context = context || "";
|
---|
315 | this.fs = fs;
|
---|
316 | this._globalParserOptions = options.parser;
|
---|
317 | this._globalGeneratorOptions = options.generator;
|
---|
318 | /** @type {Map<string, WeakMap<object, Parser>>} */
|
---|
319 | this.parserCache = new Map();
|
---|
320 | /** @type {Map<string, WeakMap<object, Generator>>} */
|
---|
321 | this.generatorCache = new Map();
|
---|
322 | /** @type {Set<Module>} */
|
---|
323 | this._restoredUnsafeCacheEntries = new Set();
|
---|
324 |
|
---|
325 | const cacheParseResource = parseResource.bindCache(
|
---|
326 | associatedObjectForCache
|
---|
327 | );
|
---|
328 | const cachedParseResourceWithoutFragment =
|
---|
329 | parseResourceWithoutFragment.bindCache(associatedObjectForCache);
|
---|
330 | this._parseResourceWithoutFragment = cachedParseResourceWithoutFragment;
|
---|
331 |
|
---|
332 | this.hooks.factorize.tapAsync(
|
---|
333 | {
|
---|
334 | name: "NormalModuleFactory",
|
---|
335 | stage: 100
|
---|
336 | },
|
---|
337 | (resolveData, callback) => {
|
---|
338 | this.hooks.resolve.callAsync(resolveData, (err, result) => {
|
---|
339 | if (err) return callback(err);
|
---|
340 |
|
---|
341 | // Ignored
|
---|
342 | if (result === false) return callback();
|
---|
343 |
|
---|
344 | // direct module
|
---|
345 | if (result instanceof Module) return callback(null, result);
|
---|
346 |
|
---|
347 | if (typeof result === "object")
|
---|
348 | throw new Error(
|
---|
349 | `${deprecationChangedHookMessage(
|
---|
350 | "resolve",
|
---|
351 | this.hooks.resolve
|
---|
352 | )} Returning a Module object will result in this module used as result.`
|
---|
353 | );
|
---|
354 |
|
---|
355 | this.hooks.afterResolve.callAsync(resolveData, (err, result) => {
|
---|
356 | if (err) return callback(err);
|
---|
357 |
|
---|
358 | if (typeof result === "object")
|
---|
359 | throw new Error(
|
---|
360 | deprecationChangedHookMessage(
|
---|
361 | "afterResolve",
|
---|
362 | this.hooks.afterResolve
|
---|
363 | )
|
---|
364 | );
|
---|
365 |
|
---|
366 | // Ignored
|
---|
367 | if (result === false) return callback();
|
---|
368 |
|
---|
369 | const createData = resolveData.createData;
|
---|
370 |
|
---|
371 | this.hooks.createModule.callAsync(
|
---|
372 | createData,
|
---|
373 | resolveData,
|
---|
374 | (err, createdModule) => {
|
---|
375 | if (!createdModule) {
|
---|
376 | if (!resolveData.request) {
|
---|
377 | return callback(new Error("Empty dependency (no request)"));
|
---|
378 | }
|
---|
379 |
|
---|
380 | // TODO webpack 6 make it required and move javascript/wasm/asset properties to own module
|
---|
381 | createdModule = this.hooks.createModuleClass
|
---|
382 | .for(
|
---|
383 | /** @type {ModuleSettings} */
|
---|
384 | (createData.settings).type
|
---|
385 | )
|
---|
386 | .call(createData, resolveData);
|
---|
387 |
|
---|
388 | if (!createdModule) {
|
---|
389 | createdModule = /** @type {Module} */ (
|
---|
390 | new NormalModule(
|
---|
391 | /** @type {NormalModuleCreateData} */
|
---|
392 | (createData)
|
---|
393 | )
|
---|
394 | );
|
---|
395 | }
|
---|
396 | }
|
---|
397 |
|
---|
398 | createdModule = this.hooks.module.call(
|
---|
399 | createdModule,
|
---|
400 | createData,
|
---|
401 | resolveData
|
---|
402 | );
|
---|
403 |
|
---|
404 | return callback(null, createdModule);
|
---|
405 | }
|
---|
406 | );
|
---|
407 | });
|
---|
408 | });
|
---|
409 | }
|
---|
410 | );
|
---|
411 | this.hooks.resolve.tapAsync(
|
---|
412 | {
|
---|
413 | name: "NormalModuleFactory",
|
---|
414 | stage: 100
|
---|
415 | },
|
---|
416 | (data, callback) => {
|
---|
417 | const {
|
---|
418 | contextInfo,
|
---|
419 | context,
|
---|
420 | dependencies,
|
---|
421 | dependencyType,
|
---|
422 | request,
|
---|
423 | assertions,
|
---|
424 | resolveOptions,
|
---|
425 | fileDependencies,
|
---|
426 | missingDependencies,
|
---|
427 | contextDependencies
|
---|
428 | } = data;
|
---|
429 | const loaderResolver = this.getResolver("loader");
|
---|
430 |
|
---|
431 | /** @type {ResourceData | undefined} */
|
---|
432 | let matchResourceData;
|
---|
433 | /** @type {string} */
|
---|
434 | let unresolvedResource;
|
---|
435 | /** @type {ParsedLoaderRequest[]} */
|
---|
436 | let elements;
|
---|
437 | let noPreAutoLoaders = false;
|
---|
438 | let noAutoLoaders = false;
|
---|
439 | let noPrePostAutoLoaders = false;
|
---|
440 |
|
---|
441 | const contextScheme = getScheme(context);
|
---|
442 | /** @type {string | undefined} */
|
---|
443 | let scheme = getScheme(request);
|
---|
444 |
|
---|
445 | if (!scheme) {
|
---|
446 | /** @type {string} */
|
---|
447 | let requestWithoutMatchResource = request;
|
---|
448 | const matchResourceMatch = MATCH_RESOURCE_REGEX.exec(request);
|
---|
449 | if (matchResourceMatch) {
|
---|
450 | let matchResource = matchResourceMatch[1];
|
---|
451 | if (matchResource.charCodeAt(0) === 46) {
|
---|
452 | // 46 === ".", 47 === "/"
|
---|
453 | const secondChar = matchResource.charCodeAt(1);
|
---|
454 | if (
|
---|
455 | secondChar === 47 ||
|
---|
456 | (secondChar === 46 && matchResource.charCodeAt(2) === 47)
|
---|
457 | ) {
|
---|
458 | // if matchResources startsWith ../ or ./
|
---|
459 | matchResource = join(this.fs, context, matchResource);
|
---|
460 | }
|
---|
461 | }
|
---|
462 | matchResourceData = {
|
---|
463 | resource: matchResource,
|
---|
464 | ...cacheParseResource(matchResource)
|
---|
465 | };
|
---|
466 | requestWithoutMatchResource = request.slice(
|
---|
467 | matchResourceMatch[0].length
|
---|
468 | );
|
---|
469 | }
|
---|
470 |
|
---|
471 | scheme = getScheme(requestWithoutMatchResource);
|
---|
472 |
|
---|
473 | if (!scheme && !contextScheme) {
|
---|
474 | const firstChar = requestWithoutMatchResource.charCodeAt(0);
|
---|
475 | const secondChar = requestWithoutMatchResource.charCodeAt(1);
|
---|
476 | noPreAutoLoaders = firstChar === 45 && secondChar === 33; // startsWith "-!"
|
---|
477 | noAutoLoaders = noPreAutoLoaders || firstChar === 33; // startsWith "!"
|
---|
478 | noPrePostAutoLoaders = firstChar === 33 && secondChar === 33; // startsWith "!!";
|
---|
479 | const rawElements = requestWithoutMatchResource
|
---|
480 | .slice(
|
---|
481 | noPreAutoLoaders || noPrePostAutoLoaders
|
---|
482 | ? 2
|
---|
483 | : noAutoLoaders
|
---|
484 | ? 1
|
---|
485 | : 0
|
---|
486 | )
|
---|
487 | .split(/!+/);
|
---|
488 | unresolvedResource = /** @type {string} */ (rawElements.pop());
|
---|
489 | elements = rawElements.map(el => {
|
---|
490 | const { path, query } = cachedParseResourceWithoutFragment(el);
|
---|
491 | return {
|
---|
492 | loader: path,
|
---|
493 | options: query ? query.slice(1) : undefined
|
---|
494 | };
|
---|
495 | });
|
---|
496 | scheme = getScheme(unresolvedResource);
|
---|
497 | } else {
|
---|
498 | unresolvedResource = requestWithoutMatchResource;
|
---|
499 | elements = EMPTY_ELEMENTS;
|
---|
500 | }
|
---|
501 | } else {
|
---|
502 | unresolvedResource = request;
|
---|
503 | elements = EMPTY_ELEMENTS;
|
---|
504 | }
|
---|
505 |
|
---|
506 | /** @type {ResolveContext} */
|
---|
507 | const resolveContext = {
|
---|
508 | fileDependencies,
|
---|
509 | missingDependencies,
|
---|
510 | contextDependencies
|
---|
511 | };
|
---|
512 |
|
---|
513 | /** @type {ResourceDataWithData} */
|
---|
514 | let resourceData;
|
---|
515 |
|
---|
516 | /** @type {undefined | LoaderItem[]} */
|
---|
517 | let loaders;
|
---|
518 |
|
---|
519 | const continueCallback = needCalls(2, err => {
|
---|
520 | if (err) return callback(err);
|
---|
521 |
|
---|
522 | // translate option idents
|
---|
523 | try {
|
---|
524 | for (const item of /** @type {LoaderItem[]} */ (loaders)) {
|
---|
525 | if (typeof item.options === "string" && item.options[0] === "?") {
|
---|
526 | const ident = item.options.slice(1);
|
---|
527 | if (ident === "[[missing ident]]") {
|
---|
528 | throw new Error(
|
---|
529 | "No ident is provided by referenced loader. " +
|
---|
530 | "When using a function for Rule.use in config you need to " +
|
---|
531 | "provide an 'ident' property for referenced loader options."
|
---|
532 | );
|
---|
533 | }
|
---|
534 | item.options = this.ruleSet.references.get(ident);
|
---|
535 | if (item.options === undefined) {
|
---|
536 | throw new Error(
|
---|
537 | "Invalid ident is provided by referenced loader"
|
---|
538 | );
|
---|
539 | }
|
---|
540 | item.ident = ident;
|
---|
541 | }
|
---|
542 | }
|
---|
543 | } catch (identErr) {
|
---|
544 | return callback(/** @type {Error} */ (identErr));
|
---|
545 | }
|
---|
546 |
|
---|
547 | if (!resourceData) {
|
---|
548 | // ignored
|
---|
549 | return callback(null, dependencies[0].createIgnoredModule(context));
|
---|
550 | }
|
---|
551 |
|
---|
552 | const userRequest =
|
---|
553 | (matchResourceData !== undefined
|
---|
554 | ? `${matchResourceData.resource}!=!`
|
---|
555 | : "") +
|
---|
556 | stringifyLoadersAndResource(
|
---|
557 | /** @type {LoaderItem[]} */ (loaders),
|
---|
558 | resourceData.resource
|
---|
559 | );
|
---|
560 |
|
---|
561 | /** @type {ModuleSettings} */
|
---|
562 | const settings = {};
|
---|
563 | const useLoadersPost = [];
|
---|
564 | const useLoaders = [];
|
---|
565 | const useLoadersPre = [];
|
---|
566 |
|
---|
567 | // handle .webpack[] suffix
|
---|
568 | let resource;
|
---|
569 | let match;
|
---|
570 | if (
|
---|
571 | matchResourceData &&
|
---|
572 | typeof (resource = matchResourceData.resource) === "string" &&
|
---|
573 | (match = /\.webpack\[([^\]]+)\]$/.exec(resource))
|
---|
574 | ) {
|
---|
575 | settings.type = match[1];
|
---|
576 | matchResourceData.resource = matchResourceData.resource.slice(
|
---|
577 | 0,
|
---|
578 | -settings.type.length - 10
|
---|
579 | );
|
---|
580 | } else {
|
---|
581 | settings.type = JAVASCRIPT_MODULE_TYPE_AUTO;
|
---|
582 | const resourceDataForRules = matchResourceData || resourceData;
|
---|
583 | const result = this.ruleSet.exec({
|
---|
584 | resource: resourceDataForRules.path,
|
---|
585 | realResource: resourceData.path,
|
---|
586 | resourceQuery: resourceDataForRules.query,
|
---|
587 | resourceFragment: resourceDataForRules.fragment,
|
---|
588 | scheme,
|
---|
589 | assertions,
|
---|
590 | mimetype: matchResourceData
|
---|
591 | ? ""
|
---|
592 | : resourceData.data.mimetype || "",
|
---|
593 | dependency: dependencyType,
|
---|
594 | descriptionData: matchResourceData
|
---|
595 | ? undefined
|
---|
596 | : resourceData.data.descriptionFileData,
|
---|
597 | issuer: contextInfo.issuer,
|
---|
598 | compiler: contextInfo.compiler,
|
---|
599 | issuerLayer: contextInfo.issuerLayer || ""
|
---|
600 | });
|
---|
601 | for (const r of result) {
|
---|
602 | // https://github.com/webpack/webpack/issues/16466
|
---|
603 | // if a request exists PrePostAutoLoaders, should disable modifying Rule.type
|
---|
604 | if (r.type === "type" && noPrePostAutoLoaders) {
|
---|
605 | continue;
|
---|
606 | }
|
---|
607 | if (r.type === "use") {
|
---|
608 | if (!noAutoLoaders && !noPrePostAutoLoaders) {
|
---|
609 | useLoaders.push(r.value);
|
---|
610 | }
|
---|
611 | } else if (r.type === "use-post") {
|
---|
612 | if (!noPrePostAutoLoaders) {
|
---|
613 | useLoadersPost.push(r.value);
|
---|
614 | }
|
---|
615 | } else if (r.type === "use-pre") {
|
---|
616 | if (!noPreAutoLoaders && !noPrePostAutoLoaders) {
|
---|
617 | useLoadersPre.push(r.value);
|
---|
618 | }
|
---|
619 | } else if (
|
---|
620 | typeof r.value === "object" &&
|
---|
621 | r.value !== null &&
|
---|
622 | typeof settings[
|
---|
623 | /** @type {keyof ModuleSettings} */ (r.type)
|
---|
624 | ] === "object" &&
|
---|
625 | settings[/** @type {keyof ModuleSettings} */ (r.type)] !== null
|
---|
626 | ) {
|
---|
627 | settings[r.type] = cachedCleverMerge(
|
---|
628 | settings[/** @type {keyof ModuleSettings} */ (r.type)],
|
---|
629 | r.value
|
---|
630 | );
|
---|
631 | } else {
|
---|
632 | settings[r.type] = r.value;
|
---|
633 | }
|
---|
634 | }
|
---|
635 | }
|
---|
636 |
|
---|
637 | /** @type {undefined | LoaderItem[]} */
|
---|
638 | let postLoaders;
|
---|
639 | /** @type {undefined | LoaderItem[]} */
|
---|
640 | let normalLoaders;
|
---|
641 | /** @type {undefined | LoaderItem[]} */
|
---|
642 | let preLoaders;
|
---|
643 |
|
---|
644 | const continueCallback = needCalls(3, err => {
|
---|
645 | if (err) {
|
---|
646 | return callback(err);
|
---|
647 | }
|
---|
648 | const allLoaders = /** @type {LoaderItem[]} */ (postLoaders);
|
---|
649 | if (matchResourceData === undefined) {
|
---|
650 | for (const loader of /** @type {LoaderItem[]} */ (loaders))
|
---|
651 | allLoaders.push(loader);
|
---|
652 | for (const loader of /** @type {LoaderItem[]} */ (normalLoaders))
|
---|
653 | allLoaders.push(loader);
|
---|
654 | } else {
|
---|
655 | for (const loader of /** @type {LoaderItem[]} */ (normalLoaders))
|
---|
656 | allLoaders.push(loader);
|
---|
657 | for (const loader of /** @type {LoaderItem[]} */ (loaders))
|
---|
658 | allLoaders.push(loader);
|
---|
659 | }
|
---|
660 | for (const loader of /** @type {LoaderItem[]} */ (preLoaders))
|
---|
661 | allLoaders.push(loader);
|
---|
662 | const type = /** @type {string} */ (settings.type);
|
---|
663 | const resolveOptions = settings.resolve;
|
---|
664 | const layer = settings.layer;
|
---|
665 | if (layer !== undefined && !layers) {
|
---|
666 | return callback(
|
---|
667 | new Error(
|
---|
668 | "'Rule.layer' is only allowed when 'experiments.layers' is enabled"
|
---|
669 | )
|
---|
670 | );
|
---|
671 | }
|
---|
672 | try {
|
---|
673 | Object.assign(data.createData, {
|
---|
674 | layer:
|
---|
675 | layer === undefined ? contextInfo.issuerLayer || null : layer,
|
---|
676 | request: stringifyLoadersAndResource(
|
---|
677 | allLoaders,
|
---|
678 | resourceData.resource
|
---|
679 | ),
|
---|
680 | userRequest,
|
---|
681 | rawRequest: request,
|
---|
682 | loaders: allLoaders,
|
---|
683 | resource: resourceData.resource,
|
---|
684 | context:
|
---|
685 | resourceData.context || getContext(resourceData.resource),
|
---|
686 | matchResource: matchResourceData
|
---|
687 | ? matchResourceData.resource
|
---|
688 | : undefined,
|
---|
689 | resourceResolveData: resourceData.data,
|
---|
690 | settings,
|
---|
691 | type,
|
---|
692 | parser: this.getParser(type, settings.parser),
|
---|
693 | parserOptions: settings.parser,
|
---|
694 | generator: this.getGenerator(type, settings.generator),
|
---|
695 | generatorOptions: settings.generator,
|
---|
696 | resolveOptions
|
---|
697 | });
|
---|
698 | } catch (createDataErr) {
|
---|
699 | return callback(/** @type {Error} */ (createDataErr));
|
---|
700 | }
|
---|
701 | callback();
|
---|
702 | });
|
---|
703 | this.resolveRequestArray(
|
---|
704 | contextInfo,
|
---|
705 | this.context,
|
---|
706 | useLoadersPost,
|
---|
707 | loaderResolver,
|
---|
708 | resolveContext,
|
---|
709 | (err, result) => {
|
---|
710 | postLoaders = result;
|
---|
711 | continueCallback(err);
|
---|
712 | }
|
---|
713 | );
|
---|
714 | this.resolveRequestArray(
|
---|
715 | contextInfo,
|
---|
716 | this.context,
|
---|
717 | useLoaders,
|
---|
718 | loaderResolver,
|
---|
719 | resolveContext,
|
---|
720 | (err, result) => {
|
---|
721 | normalLoaders = result;
|
---|
722 | continueCallback(err);
|
---|
723 | }
|
---|
724 | );
|
---|
725 | this.resolveRequestArray(
|
---|
726 | contextInfo,
|
---|
727 | this.context,
|
---|
728 | useLoadersPre,
|
---|
729 | loaderResolver,
|
---|
730 | resolveContext,
|
---|
731 | (err, result) => {
|
---|
732 | preLoaders = result;
|
---|
733 | continueCallback(err);
|
---|
734 | }
|
---|
735 | );
|
---|
736 | });
|
---|
737 |
|
---|
738 | this.resolveRequestArray(
|
---|
739 | contextInfo,
|
---|
740 | contextScheme ? this.context : context,
|
---|
741 | /** @type {LoaderItem[]} */ (elements),
|
---|
742 | loaderResolver,
|
---|
743 | resolveContext,
|
---|
744 | (err, result) => {
|
---|
745 | if (err) return continueCallback(err);
|
---|
746 | loaders = result;
|
---|
747 | continueCallback();
|
---|
748 | }
|
---|
749 | );
|
---|
750 |
|
---|
751 | /**
|
---|
752 | * @param {string} context context
|
---|
753 | */
|
---|
754 | const defaultResolve = context => {
|
---|
755 | if (/^($|\?)/.test(unresolvedResource)) {
|
---|
756 | resourceData = {
|
---|
757 | resource: unresolvedResource,
|
---|
758 | data: {},
|
---|
759 | ...cacheParseResource(unresolvedResource)
|
---|
760 | };
|
---|
761 | continueCallback();
|
---|
762 | }
|
---|
763 |
|
---|
764 | // resource without scheme and with path
|
---|
765 | else {
|
---|
766 | const normalResolver = this.getResolver(
|
---|
767 | "normal",
|
---|
768 | dependencyType
|
---|
769 | ? cachedSetProperty(
|
---|
770 | resolveOptions || EMPTY_RESOLVE_OPTIONS,
|
---|
771 | "dependencyType",
|
---|
772 | dependencyType
|
---|
773 | )
|
---|
774 | : resolveOptions
|
---|
775 | );
|
---|
776 | this.resolveResource(
|
---|
777 | contextInfo,
|
---|
778 | context,
|
---|
779 | unresolvedResource,
|
---|
780 | normalResolver,
|
---|
781 | resolveContext,
|
---|
782 | (err, _resolvedResource, resolvedResourceResolveData) => {
|
---|
783 | if (err) return continueCallback(err);
|
---|
784 | if (_resolvedResource !== false) {
|
---|
785 | const resolvedResource =
|
---|
786 | /** @type {string} */
|
---|
787 | (_resolvedResource);
|
---|
788 | resourceData = {
|
---|
789 | resource: resolvedResource,
|
---|
790 | data:
|
---|
791 | /** @type {ResolveRequest} */
|
---|
792 | (resolvedResourceResolveData),
|
---|
793 | ...cacheParseResource(resolvedResource)
|
---|
794 | };
|
---|
795 | }
|
---|
796 | continueCallback();
|
---|
797 | }
|
---|
798 | );
|
---|
799 | }
|
---|
800 | };
|
---|
801 |
|
---|
802 | // resource with scheme
|
---|
803 | if (scheme) {
|
---|
804 | resourceData = {
|
---|
805 | resource: unresolvedResource,
|
---|
806 | data: {},
|
---|
807 | path: undefined,
|
---|
808 | query: undefined,
|
---|
809 | fragment: undefined,
|
---|
810 | context: undefined
|
---|
811 | };
|
---|
812 | this.hooks.resolveForScheme
|
---|
813 | .for(scheme)
|
---|
814 | .callAsync(resourceData, data, err => {
|
---|
815 | if (err) return continueCallback(err);
|
---|
816 | continueCallback();
|
---|
817 | });
|
---|
818 | }
|
---|
819 |
|
---|
820 | // resource within scheme
|
---|
821 | else if (contextScheme) {
|
---|
822 | resourceData = {
|
---|
823 | resource: unresolvedResource,
|
---|
824 | data: {},
|
---|
825 | path: undefined,
|
---|
826 | query: undefined,
|
---|
827 | fragment: undefined,
|
---|
828 | context: undefined
|
---|
829 | };
|
---|
830 | this.hooks.resolveInScheme
|
---|
831 | .for(contextScheme)
|
---|
832 | .callAsync(resourceData, data, (err, handled) => {
|
---|
833 | if (err) return continueCallback(err);
|
---|
834 | if (!handled) return defaultResolve(this.context);
|
---|
835 | continueCallback();
|
---|
836 | });
|
---|
837 | }
|
---|
838 |
|
---|
839 | // resource without scheme and without path
|
---|
840 | else defaultResolve(context);
|
---|
841 | }
|
---|
842 | );
|
---|
843 | }
|
---|
844 |
|
---|
845 | cleanupForCache() {
|
---|
846 | for (const module of this._restoredUnsafeCacheEntries) {
|
---|
847 | ChunkGraph.clearChunkGraphForModule(module);
|
---|
848 | ModuleGraph.clearModuleGraphForModule(module);
|
---|
849 | module.cleanupForCache();
|
---|
850 | }
|
---|
851 | }
|
---|
852 |
|
---|
853 | /**
|
---|
854 | * @param {ModuleFactoryCreateData} data data object
|
---|
855 | * @param {function((Error | null)=, ModuleFactoryResult=): void} callback callback
|
---|
856 | * @returns {void}
|
---|
857 | */
|
---|
858 | create(data, callback) {
|
---|
859 | const dependencies = /** @type {ModuleDependency[]} */ (data.dependencies);
|
---|
860 | const context = data.context || this.context;
|
---|
861 | const resolveOptions = data.resolveOptions || EMPTY_RESOLVE_OPTIONS;
|
---|
862 | const dependency = dependencies[0];
|
---|
863 | const request = dependency.request;
|
---|
864 | const assertions = dependency.assertions;
|
---|
865 | const contextInfo = data.contextInfo;
|
---|
866 | const fileDependencies = new LazySet();
|
---|
867 | const missingDependencies = new LazySet();
|
---|
868 | const contextDependencies = new LazySet();
|
---|
869 | const dependencyType =
|
---|
870 | (dependencies.length > 0 && dependencies[0].category) || "";
|
---|
871 | /** @type {ResolveData} */
|
---|
872 | const resolveData = {
|
---|
873 | contextInfo,
|
---|
874 | resolveOptions,
|
---|
875 | context,
|
---|
876 | request,
|
---|
877 | assertions,
|
---|
878 | dependencies,
|
---|
879 | dependencyType,
|
---|
880 | fileDependencies,
|
---|
881 | missingDependencies,
|
---|
882 | contextDependencies,
|
---|
883 | createData: {},
|
---|
884 | cacheable: true
|
---|
885 | };
|
---|
886 | this.hooks.beforeResolve.callAsync(resolveData, (err, result) => {
|
---|
887 | if (err) {
|
---|
888 | return callback(err, {
|
---|
889 | fileDependencies,
|
---|
890 | missingDependencies,
|
---|
891 | contextDependencies,
|
---|
892 | cacheable: false
|
---|
893 | });
|
---|
894 | }
|
---|
895 |
|
---|
896 | // Ignored
|
---|
897 | if (result === false) {
|
---|
898 | /** @type {ModuleFactoryResult} * */
|
---|
899 | const factoryResult = {
|
---|
900 | fileDependencies,
|
---|
901 | missingDependencies,
|
---|
902 | contextDependencies,
|
---|
903 | cacheable: resolveData.cacheable
|
---|
904 | };
|
---|
905 |
|
---|
906 | if (resolveData.ignoredModule) {
|
---|
907 | factoryResult.module = resolveData.ignoredModule;
|
---|
908 | }
|
---|
909 |
|
---|
910 | return callback(null, factoryResult);
|
---|
911 | }
|
---|
912 |
|
---|
913 | if (typeof result === "object")
|
---|
914 | throw new Error(
|
---|
915 | deprecationChangedHookMessage(
|
---|
916 | "beforeResolve",
|
---|
917 | this.hooks.beforeResolve
|
---|
918 | )
|
---|
919 | );
|
---|
920 |
|
---|
921 | this.hooks.factorize.callAsync(resolveData, (err, module) => {
|
---|
922 | if (err) {
|
---|
923 | return callback(err, {
|
---|
924 | fileDependencies,
|
---|
925 | missingDependencies,
|
---|
926 | contextDependencies,
|
---|
927 | cacheable: false
|
---|
928 | });
|
---|
929 | }
|
---|
930 |
|
---|
931 | /** @type {ModuleFactoryResult} * */
|
---|
932 | const factoryResult = {
|
---|
933 | module,
|
---|
934 | fileDependencies,
|
---|
935 | missingDependencies,
|
---|
936 | contextDependencies,
|
---|
937 | cacheable: resolveData.cacheable
|
---|
938 | };
|
---|
939 |
|
---|
940 | callback(null, factoryResult);
|
---|
941 | });
|
---|
942 | });
|
---|
943 | }
|
---|
944 |
|
---|
945 | /**
|
---|
946 | * @param {ModuleFactoryCreateDataContextInfo} contextInfo context info
|
---|
947 | * @param {string} context context
|
---|
948 | * @param {string} unresolvedResource unresolved resource
|
---|
949 | * @param {ResolverWithOptions} resolver resolver
|
---|
950 | * @param {ResolveContext} resolveContext resolver context
|
---|
951 | * @param {(err: null | Error, res?: string | false, req?: ResolveRequest) => void} callback callback
|
---|
952 | */
|
---|
953 | resolveResource(
|
---|
954 | contextInfo,
|
---|
955 | context,
|
---|
956 | unresolvedResource,
|
---|
957 | resolver,
|
---|
958 | resolveContext,
|
---|
959 | callback
|
---|
960 | ) {
|
---|
961 | resolver.resolve(
|
---|
962 | contextInfo,
|
---|
963 | context,
|
---|
964 | unresolvedResource,
|
---|
965 | resolveContext,
|
---|
966 | (err, resolvedResource, resolvedResourceResolveData) => {
|
---|
967 | if (err) {
|
---|
968 | return this._resolveResourceErrorHints(
|
---|
969 | err,
|
---|
970 | contextInfo,
|
---|
971 | context,
|
---|
972 | unresolvedResource,
|
---|
973 | resolver,
|
---|
974 | resolveContext,
|
---|
975 | (err2, hints) => {
|
---|
976 | if (err2) {
|
---|
977 | err.message += `
|
---|
978 | A fatal error happened during resolving additional hints for this error: ${err2.message}`;
|
---|
979 | err.stack += `
|
---|
980 |
|
---|
981 | A fatal error happened during resolving additional hints for this error:
|
---|
982 | ${err2.stack}`;
|
---|
983 | return callback(err);
|
---|
984 | }
|
---|
985 | if (hints && hints.length > 0) {
|
---|
986 | err.message += `
|
---|
987 | ${hints.join("\n\n")}`;
|
---|
988 | }
|
---|
989 |
|
---|
990 | // Check if the extension is missing a leading dot (e.g. "js" instead of ".js")
|
---|
991 | let appendResolveExtensionsHint = false;
|
---|
992 | const specifiedExtensions = Array.from(
|
---|
993 | resolver.options.extensions
|
---|
994 | );
|
---|
995 | const expectedExtensions = specifiedExtensions.map(extension => {
|
---|
996 | if (LEADING_DOT_EXTENSION_REGEX.test(extension)) {
|
---|
997 | appendResolveExtensionsHint = true;
|
---|
998 | return `.${extension}`;
|
---|
999 | }
|
---|
1000 | return extension;
|
---|
1001 | });
|
---|
1002 | if (appendResolveExtensionsHint) {
|
---|
1003 | err.message += `\nDid you miss the leading dot in 'resolve.extensions'? Did you mean '${JSON.stringify(
|
---|
1004 | expectedExtensions
|
---|
1005 | )}' instead of '${JSON.stringify(specifiedExtensions)}'?`;
|
---|
1006 | }
|
---|
1007 |
|
---|
1008 | callback(err);
|
---|
1009 | }
|
---|
1010 | );
|
---|
1011 | }
|
---|
1012 | callback(err, resolvedResource, resolvedResourceResolveData);
|
---|
1013 | }
|
---|
1014 | );
|
---|
1015 | }
|
---|
1016 |
|
---|
1017 | /**
|
---|
1018 | * @param {Error} error error
|
---|
1019 | * @param {ModuleFactoryCreateDataContextInfo} contextInfo context info
|
---|
1020 | * @param {string} context context
|
---|
1021 | * @param {string} unresolvedResource unresolved resource
|
---|
1022 | * @param {ResolverWithOptions} resolver resolver
|
---|
1023 | * @param {ResolveContext} resolveContext resolver context
|
---|
1024 | * @param {Callback<string[]>} callback callback
|
---|
1025 | * @private
|
---|
1026 | */
|
---|
1027 | _resolveResourceErrorHints(
|
---|
1028 | error,
|
---|
1029 | contextInfo,
|
---|
1030 | context,
|
---|
1031 | unresolvedResource,
|
---|
1032 | resolver,
|
---|
1033 | resolveContext,
|
---|
1034 | callback
|
---|
1035 | ) {
|
---|
1036 | asyncLib.parallel(
|
---|
1037 | [
|
---|
1038 | callback => {
|
---|
1039 | if (!resolver.options.fullySpecified) return callback();
|
---|
1040 | resolver
|
---|
1041 | .withOptions({
|
---|
1042 | fullySpecified: false
|
---|
1043 | })
|
---|
1044 | .resolve(
|
---|
1045 | contextInfo,
|
---|
1046 | context,
|
---|
1047 | unresolvedResource,
|
---|
1048 | resolveContext,
|
---|
1049 | (err, resolvedResource) => {
|
---|
1050 | if (!err && resolvedResource) {
|
---|
1051 | const resource = parseResource(resolvedResource).path.replace(
|
---|
1052 | /^.*[\\/]/,
|
---|
1053 | ""
|
---|
1054 | );
|
---|
1055 | return callback(
|
---|
1056 | null,
|
---|
1057 | `Did you mean '${resource}'?
|
---|
1058 | BREAKING CHANGE: The request '${unresolvedResource}' failed to resolve only because it was resolved as fully specified
|
---|
1059 | (probably because the origin is strict EcmaScript Module, e. g. a module with javascript mimetype, a '*.mjs' file, or a '*.js' file where the package.json contains '"type": "module"').
|
---|
1060 | The extension in the request is mandatory for it to be fully specified.
|
---|
1061 | Add the extension to the request.`
|
---|
1062 | );
|
---|
1063 | }
|
---|
1064 | callback();
|
---|
1065 | }
|
---|
1066 | );
|
---|
1067 | },
|
---|
1068 | callback => {
|
---|
1069 | if (!resolver.options.enforceExtension) return callback();
|
---|
1070 | resolver
|
---|
1071 | .withOptions({
|
---|
1072 | enforceExtension: false,
|
---|
1073 | extensions: []
|
---|
1074 | })
|
---|
1075 | .resolve(
|
---|
1076 | contextInfo,
|
---|
1077 | context,
|
---|
1078 | unresolvedResource,
|
---|
1079 | resolveContext,
|
---|
1080 | (err, resolvedResource) => {
|
---|
1081 | if (!err && resolvedResource) {
|
---|
1082 | let hint = "";
|
---|
1083 | const match = /(\.[^.]+)(\?|$)/.exec(unresolvedResource);
|
---|
1084 | if (match) {
|
---|
1085 | const fixedRequest = unresolvedResource.replace(
|
---|
1086 | /(\.[^.]+)(\?|$)/,
|
---|
1087 | "$2"
|
---|
1088 | );
|
---|
1089 | hint = resolver.options.extensions.has(match[1])
|
---|
1090 | ? `Did you mean '${fixedRequest}'?`
|
---|
1091 | : `Did you mean '${fixedRequest}'? Also note that '${match[1]}' is not in 'resolve.extensions' yet and need to be added for this to work?`;
|
---|
1092 | } else {
|
---|
1093 | hint =
|
---|
1094 | "Did you mean to omit the extension or to remove 'resolve.enforceExtension'?";
|
---|
1095 | }
|
---|
1096 | return callback(
|
---|
1097 | null,
|
---|
1098 | `The request '${unresolvedResource}' failed to resolve only because 'resolve.enforceExtension' was specified.
|
---|
1099 | ${hint}
|
---|
1100 | Including the extension in the request is no longer possible. Did you mean to enforce including the extension in requests with 'resolve.extensions: []' instead?`
|
---|
1101 | );
|
---|
1102 | }
|
---|
1103 | callback();
|
---|
1104 | }
|
---|
1105 | );
|
---|
1106 | },
|
---|
1107 | callback => {
|
---|
1108 | if (
|
---|
1109 | /^\.\.?\//.test(unresolvedResource) ||
|
---|
1110 | resolver.options.preferRelative
|
---|
1111 | ) {
|
---|
1112 | return callback();
|
---|
1113 | }
|
---|
1114 | resolver.resolve(
|
---|
1115 | contextInfo,
|
---|
1116 | context,
|
---|
1117 | `./${unresolvedResource}`,
|
---|
1118 | resolveContext,
|
---|
1119 | (err, resolvedResource) => {
|
---|
1120 | if (err || !resolvedResource) return callback();
|
---|
1121 | const moduleDirectories = resolver.options.modules
|
---|
1122 | .map(m => (Array.isArray(m) ? m.join(", ") : m))
|
---|
1123 | .join(", ");
|
---|
1124 | callback(
|
---|
1125 | null,
|
---|
1126 | `Did you mean './${unresolvedResource}'?
|
---|
1127 | Requests that should resolve in the current directory need to start with './'.
|
---|
1128 | Requests that start with a name are treated as module requests and resolve within module directories (${moduleDirectories}).
|
---|
1129 | If changing the source code is not an option there is also a resolve options called 'preferRelative' which tries to resolve these kind of requests in the current directory too.`
|
---|
1130 | );
|
---|
1131 | }
|
---|
1132 | );
|
---|
1133 | }
|
---|
1134 | ],
|
---|
1135 | (err, hints) => {
|
---|
1136 | if (err) return callback(err);
|
---|
1137 | callback(null, /** @type {string[]} */ (hints).filter(Boolean));
|
---|
1138 | }
|
---|
1139 | );
|
---|
1140 | }
|
---|
1141 |
|
---|
1142 | /**
|
---|
1143 | * @param {ModuleFactoryCreateDataContextInfo} contextInfo context info
|
---|
1144 | * @param {string} context context
|
---|
1145 | * @param {LoaderItem[]} array array
|
---|
1146 | * @param {ResolverWithOptions} resolver resolver
|
---|
1147 | * @param {ResolveContext} resolveContext resolve context
|
---|
1148 | * @param {Callback<LoaderItem[]>} callback callback
|
---|
1149 | * @returns {void} result
|
---|
1150 | */
|
---|
1151 | resolveRequestArray(
|
---|
1152 | contextInfo,
|
---|
1153 | context,
|
---|
1154 | array,
|
---|
1155 | resolver,
|
---|
1156 | resolveContext,
|
---|
1157 | callback
|
---|
1158 | ) {
|
---|
1159 | // LoaderItem
|
---|
1160 | if (array.length === 0) return callback(null, array);
|
---|
1161 | asyncLib.map(
|
---|
1162 | array,
|
---|
1163 | (item, callback) => {
|
---|
1164 | resolver.resolve(
|
---|
1165 | contextInfo,
|
---|
1166 | context,
|
---|
1167 | item.loader,
|
---|
1168 | resolveContext,
|
---|
1169 | (err, result, resolveRequest) => {
|
---|
1170 | if (
|
---|
1171 | err &&
|
---|
1172 | /^[^/]*$/.test(item.loader) &&
|
---|
1173 | !item.loader.endsWith("-loader")
|
---|
1174 | ) {
|
---|
1175 | return resolver.resolve(
|
---|
1176 | contextInfo,
|
---|
1177 | context,
|
---|
1178 | `${item.loader}-loader`,
|
---|
1179 | resolveContext,
|
---|
1180 | err2 => {
|
---|
1181 | if (!err2) {
|
---|
1182 | err.message =
|
---|
1183 | `${err.message}\n` +
|
---|
1184 | "BREAKING CHANGE: It's no longer allowed to omit the '-loader' suffix when using loaders.\n" +
|
---|
1185 | ` You need to specify '${item.loader}-loader' instead of '${item.loader}',\n` +
|
---|
1186 | " see https://webpack.js.org/migrate/3/#automatic-loader-module-name-extension-removed";
|
---|
1187 | }
|
---|
1188 | callback(err);
|
---|
1189 | }
|
---|
1190 | );
|
---|
1191 | }
|
---|
1192 | if (err) return callback(err);
|
---|
1193 |
|
---|
1194 | const parsedResult = this._parseResourceWithoutFragment(
|
---|
1195 | /** @type {string} */ (result)
|
---|
1196 | );
|
---|
1197 |
|
---|
1198 | const type = /\.mjs$/i.test(parsedResult.path)
|
---|
1199 | ? "module"
|
---|
1200 | : /\.cjs$/i.test(parsedResult.path)
|
---|
1201 | ? "commonjs"
|
---|
1202 | : /** @type {ResolveRequest} */
|
---|
1203 | (resolveRequest).descriptionFileData === undefined
|
---|
1204 | ? undefined
|
---|
1205 | : /** @type {ResolveRequest} */
|
---|
1206 | (resolveRequest).descriptionFileData.type;
|
---|
1207 | const resolved = {
|
---|
1208 | loader: parsedResult.path,
|
---|
1209 | type,
|
---|
1210 | options:
|
---|
1211 | item.options === undefined
|
---|
1212 | ? parsedResult.query
|
---|
1213 | ? parsedResult.query.slice(1)
|
---|
1214 | : undefined
|
---|
1215 | : item.options,
|
---|
1216 | ident:
|
---|
1217 | item.options === undefined
|
---|
1218 | ? undefined
|
---|
1219 | : /** @type {string} */ (item.ident)
|
---|
1220 | };
|
---|
1221 |
|
---|
1222 | return callback(null, /** @type {LoaderItem} */ (resolved));
|
---|
1223 | }
|
---|
1224 | );
|
---|
1225 | },
|
---|
1226 | /** @type {Callback<TODO>} */ (callback)
|
---|
1227 | );
|
---|
1228 | }
|
---|
1229 |
|
---|
1230 | /**
|
---|
1231 | * @param {string} type type
|
---|
1232 | * @param {ParserOptions} parserOptions parser options
|
---|
1233 | * @returns {Parser} parser
|
---|
1234 | */
|
---|
1235 | getParser(type, parserOptions = EMPTY_PARSER_OPTIONS) {
|
---|
1236 | let cache = this.parserCache.get(type);
|
---|
1237 |
|
---|
1238 | if (cache === undefined) {
|
---|
1239 | cache = new WeakMap();
|
---|
1240 | this.parserCache.set(type, cache);
|
---|
1241 | }
|
---|
1242 |
|
---|
1243 | let parser = cache.get(parserOptions);
|
---|
1244 |
|
---|
1245 | if (parser === undefined) {
|
---|
1246 | parser = this.createParser(type, parserOptions);
|
---|
1247 | cache.set(parserOptions, parser);
|
---|
1248 | }
|
---|
1249 |
|
---|
1250 | return parser;
|
---|
1251 | }
|
---|
1252 |
|
---|
1253 | /**
|
---|
1254 | * @param {string} type type
|
---|
1255 | * @param {ParserOptions} parserOptions parser options
|
---|
1256 | * @returns {Parser} parser
|
---|
1257 | */
|
---|
1258 | createParser(type, parserOptions = {}) {
|
---|
1259 | parserOptions = mergeGlobalOptions(
|
---|
1260 | this._globalParserOptions,
|
---|
1261 | type,
|
---|
1262 | parserOptions
|
---|
1263 | );
|
---|
1264 | const parser = this.hooks.createParser.for(type).call(parserOptions);
|
---|
1265 | if (!parser) {
|
---|
1266 | throw new Error(`No parser registered for ${type}`);
|
---|
1267 | }
|
---|
1268 | this.hooks.parser.for(type).call(parser, parserOptions);
|
---|
1269 | return parser;
|
---|
1270 | }
|
---|
1271 |
|
---|
1272 | /**
|
---|
1273 | * @param {string} type type of generator
|
---|
1274 | * @param {GeneratorOptions} generatorOptions generator options
|
---|
1275 | * @returns {Generator} generator
|
---|
1276 | */
|
---|
1277 | getGenerator(type, generatorOptions = EMPTY_GENERATOR_OPTIONS) {
|
---|
1278 | let cache = this.generatorCache.get(type);
|
---|
1279 |
|
---|
1280 | if (cache === undefined) {
|
---|
1281 | cache = new WeakMap();
|
---|
1282 | this.generatorCache.set(type, cache);
|
---|
1283 | }
|
---|
1284 |
|
---|
1285 | let generator = cache.get(generatorOptions);
|
---|
1286 |
|
---|
1287 | if (generator === undefined) {
|
---|
1288 | generator = this.createGenerator(type, generatorOptions);
|
---|
1289 | cache.set(generatorOptions, generator);
|
---|
1290 | }
|
---|
1291 |
|
---|
1292 | return generator;
|
---|
1293 | }
|
---|
1294 |
|
---|
1295 | /**
|
---|
1296 | * @param {string} type type of generator
|
---|
1297 | * @param {GeneratorOptions} generatorOptions generator options
|
---|
1298 | * @returns {Generator} generator
|
---|
1299 | */
|
---|
1300 | createGenerator(type, generatorOptions = {}) {
|
---|
1301 | generatorOptions = mergeGlobalOptions(
|
---|
1302 | this._globalGeneratorOptions,
|
---|
1303 | type,
|
---|
1304 | generatorOptions
|
---|
1305 | );
|
---|
1306 | const generator = this.hooks.createGenerator
|
---|
1307 | .for(type)
|
---|
1308 | .call(generatorOptions);
|
---|
1309 | if (!generator) {
|
---|
1310 | throw new Error(`No generator registered for ${type}`);
|
---|
1311 | }
|
---|
1312 | this.hooks.generator.for(type).call(generator, generatorOptions);
|
---|
1313 | return generator;
|
---|
1314 | }
|
---|
1315 |
|
---|
1316 | /**
|
---|
1317 | * @param {Parameters<ResolverFactory["get"]>[0]} type type of resolver
|
---|
1318 | * @param {Parameters<ResolverFactory["get"]>[1]=} resolveOptions options
|
---|
1319 | * @returns {ReturnType<ResolverFactory["get"]>} the resolver
|
---|
1320 | */
|
---|
1321 | getResolver(type, resolveOptions) {
|
---|
1322 | return this.resolverFactory.get(type, resolveOptions);
|
---|
1323 | }
|
---|
1324 | }
|
---|
1325 |
|
---|
1326 | module.exports = NormalModuleFactory;
|
---|