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