source: trip-planner-front/node_modules/enhanced-resolve/lib/ResolverFactory.js@ fa375fe

Last change on this file since fa375fe was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 19.0 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const versions = require("process").versions;
9const Resolver = require("./Resolver");
10const { getType, PathType } = require("./util/path");
11
12const SyncAsyncFileSystemDecorator = require("./SyncAsyncFileSystemDecorator");
13
14const AliasFieldPlugin = require("./AliasFieldPlugin");
15const AliasPlugin = require("./AliasPlugin");
16const AppendPlugin = require("./AppendPlugin");
17const ConditionalPlugin = require("./ConditionalPlugin");
18const DescriptionFilePlugin = require("./DescriptionFilePlugin");
19const DirectoryExistsPlugin = require("./DirectoryExistsPlugin");
20const ExportsFieldPlugin = require("./ExportsFieldPlugin");
21const FileExistsPlugin = require("./FileExistsPlugin");
22const ImportsFieldPlugin = require("./ImportsFieldPlugin");
23const JoinRequestPartPlugin = require("./JoinRequestPartPlugin");
24const JoinRequestPlugin = require("./JoinRequestPlugin");
25const MainFieldPlugin = require("./MainFieldPlugin");
26const ModulesInHierachicDirectoriesPlugin = require("./ModulesInHierachicDirectoriesPlugin");
27const ModulesInRootPlugin = require("./ModulesInRootPlugin");
28const NextPlugin = require("./NextPlugin");
29const ParsePlugin = require("./ParsePlugin");
30const PnpPlugin = require("./PnpPlugin");
31const RestrictionsPlugin = require("./RestrictionsPlugin");
32const ResultPlugin = require("./ResultPlugin");
33const RootsPlugin = require("./RootsPlugin");
34const SelfReferencePlugin = require("./SelfReferencePlugin");
35const SymlinkPlugin = require("./SymlinkPlugin");
36const TryNextPlugin = require("./TryNextPlugin");
37const UnsafeCachePlugin = require("./UnsafeCachePlugin");
38const UseFilePlugin = require("./UseFilePlugin");
39
40/** @typedef {import("./AliasPlugin").AliasOption} AliasOptionEntry */
41/** @typedef {import("./PnpPlugin").PnpApiImpl} PnpApi */
42/** @typedef {import("./Resolver").FileSystem} FileSystem */
43/** @typedef {import("./Resolver").ResolveRequest} ResolveRequest */
44/** @typedef {import("./Resolver").SyncFileSystem} SyncFileSystem */
45
46/** @typedef {string|string[]|false} AliasOptionNewRequest */
47/** @typedef {{[k: string]: AliasOptionNewRequest}} AliasOptions */
48/** @typedef {{apply: function(Resolver): void} | function(this: Resolver, Resolver): void} Plugin */
49
50/**
51 * @typedef {Object} UserResolveOptions
52 * @property {(AliasOptions | AliasOptionEntry[])=} alias A list of module alias configurations or an object which maps key to value
53 * @property {(AliasOptions | AliasOptionEntry[])=} fallback A list of module alias configurations or an object which maps key to value, applied only after modules option
54 * @property {(string | string[])[]=} aliasFields A list of alias fields in description files
55 * @property {(function(ResolveRequest): boolean)=} cachePredicate A function which decides whether a request should be cached or not. An object is passed with at least `path` and `request` properties.
56 * @property {boolean=} cacheWithContext Whether or not the unsafeCache should include request context as part of the cache key.
57 * @property {string[]=} descriptionFiles A list of description files to read from
58 * @property {string[]=} conditionNames A list of exports field condition names.
59 * @property {boolean=} enforceExtension Enforce that a extension from extensions must be used
60 * @property {(string | string[])[]=} exportsFields A list of exports fields in description files
61 * @property {(string | string[])[]=} importsFields A list of imports fields in description files
62 * @property {string[]=} extensions A list of extensions which should be tried for files
63 * @property {FileSystem} fileSystem The file system which should be used
64 * @property {(object | boolean)=} unsafeCache Use this cache object to unsafely cache the successful requests
65 * @property {boolean=} symlinks Resolve symlinks to their symlinked location
66 * @property {Resolver=} resolver A prepared Resolver to which the plugins are attached
67 * @property {string[] | string=} modules A list of directories to resolve modules from, can be absolute path or folder name
68 * @property {(string | string[] | {name: string | string[], forceRelative: boolean})[]=} mainFields A list of main fields in description files
69 * @property {string[]=} mainFiles A list of main files in directories
70 * @property {Plugin[]=} plugins A list of additional resolve plugins which should be applied
71 * @property {PnpApi | null=} pnpApi A PnP API that should be used - null is "never", undefined is "auto"
72 * @property {string[]=} roots A list of root paths
73 * @property {boolean=} fullySpecified The request is already fully specified and no extensions or directories are resolved for it
74 * @property {boolean=} resolveToContext Resolve to a context instead of a file
75 * @property {(string|RegExp)[]=} restrictions A list of resolve restrictions
76 * @property {boolean=} useSyncFileSystemCalls Use only the sync constiants of the file system calls
77 * @property {boolean=} preferRelative Prefer to resolve module requests as relative requests before falling back to modules
78 * @property {boolean=} preferAbsolute Prefer to resolve server-relative urls as absolute paths before falling back to resolve in roots
79 */
80
81/**
82 * @typedef {Object} ResolveOptions
83 * @property {AliasOptionEntry[]} alias
84 * @property {AliasOptionEntry[]} fallback
85 * @property {Set<string | string[]>} aliasFields
86 * @property {(function(ResolveRequest): boolean)} cachePredicate
87 * @property {boolean} cacheWithContext
88 * @property {Set<string>} conditionNames A list of exports field condition names.
89 * @property {string[]} descriptionFiles
90 * @property {boolean} enforceExtension
91 * @property {Set<string | string[]>} exportsFields
92 * @property {Set<string | string[]>} importsFields
93 * @property {Set<string>} extensions
94 * @property {FileSystem} fileSystem
95 * @property {object | false} unsafeCache
96 * @property {boolean} symlinks
97 * @property {Resolver=} resolver
98 * @property {Array<string | string[]>} modules
99 * @property {{name: string[], forceRelative: boolean}[]} mainFields
100 * @property {Set<string>} mainFiles
101 * @property {Plugin[]} plugins
102 * @property {PnpApi | null} pnpApi
103 * @property {Set<string>} roots
104 * @property {boolean} fullySpecified
105 * @property {boolean} resolveToContext
106 * @property {Set<string|RegExp>} restrictions
107 * @property {boolean} preferRelative
108 * @property {boolean} preferAbsolute
109 */
110
111/**
112 * @param {PnpApi | null=} option option
113 * @returns {PnpApi | null} processed option
114 */
115function processPnpApiOption(option) {
116 if (
117 option === undefined &&
118 /** @type {NodeJS.ProcessVersions & {pnp: string}} */ versions.pnp
119 ) {
120 // @ts-ignore
121 return require("pnpapi"); // eslint-disable-line node/no-missing-require
122 }
123
124 return option || null;
125}
126
127/**
128 * @param {AliasOptions | AliasOptionEntry[] | undefined} alias alias
129 * @returns {AliasOptionEntry[]} normalized aliases
130 */
131function normalizeAlias(alias) {
132 return typeof alias === "object" && !Array.isArray(alias) && alias !== null
133 ? Object.keys(alias).map(key => {
134 /** @type {AliasOptionEntry} */
135 const obj = { name: key, onlyModule: false, alias: alias[key] };
136
137 if (/\$$/.test(key)) {
138 obj.onlyModule = true;
139 obj.name = key.substr(0, key.length - 1);
140 }
141
142 return obj;
143 })
144 : /** @type {Array<AliasOptionEntry>} */ (alias) || [];
145}
146
147/**
148 * @param {UserResolveOptions} options input options
149 * @returns {ResolveOptions} output options
150 */
151function createOptions(options) {
152 const mainFieldsSet = new Set(options.mainFields || ["main"]);
153 const mainFields = [];
154
155 for (const item of mainFieldsSet) {
156 if (typeof item === "string") {
157 mainFields.push({
158 name: [item],
159 forceRelative: true
160 });
161 } else if (Array.isArray(item)) {
162 mainFields.push({
163 name: item,
164 forceRelative: true
165 });
166 } else {
167 mainFields.push({
168 name: Array.isArray(item.name) ? item.name : [item.name],
169 forceRelative: item.forceRelative
170 });
171 }
172 }
173
174 return {
175 alias: normalizeAlias(options.alias),
176 fallback: normalizeAlias(options.fallback),
177 aliasFields: new Set(options.aliasFields),
178 cachePredicate:
179 options.cachePredicate ||
180 function () {
181 return true;
182 },
183 cacheWithContext:
184 typeof options.cacheWithContext !== "undefined"
185 ? options.cacheWithContext
186 : true,
187 exportsFields: new Set(options.exportsFields || ["exports"]),
188 importsFields: new Set(options.importsFields || ["imports"]),
189 conditionNames: new Set(options.conditionNames),
190 descriptionFiles: Array.from(
191 new Set(options.descriptionFiles || ["package.json"])
192 ),
193 enforceExtension:
194 options.enforceExtension === undefined
195 ? options.extensions && options.extensions.includes("")
196 ? true
197 : false
198 : options.enforceExtension,
199 extensions: new Set(options.extensions || [".js", ".json", ".node"]),
200 fileSystem: options.useSyncFileSystemCalls
201 ? new SyncAsyncFileSystemDecorator(
202 /** @type {SyncFileSystem} */ (
203 /** @type {unknown} */ (options.fileSystem)
204 )
205 )
206 : options.fileSystem,
207 unsafeCache:
208 options.unsafeCache && typeof options.unsafeCache !== "object"
209 ? {}
210 : options.unsafeCache || false,
211 symlinks: typeof options.symlinks !== "undefined" ? options.symlinks : true,
212 resolver: options.resolver,
213 modules: mergeFilteredToArray(
214 Array.isArray(options.modules)
215 ? options.modules
216 : options.modules
217 ? [options.modules]
218 : ["node_modules"],
219 item => {
220 const type = getType(item);
221 return type === PathType.Normal || type === PathType.Relative;
222 }
223 ),
224 mainFields,
225 mainFiles: new Set(options.mainFiles || ["index"]),
226 plugins: options.plugins || [],
227 pnpApi: processPnpApiOption(options.pnpApi),
228 roots: new Set(options.roots || undefined),
229 fullySpecified: options.fullySpecified || false,
230 resolveToContext: options.resolveToContext || false,
231 preferRelative: options.preferRelative || false,
232 preferAbsolute: options.preferAbsolute || false,
233 restrictions: new Set(options.restrictions)
234 };
235}
236
237/**
238 * @param {UserResolveOptions} options resolve options
239 * @returns {Resolver} created resolver
240 */
241exports.createResolver = function (options) {
242 const normalizedOptions = createOptions(options);
243
244 const {
245 alias,
246 fallback,
247 aliasFields,
248 cachePredicate,
249 cacheWithContext,
250 conditionNames,
251 descriptionFiles,
252 enforceExtension,
253 exportsFields,
254 importsFields,
255 extensions,
256 fileSystem,
257 fullySpecified,
258 mainFields,
259 mainFiles,
260 modules,
261 plugins: userPlugins,
262 pnpApi,
263 resolveToContext,
264 preferRelative,
265 preferAbsolute,
266 symlinks,
267 unsafeCache,
268 resolver: customResolver,
269 restrictions,
270 roots
271 } = normalizedOptions;
272
273 const plugins = userPlugins.slice();
274
275 const resolver = customResolver
276 ? customResolver
277 : new Resolver(fileSystem, normalizedOptions);
278
279 //// pipeline ////
280
281 resolver.ensureHook("resolve");
282 resolver.ensureHook("internalResolve");
283 resolver.ensureHook("newInteralResolve");
284 resolver.ensureHook("parsedResolve");
285 resolver.ensureHook("describedResolve");
286 resolver.ensureHook("internal");
287 resolver.ensureHook("rawModule");
288 resolver.ensureHook("module");
289 resolver.ensureHook("resolveAsModule");
290 resolver.ensureHook("undescribedResolveInPackage");
291 resolver.ensureHook("resolveInPackage");
292 resolver.ensureHook("resolveInExistingDirectory");
293 resolver.ensureHook("relative");
294 resolver.ensureHook("describedRelative");
295 resolver.ensureHook("directory");
296 resolver.ensureHook("undescribedExistingDirectory");
297 resolver.ensureHook("existingDirectory");
298 resolver.ensureHook("undescribedRawFile");
299 resolver.ensureHook("rawFile");
300 resolver.ensureHook("file");
301 resolver.ensureHook("finalFile");
302 resolver.ensureHook("existingFile");
303 resolver.ensureHook("resolved");
304
305 // resolve
306 for (const { source, resolveOptions } of [
307 { source: "resolve", resolveOptions: { fullySpecified } },
308 { source: "internal-resolve", resolveOptions: { fullySpecified: false } }
309 ]) {
310 if (unsafeCache) {
311 plugins.push(
312 new UnsafeCachePlugin(
313 source,
314 cachePredicate,
315 unsafeCache,
316 cacheWithContext,
317 `new-${source}`
318 )
319 );
320 plugins.push(
321 new ParsePlugin(`new-${source}`, resolveOptions, "parsed-resolve")
322 );
323 } else {
324 plugins.push(new ParsePlugin(source, resolveOptions, "parsed-resolve"));
325 }
326 }
327
328 // parsed-resolve
329 plugins.push(
330 new DescriptionFilePlugin(
331 "parsed-resolve",
332 descriptionFiles,
333 false,
334 "described-resolve"
335 )
336 );
337 plugins.push(new NextPlugin("after-parsed-resolve", "described-resolve"));
338
339 // described-resolve
340 plugins.push(new NextPlugin("described-resolve", "normal-resolve"));
341 if (fallback.length > 0) {
342 plugins.push(
343 new AliasPlugin("described-resolve", fallback, "internal-resolve")
344 );
345 }
346
347 // normal-resolve
348 if (alias.length > 0)
349 plugins.push(new AliasPlugin("normal-resolve", alias, "internal-resolve"));
350 aliasFields.forEach(item => {
351 plugins.push(
352 new AliasFieldPlugin("normal-resolve", item, "internal-resolve")
353 );
354 });
355 if (preferRelative) {
356 plugins.push(new JoinRequestPlugin("after-normal-resolve", "relative"));
357 }
358 plugins.push(
359 new ConditionalPlugin(
360 "after-normal-resolve",
361 { module: true },
362 "resolve as module",
363 false,
364 "raw-module"
365 )
366 );
367 plugins.push(
368 new ConditionalPlugin(
369 "after-normal-resolve",
370 { internal: true },
371 "resolve as internal import",
372 false,
373 "internal"
374 )
375 );
376 if (preferAbsolute) {
377 plugins.push(new JoinRequestPlugin("after-normal-resolve", "relative"));
378 }
379 if (roots.size > 0) {
380 plugins.push(new RootsPlugin("after-normal-resolve", roots, "relative"));
381 }
382 if (!preferRelative && !preferAbsolute) {
383 plugins.push(new JoinRequestPlugin("after-normal-resolve", "relative"));
384 }
385
386 // internal
387 importsFields.forEach(importsField => {
388 plugins.push(
389 new ImportsFieldPlugin(
390 "internal",
391 conditionNames,
392 importsField,
393 "relative",
394 "internal-resolve"
395 )
396 );
397 });
398
399 // raw-module
400 exportsFields.forEach(exportsField => {
401 plugins.push(
402 new SelfReferencePlugin("raw-module", exportsField, "resolve-as-module")
403 );
404 });
405 modules.forEach(item => {
406 if (Array.isArray(item)) {
407 if (item.includes("node_modules") && pnpApi) {
408 plugins.push(
409 new ModulesInHierachicDirectoriesPlugin(
410 "raw-module",
411 item.filter(i => i !== "node_modules"),
412 "module"
413 )
414 );
415 plugins.push(
416 new PnpPlugin("raw-module", pnpApi, "undescribed-resolve-in-package")
417 );
418 } else {
419 plugins.push(
420 new ModulesInHierachicDirectoriesPlugin("raw-module", item, "module")
421 );
422 }
423 } else {
424 plugins.push(new ModulesInRootPlugin("raw-module", item, "module"));
425 }
426 });
427
428 // module
429 plugins.push(new JoinRequestPartPlugin("module", "resolve-as-module"));
430
431 // resolve-as-module
432 if (!resolveToContext) {
433 plugins.push(
434 new ConditionalPlugin(
435 "resolve-as-module",
436 { directory: false, request: "." },
437 "single file module",
438 true,
439 "undescribed-raw-file"
440 )
441 );
442 }
443 plugins.push(
444 new DirectoryExistsPlugin(
445 "resolve-as-module",
446 "undescribed-resolve-in-package"
447 )
448 );
449
450 // undescribed-resolve-in-package
451 plugins.push(
452 new DescriptionFilePlugin(
453 "undescribed-resolve-in-package",
454 descriptionFiles,
455 false,
456 "resolve-in-package"
457 )
458 );
459 plugins.push(
460 new NextPlugin("after-undescribed-resolve-in-package", "resolve-in-package")
461 );
462
463 // resolve-in-package
464 exportsFields.forEach(exportsField => {
465 plugins.push(
466 new ExportsFieldPlugin(
467 "resolve-in-package",
468 conditionNames,
469 exportsField,
470 "relative"
471 )
472 );
473 });
474 plugins.push(
475 new NextPlugin("resolve-in-package", "resolve-in-existing-directory")
476 );
477
478 // resolve-in-existing-directory
479 plugins.push(
480 new JoinRequestPlugin("resolve-in-existing-directory", "relative")
481 );
482
483 // relative
484 plugins.push(
485 new DescriptionFilePlugin(
486 "relative",
487 descriptionFiles,
488 true,
489 "described-relative"
490 )
491 );
492 plugins.push(new NextPlugin("after-relative", "described-relative"));
493
494 // described-relative
495 if (resolveToContext) {
496 plugins.push(new NextPlugin("described-relative", "directory"));
497 } else {
498 plugins.push(
499 new ConditionalPlugin(
500 "described-relative",
501 { directory: false },
502 null,
503 true,
504 "raw-file"
505 )
506 );
507 plugins.push(
508 new ConditionalPlugin(
509 "described-relative",
510 { fullySpecified: false },
511 "as directory",
512 true,
513 "directory"
514 )
515 );
516 }
517
518 // directory
519 plugins.push(
520 new DirectoryExistsPlugin("directory", "undescribed-existing-directory")
521 );
522
523 if (resolveToContext) {
524 // undescribed-existing-directory
525 plugins.push(new NextPlugin("undescribed-existing-directory", "resolved"));
526 } else {
527 // undescribed-existing-directory
528 plugins.push(
529 new DescriptionFilePlugin(
530 "undescribed-existing-directory",
531 descriptionFiles,
532 false,
533 "existing-directory"
534 )
535 );
536 mainFiles.forEach(item => {
537 plugins.push(
538 new UseFilePlugin(
539 "undescribed-existing-directory",
540 item,
541 "undescribed-raw-file"
542 )
543 );
544 });
545
546 // described-existing-directory
547 mainFields.forEach(item => {
548 plugins.push(
549 new MainFieldPlugin(
550 "existing-directory",
551 item,
552 "resolve-in-existing-directory"
553 )
554 );
555 });
556 mainFiles.forEach(item => {
557 plugins.push(
558 new UseFilePlugin("existing-directory", item, "undescribed-raw-file")
559 );
560 });
561
562 // undescribed-raw-file
563 plugins.push(
564 new DescriptionFilePlugin(
565 "undescribed-raw-file",
566 descriptionFiles,
567 true,
568 "raw-file"
569 )
570 );
571 plugins.push(new NextPlugin("after-undescribed-raw-file", "raw-file"));
572
573 // raw-file
574 plugins.push(
575 new ConditionalPlugin(
576 "raw-file",
577 { fullySpecified: true },
578 null,
579 false,
580 "file"
581 )
582 );
583 if (!enforceExtension) {
584 plugins.push(new TryNextPlugin("raw-file", "no extension", "file"));
585 }
586 extensions.forEach(item => {
587 plugins.push(new AppendPlugin("raw-file", item, "file"));
588 });
589
590 // file
591 if (alias.length > 0)
592 plugins.push(new AliasPlugin("file", alias, "internal-resolve"));
593 aliasFields.forEach(item => {
594 plugins.push(new AliasFieldPlugin("file", item, "internal-resolve"));
595 });
596 plugins.push(new NextPlugin("file", "final-file"));
597
598 // final-file
599 plugins.push(new FileExistsPlugin("final-file", "existing-file"));
600
601 // existing-file
602 if (symlinks)
603 plugins.push(new SymlinkPlugin("existing-file", "existing-file"));
604 plugins.push(new NextPlugin("existing-file", "resolved"));
605 }
606
607 // resolved
608 if (restrictions.size > 0) {
609 plugins.push(new RestrictionsPlugin(resolver.hooks.resolved, restrictions));
610 }
611 plugins.push(new ResultPlugin(resolver.hooks.resolved));
612
613 //// RESOLVER ////
614
615 for (const plugin of plugins) {
616 if (typeof plugin === "function") {
617 plugin.call(resolver, resolver);
618 } else {
619 plugin.apply(resolver);
620 }
621 }
622
623 return resolver;
624};
625
626/**
627 * Merging filtered elements
628 * @param {string[]} array source array
629 * @param {function(string): boolean} filter predicate
630 * @returns {Array<string | string[]>} merge result
631 */
632function mergeFilteredToArray(array, filter) {
633 /** @type {Array<string | string[]>} */
634 const result = [];
635 const set = new Set(array);
636
637 for (const item of set) {
638 if (filter(item)) {
639 const lastElement =
640 result.length > 0 ? result[result.length - 1] : undefined;
641 if (Array.isArray(lastElement)) {
642 lastElement.push(item);
643 } else {
644 result.push([item]);
645 }
646 } else {
647 result.push(item);
648 }
649 }
650
651 return result;
652}
Note: See TracBrowser for help on using the repository browser.