source: imaps-frontend/node_modules/enhanced-resolve/lib/Resolver.js@ 79a0317

main
Last change on this file since 79a0317 was 79a0317, checked in by stefan toskovski <stefantoska84@…>, 4 days ago

F4 Finalna Verzija

  • Property mode set to 100644
File size: 24.0 KB
RevLine 
[79a0317]1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const { AsyncSeriesBailHook, AsyncSeriesHook, SyncHook } = require("tapable");
9const createInnerContext = require("./createInnerContext");
10const { parseIdentifier } = require("./util/identifier");
11const {
12 normalize,
13 cachedJoin: join,
14 getType,
15 PathType
16} = require("./util/path");
17
18/** @typedef {import("./ResolverFactory").ResolveOptions} ResolveOptions */
19
20/** @typedef {Error & { details?: string }} ErrorWithDetail */
21
22/** @typedef {(err: ErrorWithDetail | null, res?: string | false, req?: ResolveRequest) => void} ResolveCallback */
23
24/**
25 * @typedef {Object} PossibleFileSystemError
26 * @property {string=} code
27 * @property {number=} errno
28 * @property {string=} path
29 * @property {string=} syscall
30 */
31
32/**
33 * @template T
34 * @callback FileSystemCallback
35 * @param {PossibleFileSystemError & Error | null} err
36 * @param {T=} result
37 */
38
39/**
40 * @typedef {string | Buffer | URL} PathLike
41 */
42
43/**
44 * @typedef {PathLike | number} PathOrFileDescriptor
45 */
46
47/**
48 * @typedef {Object} ObjectEncodingOptions
49 * @property {BufferEncoding | null | undefined} [encoding]
50 */
51
52/** @typedef {function(NodeJS.ErrnoException | null, string=): void} StringCallback */
53/** @typedef {function(NodeJS.ErrnoException | null, Buffer=): void} BufferCallback */
54/** @typedef {function(NodeJS.ErrnoException | null, (string | Buffer)=): void} StringOrBufferCallback */
55/** @typedef {function(NodeJS.ErrnoException | null, IStats=): void} StatsCallback */
56/** @typedef {function(NodeJS.ErrnoException | null, IBigIntStats=): void} BigIntStatsCallback */
57/** @typedef {function(NodeJS.ErrnoException | null, (IStats | IBigIntStats)=): void} StatsOrBigIntStatsCallback */
58/** @typedef {function(NodeJS.ErrnoException | Error | null, JsonObject=): void} ReadJsonCallback */
59/** @typedef {function(NodeJS.ErrnoException | null, string[]=): void} ReaddirStringCallback */
60/** @typedef {function(NodeJS.ErrnoException | null, Buffer[]=): void} ReaddirBufferCallback */
61/** @typedef {function(NodeJS.ErrnoException | null, (string[] | Buffer[])=): void} ReaddirStringOrBufferCallback */
62/** @typedef {function(NodeJS.ErrnoException | null, Dirent[]=): void} ReaddirDirentCallback */
63
64/**
65 * @template T
66 * @typedef {Object} IStatsBase
67 * @property {() => boolean} isFile
68 * @property {() => boolean} isDirectory
69 * @property {() => boolean} isBlockDevice
70 * @property {() => boolean} isCharacterDevice
71 * @property {() => boolean} isSymbolicLink
72 * @property {() => boolean} isFIFO
73 * @property {() => boolean} isSocket
74 * @property {T} dev
75 * @property {T} ino
76 * @property {T} mode
77 * @property {T} nlink
78 * @property {T} uid
79 * @property {T} gid
80 * @property {T} rdev
81 * @property {T} size
82 * @property {T} blksize
83 * @property {T} blocks
84 * @property {T} atimeMs
85 * @property {T} mtimeMs
86 * @property {T} ctimeMs
87 * @property {T} birthtimeMs
88 * @property {Date} atime
89 * @property {Date} mtime
90 * @property {Date} ctime
91 * @property {Date} birthtime
92 */
93
94/**
95 * @typedef {IStatsBase<number>} IStats
96 */
97
98/**
99 * @typedef {IStatsBase<bigint> & { atimeNs: bigint, mtimeNs: bigint, ctimeNs: bigint, birthtimeNs: bigint }} IBigIntStats
100 */
101
102/**
103 * @typedef {Object} Dirent
104 * @property {() => boolean} isFile
105 * @property {() => boolean} isDirectory
106 * @property {() => boolean} isBlockDevice
107 * @property {() => boolean} isCharacterDevice
108 * @property {() => boolean} isSymbolicLink
109 * @property {() => boolean} isFIFO
110 * @property {() => boolean} isSocket
111 * @property {string} name
112 * @property {string} path
113 */
114
115/**
116 * @typedef {Object} StatOptions
117 * @property {(boolean | undefined)=} bigint
118 */
119
120/**
121 * @typedef {Object} StatSyncOptions
122 * @property {(boolean | undefined)=} bigint
123 * @property {(boolean | undefined)=} throwIfNoEntry
124 */
125
126/**
127 * @typedef {{
128 * (path: PathOrFileDescriptor, options: ({ encoding?: null | undefined, flag?: string | undefined } & import("events").Abortable) | undefined | null, callback: BufferCallback): void;
129 * (path: PathOrFileDescriptor, options: ({ encoding: BufferEncoding, flag?: string | undefined } & import("events").Abortable) | BufferEncoding, callback: StringCallback): void;
130 * (path: PathOrFileDescriptor, options: (ObjectEncodingOptions & { flag?: string | undefined } & import("events").Abortable) | BufferEncoding | undefined | null, callback: StringOrBufferCallback): void;
131 * (path: PathOrFileDescriptor, callback: BufferCallback): void;
132 * }} ReadFile
133 */
134
135/**
136 * @typedef {ObjectEncodingOptions | BufferEncoding | undefined | null} EncodingOption
137 */
138
139/**
140 * @typedef {'buffer'| { encoding: 'buffer' }} BufferEncodingOption
141 */
142
143/**
144 * @typedef {{
145 * (path: PathOrFileDescriptor, options?: { encoding?: null | undefined, flag?: string | undefined } | null): Buffer;
146 * (path: PathOrFileDescriptor, options: { encoding: BufferEncoding, flag?: string | undefined } | BufferEncoding): string;
147 * (path: PathOrFileDescriptor, options?: (ObjectEncodingOptions & { flag?: string | undefined }) | BufferEncoding | null): string | Buffer;
148 * }} ReadFileSync
149 */
150
151/**
152 * @typedef {{
153 * (path: PathLike, options: { encoding: BufferEncoding | null, withFileTypes?: false | undefined, recursive?: boolean | undefined } | BufferEncoding | undefined | null, callback: ReaddirStringCallback): void;
154 * (path: PathLike, options: { encoding: 'buffer', withFileTypes?: false | undefined, recursive?: boolean | undefined } | 'buffer', callback: ReaddirBufferCallback): void;
155 * (path: PathLike, callback: ReaddirStringCallback): void;
156 * (path: PathLike, options: (ObjectEncodingOptions & { withFileTypes?: false | undefined, recursive?: boolean | undefined }) | BufferEncoding | undefined | null, callback: ReaddirStringOrBufferCallback): void;
157 * (path: PathLike, options: ObjectEncodingOptions & { withFileTypes: true, recursive?: boolean | undefined }, callback: ReaddirDirentCallback): void;
158 * }} Readdir
159 */
160
161/**
162 * @typedef {{
163 * (path: PathLike, options?: { encoding: BufferEncoding | null, withFileTypes?: false | undefined, recursive?: boolean | undefined } | BufferEncoding | null): string[];
164 * (path: PathLike, options: { encoding: 'buffer', withFileTypes?: false | undefined, recursive?: boolean | undefined } | 'buffer'): Buffer[];
165 * (path: PathLike, options?: (ObjectEncodingOptions & { withFileTypes?: false | undefined, recursive?: boolean | undefined }) | BufferEncoding | null): string[] | Buffer[];
166 * (path: PathLike, options: ObjectEncodingOptions & { withFileTypes: true, recursive?: boolean | undefined }): Dirent[];
167 * }} ReaddirSync
168
169 /**
170 * @typedef {function(PathOrFileDescriptor, ReadJsonCallback): void} ReadJson
171 */
172
173/**
174 * @typedef {function(PathOrFileDescriptor): JsonObject} ReadJsonSync
175 */
176
177/**
178 * @typedef {{
179 * (path: PathLike, options: EncodingOption, callback: StringCallback): void;
180 * (path: PathLike, options: BufferEncodingOption, callback: BufferCallback): void;
181 * (path: PathLike, options: EncodingOption, callback: StringOrBufferCallback): void;
182 * (path: PathLike, callback: StringCallback): void;
183 * }} Readlink
184 */
185
186/**
187 * @typedef {{
188 * (path: PathLike, options?: EncodingOption): string;
189 * (path: PathLike, options: BufferEncodingOption): Buffer;
190 * (path: PathLike, options?: EncodingOption): string | Buffer;
191 * }} ReadlinkSync
192 */
193
194/**
195 * @typedef {{
196 * (path: PathLike, callback: StatsCallback): void;
197 * (path: PathLike, options: (StatOptions & { bigint?: false | undefined }) | undefined, callback: StatsCallback): void;
198 * (path: PathLike, options: StatOptions & { bigint: true }, callback: BigIntStatsCallback): void;
199 * (path: PathLike, options: StatOptions | undefined, callback: StatsOrBigIntStatsCallback): void;
200 * }} LStat
201 */
202
203/**
204 * @typedef {{
205 * (path: PathLike, options?: undefined): IStats;
206 * (path: PathLike, options?: StatSyncOptions & { bigint?: false | undefined, throwIfNoEntry: false }): IStats | undefined;
207 * (path: PathLike, options: StatSyncOptions & { bigint: true, throwIfNoEntry: false }): IBigIntStats | undefined;
208 * (path: PathLike, options?: StatSyncOptions & { bigint?: false | undefined }): IStats;
209 * (path: PathLike, options: StatSyncOptions & { bigint: true }): IBigIntStats;
210 * (path: PathLike, options: StatSyncOptions & { bigint: boolean, throwIfNoEntry?: false | undefined }): IStats | IBigIntStats;
211 * (path: PathLike, options?: StatSyncOptions): IStats | IBigIntStats | undefined;
212 * }} LStatSync
213 */
214
215/**
216 * @typedef {{
217 * (path: PathLike, callback: StatsCallback): void;
218 * (path: PathLike, options: (StatOptions & { bigint?: false | undefined }) | undefined, callback: StatsCallback): void;
219 * (path: PathLike, options: StatOptions & { bigint: true }, callback: BigIntStatsCallback): void;
220 * (path: PathLike, options: StatOptions | undefined, callback: StatsOrBigIntStatsCallback): void;
221 * }} Stat
222 */
223
224/**
225 * @typedef {{
226 * (path: PathLike, options?: undefined): IStats;
227 * (path: PathLike, options?: StatSyncOptions & { bigint?: false | undefined, throwIfNoEntry: false }): IStats | undefined;
228 * (path: PathLike, options: StatSyncOptions & { bigint: true, throwIfNoEntry: false }): IBigIntStats | undefined;
229 * (path: PathLike, options?: StatSyncOptions & { bigint?: false | undefined }): IStats;
230 * (path: PathLike, options: StatSyncOptions & { bigint: true }): IBigIntStats;
231 * (path: PathLike, options: StatSyncOptions & { bigint: boolean, throwIfNoEntry?: false | undefined }): IStats | IBigIntStats;
232 * (path: PathLike, options?: StatSyncOptions): IStats | IBigIntStats | undefined;
233 * }} StatSync
234 */
235
236/**
237 * @typedef {{
238 * (path: PathLike, options: EncodingOption, callback: StringCallback): void;
239 * (path: PathLike, options: BufferEncodingOption, callback: BufferCallback): void;
240 * (path: PathLike, options: EncodingOption, callback: StringOrBufferCallback): void;
241 * (path: PathLike, callback: StringCallback): void;
242 * }} RealPath
243 */
244
245/**
246 * @typedef {{
247 * (path: PathLike, options?: EncodingOption): string;
248 * (path: PathLike, options: BufferEncodingOption): Buffer;
249 * (path: PathLike, options?: EncodingOption): string | Buffer;
250 * }} RealPathSync
251 */
252
253/**
254 * @typedef {Object} FileSystem
255 * @property {ReadFile} readFile
256 * @property {Readdir} readdir
257 * @property {ReadJson=} readJson
258 * @property {Readlink} readlink
259 * @property {LStat=} lstat
260 * @property {Stat} stat
261 * @property {RealPath=} realpath
262 */
263
264/**
265 * @typedef {Object} SyncFileSystem
266 * @property {ReadFileSync} readFileSync
267 * @property {ReaddirSync} readdirSync
268 * @property {ReadJsonSync=} readJsonSync
269 * @property {ReadlinkSync} readlinkSync
270 * @property {LStatSync=} lstatSync
271 * @property {StatSync} statSync
272 * @property {RealPathSync=} realpathSync
273 */
274
275/**
276 * @typedef {Object} ParsedIdentifier
277 * @property {string} request
278 * @property {string} query
279 * @property {string} fragment
280 * @property {boolean} directory
281 * @property {boolean} module
282 * @property {boolean} file
283 * @property {boolean} internal
284 */
285
286/** @typedef {string | number | boolean | null} JsonPrimitive */
287/** @typedef {JsonValue[]} JsonArray */
288/** @typedef {JsonPrimitive | JsonObject | JsonArray} JsonValue */
289/** @typedef {{[Key in string]: JsonValue} & {[Key in string]?: JsonValue | undefined}} JsonObject */
290
291/**
292 * @typedef {Object} BaseResolveRequest
293 * @property {string | false} path
294 * @property {object=} context
295 * @property {string=} descriptionFilePath
296 * @property {string=} descriptionFileRoot
297 * @property {JsonObject=} descriptionFileData
298 * @property {string=} relativePath
299 * @property {boolean=} ignoreSymlinks
300 * @property {boolean=} fullySpecified
301 * @property {string=} __innerRequest
302 * @property {string=} __innerRequest_request
303 * @property {string=} __innerRequest_relativePath
304 */
305
306/** @typedef {BaseResolveRequest & Partial<ParsedIdentifier>} ResolveRequest */
307
308/**
309 * String with special formatting
310 * @typedef {string} StackEntry
311 */
312
313/**
314 * @template T
315 * @typedef {{ add: (item: T) => void }} WriteOnlySet
316 */
317
318/** @typedef {(function (ResolveRequest): void)} ResolveContextYield */
319
320/**
321 * Resolve context
322 * @typedef {Object} ResolveContext
323 * @property {WriteOnlySet<string>=} contextDependencies
324 * @property {WriteOnlySet<string>=} fileDependencies files that was found on file system
325 * @property {WriteOnlySet<string>=} missingDependencies dependencies that was not found on file system
326 * @property {Set<StackEntry>=} stack set of hooks' calls. For instance, `resolve → parsedResolve → describedResolve`,
327 * @property {(function(string): void)=} log log function
328 * @property {ResolveContextYield=} yield yield result, if provided plugins can return several results
329 */
330
331/** @typedef {AsyncSeriesBailHook<[ResolveRequest, ResolveContext], ResolveRequest | null>} ResolveStepHook */
332
333/**
334 * @typedef {Object} KnownHooks
335 * @property {SyncHook<[ResolveStepHook, ResolveRequest], void>} resolveStep
336 * @property {SyncHook<[ResolveRequest, Error]>} noResolve
337 * @property {ResolveStepHook} resolve
338 * @property {AsyncSeriesHook<[ResolveRequest, ResolveContext]>} result
339 */
340
341/**
342 * @typedef {{[key: string]: ResolveStepHook}} EnsuredHooks
343 */
344
345/**
346 * @param {string} str input string
347 * @returns {string} in camel case
348 */
349function toCamelCase(str) {
350 return str.replace(/-([a-z])/g, str => str.slice(1).toUpperCase());
351}
352
353class Resolver {
354 /**
355 * @param {ResolveStepHook} hook hook
356 * @param {ResolveRequest} request request
357 * @returns {StackEntry} stack entry
358 */
359 static createStackEntry(hook, request) {
360 return (
361 hook.name +
362 ": (" +
363 request.path +
364 ") " +
365 (request.request || "") +
366 (request.query || "") +
367 (request.fragment || "") +
368 (request.directory ? " directory" : "") +
369 (request.module ? " module" : "")
370 );
371 }
372
373 /**
374 * @param {FileSystem} fileSystem a filesystem
375 * @param {ResolveOptions} options options
376 */
377 constructor(fileSystem, options) {
378 this.fileSystem = fileSystem;
379 this.options = options;
380 /** @type {KnownHooks} */
381 this.hooks = {
382 resolveStep: new SyncHook(["hook", "request"], "resolveStep"),
383 noResolve: new SyncHook(["request", "error"], "noResolve"),
384 resolve: new AsyncSeriesBailHook(
385 ["request", "resolveContext"],
386 "resolve"
387 ),
388 result: new AsyncSeriesHook(["result", "resolveContext"], "result")
389 };
390 }
391
392 /**
393 * @param {string | ResolveStepHook} name hook name or hook itself
394 * @returns {ResolveStepHook} the hook
395 */
396 ensureHook(name) {
397 if (typeof name !== "string") {
398 return name;
399 }
400 name = toCamelCase(name);
401 if (/^before/.test(name)) {
402 return /** @type {ResolveStepHook} */ (
403 this.ensureHook(name[6].toLowerCase() + name.slice(7)).withOptions({
404 stage: -10
405 })
406 );
407 }
408 if (/^after/.test(name)) {
409 return /** @type {ResolveStepHook} */ (
410 this.ensureHook(name[5].toLowerCase() + name.slice(6)).withOptions({
411 stage: 10
412 })
413 );
414 }
415 /** @type {ResolveStepHook} */
416 const hook = /** @type {KnownHooks & EnsuredHooks} */ (this.hooks)[name];
417 if (!hook) {
418 /** @type {KnownHooks & EnsuredHooks} */
419 (this.hooks)[name] = new AsyncSeriesBailHook(
420 ["request", "resolveContext"],
421 name
422 );
423
424 return /** @type {KnownHooks & EnsuredHooks} */ (this.hooks)[name];
425 }
426 return hook;
427 }
428
429 /**
430 * @param {string | ResolveStepHook} name hook name or hook itself
431 * @returns {ResolveStepHook} the hook
432 */
433 getHook(name) {
434 if (typeof name !== "string") {
435 return name;
436 }
437 name = toCamelCase(name);
438 if (/^before/.test(name)) {
439 return /** @type {ResolveStepHook} */ (
440 this.getHook(name[6].toLowerCase() + name.slice(7)).withOptions({
441 stage: -10
442 })
443 );
444 }
445 if (/^after/.test(name)) {
446 return /** @type {ResolveStepHook} */ (
447 this.getHook(name[5].toLowerCase() + name.slice(6)).withOptions({
448 stage: 10
449 })
450 );
451 }
452 /** @type {ResolveStepHook} */
453 const hook = /** @type {KnownHooks & EnsuredHooks} */ (this.hooks)[name];
454 if (!hook) {
455 throw new Error(`Hook ${name} doesn't exist`);
456 }
457 return hook;
458 }
459
460 /**
461 * @param {object} context context information object
462 * @param {string} path context path
463 * @param {string} request request string
464 * @returns {string | false} result
465 */
466 resolveSync(context, path, request) {
467 /** @type {Error | null | undefined} */
468 let err = undefined;
469 /** @type {string | false | undefined} */
470 let result = undefined;
471 let sync = false;
472 this.resolve(context, path, request, {}, (e, r) => {
473 err = e;
474 result = r;
475 sync = true;
476 });
477 if (!sync) {
478 throw new Error(
479 "Cannot 'resolveSync' because the fileSystem is not sync. Use 'resolve'!"
480 );
481 }
482 if (err) throw err;
483 if (result === undefined) throw new Error("No result");
484 return result;
485 }
486
487 /**
488 * @param {object} context context information object
489 * @param {string} path context path
490 * @param {string} request request string
491 * @param {ResolveContext} resolveContext resolve context
492 * @param {ResolveCallback} callback callback function
493 * @returns {void}
494 */
495 resolve(context, path, request, resolveContext, callback) {
496 if (!context || typeof context !== "object")
497 return callback(new Error("context argument is not an object"));
498 if (typeof path !== "string")
499 return callback(new Error("path argument is not a string"));
500 if (typeof request !== "string")
501 return callback(new Error("request argument is not a string"));
502 if (!resolveContext)
503 return callback(new Error("resolveContext argument is not set"));
504
505 /** @type {ResolveRequest} */
506 const obj = {
507 context: context,
508 path: path,
509 request: request
510 };
511
512 /** @type {ResolveContextYield | undefined} */
513 let yield_;
514 let yieldCalled = false;
515 /** @type {ResolveContextYield | undefined} */
516 let finishYield;
517 if (typeof resolveContext.yield === "function") {
518 const old = resolveContext.yield;
519 /**
520 * @param {ResolveRequest} obj object
521 */
522 yield_ = obj => {
523 old(obj);
524 yieldCalled = true;
525 };
526 /**
527 * @param {ResolveRequest} result result
528 * @returns {void}
529 */
530 finishYield = result => {
531 if (result) {
532 /** @type {ResolveContextYield} */ (yield_)(result);
533 }
534 callback(null);
535 };
536 }
537
538 const message = `resolve '${request}' in '${path}'`;
539
540 /**
541 * @param {ResolveRequest} result result
542 * @returns {void}
543 */
544 const finishResolved = result => {
545 return callback(
546 null,
547 result.path === false
548 ? false
549 : `${result.path.replace(/#/g, "\0#")}${
550 result.query ? result.query.replace(/#/g, "\0#") : ""
551 }${result.fragment || ""}`,
552 result
553 );
554 };
555
556 /**
557 * @param {string[]} log logs
558 * @returns {void}
559 */
560 const finishWithoutResolve = log => {
561 /**
562 * @type {ErrorWithDetail}
563 */
564 const error = new Error("Can't " + message);
565 error.details = log.join("\n");
566 this.hooks.noResolve.call(obj, error);
567 return callback(error);
568 };
569
570 if (resolveContext.log) {
571 // We need log anyway to capture it in case of an error
572 const parentLog = resolveContext.log;
573 /** @type {string[]} */
574 const log = [];
575 return this.doResolve(
576 this.hooks.resolve,
577 obj,
578 message,
579 {
580 log: msg => {
581 parentLog(msg);
582 log.push(msg);
583 },
584 yield: yield_,
585 fileDependencies: resolveContext.fileDependencies,
586 contextDependencies: resolveContext.contextDependencies,
587 missingDependencies: resolveContext.missingDependencies,
588 stack: resolveContext.stack
589 },
590 (err, result) => {
591 if (err) return callback(err);
592
593 if (yieldCalled || (result && yield_)) {
594 return /** @type {ResolveContextYield} */ (finishYield)(
595 /** @type {ResolveRequest} */ (result)
596 );
597 }
598
599 if (result) return finishResolved(result);
600
601 return finishWithoutResolve(log);
602 }
603 );
604 } else {
605 // Try to resolve assuming there is no error
606 // We don't log stuff in this case
607 return this.doResolve(
608 this.hooks.resolve,
609 obj,
610 message,
611 {
612 log: undefined,
613 yield: yield_,
614 fileDependencies: resolveContext.fileDependencies,
615 contextDependencies: resolveContext.contextDependencies,
616 missingDependencies: resolveContext.missingDependencies,
617 stack: resolveContext.stack
618 },
619 (err, result) => {
620 if (err) return callback(err);
621
622 if (yieldCalled || (result && yield_)) {
623 return /** @type {ResolveContextYield} */ (finishYield)(
624 /** @type {ResolveRequest} */ (result)
625 );
626 }
627
628 if (result) return finishResolved(result);
629
630 // log is missing for the error details
631 // so we redo the resolving for the log info
632 // this is more expensive to the success case
633 // is assumed by default
634 /** @type {string[]} */
635 const log = [];
636
637 return this.doResolve(
638 this.hooks.resolve,
639 obj,
640 message,
641 {
642 log: msg => log.push(msg),
643 yield: yield_,
644 stack: resolveContext.stack
645 },
646 (err, result) => {
647 if (err) return callback(err);
648
649 // In a case that there is a race condition and yield will be called
650 if (yieldCalled || (result && yield_)) {
651 return /** @type {ResolveContextYield} */ (finishYield)(
652 /** @type {ResolveRequest} */ (result)
653 );
654 }
655
656 return finishWithoutResolve(log);
657 }
658 );
659 }
660 );
661 }
662 }
663
664 /**
665 * @param {ResolveStepHook} hook hook
666 * @param {ResolveRequest} request request
667 * @param {null|string} message string
668 * @param {ResolveContext} resolveContext resolver context
669 * @param {(err?: null|Error, result?: ResolveRequest) => void} callback callback
670 * @returns {void}
671 */
672 doResolve(hook, request, message, resolveContext, callback) {
673 const stackEntry = Resolver.createStackEntry(hook, request);
674
675 /** @type {Set<string> | undefined} */
676 let newStack;
677 if (resolveContext.stack) {
678 newStack = new Set(resolveContext.stack);
679 if (resolveContext.stack.has(stackEntry)) {
680 /**
681 * Prevent recursion
682 * @type {Error & {recursion?: boolean}}
683 */
684 const recursionError = new Error(
685 "Recursion in resolving\nStack:\n " +
686 Array.from(newStack).join("\n ")
687 );
688 recursionError.recursion = true;
689 if (resolveContext.log)
690 resolveContext.log("abort resolving because of recursion");
691 return callback(recursionError);
692 }
693 newStack.add(stackEntry);
694 } else {
695 // creating a set with new Set([item])
696 // allocates a new array that has to be garbage collected
697 // this is an EXTREMELY hot path, so let's avoid it
698 newStack = new Set();
699 newStack.add(stackEntry);
700 }
701 this.hooks.resolveStep.call(hook, request);
702
703 if (hook.isUsed()) {
704 const innerContext = createInnerContext(
705 {
706 log: resolveContext.log,
707 yield: resolveContext.yield,
708 fileDependencies: resolveContext.fileDependencies,
709 contextDependencies: resolveContext.contextDependencies,
710 missingDependencies: resolveContext.missingDependencies,
711 stack: newStack
712 },
713 message
714 );
715 return hook.callAsync(request, innerContext, (err, result) => {
716 if (err) return callback(err);
717 if (result) return callback(null, result);
718 callback();
719 });
720 } else {
721 callback();
722 }
723 }
724
725 /**
726 * @param {string} identifier identifier
727 * @returns {ParsedIdentifier} parsed identifier
728 */
729 parse(identifier) {
730 const part = {
731 request: "",
732 query: "",
733 fragment: "",
734 module: false,
735 directory: false,
736 file: false,
737 internal: false
738 };
739
740 const parsedIdentifier = parseIdentifier(identifier);
741
742 if (!parsedIdentifier) return part;
743
744 [part.request, part.query, part.fragment] = parsedIdentifier;
745
746 if (part.request.length > 0) {
747 part.internal = this.isPrivate(identifier);
748 part.module = this.isModule(part.request);
749 part.directory = this.isDirectory(part.request);
750 if (part.directory) {
751 part.request = part.request.slice(0, -1);
752 }
753 }
754
755 return part;
756 }
757
758 /**
759 * @param {string} path path
760 * @returns {boolean} true, if the path is a module
761 */
762 isModule(path) {
763 return getType(path) === PathType.Normal;
764 }
765
766 /**
767 * @param {string} path path
768 * @returns {boolean} true, if the path is private
769 */
770 isPrivate(path) {
771 return getType(path) === PathType.Internal;
772 }
773
774 /**
775 * @param {string} path a path
776 * @returns {boolean} true, if the path is a directory path
777 */
778 isDirectory(path) {
779 return path.endsWith("/");
780 }
781
782 /**
783 * @param {string} path path
784 * @param {string} request request
785 * @returns {string} joined path
786 */
787 join(path, request) {
788 return join(path, request);
789 }
790
791 /**
792 * @param {string} path path
793 * @returns {string} normalized path
794 */
795 normalize(path) {
796 return normalize(path);
797 }
798}
799
800module.exports = Resolver;
Note: See TracBrowser for help on using the repository browser.