Skip to content

Commit

Permalink
feat: apply realpath for file: URLs in traces (#52)
Browse files Browse the repository at this point in the history
  • Loading branch information
guybedford authored Aug 1, 2021
1 parent 3df869c commit b7df726
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 14 deletions.
2 changes: 1 addition & 1 deletion jspm-core
20 changes: 18 additions & 2 deletions src/trace/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,21 @@ import { getProvider, defaultProviders, Provider } from '../providers/index.js';
import { Analysis, createSystemAnalysis, parseTs } from './analysis.js';
import { createEsmAnalysis } from './analysis.js';
import { createCjsAnalysis } from './cjs.js';
import { getMapMatch, resolveConditional } from '@jspm/import-map';
import { getMapMatch } from '@jspm/import-map';

let realpath, pathToFileURL;

export class Resolver {
log: Log;
pcfgPromises: Record<string, Promise<void>> = Object.create(null);
pcfgs: Record<string, PackageConfig | null> = Object.create(null);
fetchOpts: any;
preserveSymlinks = false;
providers = defaultProviders;
constructor (log: Log, fetchOpts?: any) {
constructor (log: Log, fetchOpts?: any, preserveSymlinks = false) {
this.log = log;
this.fetchOpts = fetchOpts;
this.preserveSymlinks = preserveSymlinks;
}

addCustomProvider (name: string, provider: Provider) {
Expand Down Expand Up @@ -181,6 +185,18 @@ export class Resolver {
return pcfg?.exports?.[subpath + '!cjs'] ? true : false;
}

async realPath (url: string): Promise<string> {
if (!url.startsWith('file:') || this.preserveSymlinks)
return url;
if (!realpath) {
[{ realpath }, { pathToFileURL }] = await Promise.all([
import('fs' as any),
import('url' as any)
]);
}
return pathToFileURL(await new Promise((resolve, reject) => realpath(new URL(url), (err, result) => err ? reject(err) : resolve(result)))).href;
}

async finalizeResolve (url: string, parentIsCjs: boolean, env: string[], pkgUrl?: string): Promise<string> {
if (parentIsCjs && url.endsWith('/'))
url = url.slice(0, -1);
Expand Down
20 changes: 10 additions & 10 deletions src/trace/tracemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ export default class TraceMap {
if (resolvedUrl.protocol !== 'file:' && resolvedUrl.protocol !== 'https:' && resolvedUrl.protocol !== 'http:' && resolvedUrl.protocol !== 'node:' && resolvedUrl.protocol !== 'data:')
throw new JspmError(`Found unexpected protocol ${resolvedUrl.protocol}${importedFrom(parentUrl)}`);
const resolvedHref = resolvedUrl.href;
const finalized = await this.resolver.finalizeResolve(resolvedHref, parentIsCjs, env);
const finalized = await this.resolver.realPath(await this.resolver.finalizeResolve(resolvedHref, parentIsCjs, env));
if (finalized !== resolvedHref) {
this.map.set(resolvedHref.endsWith('/') ? resolvedHref.slice(0, -1) : resolvedHref, finalized);
resolvedUrl = new URL(finalized);
Expand All @@ -244,7 +244,7 @@ export default class TraceMap {
for (const [scope] of pkgSubscopes) {
const mapMatch = getMapMatch(specifier, this.map.scopes[scope]);
if (mapMatch) {
const resolved = new URL(this.map.scopes[scope][mapMatch] + specifier.slice(mapMatch.length), this.map.baseUrl).href;
const resolved = await this.resolver.realPath(new URL(this.map.scopes[scope][mapMatch] + specifier.slice(mapMatch.length), this.map.baseUrl).href);
this.log('trace', `${specifier} ${parentUrl.href} -> ${resolved}`);
await this.traceUrl(resolved, parentUrl, env);
return resolved;
Expand All @@ -257,7 +257,7 @@ export default class TraceMap {
if (userScopeMatch) {
const imports = this.map.scopes[userScopeMatch[0]];
const userImportsMatch = getMapMatch(specifier, imports);
const userImportsResolved = userImportsMatch ? new URL(imports[userImportsMatch] + specifier.slice(userImportsMatch.length), this.map.baseUrl).href : null;
const userImportsResolved = userImportsMatch ? await this.resolver.realPath(new URL(imports[userImportsMatch] + specifier.slice(userImportsMatch.length), this.map.baseUrl).href) : null;
if (userImportsResolved) {
this.log('trace', `${specifier} ${parentUrl.href} -> ${userImportsResolved}`);
await this.traceUrl(userImportsResolved, parentUrl, env);
Expand All @@ -268,7 +268,7 @@ export default class TraceMap {
// Own name import
const pcfg = await this.resolver.getPackageConfig(parentPkgUrl) || {};
if (pcfg.exports && pcfg.name === pkgName) {
const resolved = await this.resolver.resolveExport(parentPkgUrl, subpath, env, parentIsCjs, specifier, parentUrl);
const resolved = await this.resolver.realPath(await this.resolver.resolveExport(parentPkgUrl, subpath, env, parentIsCjs, specifier, parentUrl));
this.log('trace', `${specifier} ${parentUrl.href} -> ${resolved}`);
await this.traceUrl(resolved, parentUrl, env);
return resolved;
Expand All @@ -279,7 +279,7 @@ export default class TraceMap {
const match = getMapMatch(pkgName, pcfg.imports);
if (!match)
throw new JspmError(`No '${pkgName}' import defined in ${parentPkgUrl}${importedFrom(parentUrl)}.`);
const resolved = resolvePackageTarget(pcfg.imports[match], parentPkgUrl, env, subpath === '.' ? '' : subpath.slice(2));
const resolved = await this.resolver.realPath(resolvePackageTarget(pcfg.imports[match], parentPkgUrl, env, subpath === '.' ? '' : subpath.slice(2)));
setResolution(this.installer.installs, pkgName, parentPkgUrl, resolved);
this.log('trace', `${specifier} ${parentUrl.href} -> ${resolved}`);
await this.traceUrl(resolved, parentUrl, env);
Expand All @@ -293,15 +293,15 @@ export default class TraceMap {
if (subpathBase && !pkgUrl.endsWith('/'))
pkgUrl += '/';
const key = subpathBase ? './' + subpathBase + subpath.slice(1) : subpath;
const resolved = await this.resolver.resolveExport(pkgUrl, key, env, parentIsCjs, specifier, parentUrl);
const resolved = await this.resolver.realPath(await this.resolver.resolveExport(pkgUrl, key, env, parentIsCjs, specifier, parentUrl));
this.log('trace', `${specifier} ${parentUrl.href} -> ${resolved}`);
await this.traceUrl(resolved, parentUrl, env);
return resolved;
}

// User import overrides
const userImportsMatch = getMapMatch(specifier, this.map.imports);
const userImportsResolved = userImportsMatch ? new URL(this.map.imports[userImportsMatch] + specifier.slice(userImportsMatch.length), this.map.baseUrl).href : null;
const userImportsResolved = userImportsMatch ? await this.resolver.realPath(new URL(this.map.imports[userImportsMatch] + specifier.slice(userImportsMatch.length), this.map.baseUrl).href) : null;
if (userImportsResolved) {
this.log('trace', `${specifier} ${parentUrl.href} -> ${userImportsResolved}`);
await this.traceUrl(userImportsResolved, parentUrl, env);
Expand All @@ -313,7 +313,7 @@ export default class TraceMap {

private async traceUrl (resolvedUrl: string, parentUrl: URL, env: string[]): Promise<void> {
if (resolvedUrl in this.tracedUrls) return;

const traceEntry: TraceEntry = this.tracedUrls[resolvedUrl] = {
wasCJS: false,
deps: Object.create(null),
Expand All @@ -324,10 +324,10 @@ export default class TraceMap {
integrity: '',
format: undefined
};

if (resolvedUrl.endsWith('/'))
throw new JspmError(`Trailing "/" installs not yet supported installing ${resolvedUrl} for ${parentUrl.href}`);

const parentIsCjs = this.tracedUrls[parentUrl.href]?.format === 'commonjs';

const { deps, dynamicDeps, cjsLazyDeps, size, format } = await this.resolver.analyze(resolvedUrl, parentUrl, this.opts.system, parentIsCjs);
Expand Down
2 changes: 1 addition & 1 deletion test/api/self.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const generator = new Generator({

const { staticDeps, dynamicDeps } = await generator.install('@jspm/generator');

assert.strictEqual(staticDeps.length, 10);
assert.strictEqual(staticDeps.length, 14);
assert.strictEqual(dynamicDeps.length, 0);

const json = generator.getMap();
Expand Down

0 comments on commit b7df726

Please sign in to comment.