@jspm/generator
Version:
Package Import Map Generation Tool
742 lines (740 loc) • 38 kB
JavaScript
function _define_property(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
import { trimPackageConfig } from '../install/package.js';
import { JspmError } from '../common/err.js';
// @ts-ignore
import { fetch, isVirtualUrl } from '../common/fetch.js';
import { importedFrom, isFetchProtocol } from '../common/url.js';
// @ts-ignore
import { parse } from 'es-module-lexer/js';
import { getMapMatch, allDotKeys, getWildcardPrefixes } from '../common/package.js';
import { builtinSchemes, mappableSchemes } from '../providers/index.js';
import { createSystemAnalysis, createCjsAnalysis, createEsmAnalysis, createTsAnalysis } from './analysis.js';
import { SemverRange } from 'sver';
import { getIntegrity } from '../common/integrity.js';
import { isNode } from '../common/env.js';
let realpath, pathToFileURL;
export function setPathFns(_realpath, _pathToFileURL) {
realpath = _realpath, pathToFileURL = _pathToFileURL;
}
function isResponseImmutable(headers) {
if (!(headers === null || headers === void 0 ? void 0 : headers.get)) return false;
const cc = headers.get('cache-control');
if (!cc) return false;
if (cc.includes('immutable')) return true;
const maxAgeMatch = cc.match(/max-age=(\d+)/);
if (maxAgeMatch && parseInt(maxAgeMatch[1]) >= 31536000) return true;
return false;
}
export function isBuiltinScheme(specifier) {
if (specifier.indexOf(':') === -1) return false;
return builtinSchemes.has(specifier.slice(0, specifier.indexOf(':')));
}
export function isMappableScheme(specifier) {
if (specifier.indexOf(':') === -1) return false;
return mappableSchemes.has(specifier.slice(0, specifier.indexOf(':')));
}
export class Resolver {
resolveBuiltin(specifier) {
return this.pm.resolveBuiltin(specifier, this.env);
}
getPackageBase(url) {
const cached = this.packageBaseCache[url];
if (cached !== undefined) return cached;
const pkg = this.pm.parseUrlPkg(url);
if (pkg) {
const result = this.pm.pkgToUrl(pkg.pkg, pkg.source.provider, pkg.source.layer);
if (typeof result === 'string') {
return this.packageBaseCache[url] = result;
}
return result.then((r)=>this.packageBaseCache[url] = r);
}
let testUrl;
try {
testUrl = new URL('./', url);
} catch {
return this.packageBaseCache[url] = url;
}
const packageBasePromise = (async ()=>{
const rootUrl = new URL('/', testUrl).href;
do {
let testUrlHref = testUrl.href;
if (await this.checkPjson(testUrlHref)) return testUrlHref;
if (testUrl.href === rootUrl) return new URL('./', url).href;
}while (testUrl = new URL('../', testUrl))
})();
this.packageBaseCache[url] = packageBasePromise;
return packageBasePromise.then((packageBase)=>{
this.packageBaseCache[url] = packageBase;
return packageBase;
}, (error)=>{
delete this.packageBaseCache[url];
throw error;
});
}
// TODO: there are actually two different kinds of "package" in the codebase.
// There's a registry package, which is something that can be pinned exactly
// by name and version against a registry like "npm" or "denoland". Then we
// have a resolver package, which is any URL that has a "package.json" as a
// child. We should only be doing providerForUrl checks for _registry_
// packages, and in resolution contexts we should skip straight to npm-style
// backtracking to find package bases.
getPackageConfig(pkgUrl) {
const protocol = pkgUrl.slice(0, pkgUrl.indexOf(':') + 1);
if (!isFetchProtocol(protocol)) return null;
if (!pkgUrl.endsWith('/')) throw new Error(`Internal Error: Package URL must end in "/". Got ${pkgUrl}`);
this.touchedPackageConfigUrls.add(pkgUrl);
let cached = this.pcfgs[pkgUrl];
// TODO: fix timing bug that requires this return to be a promise
if (cached === null) return Promise.resolve(null);
if (cached) return cached;
if (!this.pcfgPromises[pkgUrl]) this.pcfgPromises[pkgUrl] = (async ()=>{
var _res_headers_get;
const pcfg = await this.pm.getPackageConfig(pkgUrl);
if (typeof pcfg === 'object') {
if (pcfg !== null) {
return this.pcfgs[pkgUrl] = trimPackageConfig(pcfg);
}
}
try {
var res = await fetch(`${pkgUrl}package.json`, this.fetchOpts);
} catch (e) {
// CSP errors can't be detected, but should be treated as missing
// therefore we just ignore errors as none
return this.pcfgs[pkgUrl] = null;
}
switch(res.status){
case 200:
case 204:
case 304:
break;
case 400:
case 401:
case 403:
case 404:
case 406:
case 500:
return this.pcfgs[pkgUrl] = null;
default:
throw new JspmError(`Invalid status code ${res.status} reading package config for ${pkgUrl}. ${res.statusText}`);
}
if (isResponseImmutable(res.headers)) this.immutableUrls.add(pkgUrl);
if (res.headers && !((_res_headers_get = res.headers.get('Content-Type')) === null || _res_headers_get === void 0 ? void 0 : _res_headers_get.match(/^application\/json(;|$)/))) {
return this.pcfgs[pkgUrl] = null;
} else {
try {
return this.pcfgs[pkgUrl] = trimPackageConfig(await res.json());
} catch (e) {
return this.pcfgs[pkgUrl] = null;
}
}
})();
return this.pcfgPromises[pkgUrl];
}
async getFileList(pkgUrl) {
if (!pkgUrl.endsWith('/')) pkgUrl += '/';
if (pkgUrl in this.fileLists) return this.fileLists[pkgUrl];
// First try the provider's own file listing
const providerFileList = await this.pm.getFileList(pkgUrl);
if (providerFileList) return this.fileLists[pkgUrl] = providerFileList;
// Walk via the fetch shim's 204 directory convention (file: on Node, or virtual URLs)
if (isNode && pkgUrl.startsWith('file:') || isVirtualUrl(pkgUrl)) {
const fileList = new Set();
async function walk(path, basePath) {
try {
const res = await fetch(path);
if (res.status === 200) {
fileList.add(path.slice(basePath.length));
} else if (res.status === 204) {
if (!path.endsWith('/')) path += '/';
const dirListing = await res.json();
for (const entry of dirListing){
if (entry === 'node_modules' || entry === '.git') continue;
await walk(path + entry, basePath);
}
}
} catch (e) {
throw new JspmError(`Unable to read package ${path} - ${e.toString()}`);
}
}
await walk(pkgUrl, pkgUrl);
return this.fileLists[pkgUrl] = fileList;
}
// In fetch-only environments, use package.json "files" as the listing
const pcfg = await this.getPackageConfig(pkgUrl);
if (pcfg && Array.isArray(pcfg.files)) {
return this.fileLists[pkgUrl] = new Set(pcfg.files.filter((file)=>typeof file === 'string' && !file.startsWith('/') && !file.endsWith('/') && !file.startsWith('.')));
}
}
getPackageBaseCached(url) {
let testUrl;
try {
testUrl = new URL('./', url);
} catch {
return undefined;
}
const rootUrl = new URL('/', testUrl).href;
do {
const href = testUrl.href;
if (this.pcfgs[href]) return href;
if (testUrl.href === rootUrl) return undefined;
}while (testUrl = new URL('../', testUrl))
return undefined;
}
getWildcardPrefixes(pkgUrl) {
if (!pkgUrl.endsWith('/')) pkgUrl += '/';
const pcfg = this.pcfgs[pkgUrl];
if (!(pcfg === null || pcfg === void 0 ? void 0 : pcfg.exports)) return new Set();
return getWildcardPrefixes(pcfg.exports, this.env, this.fileLists[pkgUrl]);
}
async getDepList(pkgUrl, dev = false) {
const pjson = await this.getPackageConfig(pkgUrl);
if (!pjson) return [];
return [
...new Set([
Object.keys(pjson.dependencies || {}),
Object.keys(dev && pjson.devDependencies || {}),
Object.keys(pjson.peerDependencies || {}),
Object.keys(pjson.optionalDependencies || {}),
Object.keys(pjson.imports || {})
].flat())
];
}
checkPjson(url) {
const pcfg = this.getPackageConfig(url);
if (pcfg instanceof Promise) {
return pcfg.then((pcfg)=>{
if (pcfg === null) return false;
return true;
});
}
return true;
}
async exists(resolvedUrl) {
try {
await this.analyze(resolvedUrl);
} catch {
// we ignore network errors when doing exists resolutions
return false;
}
// 404 still caches as null, although this is not currently used
return !!this.traceEntries[resolvedUrl];
}
async wasCommonJS(url) {
var _pcfg_exports;
// TODO: make this a provider hook
const pkgUrl = await this.getPackageBase(url);
if (!pkgUrl) return false;
const pcfg = await this.getPackageConfig(pkgUrl);
if (!pcfg) return false;
const subpath = './' + url.slice(pkgUrl.length);
return (pcfg === null || pcfg === void 0 ? void 0 : (_pcfg_exports = pcfg.exports) === null || _pcfg_exports === void 0 ? void 0 : _pcfg_exports[subpath + '!cjs']) ? true : false;
}
async realPath(url) {
if (!isNode || !url.startsWith('file:') || this.preserveSymlinks) return url;
let encodedColon = false;
url = url.replace(/%3a/i, ()=>{
encodedColon = true;
return ':';
});
if (!realpath) {
[{ realpath }, { pathToFileURL }] = await Promise.all([
import('fs'),
import('url')
]);
}
const outUrl = pathToFileURL(await new Promise((resolve, reject)=>realpath(new URL(url), (err, result)=>err ? reject(err) : resolve(result)))).href;
if (encodedColon) return 'file:' + outUrl.slice(5).replace(':', '%3a');
return outUrl;
}
async finalizeResolve(url, parentIsCjs, exportsResolution, pkgUrl) {
if (parentIsCjs && url.endsWith('/')) url = url.slice(0, -1);
// Only CJS modules do extension searching for relative resolved paths
if (parentIsCjs) url = await (async ()=>{
// subfolder checks before file checks because of fetch
if (await this.exists(url + '/package.json')) {
const pcfg = await this.getPackageConfig(url) || {};
const urlUrl = new URL(url + '/');
if (this.env.includes('browser') && typeof pcfg.browser === 'string') return this.finalizeResolve(await legacyMainResolve.call(this, pcfg.browser, urlUrl), parentIsCjs, exportsResolution, pkgUrl);
if (this.env.includes('module') && typeof pcfg.module === 'string') return this.finalizeResolve(await legacyMainResolve.call(this, pcfg.module, urlUrl), parentIsCjs, exportsResolution, pkgUrl);
if (typeof pcfg.main === 'string') return this.finalizeResolve(await legacyMainResolve.call(this, pcfg.main, urlUrl), parentIsCjs, exportsResolution, pkgUrl);
return this.finalizeResolve(await legacyMainResolve.call(this, null, urlUrl), parentIsCjs, exportsResolution, pkgUrl);
}
if (await this.exists(url + '/index.js')) return url + '/index.js';
if (await this.exists(url + '/index.json')) return url + '/index.json';
if (await this.exists(url + '/index.node')) return url + '/index.node';
if (await this.exists(url)) return url;
if (await this.exists(url + '.js')) return url + '.js';
if (await this.exists(url + '.json')) return url + '.json';
if (await this.exists(url + '.node')) return url + '.node';
return url;
})();
// Only browser maps apply to relative resolved paths
if (this.env.includes('browser')) {
pkgUrl = pkgUrl || await this.getPackageBase(url);
if (url.startsWith(pkgUrl)) {
const pcfg = await this.getPackageConfig(pkgUrl);
if (pcfg && typeof pcfg.browser === 'object' && pcfg.browser !== null) {
const subpath = './' + url.slice(pkgUrl.length);
if (subpath in pcfg.browser) {
const target = pcfg.browser[subpath];
if (target === false) return this.resolveEmpty(parentIsCjs, url, pkgUrl);
if (!target.startsWith('./')) throw new Error(`TODO: External browser map for ${subpath} to ${target} in ${url}`);
// for browser mappings to the same module, avoid a loop
if (pkgUrl + target.slice(2) === url) return url;
return await this.finalizeResolve(pkgUrl + target.slice(2), parentIsCjs, exportsResolution, pkgUrl);
}
}
}
}
// give some compatibility for packages without "exports" field
if (!exportsResolution) {
if (await this.exists(url)) void 0;
else if (await this.exists(url + '.js')) return url + '.js';
else if (await this.exists(url + '.json')) return url + '.json';
else if (await this.exists(url + '.node')) return url + '.node';
}
return url;
}
// reverse exports resolution
// returns _a_ possible export which resolves to the given package URL and subpath
// also handles "imports"
async getExportResolution(pkgUrl, subpath, originalSpecifier) {
const resolvedUrl = subpath === '.' ? pkgUrl.slice(0, -1) : pkgUrl + subpath.slice(2);
const pcfg = await this.getPackageConfig(pkgUrl) || {};
if (originalSpecifier[0] === '#') {
if (pcfg.imports === undefined || pcfg.imports === null) return null;
const match = getMapMatch(originalSpecifier, pcfg.imports);
if (!match) return null;
const targets = enumeratePackageTargets(pcfg.imports[match]);
for (const curTarget of targets){
try {
if (await this.finalizeResolve(new URL(curTarget, pkgUrl).href, false, true, pkgUrl) === resolvedUrl) {
return '.';
}
} catch {}
}
return null;
}
if (pcfg.exports !== undefined && pcfg.exports !== null) {
if (typeof pcfg.exports === 'string') {
if (subpath !== '.') return null;
const url = new URL(pcfg.exports, pkgUrl).href;
try {
if (await this.finalizeResolve(url, false, true, pkgUrl) === resolvedUrl) return '.';
} catch {}
return null;
} else if (!allDotKeys(pcfg.exports)) {
if (subpath !== '.') return null;
const targets = enumeratePackageTargets(pcfg.exports);
for (const curTarget of targets){
try {
if (await this.finalizeResolve(new URL(curTarget, pkgUrl).href, false, true, pkgUrl) === resolvedUrl) return '.';
} catch {}
}
return null;
} else {
let bestMatch;
for (const expt of Object.keys(pcfg.exports)){
const targets = enumeratePackageTargets(pcfg.exports[expt]);
for (const curTarget of targets){
if (curTarget.indexOf('*') === -1) {
if (await this.finalizeResolve(new URL(curTarget, pkgUrl).href, false, true, pkgUrl) === resolvedUrl) {
if (bestMatch) {
if (originalSpecifier.endsWith(bestMatch.slice(2))) {
if (!originalSpecifier.endsWith(expt.slice(2))) continue;
} else if (!originalSpecifier.endsWith(expt.slice(2))) {
// Normal precedence = shortest export!
if (expt.length < bestMatch.length) bestMatch = expt;
}
}
bestMatch = expt;
}
} else {
const parts = curTarget.split('*');
if (!subpath.startsWith(parts[0])) continue;
const matchEndIndex = subpath.indexOf(parts[1], parts[0].length);
if (matchEndIndex === -1) continue;
const match = subpath.slice(parts[0].length, matchEndIndex);
const substitutedTarget = curTarget.replace(/\*/g, match);
if (subpath === substitutedTarget) {
const prefix = expt.slice(0, expt.indexOf('*'));
const suffix = expt.slice(expt.indexOf('*') + 1);
if (bestMatch) {
if (originalSpecifier.endsWith(bestMatch.slice(2))) {
if (!originalSpecifier.endsWith(expt.slice(2).replace('*', match)) || bestMatch.startsWith(prefix) && bestMatch.endsWith(suffix)) continue;
} else if (!originalSpecifier.endsWith(expt.slice(2).replace('*', match))) {
if (bestMatch.startsWith(prefix) && bestMatch.endsWith(suffix)) continue;
}
}
bestMatch = expt.replace('*', match);
}
}
}
}
return bestMatch !== null && bestMatch !== void 0 ? bestMatch : null;
}
} else {
try {
if (typeof pcfg.main === 'string' && await this.finalizeResolve(await legacyMainResolve.call(this, pcfg.main, new URL(pkgUrl), originalSpecifier, pkgUrl), false, false, pkgUrl) === resolvedUrl) return '.';
} catch {}
try {
if (await this.finalizeResolve(await legacyMainResolve.call(this, null, new URL(pkgUrl), originalSpecifier, pkgUrl), false, false, pkgUrl) === resolvedUrl) return '.';
} catch {}
try {
if (typeof pcfg.browser === 'string' && await this.finalizeResolve(await legacyMainResolve.call(this, pcfg.browser, new URL(pkgUrl), originalSpecifier, pkgUrl), false, false, pkgUrl) === resolvedUrl) return '.';
} catch {}
try {
if (typeof pcfg.module === 'string' && await this.finalizeResolve(await legacyMainResolve.call(this, pcfg.module, new URL(pkgUrl), originalSpecifier, pkgUrl), false, false, pkgUrl) === resolvedUrl) return '.';
} catch {}
try {
if (await this.finalizeResolve(new URL(subpath, new URL(pkgUrl)).href, false, false, pkgUrl) === resolvedUrl) return subpath;
} catch {}
}
return null;
}
async resolveEmpty(cjsEnv, originalSpecifier, parentUrl) {
const stdlibTarget = {
registry: 'npm',
name: '@jspm/core',
range: new SemverRange('*'),
unstable: true
};
const provider = this.installer.getProvider(stdlibTarget);
const pkg = await this.pm.resolveLatestTarget(stdlibTarget, provider, parentUrl, this);
return this.resolveExport(await this.pm.pkgToUrl(pkg, provider.provider, provider.layer), './nodelibs/@empty', cjsEnv, false, originalSpecifier, parentUrl);
}
// Note: updates here must be tracked in function above
async resolveExport(pkgUrl, subpath, cjsEnv, parentIsCjs, originalSpecifier, parentUrl) {
const env = cjsEnv ? this.cjsEnv : this.env;
const pcfg = await this.getPackageConfig(pkgUrl) || {};
// If the package has no exports then we resolve against "node:@empty":
if (typeof pcfg.exports === 'object' && pcfg.exports !== null && Object.keys(pcfg.exports).length === 0) {
return this.resolveEmpty(cjsEnv, originalSpecifier, parentUrl);
}
function throwExportNotDefined() {
throw new JspmError(`No '${subpath}' exports subpath defined in ${pkgUrl} resolving ${originalSpecifier}${importedFrom(parentUrl)}.`, 'MODULE_NOT_FOUND');
}
if (pcfg.exports !== undefined && pcfg.exports !== null) {
if (typeof pcfg.exports === 'string') {
if (subpath === '.') return this.finalizeResolve(new URL(pcfg.exports, pkgUrl).href, parentIsCjs, true, pkgUrl);
else throwExportNotDefined();
} else if (!allDotKeys(pcfg.exports)) {
if (subpath === '.') {
const url = this.resolvePackageTarget(pcfg.exports, pkgUrl, cjsEnv, '', false);
if (url === null) throwExportNotDefined();
return this.finalizeResolve(url, parentIsCjs, true, pkgUrl);
} else throwExportNotDefined();
} else {
const match = getMapMatch(subpath, pcfg.exports);
if (match) {
let replacement = '';
const wildcardIndex = match.indexOf('*');
if (wildcardIndex !== -1) {
replacement = subpath.slice(wildcardIndex, subpath.length - (match.length - wildcardIndex - 1));
} else if (match.endsWith('/')) {
replacement = subpath.slice(match.length);
}
const resolved = this.resolvePackageTarget(pcfg.exports[match], pkgUrl, cjsEnv, replacement, false);
if (resolved === null) throwExportNotDefined();
return this.finalizeResolve(resolved, parentIsCjs, true, pkgUrl);
}
throwExportNotDefined();
}
} else {
if (subpath === '.' || parentIsCjs && subpath === './') {
if (env.includes('browser') && typeof pcfg.browser === 'string') return this.finalizeResolve(await legacyMainResolve.call(this, pcfg.browser, new URL(pkgUrl), originalSpecifier, pkgUrl), parentIsCjs, false, pkgUrl);
if (env.includes('module') && typeof pcfg.module === 'string') return this.finalizeResolve(await legacyMainResolve.call(this, pcfg.module, new URL(pkgUrl), originalSpecifier, pkgUrl), parentIsCjs, false, pkgUrl);
if (typeof pcfg.main === 'string') return this.finalizeResolve(await legacyMainResolve.call(this, pcfg.main, new URL(pkgUrl), originalSpecifier, pkgUrl), parentIsCjs, false, pkgUrl);
return this.finalizeResolve(await legacyMainResolve.call(this, null, new URL(pkgUrl), originalSpecifier, pkgUrl), parentIsCjs, false, pkgUrl);
} else {
return this.finalizeResolve(new URL(subpath, new URL(pkgUrl)).href, parentIsCjs, false, pkgUrl);
}
}
}
getAnalysis(resolvedUrl) {
let traceEntry = this.traceEntries[resolvedUrl];
if (traceEntry === undefined) {
const restoredAnalysis = this.restoredAnalysis[resolvedUrl];
if (restoredAnalysis) {
traceEntry = this.traceEntries[resolvedUrl] = {
...restoredAnalysis,
hasStaticParent: true,
usesCjs: false,
cjsLazyDeps: null,
parseError: null
};
}
}
if (traceEntry === null || traceEntry === void 0 ? void 0 : traceEntry.parseError) throw traceEntry.parseError;
return traceEntry;
}
analyze(resolvedUrl) {
this.touchedAnalysisUrls.add(resolvedUrl);
const restoredTraceEntry = this.getAnalysis(resolvedUrl);
if (restoredTraceEntry !== undefined) return restoredTraceEntry;
return this._analyzeAsync(resolvedUrl);
}
async _analyzeAsync(resolvedUrl) {
if (!this.traceEntryPromises[resolvedUrl]) this.traceEntryPromises[resolvedUrl] = (async ()=>{
let traceEntry = null;
const analysis = await getAnalysis(this, resolvedUrl);
if (analysis) {
traceEntry = {
parseError: null,
wasCjs: false,
usesCjs: false,
deps: null,
dynamicDeps: null,
cjsLazyDeps: null,
hasStaticParent: true,
size: NaN,
integrity: '',
format: undefined
};
if ('parseError' in analysis) {
traceEntry.parseError = analysis.parseError;
} else {
const { deps, dynamicDeps, cjsLazyDeps, size, format, integrity } = analysis;
traceEntry.integrity = integrity;
traceEntry.format = format;
traceEntry.size = size;
traceEntry.deps = deps.sort();
traceEntry.dynamicDeps = dynamicDeps.sort();
traceEntry.cjsLazyDeps = cjsLazyDeps ? cjsLazyDeps.sort() : cjsLazyDeps;
const wasCJS = format === 'commonjs' || await this.wasCommonJS(resolvedUrl);
if (wasCJS) traceEntry.wasCjs = true;
}
}
this.traceEntries[resolvedUrl] = traceEntry;
})();
await this.traceEntryPromises[resolvedUrl];
const traceEntry = this.getAnalysis(resolvedUrl);
if (traceEntry === null || traceEntry === void 0 ? void 0 : traceEntry.parseError) throw traceEntry.parseError;
return traceEntry !== null && traceEntry !== void 0 ? traceEntry : null;
}
// Note: changes to this function must be updated enumeratePackageTargets too
resolvePackageTarget(target, packageUrl, cjsEnv, subpath, isImport) {
if (typeof target === 'string') {
if (target === '.') {
// special dot export for file packages
return packageUrl.slice(0, -1);
}
if (!target.startsWith('./')) {
if (isImport) return target;
throw new Error(`Invalid exports target ${target} resolving ./${subpath} in ${packageUrl}`);
}
if (!target.startsWith('./')) throw new Error('Invalid ');
if (subpath === '') return new URL(target, packageUrl).href;
if (target.indexOf('*') !== -1) {
return new URL(target.replace(/\*/g, subpath), packageUrl).href;
} else if (target.endsWith('/')) {
return new URL(target + subpath, packageUrl).href;
} else {
throw new Error(`Expected pattern or path export resolving ./${subpath} in ${packageUrl}`);
}
} else if (typeof target === 'object' && target !== null && !Array.isArray(target)) {
for(const condition in target){
if (condition === 'default' || (cjsEnv ? this.cjsEnv : this.env).includes(condition)) {
const resolved = this.resolvePackageTarget(target[condition], packageUrl, cjsEnv, subpath, isImport);
if (resolved) return resolved;
}
}
} else if (Array.isArray(target)) {
// TODO: Validation for arrays
for (const targetFallback of target){
return this.resolvePackageTarget(targetFallback, packageUrl, cjsEnv, subpath, isImport);
}
}
return null;
}
constructor({ env, fetchOpts, providerManager, packageConfigs = {}, preserveSymlinks = false, traceCjs = true, traceTs = true, traceSystem = true }){
_define_property(this, "pcfgPromises", Object.create(null));
_define_property(this, "analysisPromises", Object.create(null));
_define_property(this, "pcfgs", Object.create(null));
_define_property(this, "packageBaseCache", Object.create(null));
_define_property(this, "fileLists", Object.create(null));
_define_property(this, "fetchOpts", void 0);
_define_property(this, "preserveSymlinks", void 0);
_define_property(this, "pm", void 0);
// null implies "not found"
_define_property(this, "traceEntries", Object.create(null));
// any trace error is retained in this promise
_define_property(this, "traceEntryPromises", Object.create(null));
_define_property(this, "env", void 0);
_define_property(this, "cjsEnv", void 0);
_define_property(this, "traceCjs", void 0);
_define_property(this, "traceTs", void 0);
_define_property(this, "traceSystem", void 0);
_define_property(this, "installer", void 0);
// Track visited URLs during final extraction for cache pruning
// Populated by extractMap's visitor, cleared on each extractMap call
_define_property(this, "visitedUrls", new Set());
_define_property(this, "touchedPackageConfigUrls", new Set());
_define_property(this, "touchedAnalysisUrls", new Set());
_define_property(this, "immutableUrls", new Set());
// Lazy restoration from trace cache
_define_property(this, "restoredAnalysis", Object.create(null));
if (env.includes('require')) throw new Error('Cannot manually pass require condition');
if (!env.includes('import')) env.push('import');
this.env = env;
this.cjsEnv = this.env.map((e)=>e === 'import' ? 'require' : e);
this.fetchOpts = fetchOpts;
this.preserveSymlinks = preserveSymlinks;
this.traceCjs = traceCjs;
this.traceTs = traceTs;
this.traceSystem = traceSystem;
this.pm = providerManager;
Object.assign(this.pcfgs, packageConfigs);
}
}
export function enumeratePackageTargets(target, targets = new Set()) {
if (typeof target === 'string') {
targets.add(target);
} else if (typeof target === 'object' && target !== null && !Array.isArray(target)) {
for(const condition in target){
enumeratePackageTargets(target[condition], targets);
}
return targets;
} else if (Array.isArray(target)) {
// TODO: Validation for arrays
for (const targetFallback of target){
enumeratePackageTargets(targetFallback, targets);
return targets;
}
}
return targets;
}
async function legacyMainResolve(main, pkgUrl, originalSpecifier, parentUrl) {
let guess;
if (main === null || main === void 0 ? void 0 : main.endsWith('index.js')) {
if (await this.exists(guess = new URL(`./${main}`, pkgUrl).href)) return guess;
} else if (main) {
if (await this.exists(guess = new URL(`./${main}/index.js`, pkgUrl).href)) return guess;
if (await this.exists(guess = new URL(`./${main}/index.json`, pkgUrl).href)) return guess;
if (await this.exists(guess = new URL(`./${main}/index.node`, pkgUrl).href)) return guess;
if (await this.exists(guess = new URL(`./${main}`, pkgUrl).href)) return guess;
if (await this.exists(guess = new URL(`./${main}.js`, pkgUrl).href)) return guess;
if (await this.exists(guess = new URL(`./${main}.json`, pkgUrl).href)) return guess;
if (await this.exists(guess = new URL(`./${main}.node`, pkgUrl).href)) return guess;
} else {
if (pkgUrl.protocol !== 'file:' && await this.exists(guess = new URL('./mod.ts', pkgUrl).href)) return guess;
if (await this.exists(guess = new URL('./index.js', pkgUrl).href)) return guess;
if (await this.exists(guess = new URL('./index.json', pkgUrl).href)) return guess;
if (await this.exists(guess = new URL('./index.node', pkgUrl).href)) return guess;
}
// Not found.
throw new JspmError(`Unable to resolve ${main ? main + ' in ' : ''}${pkgUrl} resolving ${originalSpecifier !== null && originalSpecifier !== void 0 ? originalSpecifier : ''}${importedFrom(parentUrl)}.`, 'MODULE_NOT_FOUND');
}
// TODO: Refactor legacy intermediate Analysis type into TraceEntry directly
async function getAnalysis(resolver, resolvedUrl) {
const parentIsRequire = false;
const res = await fetch(resolvedUrl, resolver.fetchOpts);
if (isResponseImmutable(res.headers)) resolver.immutableUrls.add(resolvedUrl);
let source;
switch(res.status){
case 200:
case 304:
source = await res.arrayBuffer();
break;
// not found = null
case 404:
return null;
default:
throw new Error(`Invalid status code ${res.status}`);
}
// TODO: headers over extensions for non-file URLs
var sourceText = '';
try {
if (resolvedUrl.endsWith('.wasm')) {
try {
var compiled = await WebAssembly.compile(source);
} catch (e) {
throw e;
}
return {
deps: WebAssembly.Module.imports(compiled).map(({ module })=>module),
dynamicDeps: [],
cjsLazyDeps: null,
size: source.byteLength,
format: 'wasm',
integrity: await getIntegrity(new Uint8Array(source))
};
}
sourceText = new TextDecoder().decode(source);
if (resolver.traceTs && (resolvedUrl.endsWith('.ts') || resolvedUrl.endsWith('.tsx') || resolvedUrl.endsWith('.jsx'))) return await createTsAnalysis(sourceText, resolvedUrl);
if (resolvedUrl.endsWith('.json')) {
try {
JSON.parse(sourceText);
return {
deps: [],
dynamicDeps: [],
cjsLazyDeps: null,
size: sourceText.length,
format: 'json',
integrity: await getIntegrity(sourceText)
};
} catch {}
}
if (resolvedUrl.endsWith('.css')) {
try {
return {
deps: [],
dynamicDeps: [],
cjsLazyDeps: null,
size: sourceText.length,
format: 'css',
integrity: await getIntegrity(sourceText)
};
} catch {}
}
const [imports, exports] = parse(sourceText);
if (imports.every((impt)=>impt.d > 0) && !exports.length && resolvedUrl.startsWith('file:')) {
var _this;
// Support CommonJS package boundary checks for non-ESM on file: protocol only
if (parentIsRequire) {
var _this1;
if (resolver.traceCjs && !(resolvedUrl.endsWith('.mjs') || resolvedUrl.endsWith('.js') && ((_this1 = await resolver.getPackageConfig(await resolver.getPackageBase(resolvedUrl))) === null || _this1 === void 0 ? void 0 : _this1.type) === 'module')) {
return createCjsAnalysis(imports, sourceText, resolvedUrl);
}
} else if (resolver.traceCjs && (resolvedUrl.endsWith('.cjs') || resolvedUrl.endsWith('.js') && ((_this = await resolver.getPackageConfig(await resolver.getPackageBase(resolvedUrl))) === null || _this === void 0 ? void 0 : _this.type) !== 'module')) {
return createCjsAnalysis(imports, sourceText, resolvedUrl);
}
}
return resolver.traceSystem ? createSystemAnalysis(sourceText, imports, resolvedUrl) : createEsmAnalysis(imports, sourceText, resolvedUrl);
} catch (e) {
if (!e.message || !e.message.startsWith('Parse error @:')) {
return {
parseError: e
};
}
// TODO: better parser errors
if (e.message && e.message.startsWith('Parse error @:')) {
const [topline] = e.message.split('\n', 1);
const pos = topline.slice(14);
let [line, col] = pos.split(':');
const lines = sourceText.split('\n');
let errStack = '';
if (line > 1) errStack += '\n ' + lines[line - 2];
errStack += '\n> ' + lines[line - 1];
errStack += '\n ' + ' '.repeat(col - 1) + '^';
if (lines.length > 1) errStack += '\n ' + lines[line];
return {
parseError: new JspmError(`${errStack}\n\nError parsing ${resolvedUrl}:${pos}`)
};
}
throw e;
}
}
//# sourceMappingURL=resolver.js.map