node-esm-resolve-implementation.js 32 KB

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