UNPKG

34.6 kBJavaScriptView Raw
1import path, { dirname, resolve, extname, normalize, sep } from 'path';
2import isBuiltinModule from 'is-builtin-module';
3import deepMerge from 'deepmerge';
4import isModule from 'is-module';
5import fs, { realpathSync } from 'fs';
6import { promisify } from 'util';
7import { pathToFileURL, fileURLToPath } from 'url';
8import resolve$1 from 'resolve';
9import { createFilter } from '@rollup/pluginutils';
10
11var version = "14.0.1";
12
13promisify(fs.access);
14const readFile$1 = promisify(fs.readFile);
15const realpath = promisify(fs.realpath);
16const stat = promisify(fs.stat);
17
18async function fileExists(filePath) {
19 try {
20 const res = await stat(filePath);
21 return res.isFile();
22 } catch {
23 return false;
24 }
25}
26
27async function resolveSymlink(path) {
28 return (await fileExists(path)) ? realpath(path) : path;
29}
30
31const onError = (error) => {
32 if (error.code === 'ENOENT') {
33 return false;
34 }
35 throw error;
36};
37
38const makeCache = (fn) => {
39 const cache = new Map();
40 const wrapped = async (param, done) => {
41 if (cache.has(param) === false) {
42 cache.set(
43 param,
44 fn(param).catch((err) => {
45 cache.delete(param);
46 throw err;
47 })
48 );
49 }
50
51 try {
52 const result = cache.get(param);
53 const value = await result;
54 return done(null, value);
55 } catch (error) {
56 return done(error);
57 }
58 };
59
60 wrapped.clear = () => cache.clear();
61
62 return wrapped;
63};
64
65const isDirCached = makeCache(async (file) => {
66 try {
67 const stats = await stat(file);
68 return stats.isDirectory();
69 } catch (error) {
70 return onError(error);
71 }
72});
73
74const isFileCached = makeCache(async (file) => {
75 try {
76 const stats = await stat(file);
77 return stats.isFile();
78 } catch (error) {
79 return onError(error);
80 }
81});
82
83const readCachedFile = makeCache(readFile$1);
84
85function handleDeprecatedOptions(opts) {
86 const warnings = [];
87
88 if (opts.customResolveOptions) {
89 const { customResolveOptions } = opts;
90 if (customResolveOptions.moduleDirectory) {
91 // eslint-disable-next-line no-param-reassign
92 opts.moduleDirectories = Array.isArray(customResolveOptions.moduleDirectory)
93 ? customResolveOptions.moduleDirectory
94 : [customResolveOptions.moduleDirectory];
95
96 warnings.push(
97 'node-resolve: The `customResolveOptions.moduleDirectory` option has been deprecated. Use `moduleDirectories`, which must be an array.'
98 );
99 }
100
101 if (customResolveOptions.preserveSymlinks) {
102 throw new Error(
103 'node-resolve: `customResolveOptions.preserveSymlinks` is no longer an option. We now always use the rollup `preserveSymlinks` option.'
104 );
105 }
106
107 [
108 'basedir',
109 'package',
110 'extensions',
111 'includeCoreModules',
112 'readFile',
113 'isFile',
114 'isDirectory',
115 'realpath',
116 'packageFilter',
117 'pathFilter',
118 'paths',
119 'packageIterator'
120 ].forEach((resolveOption) => {
121 if (customResolveOptions[resolveOption]) {
122 throw new Error(
123 `node-resolve: \`customResolveOptions.${resolveOption}\` is no longer an option. If you need this, please open an issue.`
124 );
125 }
126 });
127 }
128
129 return { warnings };
130}
131
132// returns the imported package name for bare module imports
133function getPackageName(id) {
134 if (id.startsWith('.') || id.startsWith('/')) {
135 return null;
136 }
137
138 const split = id.split('/');
139
140 // @my-scope/my-package/foo.js -> @my-scope/my-package
141 // @my-scope/my-package -> @my-scope/my-package
142 if (split[0][0] === '@') {
143 return `${split[0]}/${split[1]}`;
144 }
145
146 // my-package/foo.js -> my-package
147 // my-package -> my-package
148 return split[0];
149}
150
151function getMainFields(options) {
152 let mainFields;
153 if (options.mainFields) {
154 ({ mainFields } = options);
155 } else {
156 mainFields = ['module', 'main'];
157 }
158 if (options.browser && mainFields.indexOf('browser') === -1) {
159 return ['browser'].concat(mainFields);
160 }
161 if (!mainFields.length) {
162 throw new Error('Please ensure at least one `mainFields` value is specified');
163 }
164 return mainFields;
165}
166
167function getPackageInfo(options) {
168 const {
169 cache,
170 extensions,
171 pkg,
172 mainFields,
173 preserveSymlinks,
174 useBrowserOverrides,
175 rootDir,
176 ignoreSideEffectsForRoot
177 } = options;
178 let { pkgPath } = options;
179
180 if (cache.has(pkgPath)) {
181 return cache.get(pkgPath);
182 }
183
184 // browserify/resolve doesn't realpath paths returned in its packageFilter callback
185 if (!preserveSymlinks) {
186 pkgPath = realpathSync(pkgPath);
187 }
188
189 const pkgRoot = dirname(pkgPath);
190
191 const packageInfo = {
192 // copy as we are about to munge the `main` field of `pkg`.
193 packageJson: { ...pkg },
194
195 // path to package.json file
196 packageJsonPath: pkgPath,
197
198 // directory containing the package.json
199 root: pkgRoot,
200
201 // which main field was used during resolution of this module (main, module, or browser)
202 resolvedMainField: 'main',
203
204 // whether the browser map was used to resolve the entry point to this module
205 browserMappedMain: false,
206
207 // the entry point of the module with respect to the selected main field and any
208 // relevant browser mappings.
209 resolvedEntryPoint: ''
210 };
211
212 let overriddenMain = false;
213 for (let i = 0; i < mainFields.length; i++) {
214 const field = mainFields[i];
215 if (typeof pkg[field] === 'string') {
216 pkg.main = pkg[field];
217 packageInfo.resolvedMainField = field;
218 overriddenMain = true;
219 break;
220 }
221 }
222
223 const internalPackageInfo = {
224 cachedPkg: pkg,
225 hasModuleSideEffects: () => null,
226 hasPackageEntry: overriddenMain !== false || mainFields.indexOf('main') !== -1,
227 packageBrowserField:
228 useBrowserOverrides &&
229 typeof pkg.browser === 'object' &&
230 Object.keys(pkg.browser).reduce((browser, key) => {
231 let resolved = pkg.browser[key];
232 if (resolved && resolved[0] === '.') {
233 resolved = resolve(pkgRoot, resolved);
234 }
235 /* eslint-disable no-param-reassign */
236 browser[key] = resolved;
237 if (key[0] === '.') {
238 const absoluteKey = resolve(pkgRoot, key);
239 browser[absoluteKey] = resolved;
240 if (!extname(key)) {
241 extensions.reduce((subBrowser, ext) => {
242 subBrowser[absoluteKey + ext] = subBrowser[key];
243 return subBrowser;
244 }, browser);
245 }
246 }
247 return browser;
248 }, {}),
249 packageInfo
250 };
251
252 const browserMap = internalPackageInfo.packageBrowserField;
253 if (
254 useBrowserOverrides &&
255 typeof pkg.browser === 'object' &&
256 // eslint-disable-next-line no-prototype-builtins
257 browserMap.hasOwnProperty(pkg.main)
258 ) {
259 packageInfo.resolvedEntryPoint = browserMap[pkg.main];
260 packageInfo.browserMappedMain = true;
261 } else {
262 // index.node is technically a valid default entrypoint as well...
263 packageInfo.resolvedEntryPoint = resolve(pkgRoot, pkg.main || 'index.js');
264 packageInfo.browserMappedMain = false;
265 }
266
267 if (!ignoreSideEffectsForRoot || rootDir !== pkgRoot) {
268 const packageSideEffects = pkg.sideEffects;
269 if (typeof packageSideEffects === 'boolean') {
270 internalPackageInfo.hasModuleSideEffects = () => packageSideEffects;
271 } else if (Array.isArray(packageSideEffects)) {
272 const finalPackageSideEffects = packageSideEffects.map((sideEffect) => {
273 /*
274 * The array accepts simple glob patterns to the relevant files... Patterns like .css, which do not include a /, will be treated like **\/.css.
275 * https://webpack.js.org/guides/tree-shaking/
276 */
277 if (sideEffect.includes('/')) {
278 return sideEffect;
279 }
280 return `**/${sideEffect}`;
281 });
282 internalPackageInfo.hasModuleSideEffects = createFilter(finalPackageSideEffects, null, {
283 resolve: pkgRoot
284 });
285 }
286 }
287
288 cache.set(pkgPath, internalPackageInfo);
289 return internalPackageInfo;
290}
291
292function normalizeInput(input) {
293 if (Array.isArray(input)) {
294 return input;
295 } else if (typeof input === 'object') {
296 return Object.values(input);
297 }
298
299 // otherwise it's a string
300 return [input];
301}
302
303/* eslint-disable no-await-in-loop */
304
305function isModuleDir(current, moduleDirs) {
306 return moduleDirs.some((dir) => current.endsWith(dir));
307}
308
309async function findPackageJson(base, moduleDirs) {
310 const { root } = path.parse(base);
311 let current = base;
312
313 while (current !== root && !isModuleDir(current, moduleDirs)) {
314 const pkgJsonPath = path.join(current, 'package.json');
315 if (await fileExists(pkgJsonPath)) {
316 const pkgJsonString = fs.readFileSync(pkgJsonPath, 'utf-8');
317 return { pkgJson: JSON.parse(pkgJsonString), pkgPath: current, pkgJsonPath };
318 }
319 current = path.resolve(current, '..');
320 }
321 return null;
322}
323
324function isUrl(str) {
325 try {
326 return !!new URL(str);
327 } catch (_) {
328 return false;
329 }
330}
331
332function isConditions(exports) {
333 return typeof exports === 'object' && Object.keys(exports).every((k) => !k.startsWith('.'));
334}
335
336function isMappings(exports) {
337 return typeof exports === 'object' && !isConditions(exports);
338}
339
340function isMixedExports(exports) {
341 const keys = Object.keys(exports);
342 return keys.some((k) => k.startsWith('.')) && keys.some((k) => !k.startsWith('.'));
343}
344
345function createBaseErrorMsg(importSpecifier, importer) {
346 return `Could not resolve import "${importSpecifier}" in ${importer}`;
347}
348
349function createErrorMsg(context, reason, internal) {
350 const { importSpecifier, importer, pkgJsonPath } = context;
351 const base = createBaseErrorMsg(importSpecifier, importer);
352 const field = internal ? 'imports' : 'exports';
353 return `${base} using ${field} defined in ${pkgJsonPath}.${reason ? ` ${reason}` : ''}`;
354}
355
356class ResolveError extends Error {}
357
358class InvalidConfigurationError extends ResolveError {
359 constructor(context, reason) {
360 super(createErrorMsg(context, `Invalid "exports" field. ${reason}`));
361 }
362}
363
364class InvalidModuleSpecifierError extends ResolveError {
365 constructor(context, internal, reason) {
366 super(createErrorMsg(context, reason, internal));
367 }
368}
369
370class InvalidPackageTargetError extends ResolveError {
371 constructor(context, reason) {
372 super(createErrorMsg(context, reason));
373 }
374}
375
376/* eslint-disable no-await-in-loop, no-undefined */
377
378function includesInvalidSegments(pathSegments, moduleDirs) {
379 return pathSegments
380 .split('/')
381 .slice(1)
382 .some((t) => ['.', '..', ...moduleDirs].includes(t));
383}
384
385async function resolvePackageTarget(context, { target, subpath, pattern, internal }) {
386 if (typeof target === 'string') {
387 if (!pattern && subpath.length > 0 && !target.endsWith('/')) {
388 throw new InvalidModuleSpecifierError(context);
389 }
390
391 if (!target.startsWith('./')) {
392 if (internal && !['/', '../'].some((p) => target.startsWith(p)) && !isUrl(target)) {
393 // this is a bare package import, remap it and resolve it using regular node resolve
394 if (pattern) {
395 const result = await context.resolveId(
396 target.replace(/\*/g, subpath),
397 context.pkgURL.href
398 );
399 return result ? pathToFileURL(result.location).href : null;
400 }
401
402 const result = await context.resolveId(`${target}${subpath}`, context.pkgURL.href);
403 return result ? pathToFileURL(result.location).href : null;
404 }
405 throw new InvalidPackageTargetError(context, `Invalid mapping: "${target}".`);
406 }
407
408 if (includesInvalidSegments(target, context.moduleDirs)) {
409 throw new InvalidPackageTargetError(context, `Invalid mapping: "${target}".`);
410 }
411
412 const resolvedTarget = new URL(target, context.pkgURL);
413 if (!resolvedTarget.href.startsWith(context.pkgURL.href)) {
414 throw new InvalidPackageTargetError(
415 context,
416 `Resolved to ${resolvedTarget.href} which is outside package ${context.pkgURL.href}`
417 );
418 }
419
420 if (includesInvalidSegments(subpath, context.moduleDirs)) {
421 throw new InvalidModuleSpecifierError(context);
422 }
423
424 if (pattern) {
425 return resolvedTarget.href.replace(/\*/g, subpath);
426 }
427 return new URL(subpath, resolvedTarget).href;
428 }
429
430 if (Array.isArray(target)) {
431 let lastError;
432 for (const item of target) {
433 try {
434 const resolved = await resolvePackageTarget(context, {
435 target: item,
436 subpath,
437 pattern,
438 internal
439 });
440
441 // return if defined or null, but not undefined
442 if (resolved !== undefined) {
443 return resolved;
444 }
445 } catch (error) {
446 if (!(error instanceof InvalidPackageTargetError)) {
447 throw error;
448 } else {
449 lastError = error;
450 }
451 }
452 }
453
454 if (lastError) {
455 throw lastError;
456 }
457 return null;
458 }
459
460 if (target && typeof target === 'object') {
461 for (const [key, value] of Object.entries(target)) {
462 if (key === 'default' || context.conditions.includes(key)) {
463 const resolved = await resolvePackageTarget(context, {
464 target: value,
465 subpath,
466 pattern,
467 internal
468 });
469
470 // return if defined or null, but not undefined
471 if (resolved !== undefined) {
472 return resolved;
473 }
474 }
475 }
476 return undefined;
477 }
478
479 if (target === null) {
480 return null;
481 }
482
483 throw new InvalidPackageTargetError(context, `Invalid exports field.`);
484}
485
486/* eslint-disable no-await-in-loop */
487
488async function resolvePackageImportsExports(context, { matchKey, matchObj, internal }) {
489 if (!matchKey.endsWith('*') && matchKey in matchObj) {
490 const target = matchObj[matchKey];
491 const resolved = await resolvePackageTarget(context, { target, subpath: '', internal });
492 return resolved;
493 }
494
495 const expansionKeys = Object.keys(matchObj)
496 .filter((k) => k.endsWith('/') || k.endsWith('*'))
497 .sort((a, b) => b.length - a.length);
498
499 for (const expansionKey of expansionKeys) {
500 const prefix = expansionKey.substring(0, expansionKey.length - 1);
501
502 if (expansionKey.endsWith('*') && matchKey.startsWith(prefix)) {
503 const target = matchObj[expansionKey];
504 const subpath = matchKey.substring(expansionKey.length - 1);
505 const resolved = await resolvePackageTarget(context, {
506 target,
507 subpath,
508 pattern: true,
509 internal
510 });
511 return resolved;
512 }
513
514 if (matchKey.startsWith(expansionKey)) {
515 const target = matchObj[expansionKey];
516 const subpath = matchKey.substring(expansionKey.length);
517
518 const resolved = await resolvePackageTarget(context, { target, subpath, internal });
519 return resolved;
520 }
521 }
522
523 throw new InvalidModuleSpecifierError(context, internal);
524}
525
526async function resolvePackageExports(context, subpath, exports) {
527 if (isMixedExports(exports)) {
528 throw new InvalidConfigurationError(
529 context,
530 'All keys must either start with ./, or without one.'
531 );
532 }
533
534 if (subpath === '.') {
535 let mainExport;
536 // If exports is a String or Array, or an Object containing no keys starting with ".", then
537 if (typeof exports === 'string' || Array.isArray(exports) || isConditions(exports)) {
538 mainExport = exports;
539 } else if (isMappings(exports)) {
540 mainExport = exports['.'];
541 }
542
543 if (mainExport) {
544 const resolved = await resolvePackageTarget(context, { target: mainExport, subpath: '' });
545 if (resolved) {
546 return resolved;
547 }
548 }
549 } else if (isMappings(exports)) {
550 const resolvedMatch = await resolvePackageImportsExports(context, {
551 matchKey: subpath,
552 matchObj: exports
553 });
554
555 if (resolvedMatch) {
556 return resolvedMatch;
557 }
558 }
559
560 throw new InvalidModuleSpecifierError(context);
561}
562
563async function resolvePackageImports({
564 importSpecifier,
565 importer,
566 moduleDirs,
567 conditions,
568 resolveId
569}) {
570 const result = await findPackageJson(importer, moduleDirs);
571 if (!result) {
572 throw new Error(createBaseErrorMsg('. Could not find a parent package.json.'));
573 }
574
575 const { pkgPath, pkgJsonPath, pkgJson } = result;
576 const pkgURL = pathToFileURL(`${pkgPath}/`);
577 const context = {
578 importer,
579 importSpecifier,
580 moduleDirs,
581 pkgURL,
582 pkgJsonPath,
583 conditions,
584 resolveId
585 };
586
587 const { imports } = pkgJson;
588 if (!imports) {
589 throw new InvalidModuleSpecifierError(context, true);
590 }
591
592 if (importSpecifier === '#' || importSpecifier.startsWith('#/')) {
593 throw new InvalidModuleSpecifierError(context, true, 'Invalid import specifier.');
594 }
595
596 return resolvePackageImportsExports(context, {
597 matchKey: importSpecifier,
598 matchObj: imports,
599 internal: true
600 });
601}
602
603const resolveImportPath = promisify(resolve$1);
604const readFile = promisify(fs.readFile);
605
606async function getPackageJson(importer, pkgName, resolveOptions, moduleDirectories) {
607 if (importer) {
608 const selfPackageJsonResult = await findPackageJson(importer, moduleDirectories);
609 if (selfPackageJsonResult && selfPackageJsonResult.pkgJson.name === pkgName) {
610 // the referenced package name is the current package
611 return selfPackageJsonResult;
612 }
613 }
614
615 try {
616 const pkgJsonPath = await resolveImportPath(`${pkgName}/package.json`, resolveOptions);
617 const pkgJson = JSON.parse(await readFile(pkgJsonPath, 'utf-8'));
618 return { pkgJsonPath, pkgJson, pkgPath: dirname(pkgJsonPath) };
619 } catch (_) {
620 return null;
621 }
622}
623
624async function resolveIdClassic({
625 importSpecifier,
626 packageInfoCache,
627 extensions,
628 mainFields,
629 preserveSymlinks,
630 useBrowserOverrides,
631 baseDir,
632 moduleDirectories,
633 rootDir,
634 ignoreSideEffectsForRoot
635}) {
636 let hasModuleSideEffects = () => null;
637 let hasPackageEntry = true;
638 let packageBrowserField = false;
639 let packageInfo;
640
641 const filter = (pkg, pkgPath) => {
642 const info = getPackageInfo({
643 cache: packageInfoCache,
644 extensions,
645 pkg,
646 pkgPath,
647 mainFields,
648 preserveSymlinks,
649 useBrowserOverrides,
650 rootDir,
651 ignoreSideEffectsForRoot
652 });
653
654 ({ packageInfo, hasModuleSideEffects, hasPackageEntry, packageBrowserField } = info);
655
656 return info.cachedPkg;
657 };
658
659 const resolveOptions = {
660 basedir: baseDir,
661 readFile: readCachedFile,
662 isFile: isFileCached,
663 isDirectory: isDirCached,
664 extensions,
665 includeCoreModules: false,
666 moduleDirectory: moduleDirectories,
667 preserveSymlinks,
668 packageFilter: filter
669 };
670
671 let location;
672 try {
673 location = await resolveImportPath(importSpecifier, resolveOptions);
674 } catch (error) {
675 if (error.code !== 'MODULE_NOT_FOUND') {
676 throw error;
677 }
678 return null;
679 }
680
681 return {
682 location: preserveSymlinks ? location : await resolveSymlink(location),
683 hasModuleSideEffects,
684 hasPackageEntry,
685 packageBrowserField,
686 packageInfo
687 };
688}
689
690async function resolveWithExportMap({
691 importer,
692 importSpecifier,
693 exportConditions,
694 packageInfoCache,
695 extensions,
696 mainFields,
697 preserveSymlinks,
698 useBrowserOverrides,
699 baseDir,
700 moduleDirectories,
701 rootDir,
702 ignoreSideEffectsForRoot
703}) {
704 if (importSpecifier.startsWith('#')) {
705 // this is a package internal import, resolve using package imports field
706 const resolveResult = await resolvePackageImports({
707 importSpecifier,
708 importer,
709 moduleDirs: moduleDirectories,
710 conditions: exportConditions,
711 resolveId(id /* , parent*/) {
712 return resolveIdClassic({
713 importSpecifier: id,
714 packageInfoCache,
715 extensions,
716 mainFields,
717 preserveSymlinks,
718 useBrowserOverrides,
719 baseDir,
720 moduleDirectories
721 });
722 }
723 });
724
725 const location = fileURLToPath(resolveResult);
726 return {
727 location: preserveSymlinks ? location : await resolveSymlink(location),
728 hasModuleSideEffects: () => null,
729 hasPackageEntry: true,
730 packageBrowserField: false,
731 // eslint-disable-next-line no-undefined
732 packageInfo: undefined
733 };
734 }
735
736 const pkgName = getPackageName(importSpecifier);
737 if (pkgName) {
738 // it's a bare import, find the package.json and resolve using package exports if available
739 let hasModuleSideEffects = () => null;
740 let hasPackageEntry = true;
741 let packageBrowserField = false;
742 let packageInfo;
743
744 const filter = (pkg, pkgPath) => {
745 const info = getPackageInfo({
746 cache: packageInfoCache,
747 extensions,
748 pkg,
749 pkgPath,
750 mainFields,
751 preserveSymlinks,
752 useBrowserOverrides,
753 rootDir,
754 ignoreSideEffectsForRoot
755 });
756
757 ({ packageInfo, hasModuleSideEffects, hasPackageEntry, packageBrowserField } = info);
758
759 return info.cachedPkg;
760 };
761
762 const resolveOptions = {
763 basedir: baseDir,
764 readFile: readCachedFile,
765 isFile: isFileCached,
766 isDirectory: isDirCached,
767 extensions,
768 includeCoreModules: false,
769 moduleDirectory: moduleDirectories,
770 preserveSymlinks,
771 packageFilter: filter
772 };
773
774 const result = await getPackageJson(importer, pkgName, resolveOptions, moduleDirectories);
775
776 if (result && result.pkgJson.exports) {
777 const { pkgJson, pkgJsonPath } = result;
778 const subpath =
779 pkgName === importSpecifier ? '.' : `.${importSpecifier.substring(pkgName.length)}`;
780 const pkgDr = pkgJsonPath.replace('package.json', '');
781 const pkgURL = pathToFileURL(pkgDr);
782
783 const context = {
784 importer,
785 importSpecifier,
786 moduleDirs: moduleDirectories,
787 pkgURL,
788 pkgJsonPath,
789 conditions: exportConditions
790 };
791 const resolvedPackageExport = await resolvePackageExports(context, subpath, pkgJson.exports);
792 const location = fileURLToPath(resolvedPackageExport);
793 if (location) {
794 return {
795 location: preserveSymlinks ? location : await resolveSymlink(location),
796 hasModuleSideEffects,
797 hasPackageEntry,
798 packageBrowserField,
799 packageInfo
800 };
801 }
802 }
803 }
804
805 return null;
806}
807
808async function resolveWithClassic({
809 importer,
810 importSpecifierList,
811 exportConditions,
812 warn,
813 packageInfoCache,
814 extensions,
815 mainFields,
816 preserveSymlinks,
817 useBrowserOverrides,
818 baseDir,
819 moduleDirectories,
820 rootDir,
821 ignoreSideEffectsForRoot
822}) {
823 for (let i = 0; i < importSpecifierList.length; i++) {
824 // eslint-disable-next-line no-await-in-loop
825 const result = await resolveIdClassic({
826 importer,
827 importSpecifier: importSpecifierList[i],
828 exportConditions,
829 warn,
830 packageInfoCache,
831 extensions,
832 mainFields,
833 preserveSymlinks,
834 useBrowserOverrides,
835 baseDir,
836 moduleDirectories,
837 rootDir,
838 ignoreSideEffectsForRoot
839 });
840
841 if (result) {
842 return result;
843 }
844 }
845
846 return null;
847}
848
849// Resolves to the module if found or `null`.
850// The first import specificer will first be attempted with the exports algorithm.
851// If this is unsuccesful because export maps are not being used, then all of `importSpecifierList`
852// will be tried with the classic resolution algorithm
853async function resolveImportSpecifiers({
854 importer,
855 importSpecifierList,
856 exportConditions,
857 warn,
858 packageInfoCache,
859 extensions,
860 mainFields,
861 preserveSymlinks,
862 useBrowserOverrides,
863 baseDir,
864 moduleDirectories,
865 rootDir,
866 ignoreSideEffectsForRoot
867}) {
868 try {
869 const exportMapRes = await resolveWithExportMap({
870 importer,
871 importSpecifier: importSpecifierList[0],
872 exportConditions,
873 packageInfoCache,
874 extensions,
875 mainFields,
876 preserveSymlinks,
877 useBrowserOverrides,
878 baseDir,
879 moduleDirectories,
880 rootDir,
881 ignoreSideEffectsForRoot
882 });
883 if (exportMapRes) return exportMapRes;
884 } catch (error) {
885 if (error instanceof ResolveError) {
886 warn(error);
887 return null;
888 }
889 throw error;
890 }
891
892 // package has no imports or exports, use classic node resolve
893 return resolveWithClassic({
894 importer,
895 importSpecifierList,
896 exportConditions,
897 warn,
898 packageInfoCache,
899 extensions,
900 mainFields,
901 preserveSymlinks,
902 useBrowserOverrides,
903 baseDir,
904 moduleDirectories,
905 rootDir,
906 ignoreSideEffectsForRoot
907 });
908}
909
910/* eslint-disable no-param-reassign, no-shadow, no-undefined */
911
912const ES6_BROWSER_EMPTY = '\0node-resolve:empty.js';
913const deepFreeze = (object) => {
914 Object.freeze(object);
915
916 for (const value of Object.values(object)) {
917 if (typeof value === 'object' && !Object.isFrozen(value)) {
918 deepFreeze(value);
919 }
920 }
921
922 return object;
923};
924
925const baseConditions = ['default', 'module'];
926const baseConditionsEsm = [...baseConditions, 'import'];
927const baseConditionsCjs = [...baseConditions, 'require'];
928const defaults = {
929 dedupe: [],
930 // It's important that .mjs is listed before .js so that Rollup will interpret npm modules
931 // which deploy both ESM .mjs and CommonJS .js files as ESM.
932 extensions: ['.mjs', '.js', '.json', '.node'],
933 resolveOnly: [],
934 moduleDirectories: ['node_modules'],
935 ignoreSideEffectsForRoot: false
936};
937const DEFAULTS = deepFreeze(deepMerge({}, defaults));
938
939function nodeResolve(opts = {}) {
940 const { warnings } = handleDeprecatedOptions(opts);
941
942 const options = { ...defaults, ...opts };
943 const { extensions, jail, moduleDirectories, ignoreSideEffectsForRoot } = options;
944 const conditionsEsm = [...baseConditionsEsm, ...(options.exportConditions || [])];
945 const conditionsCjs = [...baseConditionsCjs, ...(options.exportConditions || [])];
946 const packageInfoCache = new Map();
947 const idToPackageInfo = new Map();
948 const mainFields = getMainFields(options);
949 const useBrowserOverrides = mainFields.indexOf('browser') !== -1;
950 const isPreferBuiltinsSet = options.preferBuiltins === true || options.preferBuiltins === false;
951 const preferBuiltins = isPreferBuiltinsSet ? options.preferBuiltins : true;
952 const rootDir = resolve(options.rootDir || process.cwd());
953 let { dedupe } = options;
954 let rollupOptions;
955
956 if (typeof dedupe !== 'function') {
957 dedupe = (importee) =>
958 options.dedupe.includes(importee) || options.dedupe.includes(getPackageName(importee));
959 }
960
961 // creates a function from the patterns to test if a particular module should be bundled.
962 const allowPatterns = (patterns) => {
963 const regexPatterns = patterns.map((pattern) => {
964 if (pattern instanceof RegExp) {
965 return pattern;
966 }
967 const normalized = pattern.replace(/[\\^$*+?.()|[\]{}]/g, '\\$&');
968 return new RegExp(`^${normalized}$`);
969 });
970 return (id) => !regexPatterns.length || regexPatterns.some((pattern) => pattern.test(id));
971 };
972
973 const resolveOnly =
974 typeof options.resolveOnly === 'function'
975 ? options.resolveOnly
976 : allowPatterns(options.resolveOnly);
977
978 const browserMapCache = new Map();
979 let preserveSymlinks;
980
981 const resolveLikeNode = async (context, importee, importer, custom) => {
982 // strip query params from import
983 const [importPath, params] = importee.split('?');
984 const importSuffix = `${params ? `?${params}` : ''}`;
985 importee = importPath;
986
987 const baseDir = !importer || dedupe(importee) ? rootDir : dirname(importer);
988
989 // https://github.com/defunctzombie/package-browser-field-spec
990 const browser = browserMapCache.get(importer);
991 if (useBrowserOverrides && browser) {
992 const resolvedImportee = resolve(baseDir, importee);
993 if (browser[importee] === false || browser[resolvedImportee] === false) {
994 return { id: ES6_BROWSER_EMPTY };
995 }
996 const browserImportee =
997 (importee[0] !== '.' && browser[importee]) ||
998 browser[resolvedImportee] ||
999 browser[`${resolvedImportee}.js`] ||
1000 browser[`${resolvedImportee}.json`];
1001 if (browserImportee) {
1002 importee = browserImportee;
1003 }
1004 }
1005
1006 const parts = importee.split(/[/\\]/);
1007 let id = parts.shift();
1008 let isRelativeImport = false;
1009
1010 if (id[0] === '@' && parts.length > 0) {
1011 // scoped packages
1012 id += `/${parts.shift()}`;
1013 } else if (id[0] === '.') {
1014 // an import relative to the parent dir of the importer
1015 id = resolve(baseDir, importee);
1016 isRelativeImport = true;
1017 }
1018
1019 // if it's not a relative import, and it's not requested, reject it.
1020 if (!isRelativeImport && !resolveOnly(id)) {
1021 if (normalizeInput(rollupOptions.input).includes(importee)) {
1022 return null;
1023 }
1024 return false;
1025 }
1026
1027 const importSpecifierList = [importee];
1028
1029 if (importer === undefined && !importee[0].match(/^\.?\.?\//)) {
1030 // For module graph roots (i.e. when importer is undefined), we
1031 // need to handle 'path fragments` like `foo/bar` that are commonly
1032 // found in rollup config files. If importee doesn't look like a
1033 // relative or absolute path, we make it relative and attempt to
1034 // resolve it.
1035 importSpecifierList.push(`./${importee}`);
1036 }
1037
1038 // TypeScript files may import '.js' to refer to either '.ts' or '.tsx'
1039 if (importer && importee.endsWith('.js')) {
1040 for (const ext of ['.ts', '.tsx']) {
1041 if (importer.endsWith(ext) && extensions.includes(ext)) {
1042 importSpecifierList.push(importee.replace(/.js$/, ext));
1043 }
1044 }
1045 }
1046
1047 const warn = (...args) => context.warn(...args);
1048 const isRequire = custom && custom['node-resolve'] && custom['node-resolve'].isRequire;
1049 const exportConditions = isRequire ? conditionsCjs : conditionsEsm;
1050
1051 if (useBrowserOverrides && !exportConditions.includes('browser'))
1052 exportConditions.push('browser');
1053
1054 const resolvedWithoutBuiltins = await resolveImportSpecifiers({
1055 importer,
1056 importSpecifierList,
1057 exportConditions,
1058 warn,
1059 packageInfoCache,
1060 extensions,
1061 mainFields,
1062 preserveSymlinks,
1063 useBrowserOverrides,
1064 baseDir,
1065 moduleDirectories,
1066 rootDir,
1067 ignoreSideEffectsForRoot
1068 });
1069
1070 const importeeIsBuiltin = isBuiltinModule(importee);
1071 const resolved =
1072 importeeIsBuiltin && preferBuiltins
1073 ? {
1074 packageInfo: undefined,
1075 hasModuleSideEffects: () => null,
1076 hasPackageEntry: true,
1077 packageBrowserField: false
1078 }
1079 : resolvedWithoutBuiltins;
1080 if (!resolved) {
1081 return null;
1082 }
1083
1084 const { packageInfo, hasModuleSideEffects, hasPackageEntry, packageBrowserField } = resolved;
1085 let { location } = resolved;
1086 if (packageBrowserField) {
1087 if (Object.prototype.hasOwnProperty.call(packageBrowserField, location)) {
1088 if (!packageBrowserField[location]) {
1089 browserMapCache.set(location, packageBrowserField);
1090 return { id: ES6_BROWSER_EMPTY };
1091 }
1092 location = packageBrowserField[location];
1093 }
1094 browserMapCache.set(location, packageBrowserField);
1095 }
1096
1097 if (hasPackageEntry && !preserveSymlinks) {
1098 const exists = await fileExists(location);
1099 if (exists) {
1100 location = await realpath(location);
1101 }
1102 }
1103
1104 idToPackageInfo.set(location, packageInfo);
1105
1106 if (hasPackageEntry) {
1107 if (importeeIsBuiltin && preferBuiltins) {
1108 if (!isPreferBuiltinsSet && resolvedWithoutBuiltins && resolved !== importee) {
1109 context.warn(
1110 `preferring built-in module '${importee}' over local alternative at '${resolvedWithoutBuiltins.location}', pass 'preferBuiltins: false' to disable this behavior or 'preferBuiltins: true' to disable this warning`
1111 );
1112 }
1113 return false;
1114 } else if (jail && location.indexOf(normalize(jail.trim(sep))) !== 0) {
1115 return null;
1116 }
1117 }
1118
1119 if (options.modulesOnly && (await fileExists(location))) {
1120 const code = await readFile$1(location, 'utf-8');
1121 if (isModule(code)) {
1122 return {
1123 id: `${location}${importSuffix}`,
1124 moduleSideEffects: hasModuleSideEffects(location)
1125 };
1126 }
1127 return null;
1128 }
1129 return {
1130 id: `${location}${importSuffix}`,
1131 moduleSideEffects: hasModuleSideEffects(location)
1132 };
1133 };
1134
1135 return {
1136 name: 'node-resolve',
1137
1138 version,
1139
1140 buildStart(buildOptions) {
1141 rollupOptions = buildOptions;
1142
1143 for (const warning of warnings) {
1144 this.warn(warning);
1145 }
1146
1147 ({ preserveSymlinks } = buildOptions);
1148 },
1149
1150 generateBundle() {
1151 readCachedFile.clear();
1152 isFileCached.clear();
1153 isDirCached.clear();
1154 },
1155
1156 resolveId: {
1157 order: 'post',
1158 async handler(importee, importer, resolveOptions) {
1159 if (importee === ES6_BROWSER_EMPTY) {
1160 return importee;
1161 }
1162 // ignore IDs with null character, these belong to other plugins
1163 if (/\0/.test(importee)) return null;
1164
1165 const { custom = {} } = resolveOptions;
1166 const { 'node-resolve': { resolved: alreadyResolved } = {} } = custom;
1167 if (alreadyResolved) {
1168 return alreadyResolved;
1169 }
1170
1171 if (/\0/.test(importer)) {
1172 importer = undefined;
1173 }
1174
1175 const resolved = await resolveLikeNode(this, importee, importer, custom);
1176 if (resolved) {
1177 // This way, plugins may attach additional meta information to the
1178 // resolved id or make it external. We do not skip node-resolve here
1179 // because another plugin might again use `this.resolve` in its
1180 // `resolveId` hook, in which case we want to add the correct
1181 // `moduleSideEffects` information.
1182 const resolvedResolved = await this.resolve(resolved.id, importer, {
1183 ...resolveOptions,
1184 custom: { ...custom, 'node-resolve': { ...custom['node-resolve'], resolved } }
1185 });
1186 if (resolvedResolved) {
1187 // Handle plugins that manually make the result external
1188 if (resolvedResolved.external) {
1189 return false;
1190 }
1191 // Allow other plugins to take over resolution. Rollup core will not
1192 // change the id if it corresponds to an existing file
1193 if (resolvedResolved.id !== resolved.id) {
1194 return resolvedResolved;
1195 }
1196 // Pass on meta information added by other plugins
1197 return { ...resolved, meta: resolvedResolved.meta };
1198 }
1199 }
1200 return resolved;
1201 }
1202 },
1203
1204 load(importee) {
1205 if (importee === ES6_BROWSER_EMPTY) {
1206 return 'export default {};';
1207 }
1208 return null;
1209 },
1210
1211 getPackageInfoForId(id) {
1212 return idToPackageInfo.get(id);
1213 }
1214 };
1215}
1216
1217export { DEFAULTS, nodeResolve as default, nodeResolve };