You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

773 lines
24KB

  1. // Copied from https://raw.githubusercontent.com/nodejs/node/v13.12.0/lib/internal/modules/esm/resolve.js
  2. // Then modified to suite our needs.
  3. // Formatting is intentionally bad to keep the diff as small as possible, to make it easier to merge
  4. // upstream changes and understand our modifications.
  5. 'use strict';
  6. const {
  7. ArrayIsArray,
  8. JSONParse,
  9. JSONStringify,
  10. ObjectGetOwnPropertyNames,
  11. ObjectPrototypeHasOwnProperty,
  12. SafeMap,
  13. StringPrototypeEndsWith,
  14. StringPrototypeIncludes,
  15. StringPrototypeIndexOf,
  16. StringPrototypeSlice,
  17. StringPrototypeStartsWith,
  18. StringPrototypeSubstr,
  19. } = {
  20. ArrayIsArray: Array.isArray,
  21. JSONParse: JSON.parse,
  22. JSONStringify: JSON.stringify,
  23. ObjectGetOwnPropertyNames: Object.getOwnPropertyNames,
  24. ObjectPrototypeHasOwnProperty: (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop),
  25. SafeMap: Map,
  26. StringPrototypeEndsWith: (str, ...rest) => String.prototype.endsWith.apply(str, rest),
  27. StringPrototypeIncludes: (str, ...rest) => String.prototype.includes.apply(str, rest),
  28. StringPrototypeIndexOf: (str, ...rest) => String.prototype.indexOf.apply(str, rest),
  29. StringPrototypeSlice: (str, ...rest) => String.prototype.slice.apply(str, rest),
  30. StringPrototypeStartsWith: (str, ...rest) => String.prototype.startsWith.apply(str, rest),
  31. StringPrototypeSubstr: (str, ...rest) => String.prototype.substr.apply(str, rest),
  32. } // node pulls from `primordials` object
  33. // const internalFS = require('internal/fs/utils');
  34. // const { NativeModule } = require('internal/bootstrap/loaders');
  35. const Module = require('module')
  36. const NativeModule = {
  37. canBeRequiredByUsers(specifier) {
  38. return Module.builtinModules.includes(specifier)
  39. }
  40. }
  41. const {
  42. closeSync,
  43. fstatSync,
  44. openSync,
  45. readFileSync,
  46. realpathSync,
  47. statSync,
  48. Stats,
  49. } = require('fs');
  50. // const { getOptionValue } = require('internal/options');
  51. const { getOptionValue } = (() => {
  52. let options;
  53. function parseOptions() {
  54. if (!options) {
  55. options = {
  56. '--preserve-symlinks': false,
  57. '--preserve-symlinks-main': false,
  58. '--input-type': undefined,
  59. '--experimental-specifier-resolution': 'explicit',
  60. ...parseExecArgv()
  61. }
  62. }
  63. };
  64. function parseExecArgv () {
  65. return require('arg')({
  66. '--preserve-symlinks': Boolean,
  67. '--preserve-symlinks-main': Boolean,
  68. '--input-type': String,
  69. '--experimental-specifier-resolution': String
  70. }, {
  71. argv: process.execArgv,
  72. permissive: true
  73. });
  74. }
  75. return {
  76. getOptionValue: (opt) => {
  77. parseOptions();
  78. return options[opt];
  79. }
  80. };
  81. })();
  82. const { sep } = require('path');
  83. const preserveSymlinks = getOptionValue('--preserve-symlinks');
  84. const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
  85. const typeFlag = getOptionValue('--input-type');
  86. // const { URL, pathToFileURL, fileURLToPath } = require('internal/url');
  87. const { URL, pathToFileURL, fileURLToPath } = require('url');
  88. const {
  89. ERR_INPUT_TYPE_NOT_ALLOWED,
  90. ERR_INVALID_MODULE_SPECIFIER,
  91. ERR_INVALID_PACKAGE_CONFIG,
  92. ERR_INVALID_PACKAGE_TARGET,
  93. ERR_MODULE_NOT_FOUND,
  94. ERR_PACKAGE_PATH_NOT_EXPORTED,
  95. ERR_UNSUPPORTED_ESM_URL_SCHEME,
  96. // } = require('internal/errors').codes;
  97. } = {
  98. ERR_INPUT_TYPE_NOT_ALLOWED: createErrorCtor('ERR_INPUT_TYPE_NOT_ALLOWED'),
  99. ERR_INVALID_MODULE_SPECIFIER: createErrorCtor('ERR_INVALID_MODULE_SPECIFIER'),
  100. ERR_INVALID_PACKAGE_CONFIG: createErrorCtor('ERR_INVALID_PACKAGE_CONFIG'),
  101. ERR_INVALID_PACKAGE_TARGET: createErrorCtor('ERR_INVALID_PACKAGE_TARGET'),
  102. ERR_MODULE_NOT_FOUND: createErrorCtor('ERR_MODULE_NOT_FOUND'),
  103. ERR_PACKAGE_PATH_NOT_EXPORTED: createErrorCtor('ERR_PACKAGE_PATH_NOT_EXPORTED'),
  104. ERR_UNSUPPORTED_ESM_URL_SCHEME: createErrorCtor('ERR_UNSUPPORTED_ESM_URL_SCHEME'),
  105. }
  106. function createErrorCtor(name) {
  107. return class CustomError extends Error {
  108. constructor(...args) {
  109. super([name, ...args].join(' '))
  110. }
  111. }
  112. }
  113. function createResolve(opts) {
  114. // TODO receive cached fs implementations here
  115. const {tsExtensions, jsExtensions, preferTsExts} = opts;
  116. const realpathCache = new SafeMap();
  117. const packageJSONCache = new SafeMap(); /* string -> PackageConfig */
  118. function tryStatSync(path) {
  119. try {
  120. return statSync(path);
  121. } catch {
  122. return new Stats();
  123. }
  124. }
  125. function readIfFile(path) {
  126. let fd;
  127. try {
  128. fd = openSync(path, 'r');
  129. } catch {
  130. return undefined;
  131. }
  132. try {
  133. if (!fstatSync(fd).isFile()) return undefined;
  134. return readFileSync(fd, 'utf8');
  135. } finally {
  136. closeSync(fd);
  137. }
  138. }
  139. function getPackageConfig(path, base) {
  140. const existing = packageJSONCache.get(path);
  141. if (existing !== undefined) {
  142. if (!existing.isValid) {
  143. throw new ERR_INVALID_PACKAGE_CONFIG(path, fileURLToPath(base), false);
  144. }
  145. return existing;
  146. }
  147. const source = readIfFile(path);
  148. if (source === undefined) {
  149. const packageConfig = {
  150. exists: false,
  151. main: undefined,
  152. name: undefined,
  153. isValid: true,
  154. type: 'none',
  155. exports: undefined
  156. };
  157. packageJSONCache.set(path, packageConfig);
  158. return packageConfig;
  159. }
  160. let packageJSON;
  161. try {
  162. packageJSON = JSONParse(source);
  163. } catch {
  164. const packageConfig = {
  165. exists: true,
  166. main: undefined,
  167. name: undefined,
  168. isValid: false,
  169. type: 'none',
  170. exports: undefined
  171. };
  172. packageJSONCache.set(path, packageConfig);
  173. return packageConfig;
  174. }
  175. let { main, name, type } = packageJSON;
  176. const { exports } = packageJSON;
  177. if (typeof main !== 'string') main = undefined;
  178. if (typeof name !== 'string') name = undefined;
  179. // Ignore unknown types for forwards compatibility
  180. if (type !== 'module' && type !== 'commonjs') type = 'none';
  181. const packageConfig = {
  182. exists: true,
  183. main,
  184. name,
  185. isValid: true,
  186. type,
  187. exports
  188. };
  189. packageJSONCache.set(path, packageConfig);
  190. return packageConfig;
  191. }
  192. function getPackageScopeConfig(resolved, base) {
  193. let packageJSONUrl = new URL('./package.json', resolved);
  194. while (true) {
  195. const packageJSONPath = packageJSONUrl.pathname;
  196. if (StringPrototypeEndsWith(packageJSONPath, 'node_modules/package.json'))
  197. break;
  198. const packageConfig = getPackageConfig(fileURLToPath(packageJSONUrl), base);
  199. if (packageConfig.exists) return packageConfig;
  200. const lastPackageJSONUrl = packageJSONUrl;
  201. packageJSONUrl = new URL('../package.json', packageJSONUrl);
  202. // Terminates at root where ../package.json equals ../../package.json
  203. // (can't just check "/package.json" for Windows support).
  204. if (packageJSONUrl.pathname === lastPackageJSONUrl.pathname) break;
  205. }
  206. const packageConfig = {
  207. exists: false,
  208. main: undefined,
  209. name: undefined,
  210. isValid: true,
  211. type: 'none',
  212. exports: undefined
  213. };
  214. packageJSONCache.set(fileURLToPath(packageJSONUrl), packageConfig);
  215. return packageConfig;
  216. }
  217. /*
  218. * Legacy CommonJS main resolution:
  219. * 1. let M = pkg_url + (json main field)
  220. * 2. TRY(M, M.js, M.json, M.node)
  221. * 3. TRY(M/index.js, M/index.json, M/index.node)
  222. * 4. TRY(pkg_url/index.js, pkg_url/index.json, pkg_url/index.node)
  223. * 5. NOT_FOUND
  224. */
  225. function fileExists(url) {
  226. return tryStatSync(fileURLToPath(url)).isFile();
  227. }
  228. function legacyMainResolve(packageJSONUrl, packageConfig) {
  229. let guess;
  230. if (packageConfig.main !== undefined) {
  231. // Note: fs check redundances will be handled by Descriptor cache here.
  232. if (fileExists(guess = new URL(`./${packageConfig.main}`,
  233. packageJSONUrl))) {
  234. return guess;
  235. }
  236. if (fileExists(guess = new URL(`./${packageConfig.main}.js`,
  237. packageJSONUrl))) {
  238. return guess;
  239. }
  240. if (fileExists(guess = new URL(`./${packageConfig.main}.json`,
  241. packageJSONUrl))) {
  242. return guess;
  243. }
  244. if (fileExists(guess = new URL(`./${packageConfig.main}.node`,
  245. packageJSONUrl))) {
  246. return guess;
  247. }
  248. if (fileExists(guess = new URL(`./${packageConfig.main}/index.js`,
  249. packageJSONUrl))) {
  250. return guess;
  251. }
  252. if (fileExists(guess = new URL(`./${packageConfig.main}/index.json`,
  253. packageJSONUrl))) {
  254. return guess;
  255. }
  256. if (fileExists(guess = new URL(`./${packageConfig.main}/index.node`,
  257. packageJSONUrl))) {
  258. return guess;
  259. }
  260. // Fallthrough.
  261. }
  262. if (fileExists(guess = new URL('./index.js', packageJSONUrl))) {
  263. return guess;
  264. }
  265. // So fs.
  266. if (fileExists(guess = new URL('./index.json', packageJSONUrl))) {
  267. return guess;
  268. }
  269. if (fileExists(guess = new URL('./index.node', packageJSONUrl))) {
  270. return guess;
  271. }
  272. // Not found.
  273. return undefined;
  274. }
  275. function resolveExtensionsWithTryExactName(search) {
  276. if (fileExists(search)) return search;
  277. const resolvedReplacementExtension = resolveReplacementExtensions(search);
  278. if(resolvedReplacementExtension) return resolvedReplacementExtension;
  279. return resolveExtensions(search);
  280. }
  281. const extensions = Array.from(new Set([
  282. ...(preferTsExts ? tsExtensions : []),
  283. '.js',
  284. ...jsExtensions,
  285. '.json', '.node', '.mjs',
  286. ...tsExtensions
  287. ]));
  288. function resolveExtensions(search) {
  289. for (let i = 0; i < extensions.length; i++) {
  290. const extension = extensions[i];
  291. const guess = new URL(`${search.pathname}${extension}`, search);
  292. if (fileExists(guess)) return guess;
  293. }
  294. return undefined;
  295. }
  296. /**
  297. * TS's resolver can resolve foo.js to foo.ts, by replacing .js extension with several source extensions.
  298. * IMPORTANT: preserve ordering according to preferTsExts; this affects resolution behavior!
  299. */
  300. const replacementExtensions = extensions.filter(ext => ['.js', '.jsx', '.ts', '.tsx'].includes(ext));
  301. function resolveReplacementExtensions(search) {
  302. if (search.pathname.match(/\.js$/)) {
  303. const pathnameWithoutExtension = search.pathname.slice(0, search.pathname.length - 3);
  304. for (let i = 0; i < replacementExtensions.length; i++) {
  305. const extension = replacementExtensions[i];
  306. const guess = new URL(`${pathnameWithoutExtension}${extension}`, search);
  307. if (fileExists(guess)) return guess;
  308. }
  309. }
  310. return undefined;
  311. }
  312. function resolveIndex(search) {
  313. return resolveExtensions(new URL('index', search));
  314. }
  315. function finalizeResolution(resolved, base) {
  316. if (getOptionValue('--experimental-specifier-resolution') === 'node') {
  317. let file = resolveExtensionsWithTryExactName(resolved);
  318. if (file !== undefined) return file;
  319. if (!StringPrototypeEndsWith(resolved.pathname, '/')) {
  320. file = resolveIndex(new URL(`${resolved.pathname}/`, base));
  321. } else {
  322. file = resolveIndex(resolved);
  323. }
  324. if (file !== undefined) return file;
  325. throw new ERR_MODULE_NOT_FOUND(
  326. resolved.pathname, fileURLToPath(base), 'module');
  327. }
  328. if (StringPrototypeEndsWith(resolved.pathname, '/')) return resolved;
  329. const file = resolveReplacementExtensions(resolved) || resolved;
  330. const path = fileURLToPath(file);
  331. if (!tryStatSync(path).isFile()) {
  332. throw new ERR_MODULE_NOT_FOUND(
  333. path || resolved.pathname, fileURLToPath(base), 'module');
  334. }
  335. return file;
  336. }
  337. function throwExportsNotFound(subpath, packageJSONUrl, base) {
  338. throw new ERR_PACKAGE_PATH_NOT_EXPORTED(
  339. fileURLToPath(packageJSONUrl), subpath, fileURLToPath(base));
  340. }
  341. function throwSubpathInvalid(subpath, packageJSONUrl, base) {
  342. throw new ERR_INVALID_MODULE_SPECIFIER(
  343. fileURLToPath(packageJSONUrl), subpath, fileURLToPath(base));
  344. }
  345. function throwExportsInvalid(
  346. subpath, target, packageJSONUrl, base) {
  347. if (typeof target === 'object' && target !== null) {
  348. target = JSONStringify(target, null, '');
  349. } else if (ArrayIsArray(target)) {
  350. target = `[${target}]`;
  351. } else {
  352. target = `${target}`;
  353. }
  354. throw new ERR_INVALID_PACKAGE_TARGET(
  355. fileURLToPath(packageJSONUrl), null, subpath, target, fileURLToPath(base));
  356. }
  357. function resolveExportsTargetString(
  358. target, subpath, match, packageJSONUrl, base) {
  359. if (target[0] !== '.' || target[1] !== '/' ||
  360. (subpath !== '' && target[target.length - 1] !== '/')) {
  361. throwExportsInvalid(match, target, packageJSONUrl, base);
  362. }
  363. const resolved = new URL(target, packageJSONUrl);
  364. const resolvedPath = resolved.pathname;
  365. const packagePath = new URL('.', packageJSONUrl).pathname;
  366. if (!StringPrototypeStartsWith(resolvedPath, packagePath) ||
  367. StringPrototypeIncludes(
  368. resolvedPath, '/node_modules/', packagePath.length - 1)) {
  369. throwExportsInvalid(match, target, packageJSONUrl, base);
  370. }
  371. if (subpath === '') return resolved;
  372. const subpathResolved = new URL(subpath, resolved);
  373. const subpathResolvedPath = subpathResolved.pathname;
  374. if (!StringPrototypeStartsWith(subpathResolvedPath, resolvedPath) ||
  375. StringPrototypeIncludes(subpathResolvedPath,
  376. '/node_modules/', packagePath.length - 1)) {
  377. throwSubpathInvalid(match + subpath, packageJSONUrl, base);
  378. }
  379. return subpathResolved;
  380. }
  381. function isArrayIndex(key /* string */) { /* -> boolean */
  382. const keyNum = +key;
  383. if (`${keyNum}` !== key) return false;
  384. return keyNum >= 0 && keyNum < 0xFFFF_FFFF;
  385. }
  386. function resolveExportsTarget(
  387. packageJSONUrl, target, subpath, packageSubpath, base) {
  388. if (typeof target === 'string') {
  389. const resolved = resolveExportsTargetString(
  390. target, subpath, packageSubpath, packageJSONUrl, base);
  391. return finalizeResolution(resolved, base);
  392. } else if (ArrayIsArray(target)) {
  393. if (target.length === 0)
  394. throwExportsInvalid(packageSubpath, target, packageJSONUrl, base);
  395. let lastException;
  396. for (let i = 0; i < target.length; i++) {
  397. const targetItem = target[i];
  398. let resolved;
  399. try {
  400. resolved = resolveExportsTarget(
  401. packageJSONUrl, targetItem, subpath, packageSubpath, base);
  402. } catch (e) {
  403. lastException = e;
  404. if (e.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED' ||
  405. e.code === 'ERR_INVALID_PACKAGE_TARGET') {
  406. continue;
  407. }
  408. throw e;
  409. }
  410. return finalizeResolution(resolved, base);
  411. }
  412. throw lastException;
  413. } else if (typeof target === 'object' && target !== null) {
  414. const keys = ObjectGetOwnPropertyNames(target);
  415. for (let i = 0; i < keys.length; i++) {
  416. const key = keys[i];
  417. if (isArrayIndex(key)) {
  418. throw new ERR_INVALID_PACKAGE_CONFIG(
  419. fileURLToPath(packageJSONUrl),
  420. '"exports" cannot contain numeric property keys');
  421. }
  422. }
  423. for (let i = 0; i < keys.length; i++) {
  424. const key = keys[i];
  425. if (key === 'node' || key === 'import' || key === 'default') {
  426. const conditionalTarget = target[key];
  427. try {
  428. return resolveExportsTarget(
  429. packageJSONUrl, conditionalTarget, subpath, packageSubpath, base);
  430. } catch (e) {
  431. if (e.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED') continue;
  432. throw e;
  433. }
  434. }
  435. }
  436. throwExportsNotFound(packageSubpath, packageJSONUrl, base);
  437. }
  438. throwExportsInvalid(packageSubpath, target, packageJSONUrl, base);
  439. }
  440. function isConditionalExportsMainSugar(exports, packageJSONUrl, base) {
  441. if (typeof exports === 'string' || ArrayIsArray(exports)) return true;
  442. if (typeof exports !== 'object' || exports === null) return false;
  443. const keys = ObjectGetOwnPropertyNames(exports);
  444. let isConditionalSugar = false;
  445. let i = 0;
  446. for (let j = 0; j < keys.length; j++) {
  447. const key = keys[j];
  448. const curIsConditionalSugar = key === '' || key[0] !== '.';
  449. if (i++ === 0) {
  450. isConditionalSugar = curIsConditionalSugar;
  451. } else if (isConditionalSugar !== curIsConditionalSugar) {
  452. throw new ERR_INVALID_PACKAGE_CONFIG(
  453. fileURLToPath(packageJSONUrl),
  454. '"exports" cannot contain some keys starting with \'.\' and some not.' +
  455. ' The exports object must either be an object of package subpath keys' +
  456. ' or an object of main entry condition name keys only.');
  457. }
  458. }
  459. return isConditionalSugar;
  460. }
  461. function packageMainResolve(packageJSONUrl, packageConfig, base) {
  462. if (packageConfig.exists) {
  463. const exports = packageConfig.exports;
  464. if (exports !== undefined) {
  465. if (isConditionalExportsMainSugar(exports, packageJSONUrl, base)) {
  466. return resolveExportsTarget(packageJSONUrl, exports, '', '', base);
  467. } else if (typeof exports === 'object' && exports !== null) {
  468. const target = exports['.'];
  469. if (target !== undefined)
  470. return resolveExportsTarget(packageJSONUrl, target, '', '', base);
  471. }
  472. throw new ERR_PACKAGE_PATH_NOT_EXPORTED(packageJSONUrl, '.');
  473. }
  474. if (packageConfig.main !== undefined) {
  475. const resolved = new URL(packageConfig.main, packageJSONUrl);
  476. const path = fileURLToPath(resolved);
  477. if (tryStatSync(path).isFile()) return resolved;
  478. }
  479. if (getOptionValue('--experimental-specifier-resolution') === 'node') {
  480. if (packageConfig.main !== undefined) {
  481. return finalizeResolution(
  482. new URL(packageConfig.main, packageJSONUrl), base);
  483. } else {
  484. return finalizeResolution(
  485. new URL('index', packageJSONUrl), base);
  486. }
  487. }
  488. if (packageConfig.type !== 'module') {
  489. return legacyMainResolve(packageJSONUrl, packageConfig);
  490. }
  491. }
  492. throw new ERR_MODULE_NOT_FOUND(
  493. fileURLToPath(new URL('.', packageJSONUrl)), fileURLToPath(base));
  494. }
  495. function packageExportsResolve(
  496. packageJSONUrl, packageSubpath, packageConfig, base) /* -> URL */ {
  497. const exports = packageConfig.exports;
  498. if (exports === undefined ||
  499. isConditionalExportsMainSugar(exports, packageJSONUrl, base)) {
  500. throwExportsNotFound(packageSubpath, packageJSONUrl, base);
  501. }
  502. if (ObjectPrototypeHasOwnProperty(exports, packageSubpath)) {
  503. const target = exports[packageSubpath];
  504. const resolved = resolveExportsTarget(
  505. packageJSONUrl, target, '', packageSubpath, base);
  506. return finalizeResolution(resolved, base);
  507. }
  508. let bestMatch = '';
  509. const keys = ObjectGetOwnPropertyNames(exports);
  510. for (let i = 0; i < keys.length; i++) {
  511. const key = keys[i];
  512. if (key[key.length - 1] !== '/') continue;
  513. if (StringPrototypeStartsWith(packageSubpath, key) &&
  514. key.length > bestMatch.length) {
  515. bestMatch = key;
  516. }
  517. }
  518. if (bestMatch) {
  519. const target = exports[bestMatch];
  520. const subpath = StringPrototypeSubstr(packageSubpath, bestMatch.length);
  521. const resolved = resolveExportsTarget(
  522. packageJSONUrl, target, subpath, packageSubpath, base);
  523. return finalizeResolution(resolved, base);
  524. }
  525. throwExportsNotFound(packageSubpath, packageJSONUrl, base);
  526. }
  527. function getPackageType(url) {
  528. const packageConfig = getPackageScopeConfig(url, url);
  529. return packageConfig.type;
  530. }
  531. function packageResolve(specifier /* string */, base /* URL */) { /* -> URL */
  532. let separatorIndex = StringPrototypeIndexOf(specifier, '/');
  533. let validPackageName = true;
  534. let isScoped = false;
  535. if (specifier[0] === '@') {
  536. isScoped = true;
  537. if (separatorIndex === -1 || specifier.length === 0) {
  538. validPackageName = false;
  539. } else {
  540. separatorIndex = StringPrototypeIndexOf(
  541. specifier, '/', separatorIndex + 1);
  542. }
  543. }
  544. const packageName = separatorIndex === -1 ?
  545. specifier : StringPrototypeSlice(specifier, 0, separatorIndex);
  546. // Package name cannot have leading . and cannot have percent-encoding or
  547. // separators.
  548. for (let i = 0; i < packageName.length; i++) {
  549. if (packageName[i] === '%' || packageName[i] === '\\') {
  550. validPackageName = false;
  551. break;
  552. }
  553. }
  554. if (!validPackageName) {
  555. throw new ERR_INVALID_MODULE_SPECIFIER(
  556. specifier, undefined, fileURLToPath(base));
  557. }
  558. const packageSubpath = separatorIndex === -1 ?
  559. '' : '.' + StringPrototypeSlice(specifier, separatorIndex);
  560. // ResolveSelf
  561. const packageConfig = getPackageScopeConfig(base, base);
  562. if (packageConfig.exists) {
  563. // TODO(jkrems): Find a way to forward the pair/iterator already generated
  564. // while executing GetPackageScopeConfig
  565. let packageJSONUrl;
  566. for (const [ filename, packageConfigCandidate ] of packageJSONCache) {
  567. if (packageConfig === packageConfigCandidate) {
  568. packageJSONUrl = pathToFileURL(filename);
  569. break;
  570. }
  571. }
  572. if (packageJSONUrl !== undefined &&
  573. packageConfig.name === packageName &&
  574. packageConfig.exports !== undefined) {
  575. if (packageSubpath === './') {
  576. return new URL('./', packageJSONUrl);
  577. } else if (packageSubpath === '') {
  578. return packageMainResolve(packageJSONUrl, packageConfig, base);
  579. } else {
  580. return packageExportsResolve(
  581. packageJSONUrl, packageSubpath, packageConfig, base);
  582. }
  583. }
  584. }
  585. let packageJSONUrl =
  586. new URL('./node_modules/' + packageName + '/package.json', base);
  587. let packageJSONPath = fileURLToPath(packageJSONUrl);
  588. let lastPath;
  589. do {
  590. const stat = tryStatSync(
  591. StringPrototypeSlice(packageJSONPath, 0, packageJSONPath.length - 13));
  592. if (!stat.isDirectory()) {
  593. lastPath = packageJSONPath;
  594. packageJSONUrl = new URL((isScoped ?
  595. '../../../../node_modules/' : '../../../node_modules/') +
  596. packageName + '/package.json', packageJSONUrl);
  597. packageJSONPath = fileURLToPath(packageJSONUrl);
  598. continue;
  599. }
  600. // Package match.
  601. const packageConfig = getPackageConfig(packageJSONPath, base);
  602. if (packageSubpath === './') {
  603. return new URL('./', packageJSONUrl);
  604. } else if (packageSubpath === '') {
  605. return packageMainResolve(packageJSONUrl, packageConfig, base);
  606. } else if (packageConfig.exports !== undefined) {
  607. return packageExportsResolve(
  608. packageJSONUrl, packageSubpath, packageConfig, base);
  609. } else {
  610. return finalizeResolution(
  611. new URL(packageSubpath, packageJSONUrl), base);
  612. }
  613. // Cross-platform root check.
  614. } while (packageJSONPath.length !== lastPath.length);
  615. // eslint can't handle the above code.
  616. // eslint-disable-next-line no-unreachable
  617. throw new ERR_MODULE_NOT_FOUND(packageName, fileURLToPath(base));
  618. }
  619. function shouldBeTreatedAsRelativeOrAbsolutePath(specifier) {
  620. if (specifier === '') return false;
  621. if (specifier[0] === '/') return true;
  622. if (specifier[0] === '.') {
  623. if (specifier.length === 1 || specifier[1] === '/') return true;
  624. if (specifier[1] === '.') {
  625. if (specifier.length === 2 || specifier[2] === '/') return true;
  626. }
  627. }
  628. return false;
  629. }
  630. function moduleResolve(specifier /* string */, base /* URL */) { /* -> URL */
  631. // Order swapped from spec for minor perf gain.
  632. // Ok since relative URLs cannot parse as URLs.
  633. let resolved;
  634. if (shouldBeTreatedAsRelativeOrAbsolutePath(specifier)) {
  635. resolved = new URL(specifier, base);
  636. } else {
  637. try {
  638. resolved = new URL(specifier);
  639. } catch {
  640. return packageResolve(specifier, base);
  641. }
  642. }
  643. return finalizeResolution(resolved, base);
  644. }
  645. function defaultResolve(specifier, { parentURL } = {}, defaultResolveUnused) {
  646. let parsed;
  647. try {
  648. parsed = new URL(specifier);
  649. if (parsed.protocol === 'data:') {
  650. return {
  651. url: specifier
  652. };
  653. }
  654. } catch {}
  655. if (parsed && parsed.protocol === 'nodejs:')
  656. return { url: specifier };
  657. if (parsed && parsed.protocol !== 'file:' && parsed.protocol !== 'data:')
  658. throw new ERR_UNSUPPORTED_ESM_URL_SCHEME();
  659. if (NativeModule.canBeRequiredByUsers(specifier)) {
  660. return {
  661. url: 'nodejs:' + specifier
  662. };
  663. }
  664. if (parentURL && StringPrototypeStartsWith(parentURL, 'data:')) {
  665. // This is gonna blow up, we want the error
  666. new URL(specifier, parentURL);
  667. }
  668. const isMain = parentURL === undefined;
  669. if (isMain) {
  670. parentURL = pathToFileURL(`${process.cwd()}/`).href;
  671. // This is the initial entry point to the program, and --input-type has
  672. // been passed as an option; but --input-type can only be used with
  673. // --eval, --print or STDIN string input. It is not allowed with file
  674. // input, to avoid user confusion over how expansive the effect of the
  675. // flag should be (i.e. entry point only, package scope surrounding the
  676. // entry point, etc.).
  677. if (typeFlag)
  678. throw new ERR_INPUT_TYPE_NOT_ALLOWED();
  679. }
  680. let url = moduleResolve(specifier, new URL(parentURL));
  681. if (isMain ? !preserveSymlinksMain : !preserveSymlinks) {
  682. const urlPath = fileURLToPath(url);
  683. const real = realpathSync(urlPath, {
  684. // [internalFS.realpathCacheKey]: realpathCache
  685. });
  686. const old = url;
  687. url = pathToFileURL(real + (urlPath.endsWith(sep) ? '/' : ''));
  688. url.search = old.search;
  689. url.hash = old.hash;
  690. }
  691. return { url: `${url}` };
  692. }
  693. return {
  694. defaultResolve,
  695. getPackageType
  696. };
  697. }
  698. module.exports = {
  699. createResolve
  700. }