333 lines
13 KiB
JavaScript
333 lines
13 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.NotFoundError = void 0;
|
|
exports.default = resolveDependency;
|
|
const path_1 = require("path");
|
|
const module_1 = require("module");
|
|
// node resolver
|
|
// custom implementation to emit only needed package.json files for resolver
|
|
// (package.json files are emitted as they are hit)
|
|
async function resolveDependency(specifier, parent, job, cjsResolve = true) {
|
|
let resolved;
|
|
if ((0, path_1.isAbsolute)(specifier) ||
|
|
specifier === '.' ||
|
|
specifier === '..' ||
|
|
specifier.startsWith('./') ||
|
|
specifier.startsWith('../')) {
|
|
const trailingSlash = specifier.endsWith('/');
|
|
resolved = await resolvePath((0, path_1.resolve)(parent, '..', specifier) + (trailingSlash ? '/' : ''), parent, job);
|
|
}
|
|
else if (specifier[0] === '#') {
|
|
resolved = await packageImportsResolve(specifier, parent, job, cjsResolve);
|
|
}
|
|
else {
|
|
resolved = await resolvePackage(specifier, parent, job, cjsResolve);
|
|
}
|
|
if (Array.isArray(resolved)) {
|
|
return Promise.all(resolved.map((resolved) => job.realpath(resolved, parent)));
|
|
}
|
|
else if (resolved.startsWith('node:')) {
|
|
return resolved;
|
|
}
|
|
else {
|
|
return job.realpath(resolved, parent);
|
|
}
|
|
}
|
|
async function resolvePath(path, parent, job) {
|
|
const result = (await resolveFile(path, parent, job)) ||
|
|
(await resolveDir(path, parent, job));
|
|
if (!result) {
|
|
throw new NotFoundError(path, parent);
|
|
}
|
|
return result;
|
|
}
|
|
async function resolveFile(path, parent, job) {
|
|
if (path.endsWith('/'))
|
|
return undefined;
|
|
path = await job.realpath(path, parent);
|
|
if (await job.isFile(path))
|
|
return path;
|
|
if (job.ts &&
|
|
path.startsWith(job.base) &&
|
|
path.slice(job.base.length).indexOf(path_1.sep + 'node_modules' + path_1.sep) === -1 &&
|
|
(await job.isFile(path + '.ts')))
|
|
return path + '.ts';
|
|
if (job.ts &&
|
|
path.startsWith(job.base) &&
|
|
path.slice(job.base.length).indexOf(path_1.sep + 'node_modules' + path_1.sep) === -1 &&
|
|
(await job.isFile(path + '.tsx')))
|
|
return path + '.tsx';
|
|
if (await job.isFile(path + '.js'))
|
|
return path + '.js';
|
|
if (await job.isFile(path + '.json'))
|
|
return path + '.json';
|
|
if (await job.isFile(path + '.node'))
|
|
return path + '.node';
|
|
return undefined;
|
|
}
|
|
async function resolveDir(path, parent, job) {
|
|
if (path.endsWith('/'))
|
|
path = path.slice(0, -1);
|
|
if (!(await job.isDir(path)))
|
|
return;
|
|
const pkgCfg = await getPkgCfg(path, job);
|
|
if (pkgCfg && typeof pkgCfg.main === 'string') {
|
|
const resolved = (await resolveFile((0, path_1.resolve)(path, pkgCfg.main), parent, job)) ||
|
|
(await resolveFile((0, path_1.resolve)(path, pkgCfg.main, 'index'), parent, job));
|
|
if (resolved) {
|
|
await job.emitFile(path + path_1.sep + 'package.json', 'resolve', parent);
|
|
return resolved;
|
|
}
|
|
}
|
|
return resolveFile((0, path_1.resolve)(path, 'index'), parent, job);
|
|
}
|
|
class NotFoundError extends Error {
|
|
code;
|
|
constructor(specifier, parent) {
|
|
super("Cannot find module '" + specifier + "' loaded from " + parent);
|
|
this.code = 'MODULE_NOT_FOUND';
|
|
}
|
|
}
|
|
exports.NotFoundError = NotFoundError;
|
|
const nodeBuiltins = new Set(module_1.builtinModules);
|
|
function getPkgName(name) {
|
|
const segments = name.split('/');
|
|
if (name[0] === '@' && segments.length > 1)
|
|
return segments.length > 1 ? segments.slice(0, 2).join('/') : null;
|
|
return segments.length ? segments[0] : null;
|
|
}
|
|
async function getPkgCfg(pkgPath, job) {
|
|
const pjsonSource = await job.readFile(pkgPath + path_1.sep + 'package.json');
|
|
if (pjsonSource) {
|
|
try {
|
|
return JSON.parse(pjsonSource.toString());
|
|
}
|
|
catch (e) { }
|
|
}
|
|
return undefined;
|
|
}
|
|
function getExportsTarget(exports, conditions, cjsResolve) {
|
|
if (typeof exports === 'string') {
|
|
return exports;
|
|
}
|
|
else if (exports === null) {
|
|
return exports;
|
|
}
|
|
else if (Array.isArray(exports)) {
|
|
for (const item of exports) {
|
|
const target = getExportsTarget(item, conditions, cjsResolve);
|
|
if (target === null ||
|
|
(typeof target === 'string' && target.startsWith('./')))
|
|
return target;
|
|
}
|
|
}
|
|
else if (typeof exports === 'object') {
|
|
for (const condition of Object.keys(exports)) {
|
|
if (condition === 'default' ||
|
|
(condition === 'require' && cjsResolve) ||
|
|
(condition === 'import' && !cjsResolve) ||
|
|
conditions.includes(condition)) {
|
|
const target = getExportsTarget(exports[condition], conditions, cjsResolve);
|
|
if (target !== undefined)
|
|
return target;
|
|
}
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
function resolveExportsImports(pkgPath, obj, subpath, job, isImports, cjsResolve) {
|
|
let matchObj;
|
|
if (isImports) {
|
|
if (!(typeof obj === 'object' && !Array.isArray(obj) && obj !== null))
|
|
return undefined;
|
|
matchObj = obj;
|
|
}
|
|
else if (typeof obj === 'string' ||
|
|
Array.isArray(obj) ||
|
|
obj === null ||
|
|
(typeof obj === 'object' &&
|
|
Object.keys(obj).length &&
|
|
Object.keys(obj)[0][0] !== '.')) {
|
|
matchObj = { '.': obj };
|
|
}
|
|
else {
|
|
matchObj = obj;
|
|
}
|
|
if (subpath in matchObj) {
|
|
const target = getExportsTarget(matchObj[subpath], job.conditions, cjsResolve);
|
|
if (typeof target === 'string' && target.startsWith('./'))
|
|
return pkgPath + target.slice(1);
|
|
}
|
|
for (const match of Object.keys(matchObj).sort((a, b) => b.length - a.length)) {
|
|
if (match.endsWith('*') && subpath.startsWith(match.slice(0, -1))) {
|
|
const target = getExportsTarget(matchObj[match], job.conditions, cjsResolve);
|
|
if (typeof target === 'string' && target.startsWith('./'))
|
|
return (pkgPath +
|
|
target.slice(1).replace(/\*/g, subpath.slice(match.length - 1)));
|
|
}
|
|
if (!match.endsWith('/'))
|
|
continue;
|
|
if (subpath.startsWith(match)) {
|
|
const target = getExportsTarget(matchObj[match], job.conditions, cjsResolve);
|
|
if (typeof target === 'string' &&
|
|
target.endsWith('/') &&
|
|
target.startsWith('./'))
|
|
return pkgPath + target.slice(1) + subpath.slice(match.length);
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
async function resolveRemappings(pkgPath, pkgCfg, parent, job) {
|
|
if (job.conditions?.includes('browser')) {
|
|
const { browser: pkgBrowser } = pkgCfg;
|
|
if (!pkgBrowser) {
|
|
return;
|
|
}
|
|
if (typeof pkgBrowser === 'object') {
|
|
for (const [key, value] of Object.entries(pkgBrowser)) {
|
|
if (typeof value !== 'string') {
|
|
/**
|
|
* `false` can be used to specify that a file is not meant to be included.
|
|
* Downstream processing is expected to handle this case, and it should remain in the mapping result
|
|
*/
|
|
continue;
|
|
}
|
|
if (!key.startsWith('./') || !value.startsWith('./')) {
|
|
continue;
|
|
}
|
|
const keyResolved = await resolveFile(pkgPath + path_1.sep + key, parent, job);
|
|
const valueResolved = await resolveFile(pkgPath + path_1.sep + value, parent, job);
|
|
if (keyResolved && valueResolved) {
|
|
job.addRemapping(keyResolved, valueResolved);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
async function packageImportsResolve(name, parent, job, cjsResolve) {
|
|
if (name !== '#' && !name.startsWith('#/') && job.conditions) {
|
|
const pjsonBoundary = await job.getPjsonBoundary(parent);
|
|
if (pjsonBoundary) {
|
|
const pkgCfg = await getPkgCfg(pjsonBoundary, job);
|
|
const { imports: pkgImports } = pkgCfg || {};
|
|
if (pkgCfg && pkgImports !== null && pkgImports !== undefined) {
|
|
let importsResolved = resolveExportsImports(pjsonBoundary, pkgImports, name, job, true, cjsResolve);
|
|
if (importsResolved) {
|
|
if (cjsResolve)
|
|
importsResolved =
|
|
(await resolveFile(importsResolved, parent, job)) ||
|
|
(await resolveDir(importsResolved, parent, job));
|
|
else if (!(await job.isFile(importsResolved)))
|
|
throw new NotFoundError(importsResolved, parent);
|
|
if (importsResolved) {
|
|
await job.emitFile(pjsonBoundary + path_1.sep + 'package.json', 'resolve', parent);
|
|
return importsResolved;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
throw new NotFoundError(name, parent);
|
|
}
|
|
async function resolvePackage(name, parent, job, cjsResolve) {
|
|
let packageParent = parent;
|
|
if (nodeBuiltins.has(name))
|
|
return 'node:' + name;
|
|
if (name.startsWith('node:'))
|
|
return name;
|
|
const pkgName = getPkgName(name) || '';
|
|
// package own name resolution
|
|
let selfResolved;
|
|
if (job.conditions) {
|
|
const pjsonBoundary = await job.getPjsonBoundary(parent);
|
|
if (pjsonBoundary) {
|
|
const pkgCfg = await getPkgCfg(pjsonBoundary, job);
|
|
const { exports: pkgExports } = pkgCfg || {};
|
|
if (pkgCfg &&
|
|
pkgCfg.name &&
|
|
pkgCfg.name === pkgName &&
|
|
pkgExports !== null &&
|
|
pkgExports !== undefined) {
|
|
selfResolved = resolveExportsImports(pjsonBoundary, pkgExports, '.' + name.slice(pkgName.length), job, false, cjsResolve);
|
|
if (selfResolved) {
|
|
if (cjsResolve)
|
|
selfResolved =
|
|
(await resolveFile(selfResolved, parent, job)) ||
|
|
(await resolveDir(selfResolved, parent, job));
|
|
else if (!(await job.isFile(selfResolved)))
|
|
throw new NotFoundError(selfResolved, parent);
|
|
}
|
|
if (selfResolved)
|
|
await job.emitFile(pjsonBoundary + path_1.sep + 'package.json', 'resolve', parent);
|
|
}
|
|
}
|
|
}
|
|
let separatorIndex;
|
|
const rootSeparatorIndex = packageParent.indexOf(path_1.sep);
|
|
while ((separatorIndex = packageParent.lastIndexOf(path_1.sep)) > rootSeparatorIndex) {
|
|
packageParent = packageParent.slice(0, separatorIndex);
|
|
const nodeModulesDir = packageParent + path_1.sep + 'node_modules';
|
|
const stat = await job.stat(nodeModulesDir);
|
|
if (!stat || !stat.isDirectory())
|
|
continue;
|
|
const pkgCfg = await getPkgCfg(nodeModulesDir + path_1.sep + pkgName, job);
|
|
const { exports: pkgExports } = pkgCfg || {};
|
|
if (pkgCfg) {
|
|
await resolveRemappings(nodeModulesDir + path_1.sep + pkgName, pkgCfg, parent, job);
|
|
}
|
|
if (job.conditions &&
|
|
pkgExports !== undefined &&
|
|
pkgExports !== null &&
|
|
!selfResolved) {
|
|
let legacyResolved;
|
|
if (!job.exportsOnly)
|
|
legacyResolved =
|
|
(await resolveFile(nodeModulesDir + path_1.sep + name, parent, job)) ||
|
|
(await resolveDir(nodeModulesDir + path_1.sep + name, parent, job));
|
|
let resolved = resolveExportsImports(nodeModulesDir + path_1.sep + pkgName, pkgExports, '.' + name.slice(pkgName.length), job, false, cjsResolve);
|
|
if (resolved) {
|
|
if (cjsResolve)
|
|
resolved =
|
|
(await resolveFile(resolved, parent, job)) ||
|
|
(await resolveDir(resolved, parent, job));
|
|
else if (!(await job.isFile(resolved)))
|
|
throw new NotFoundError(resolved, parent);
|
|
}
|
|
if (resolved) {
|
|
await job.emitFile(nodeModulesDir + path_1.sep + pkgName + path_1.sep + 'package.json', 'resolve', parent);
|
|
if (legacyResolved && legacyResolved !== resolved)
|
|
return [resolved, legacyResolved];
|
|
return resolved;
|
|
}
|
|
if (legacyResolved)
|
|
return legacyResolved;
|
|
}
|
|
else {
|
|
const resolved = (await resolveFile(nodeModulesDir + path_1.sep + name, parent, job)) ||
|
|
(await resolveDir(nodeModulesDir + path_1.sep + name, parent, job));
|
|
if (resolved) {
|
|
if (selfResolved && selfResolved !== resolved)
|
|
return [resolved, selfResolved];
|
|
return resolved;
|
|
}
|
|
}
|
|
}
|
|
if (selfResolved)
|
|
return selfResolved;
|
|
if (Object.hasOwnProperty.call(job.paths, name)) {
|
|
return job.paths[name];
|
|
}
|
|
for (const path of Object.keys(job.paths)) {
|
|
if (path.endsWith('/') && name.startsWith(path)) {
|
|
const pathTarget = job.paths[path] + name.slice(path.length);
|
|
const resolved = (await resolveFile(pathTarget, parent, job)) ||
|
|
(await resolveDir(pathTarget, parent, job));
|
|
if (!resolved) {
|
|
throw new NotFoundError(name, parent);
|
|
}
|
|
return resolved;
|
|
}
|
|
}
|
|
throw new NotFoundError(name, parent);
|
|
}
|