UNPKG

33 kBJavaScriptView Raw
1// Copied from https://raw.githubusercontent.com/nodejs/node/v15.3.0/lib/internal/modules/esm/resolve.js
2
3'use strict';
4
5const {versionGteLt} = require('../dist/util');
6
7// Test for node >14.13.1 || (>=12.20.0 && <13)
8const builtinModuleProtocol =
9 versionGteLt(process.versions.node, '14.13.1') ||
10 versionGteLt(process.versions.node, '12.20.0', '13.0.0')
11 ? 'node:'
12 : 'nodejs:';
13
14const {
15 ArrayIsArray,
16 ArrayPrototypeJoin,
17 ArrayPrototypeShift,
18 JSONParse,
19 JSONStringify,
20 ObjectFreeze,
21 ObjectGetOwnPropertyNames,
22 ObjectPrototypeHasOwnProperty,
23 RegExpPrototypeTest,
24 SafeMap,
25 SafeSet,
26 StringPrototypeEndsWith,
27 StringPrototypeIndexOf,
28 StringPrototypeLastIndexOf,
29 StringPrototypeReplace,
30 StringPrototypeSlice,
31 StringPrototypeSplit,
32 StringPrototypeStartsWith,
33 StringPrototypeSubstr,
34} = require('./node-primordials');
35
36// const internalFS = require('internal/fs/utils');
37const Module = require('module');
38const { NativeModule } = require('./node-nativemodule');
39const {
40 realpathSync,
41 statSync,
42 Stats,
43} = require('fs');
44// const { getOptionValue } = require('internal/options');
45const { getOptionValue } = require('./node-options');
46// // Do not eagerly grab .manifest, it may be in TDZ
47// const policy = getOptionValue('--experimental-policy') ?
48// require('internal/process/policy') :
49// null;
50// disabled for now. I am not sure if/how we should support this
51const policy = null;
52const { sep, relative } = require('path');
53const preserveSymlinks = getOptionValue('--preserve-symlinks');
54const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
55const typeFlag = getOptionValue('--input-type');
56// const { URL, pathToFileURL, fileURLToPath } = require('internal/url');
57const { URL, pathToFileURL, fileURLToPath } = require('url');
58const {
59 ERR_INPUT_TYPE_NOT_ALLOWED,
60 ERR_INVALID_ARG_VALUE,
61 ERR_INVALID_MODULE_SPECIFIER,
62 ERR_INVALID_PACKAGE_CONFIG,
63 ERR_INVALID_PACKAGE_TARGET,
64 ERR_MANIFEST_DEPENDENCY_MISSING,
65 ERR_MODULE_NOT_FOUND,
66 ERR_PACKAGE_IMPORT_NOT_DEFINED,
67 ERR_PACKAGE_PATH_NOT_EXPORTED,
68 ERR_UNSUPPORTED_DIR_IMPORT,
69 ERR_UNSUPPORTED_ESM_URL_SCHEME,
70// } = require('internal/errors').codes;
71} = require('./node-internal-errors').codes;
72
73// const { Module: CJSModule } = require('internal/modules/cjs/loader');
74const CJSModule = Module;
75
76// const packageJsonReader = require('internal/modules/package_json_reader');
77const packageJsonReader = require('./node-internal-modules-package_json_reader');
78const userConditions = getOptionValue('--conditions');
79const DEFAULT_CONDITIONS = ObjectFreeze(['node', 'import', ...userConditions]);
80const DEFAULT_CONDITIONS_SET = new SafeSet(DEFAULT_CONDITIONS);
81
82const pendingDeprecation = getOptionValue('--pending-deprecation');
83
84/**
85 * @param {{
86 * extensions: import('../src/file-extensions').Extensions,
87 * preferTsExts: boolean | undefined;
88 * tsNodeExperimentalSpecifierResolution: import('../src/index').ExperimentalSpecifierResolution | undefined;
89 * }} opts
90 */
91function createResolve(opts) {
92// TODO receive cached fs implementations here
93const {preferTsExts, tsNodeExperimentalSpecifierResolution, extensions} = opts;
94const esrnExtensions = extensions.experimentalSpecifierResolutionAddsIfOmitted;
95const {legacyMainResolveAddsIfOmitted, replacementsForCjs, replacementsForJs, replacementsForMjs, replacementsForJsx} = extensions;
96// const experimentalSpecifierResolution = tsNodeExperimentalSpecifierResolution ?? getOptionValue('--experimental-specifier-resolution');
97const experimentalSpecifierResolution = tsNodeExperimentalSpecifierResolution != null ? tsNodeExperimentalSpecifierResolution : getOptionValue('--experimental-specifier-resolution');
98
99const emittedPackageWarnings = new SafeSet();
100function emitFolderMapDeprecation(match, pjsonUrl, isExports, base) {
101 const pjsonPath = fileURLToPath(pjsonUrl);
102 if (!pendingDeprecation) {
103 const nodeModulesIndex = StringPrototypeLastIndexOf(pjsonPath,
104 '/node_modules/');
105 if (nodeModulesIndex !== -1) {
106 const afterNodeModulesPath = StringPrototypeSlice(pjsonPath,
107 nodeModulesIndex + 14,
108 -13);
109 try {
110 const { packageSubpath } = parsePackageName(afterNodeModulesPath);
111 if (packageSubpath === '.')
112 return;
113 } catch {}
114 }
115 }
116 if (emittedPackageWarnings.has(pjsonPath + '|' + match))
117 return;
118 emittedPackageWarnings.add(pjsonPath + '|' + match);
119 process.emitWarning(
120 `Use of deprecated folder mapping "${match}" in the ${isExports ?
121 '"exports"' : '"imports"'} field module resolution of the package at ${
122 pjsonPath}${base ? ` imported from ${fileURLToPath(base)}` : ''}.\n` +
123 `Update this package.json to use a subpath pattern like "${match}*".`,
124 'DeprecationWarning',
125 'DEP0148'
126 );
127}
128
129function getConditionsSet(conditions) {
130 if (conditions !== undefined && conditions !== DEFAULT_CONDITIONS) {
131 if (!ArrayIsArray(conditions)) {
132 throw new ERR_INVALID_ARG_VALUE('conditions', conditions,
133 'expected an array');
134 }
135 return new SafeSet(conditions);
136 }
137 return DEFAULT_CONDITIONS_SET;
138}
139
140const realpathCache = new SafeMap();
141const packageJSONCache = new SafeMap(); /* string -> PackageConfig */
142
143const statSupportsThrowIfNoEntry = versionGteLt(process.versions.node, '15.3.0') ||
144 versionGteLt(process.versions.node, '14.17.0', '15.0.0');
145const tryStatSync = statSupportsThrowIfNoEntry ? tryStatSyncWithoutErrors : tryStatSyncWithErrors;
146const statsIfNotFound = new Stats();
147function tryStatSyncWithoutErrors(path) {
148 const stats = statSync(path, { throwIfNoEntry: false });
149 if(stats != null) return stats;
150 return statsIfNotFound;
151}
152function tryStatSyncWithErrors(path) {
153 try {
154 return statSync(path);
155 } catch {
156 return statsIfNotFound;
157 }
158}
159
160function getPackageConfig(path, specifier, base) {
161 const existing = packageJSONCache.get(path);
162 if (existing !== undefined) {
163 return existing;
164 }
165 const source = packageJsonReader.read(path).string;
166 if (source === undefined) {
167 const packageConfig = {
168 pjsonPath: path,
169 exists: false,
170 main: undefined,
171 name: undefined,
172 type: 'none',
173 exports: undefined,
174 imports: undefined,
175 };
176 packageJSONCache.set(path, packageConfig);
177 return packageConfig;
178 }
179
180 let packageJSON;
181 try {
182 packageJSON = JSONParse(source);
183 } catch (error) {
184 throw new ERR_INVALID_PACKAGE_CONFIG(
185 path,
186 (base ? `"${specifier}" from ` : '') + fileURLToPath(base || specifier),
187 error.message
188 );
189 }
190
191 let { imports, main, name, type } = packageJSON;
192 const { exports } = packageJSON;
193 if (typeof imports !== 'object' || imports === null) imports = undefined;
194 if (typeof main !== 'string') main = undefined;
195 if (typeof name !== 'string') name = undefined;
196 // Ignore unknown types for forwards compatibility
197 if (type !== 'module' && type !== 'commonjs') type = 'none';
198
199 const packageConfig = {
200 pjsonPath: path,
201 exists: true,
202 main,
203 name,
204 type,
205 exports,
206 imports,
207 };
208 packageJSONCache.set(path, packageConfig);
209 return packageConfig;
210}
211
212function getPackageScopeConfig(resolved) {
213 let packageJSONUrl = new URL('./package.json', resolved);
214 while (true) {
215 const packageJSONPath = packageJSONUrl.pathname;
216 if (StringPrototypeEndsWith(packageJSONPath, 'node_modules/package.json'))
217 break;
218 const packageConfig = getPackageConfig(fileURLToPath(packageJSONUrl),
219 resolved);
220 if (packageConfig.exists) return packageConfig;
221
222 const lastPackageJSONUrl = packageJSONUrl;
223 packageJSONUrl = new URL('../package.json', packageJSONUrl);
224
225 // Terminates at root where ../package.json equals ../../package.json
226 // (can't just check "/package.json" for Windows support).
227 if (packageJSONUrl.pathname === lastPackageJSONUrl.pathname) break;
228 }
229 const packageJSONPath = fileURLToPath(packageJSONUrl);
230 const packageConfig = {
231 pjsonPath: packageJSONPath,
232 exists: false,
233 main: undefined,
234 name: undefined,
235 type: 'none',
236 exports: undefined,
237 imports: undefined,
238 };
239 packageJSONCache.set(packageJSONPath, packageConfig);
240 return packageConfig;
241}
242
243/*
244 * Legacy CommonJS main resolution:
245 * 1. let M = pkg_url + (json main field)
246 * 2. TRY(M, M.js, M.json, M.node)
247 * 3. TRY(M/index.js, M/index.json, M/index.node)
248 * 4. TRY(pkg_url/index.js, pkg_url/index.json, pkg_url/index.node)
249 * 5. NOT_FOUND
250 */
251function fileExists(url) {
252 return tryStatSync(fileURLToPath(url)).isFile();
253}
254
255function legacyMainResolve(packageJSONUrl, packageConfig, base) {
256 let guess;
257 if (packageConfig.main !== undefined) {
258 // Note: fs check redundances will be handled by Descriptor cache here.
259 if(guess = resolveReplacementExtensions(new URL(`./${packageConfig.main}`, packageJSONUrl))) {
260 return guess;
261 }
262 if (fileExists(guess = new URL(`./${packageConfig.main}`,
263 packageJSONUrl))) {
264 return guess;
265 }
266 for(const extension of legacyMainResolveAddsIfOmitted) {
267 if (fileExists(guess = new URL(`./${packageConfig.main}${extension}`,
268 packageJSONUrl))) {
269 return guess;
270 }
271 }
272 for(const extension of legacyMainResolveAddsIfOmitted) {
273 if (fileExists(guess = new URL(`./${packageConfig.main}/index${extension}`,
274 packageJSONUrl))) {
275 return guess;
276 }
277 }
278 // Fallthrough.
279 }
280 for(const extension of legacyMainResolveAddsIfOmitted) {
281 if (fileExists(guess = new URL(`./index${extension}`, packageJSONUrl))) {
282 return guess;
283 }
284 }
285 // Not found.
286 throw new ERR_MODULE_NOT_FOUND(
287 fileURLToPath(new URL('.', packageJSONUrl)), fileURLToPath(base));
288}
289
290/** attempts replacement extensions, then tries exact name, then attempts appending extensions */
291function resolveExtensionsWithTryExactName(search) {
292 const resolvedReplacementExtension = resolveReplacementExtensions(search);
293 if(resolvedReplacementExtension) return resolvedReplacementExtension;
294 if (fileExists(search)) return search;
295 return resolveExtensions(search);
296}
297
298// This appends missing extensions
299function resolveExtensions(search) {
300 for (let i = 0; i < esrnExtensions.length; i++) {
301 const extension = esrnExtensions[i];
302 const guess = new URL(`${search.pathname}${extension}`, search);
303 if (fileExists(guess)) return guess;
304 }
305 return undefined;
306}
307
308/** This replaces JS with TS extensions */
309function resolveReplacementExtensions(search) {
310 const lastDotIndex = search.pathname.lastIndexOf('.');
311 if(lastDotIndex >= 0) {
312 const ext = search.pathname.slice(lastDotIndex);
313 if (ext === '.js' || ext === '.jsx' || ext === '.mjs' || ext === '.cjs') {
314 const pathnameWithoutExtension = search.pathname.slice(0, lastDotIndex);
315 const replacementExts =
316 ext === '.js' ? replacementsForJs
317 : ext === '.jsx' ? replacementsForJsx
318 : ext === '.mjs' ? replacementsForMjs
319 : replacementsForCjs;
320 const guess = new URL(search.toString());
321 for (let i = 0; i < replacementExts.length; i++) {
322 const extension = replacementExts[i];
323 guess.pathname = `${pathnameWithoutExtension}${extension}`;
324 if (fileExists(guess)) return guess;
325 }
326 }
327 }
328 return undefined;
329}
330
331function resolveIndex(search) {
332 return resolveExtensions(new URL('index', search));
333}
334
335const encodedSepRegEx = /%2F|%2C/i;
336function finalizeResolution(resolved, base) {
337 if (RegExpPrototypeTest(encodedSepRegEx, resolved.pathname))
338 throw new ERR_INVALID_MODULE_SPECIFIER(
339 resolved.pathname, 'must not include encoded "/" or "\\" characters',
340 fileURLToPath(base));
341
342 if (experimentalSpecifierResolution === 'node') {
343 const path = fileURLToPath(resolved);
344 let file = resolveExtensionsWithTryExactName(resolved);
345 if (file !== undefined) return file;
346 if (!StringPrototypeEndsWith(path, '/')) {
347 file = resolveIndex(new URL(`${resolved}/`));
348 if (file !== undefined) return file;
349 } else {
350 return resolveIndex(resolved) || resolved;
351 }
352 throw new ERR_MODULE_NOT_FOUND(
353 resolved.pathname, fileURLToPath(base), 'module');
354 }
355
356 const file = resolveReplacementExtensions(resolved) || resolved;
357 const path = fileURLToPath(file);
358
359 const stats = tryStatSync(StringPrototypeEndsWith(path, '/') ?
360 StringPrototypeSlice(path, -1) : path);
361 if (stats.isDirectory()) {
362 const err = new ERR_UNSUPPORTED_DIR_IMPORT(path, fileURLToPath(base));
363 err.url = String(resolved);
364 throw err;
365 } else if (!stats.isFile()) {
366 throw new ERR_MODULE_NOT_FOUND(
367 path || resolved.pathname, fileURLToPath(base), 'module');
368 }
369
370 return file;
371}
372
373function throwImportNotDefined(specifier, packageJSONUrl, base) {
374 throw new ERR_PACKAGE_IMPORT_NOT_DEFINED(
375 specifier, packageJSONUrl && fileURLToPath(new URL('.', packageJSONUrl)),
376 fileURLToPath(base));
377}
378
379function throwExportsNotFound(subpath, packageJSONUrl, base) {
380 throw new ERR_PACKAGE_PATH_NOT_EXPORTED(
381 fileURLToPath(new URL('.', packageJSONUrl)), subpath,
382 base && fileURLToPath(base));
383}
384
385function throwInvalidSubpath(subpath, packageJSONUrl, internal, base) {
386 const reason = `request is not a valid subpath for the "${internal ?
387 'imports' : 'exports'}" resolution of ${fileURLToPath(packageJSONUrl)}`;
388 throw new ERR_INVALID_MODULE_SPECIFIER(subpath, reason,
389 base && fileURLToPath(base));
390}
391
392function throwInvalidPackageTarget(
393 subpath, target, packageJSONUrl, internal, base) {
394 if (typeof target === 'object' && target !== null) {
395 target = JSONStringify(target, null, '');
396 } else {
397 target = `${target}`;
398 }
399 throw new ERR_INVALID_PACKAGE_TARGET(
400 fileURLToPath(new URL('.', packageJSONUrl)), subpath, target,
401 internal, base && fileURLToPath(base));
402}
403
404const invalidSegmentRegEx = /(^|\\|\/)(\.\.?|node_modules)(\\|\/|$)/;
405const patternRegEx = /\*/g;
406
407function resolvePackageTargetString(
408 target, subpath, match, packageJSONUrl, base, pattern, internal, conditions) {
409 if (subpath !== '' && !pattern && target[target.length - 1] !== '/')
410 throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base);
411
412 if (!StringPrototypeStartsWith(target, './')) {
413 if (internal && !StringPrototypeStartsWith(target, '../') &&
414 !StringPrototypeStartsWith(target, '/')) {
415 let isURL = false;
416 try {
417 new URL(target);
418 isURL = true;
419 } catch {}
420 if (!isURL) {
421 const exportTarget = pattern ?
422 StringPrototypeReplace(target, patternRegEx, subpath) :
423 target + subpath;
424 return packageResolve(exportTarget, packageJSONUrl, conditions);
425 }
426 }
427 throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base);
428 }
429
430 if (RegExpPrototypeTest(invalidSegmentRegEx, StringPrototypeSlice(target, 2)))
431 throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base);
432
433 const resolved = new URL(target, packageJSONUrl);
434 const resolvedPath = resolved.pathname;
435 const packagePath = new URL('.', packageJSONUrl).pathname;
436
437 if (!StringPrototypeStartsWith(resolvedPath, packagePath))
438 throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base);
439
440 if (subpath === '') return resolved;
441
442 if (RegExpPrototypeTest(invalidSegmentRegEx, subpath))
443 throwInvalidSubpath(match + subpath, packageJSONUrl, internal, base);
444
445 if (pattern)
446 return new URL(StringPrototypeReplace(resolved.href, patternRegEx,
447 subpath));
448 return new URL(subpath, resolved);
449}
450
451/**
452 * @param {string} key
453 * @returns {boolean}
454 */
455function isArrayIndex(key) {
456 const keyNum = +key;
457 if (`${keyNum}` !== key) return false;
458 return keyNum >= 0 && keyNum < 0xFFFF_FFFF;
459}
460
461function resolvePackageTarget(packageJSONUrl, target, subpath, packageSubpath,
462 base, pattern, internal, conditions) {
463 if (typeof target === 'string') {
464 return resolvePackageTargetString(
465 target, subpath, packageSubpath, packageJSONUrl, base, pattern, internal,
466 conditions);
467 } else if (ArrayIsArray(target)) {
468 if (target.length === 0)
469 return null;
470
471 let lastException;
472 for (let i = 0; i < target.length; i++) {
473 const targetItem = target[i];
474 let resolved;
475 try {
476 resolved = resolvePackageTarget(
477 packageJSONUrl, targetItem, subpath, packageSubpath, base, pattern,
478 internal, conditions);
479 } catch (e) {
480 lastException = e;
481 if (e.code === 'ERR_INVALID_PACKAGE_TARGET')
482 continue;
483 throw e;
484 }
485 if (resolved === undefined)
486 continue;
487 if (resolved === null) {
488 lastException = null;
489 continue;
490 }
491 return resolved;
492 }
493 if (lastException === undefined || lastException === null)
494 return lastException;
495 throw lastException;
496 } else if (typeof target === 'object' && target !== null) {
497 const keys = ObjectGetOwnPropertyNames(target);
498 for (let i = 0; i < keys.length; i++) {
499 const key = keys[i];
500 if (isArrayIndex(key)) {
501 throw new ERR_INVALID_PACKAGE_CONFIG(
502 fileURLToPath(packageJSONUrl), base,
503 '"exports" cannot contain numeric property keys.');
504 }
505 }
506 for (let i = 0; i < keys.length; i++) {
507 const key = keys[i];
508 if (key === 'default' || conditions.has(key)) {
509 const conditionalTarget = target[key];
510 const resolved = resolvePackageTarget(
511 packageJSONUrl, conditionalTarget, subpath, packageSubpath, base,
512 pattern, internal, conditions);
513 if (resolved === undefined)
514 continue;
515 return resolved;
516 }
517 }
518 return undefined;
519 } else if (target === null) {
520 return null;
521 }
522 throwInvalidPackageTarget(packageSubpath, target, packageJSONUrl, internal,
523 base);
524}
525
526function isConditionalExportsMainSugar(exports, packageJSONUrl, base) {
527 if (typeof exports === 'string' || ArrayIsArray(exports)) return true;
528 if (typeof exports !== 'object' || exports === null) return false;
529
530 const keys = ObjectGetOwnPropertyNames(exports);
531 let isConditionalSugar = false;
532 let i = 0;
533 for (let j = 0; j < keys.length; j++) {
534 const key = keys[j];
535 const curIsConditionalSugar = key === '' || key[0] !== '.';
536 if (i++ === 0) {
537 isConditionalSugar = curIsConditionalSugar;
538 } else if (isConditionalSugar !== curIsConditionalSugar) {
539 throw new ERR_INVALID_PACKAGE_CONFIG(
540 fileURLToPath(packageJSONUrl), base,
541 '"exports" cannot contain some keys starting with \'.\' and some not.' +
542 ' The exports object must either be an object of package subpath keys' +
543 ' or an object of main entry condition name keys only.');
544 }
545 }
546 return isConditionalSugar;
547}
548
549/**
550 * @param {URL} packageJSONUrl
551 * @param {string} packageSubpath
552 * @param {object} packageConfig
553 * @param {string} base
554 * @param {Set<string>} conditions
555 * @returns {{resolved: URL, exact: boolean}}
556 */
557function packageExportsResolve(
558 packageJSONUrl, packageSubpath, packageConfig, base, conditions) {
559 let exports = packageConfig.exports;
560 if (isConditionalExportsMainSugar(exports, packageJSONUrl, base))
561 exports = { '.': exports };
562
563 if (ObjectPrototypeHasOwnProperty(exports, packageSubpath)) {
564 const target = exports[packageSubpath];
565 const resolved = resolvePackageTarget(
566 packageJSONUrl, target, '', packageSubpath, base, false, false, conditions
567 );
568 if (resolved === null || resolved === undefined)
569 throwExportsNotFound(packageSubpath, packageJSONUrl, base);
570 return { resolved, exact: true };
571 }
572
573 let bestMatch = '';
574 const keys = ObjectGetOwnPropertyNames(exports);
575 for (let i = 0; i < keys.length; i++) {
576 const key = keys[i];
577 if (key[key.length - 1] === '*' &&
578 StringPrototypeStartsWith(packageSubpath,
579 StringPrototypeSlice(key, 0, -1)) &&
580 packageSubpath.length >= key.length &&
581 key.length > bestMatch.length) {
582 bestMatch = key;
583 } else if (key[key.length - 1] === '/' &&
584 StringPrototypeStartsWith(packageSubpath, key) &&
585 key.length > bestMatch.length) {
586 bestMatch = key;
587 }
588 }
589
590 if (bestMatch) {
591 const target = exports[bestMatch];
592 const pattern = bestMatch[bestMatch.length - 1] === '*';
593 const subpath = StringPrototypeSubstr(packageSubpath, bestMatch.length -
594 (pattern ? 1 : 0));
595 const resolved = resolvePackageTarget(packageJSONUrl, target, subpath,
596 bestMatch, base, pattern, false,
597 conditions);
598 if (resolved === null || resolved === undefined)
599 throwExportsNotFound(packageSubpath, packageJSONUrl, base);
600 if (!pattern)
601 emitFolderMapDeprecation(bestMatch, packageJSONUrl, true, base);
602 return { resolved, exact: pattern };
603 }
604
605 throwExportsNotFound(packageSubpath, packageJSONUrl, base);
606}
607
608function packageImportsResolve(name, base, conditions) {
609 if (name === '#' || StringPrototypeStartsWith(name, '#/')) {
610 const reason = 'is not a valid internal imports specifier name';
611 throw new ERR_INVALID_MODULE_SPECIFIER(name, reason, fileURLToPath(base));
612 }
613 let packageJSONUrl;
614 const packageConfig = getPackageScopeConfig(base);
615 if (packageConfig.exists) {
616 packageJSONUrl = pathToFileURL(packageConfig.pjsonPath);
617 const imports = packageConfig.imports;
618 if (imports) {
619 if (ObjectPrototypeHasOwnProperty(imports, name)) {
620 const resolved = resolvePackageTarget(
621 packageJSONUrl, imports[name], '', name, base, false, true, conditions
622 );
623 if (resolved !== null)
624 return { resolved, exact: true };
625 } else {
626 let bestMatch = '';
627 const keys = ObjectGetOwnPropertyNames(imports);
628 for (let i = 0; i < keys.length; i++) {
629 const key = keys[i];
630 if (key[key.length - 1] === '*' &&
631 StringPrototypeStartsWith(name,
632 StringPrototypeSlice(key, 0, -1)) &&
633 name.length >= key.length &&
634 key.length > bestMatch.length) {
635 bestMatch = key;
636 } else if (key[key.length - 1] === '/' &&
637 StringPrototypeStartsWith(name, key) &&
638 key.length > bestMatch.length) {
639 bestMatch = key;
640 }
641 }
642
643 if (bestMatch) {
644 const target = imports[bestMatch];
645 const pattern = bestMatch[bestMatch.length - 1] === '*';
646 const subpath = StringPrototypeSubstr(name, bestMatch.length -
647 (pattern ? 1 : 0));
648 const resolved = resolvePackageTarget(
649 packageJSONUrl, target, subpath, bestMatch, base, pattern, true,
650 conditions);
651 if (resolved !== null) {
652 if (!pattern)
653 emitFolderMapDeprecation(bestMatch, packageJSONUrl, false, base);
654 return { resolved, exact: pattern };
655 }
656 }
657 }
658 }
659 }
660 throwImportNotDefined(name, packageJSONUrl, base);
661}
662
663function getPackageType(url) {
664 const packageConfig = getPackageScopeConfig(url);
665 return packageConfig.type;
666}
667
668function parsePackageName(specifier, base) {
669 let separatorIndex = StringPrototypeIndexOf(specifier, '/');
670 let validPackageName = true;
671 let isScoped = false;
672 if (specifier[0] === '@') {
673 isScoped = true;
674 if (separatorIndex === -1 || specifier.length === 0) {
675 validPackageName = false;
676 } else {
677 separatorIndex = StringPrototypeIndexOf(
678 specifier, '/', separatorIndex + 1);
679 }
680 }
681
682 const packageName = separatorIndex === -1 ?
683 specifier : StringPrototypeSlice(specifier, 0, separatorIndex);
684
685 // Package name cannot have leading . and cannot have percent-encoding or
686 // separators.
687 for (let i = 0; i < packageName.length; i++) {
688 if (packageName[i] === '%' || packageName[i] === '\\') {
689 validPackageName = false;
690 break;
691 }
692 }
693
694 if (!validPackageName) {
695 throw new ERR_INVALID_MODULE_SPECIFIER(
696 specifier, 'is not a valid package name', fileURLToPath(base));
697 }
698
699 const packageSubpath = '.' + (separatorIndex === -1 ? '' :
700 StringPrototypeSlice(specifier, separatorIndex));
701
702 return { packageName, packageSubpath, isScoped };
703}
704
705/**
706 * @param {string} specifier
707 * @param {URL} base
708 * @param {Set<string>} conditions
709 * @returns {URL}
710 */
711function packageResolve(specifier, base, conditions) {
712 const { packageName, packageSubpath, isScoped } =
713 parsePackageName(specifier, base);
714
715 // ResolveSelf
716 const packageConfig = getPackageScopeConfig(base);
717 if (packageConfig.exists) {
718 const packageJSONUrl = pathToFileURL(packageConfig.pjsonPath);
719 if (packageConfig.name === packageName &&
720 packageConfig.exports !== undefined && packageConfig.exports !== null) {
721 return packageExportsResolve(
722 packageJSONUrl, packageSubpath, packageConfig, base, conditions
723 ).resolved;
724 }
725 }
726
727 let packageJSONUrl =
728 new URL('./node_modules/' + packageName + '/package.json', base);
729 let packageJSONPath = fileURLToPath(packageJSONUrl);
730 let lastPath;
731 do {
732 const stat = tryStatSync(StringPrototypeSlice(packageJSONPath, 0,
733 packageJSONPath.length - 13));
734 if (!stat.isDirectory()) {
735 lastPath = packageJSONPath;
736 packageJSONUrl = new URL((isScoped ?
737 '../../../../node_modules/' : '../../../node_modules/') +
738 packageName + '/package.json', packageJSONUrl);
739 packageJSONPath = fileURLToPath(packageJSONUrl);
740 continue;
741 }
742
743 // Package match.
744 const packageConfig = getPackageConfig(packageJSONPath, specifier, base);
745 if (packageConfig.exports !== undefined && packageConfig.exports !== null)
746 return packageExportsResolve(
747 packageJSONUrl, packageSubpath, packageConfig, base, conditions
748 ).resolved;
749 if (packageSubpath === '.')
750 return legacyMainResolve(packageJSONUrl, packageConfig, base);
751 return new URL(packageSubpath, packageJSONUrl);
752 // Cross-platform root check.
753 } while (packageJSONPath.length !== lastPath.length);
754
755 // eslint can't handle the above code.
756 // eslint-disable-next-line no-unreachable
757 throw new ERR_MODULE_NOT_FOUND(packageName, fileURLToPath(base));
758}
759
760function isBareSpecifier(specifier) {
761 return specifier[0] && specifier[0] !== '/' && specifier[0] !== '.';
762}
763
764function isRelativeSpecifier(specifier) {
765 if (specifier[0] === '.') {
766 if (specifier.length === 1 || specifier[1] === '/') return true;
767 if (specifier[1] === '.') {
768 if (specifier.length === 2 || specifier[2] === '/') return true;
769 }
770 }
771 return false;
772}
773
774function shouldBeTreatedAsRelativeOrAbsolutePath(specifier) {
775 if (specifier === '') return false;
776 if (specifier[0] === '/') return true;
777 return isRelativeSpecifier(specifier);
778}
779
780/**
781 * @param {string} specifier
782 * @param {URL} base
783 * @param {Set<string>} conditions
784 * @returns {URL}
785 */
786function moduleResolve(specifier, base, conditions) {
787 // Order swapped from spec for minor perf gain.
788 // Ok since relative URLs cannot parse as URLs.
789 let resolved;
790 if (shouldBeTreatedAsRelativeOrAbsolutePath(specifier)) {
791 resolved = new URL(specifier, base);
792 } else if (specifier[0] === '#') {
793 ({ resolved } = packageImportsResolve(specifier, base, conditions));
794 } else {
795 try {
796 resolved = new URL(specifier);
797 } catch {
798 resolved = packageResolve(specifier, base, conditions);
799 }
800 }
801 return finalizeResolution(resolved, base);
802}
803
804/**
805 * Try to resolve an import as a CommonJS module
806 * @param {string} specifier
807 * @param {string} parentURL
808 * @returns {boolean|string}
809 */
810function resolveAsCommonJS(specifier, parentURL) {
811 try {
812 const parent = fileURLToPath(parentURL);
813 const tmpModule = new CJSModule(parent, null);
814 tmpModule.paths = CJSModule._nodeModulePaths(parent);
815
816 let found = CJSModule._resolveFilename(specifier, tmpModule, false);
817
818 // If it is a relative specifier return the relative path
819 // to the parent
820 if (isRelativeSpecifier(specifier)) {
821 found = relative(parent, found);
822 // Add '.separator if the path does not start with '..separator'
823 // This should be a safe assumption because when loading
824 // esm modules there should be always a file specified so
825 // there should not be a specifier like '..' or '.'
826 if (!StringPrototypeStartsWith(found, `..${sep}`)) {
827 found = `.${sep}${found}`;
828 }
829 } else if (isBareSpecifier(specifier)) {
830 // If it is a bare specifier return the relative path within the
831 // module
832 const pkg = StringPrototypeSplit(specifier, '/')[0];
833 const index = StringPrototypeIndexOf(found, pkg);
834 if (index !== -1) {
835 found = StringPrototypeSlice(found, index);
836 }
837 }
838 // Normalize the path separator to give a valid suggestion
839 // on Windows
840 if (process.platform === 'win32') {
841 found = StringPrototypeReplace(found, new RegExp(`\\${sep}`, 'g'), '/');
842 }
843 return found;
844 } catch {
845 return false;
846 }
847}
848
849function defaultResolve(specifier, context = {}, defaultResolveUnused) {
850 let { parentURL, conditions } = context;
851 if (parentURL && policy != null && policy.manifest) {
852 const redirects = policy.manifest.getDependencyMapper(parentURL);
853 if (redirects) {
854 const { resolve, reaction } = redirects;
855 const destination = resolve(specifier, new SafeSet(conditions));
856 let missing = true;
857 if (destination === true) {
858 missing = false;
859 } else if (destination) {
860 const href = destination.href;
861 return { url: href };
862 }
863 if (missing) {
864 reaction(new ERR_MANIFEST_DEPENDENCY_MISSING(
865 parentURL,
866 specifier,
867 ArrayPrototypeJoin([...conditions], ', '))
868 );
869 }
870 }
871 }
872 let parsed;
873 try {
874 parsed = new URL(specifier);
875 if (parsed.protocol === 'data:') {
876 return {
877 url: specifier
878 };
879 }
880 } catch {}
881 if (parsed && parsed.protocol === builtinModuleProtocol)
882 return { url: specifier };
883 if (parsed && parsed.protocol !== 'file:' && parsed.protocol !== 'data:')
884 throw new ERR_UNSUPPORTED_ESM_URL_SCHEME(parsed);
885 if (NativeModule.canBeRequiredByUsers(specifier)) {
886 return {
887 url: builtinModuleProtocol + specifier
888 };
889 }
890 if (parentURL && StringPrototypeStartsWith(parentURL, 'data:')) {
891 // This is gonna blow up, we want the error
892 new URL(specifier, parentURL);
893 }
894
895 const isMain = parentURL === undefined;
896 if (isMain) {
897 parentURL = pathToFileURL(`${process.cwd()}/`).href;
898
899 // This is the initial entry point to the program, and --input-type has
900 // been passed as an option; but --input-type can only be used with
901 // --eval, --print or STDIN string input. It is not allowed with file
902 // input, to avoid user confusion over how expansive the effect of the
903 // flag should be (i.e. entry point only, package scope surrounding the
904 // entry point, etc.).
905 if (typeFlag)
906 throw new ERR_INPUT_TYPE_NOT_ALLOWED();
907 }
908
909 conditions = getConditionsSet(conditions);
910 let url;
911 try {
912 url = moduleResolve(specifier, parentURL, conditions);
913 } catch (error) {
914 // Try to give the user a hint of what would have been the
915 // resolved CommonJS module
916 if (error.code === 'ERR_MODULE_NOT_FOUND' ||
917 error.code === 'ERR_UNSUPPORTED_DIR_IMPORT') {
918 if (StringPrototypeStartsWith(specifier, 'file://')) {
919 specifier = fileURLToPath(specifier);
920 }
921 const found = resolveAsCommonJS(specifier, parentURL);
922 if (found) {
923 // Modify the stack and message string to include the hint
924 const lines = StringPrototypeSplit(error.stack, '\n');
925 const hint = `Did you mean to import ${found}?`;
926 error.stack =
927 ArrayPrototypeShift(lines) + '\n' +
928 hint + '\n' +
929 ArrayPrototypeJoin(lines, '\n');
930 error.message += `\n${hint}`;
931 }
932 }
933 throw error;
934 }
935
936 if (isMain ? !preserveSymlinksMain : !preserveSymlinks) {
937 const urlPath = fileURLToPath(url);
938 const real = realpathSync(urlPath, {
939 // [internalFS.realpathCacheKey]: realpathCache
940 });
941 const old = url;
942 url = pathToFileURL(
943 real + (StringPrototypeEndsWith(urlPath, sep) ? '/' : ''));
944 url.search = old.search;
945 url.hash = old.hash;
946 }
947
948 return { url: `${url}` };
949}
950
951return {
952 DEFAULT_CONDITIONS,
953 defaultResolve,
954 encodedSepRegEx,
955 getPackageType,
956 packageExportsResolve,
957 packageImportsResolve
958};
959}
960module.exports = {
961 createResolve
962};