Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Run chomp prettier on codebase #401

Merged
merged 1 commit into from
Jan 6, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 21 additions & 21 deletions src/common/fetch-common.ts
Original file line number Diff line number Diff line change
@@ -9,17 +9,21 @@ export interface WrappedResponse {
export type FetchFn = (
url: URL | string,
...args: any[]
) => Promise<WrappedResponse | globalThis.Response>
) => Promise<WrappedResponse | globalThis.Response>;

export type WrappedFetch = ((
url: URL | string,
...args: any[]
) => Promise<WrappedResponse | globalThis.Response>) & {
arrayBuffer: (url: URL | string, ...args: any[]) => Promise<ArrayBuffer | null>,
text: (url: URL | string, ...args: any[]) => Promise<string | null>
) => Promise<WrappedResponse | globalThis.Response>) & {
arrayBuffer: (
url: URL | string,
...args: any[]
) => Promise<ArrayBuffer | null>;
text: (url: URL | string, ...args: any[]) => Promise<string | null>;
};

let retryCount = 5, poolSize = 100;
let retryCount = 5,
poolSize = 100;

export function setRetryCount(count: number) {
retryCount = count;
@@ -58,8 +62,7 @@ export function wrappedFetch(fetch: FetchFn): WrappedFetch {
try {
var res = await fetch(url, ...args);
} catch (e) {
if (retries++ >= retryCount)
throw e;
if (retries++ >= retryCount) throw e;
continue;
}
switch (res.status) {
@@ -75,12 +78,12 @@ export function wrappedFetch(fetch: FetchFn): WrappedFetch {
try {
return await res.arrayBuffer();
} catch (e) {
if (retries++ >= retryCount &&
e.code === "ERR_SOCKET_TIMEOUT" ||
e.code === "ETIMEOUT" ||
e.code === "ECONNRESET" ||
e.code === 'FETCH_ERROR') {

if (
(retries++ >= retryCount && e.code === "ERR_SOCKET_TIMEOUT") ||
e.code === "ETIMEOUT" ||
e.code === "ECONNRESET" ||
e.code === "FETCH_ERROR"
) {
}
}
}
@@ -90,8 +93,7 @@ export function wrappedFetch(fetch: FetchFn): WrappedFetch {
};
wrappedFetch.text = async function (url, ...args) {
const arrayBuffer = await this.arrayBuffer(url, ...args);
if (!arrayBuffer)
return null;
if (!arrayBuffer) return null;
return new TextDecoder().decode(arrayBuffer);
};
return wrappedFetch;
@@ -100,12 +102,10 @@ export function wrappedFetch(fetch: FetchFn): WrappedFetch {
// restrict in-flight fetches to a pool of 100
let p = [];
let c = 0;
function pushFetchPool () {
if (++c > poolSize)
return new Promise(r => p.push(r));
function pushFetchPool() {
if (++c > poolSize) return new Promise((r) => p.push(r));
}
function popFetchPool () {
function popFetchPool() {
c--;
if (p.length)
p.shift()();
if (p.length) p.shift()();
}
46 changes: 23 additions & 23 deletions src/common/fetch.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// @ts-ignore
import { fetch as fetchImpl, clearCache } from '#fetch';
import { fetch as fetchImpl, clearCache } from "#fetch";

export interface WrappedResponse {
url: string;
@@ -15,17 +15,21 @@ export interface WrappedResponse {
export type FetchFn = (
url: URL | string,
...args: any[]
) => Promise<WrappedResponse | globalThis.Response>
) => Promise<WrappedResponse | globalThis.Response>;

export type WrappedFetch = ((
url: URL | string,
...args: any[]
) => Promise<WrappedResponse | globalThis.Response>) & {
arrayBuffer: (url: URL | string, ...args: any[]) => Promise<ArrayBuffer | null>,
text: (url: URL | string, ...args: any[]) => Promise<string | null>
) => Promise<WrappedResponse | globalThis.Response>) & {
arrayBuffer: (
url: URL | string,
...args: any[]
) => Promise<ArrayBuffer | null>;
text: (url: URL | string, ...args: any[]) => Promise<string | null>;
};

let retryCount = 5, poolSize = 100;
let retryCount = 5,
poolSize = 100;

export function setRetryCount(count: number) {
retryCount = count;
@@ -44,7 +48,7 @@ export function setFetch(fetch: typeof globalThis.fetch | WrappedFetch) {
_fetch = fetch as WrappedFetch;
}

export { clearCache, _fetch as fetch }
export { clearCache, _fetch as fetch };

/**
* Wraps a fetch request with pooling, and retry logic on exceptions (emfile / network errors).
@@ -75,8 +79,7 @@ function wrappedFetch(fetch: FetchFn): WrappedFetch {
try {
var res = await fetch(url, ...args);
} catch (e) {
if (retries++ >= retryCount)
throw e;
if (retries++ >= retryCount) throw e;
continue;
}
switch (res.status) {
@@ -92,12 +95,12 @@ function wrappedFetch(fetch: FetchFn): WrappedFetch {
try {
return await res.arrayBuffer();
} catch (e) {
if (retries++ >= retryCount &&
e.code === "ERR_SOCKET_TIMEOUT" ||
e.code === "ETIMEOUT" ||
e.code === "ECONNRESET" ||
e.code === 'FETCH_ERROR') {

if (
(retries++ >= retryCount && e.code === "ERR_SOCKET_TIMEOUT") ||
e.code === "ETIMEOUT" ||
e.code === "ECONNRESET" ||
e.code === "FETCH_ERROR"
) {
}
}
}
@@ -107,8 +110,7 @@ function wrappedFetch(fetch: FetchFn): WrappedFetch {
};
wrappedFetch.text = async function (url, ...args) {
const arrayBuffer = await this.arrayBuffer(url, ...args);
if (!arrayBuffer)
return null;
if (!arrayBuffer) return null;
return new TextDecoder().decode(arrayBuffer);
};
return wrappedFetch;
@@ -117,12 +119,10 @@ function wrappedFetch(fetch: FetchFn): WrappedFetch {
// restrict in-flight fetches to a pool of 100
let p = [];
let c = 0;
function pushFetchPool () {
if (++c > poolSize)
return new Promise(r => p.push(r));
function pushFetchPool() {
if (++c > poolSize) return new Promise((r) => p.push(r));
}
function popFetchPool () {
function popFetchPool() {
c--;
if (p.length)
p.shift()();
if (p.length) p.shift()();
}
17 changes: 11 additions & 6 deletions src/common/integrity.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
let _nodeCrypto;

export async function getIntegrityNodeLegacy(buf: Uint8Array | string): Promise<`sha384-${string}`> {
const hash = (_nodeCrypto || (_nodeCrypto = (await (0, eval)('import("node:crypto")')))).createHash("sha384");
export async function getIntegrityNodeLegacy(
buf: Uint8Array | string
): Promise<`sha384-${string}`> {
const hash = (
_nodeCrypto || (_nodeCrypto = await (0, eval)('import("node:crypto")'))
).createHash("sha384");
hash.update(buf);
return `sha384-${hash.digest("base64")}`;
}

export let getIntegrity = async function getIntegrity(buf: Uint8Array | string): Promise<`sha384-${string}`> {
export let getIntegrity = async function getIntegrity(
buf: Uint8Array | string
): Promise<`sha384-${string}`> {
const data = typeof buf === "string" ? new TextEncoder().encode(buf) : buf;
const hashBuffer = await crypto.subtle.digest("SHA-384", data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashBase64 = btoa(String.fromCharCode(...hashArray));
return `sha384-${hashBase64}`;
}
};

if (typeof crypto === 'undefined')
getIntegrity = getIntegrityNodeLegacy;
if (typeof crypto === "undefined") getIntegrity = getIntegrityNodeLegacy;
151 changes: 90 additions & 61 deletions src/generator.ts
Original file line number Diff line number Diff line change
@@ -29,7 +29,11 @@ import {
} from "./install/package.js";
import TraceMap from "./trace/tracemap.js";
// @ts-ignore
import { clearCache as clearFetchCache, fetch as _fetch, setFetch } from "./common/fetch.js";
import {
clearCache as clearFetchCache,
fetch as _fetch,
setFetch,
} from "./common/fetch.js";
import { IImportMap, ImportMap } from "@jspm/import-map";
import process from "process";
import { SemverRange } from "sver";
@@ -40,7 +44,11 @@ import { Replacer } from "./common/str.js";
import { analyzeHtml } from "./html/analyze.js";
import { InstallTarget, type InstallMode } from "./install/installer.js";
import { LockResolutions } from "./install/lock.js";
import { configureProviders, getDefaultProviderStrings, type Provider } from "./providers/index.js";
import {
configureProviders,
getDefaultProviderStrings,
type Provider,
} from "./providers/index.js";
import * as nodemodules from "./providers/nodemodules.js";
import { Resolver } from "./trace/resolver.js";
import { getMaybeWrapperUrl } from "./common/wrapper.js";
@@ -50,7 +58,7 @@ export {
// utility export
analyzeHtml,
// hook export
setFetch
setFetch,
};

// Type exports for users:
@@ -328,7 +336,7 @@ export interface GeneratorOptions {

/**
* Provider configuration options
*
*
* @example
* ```js
* const generator = new Generator({
@@ -553,8 +561,7 @@ export class Generator {
if (inputMap) this.addMappings(inputMap);

// Set the fetch retry count
if (typeof fetchRetries === 'number')
setRetryCount(fetchRetries);
if (typeof fetchRetries === "number") setRetryCount(fetchRetries);

configureProviders(providerConfig, resolver.providers);
}
@@ -639,7 +646,10 @@ export class Generator {
error = true;
throw e;
} finally {
const { map, staticDeps, dynamicDeps } = await this.traceMap.extractMap(this.traceMap.pins, this.integrity);
const { map, staticDeps, dynamicDeps } = await this.traceMap.extractMap(
this.traceMap.pins,
this.integrity
);
this.map = map;
if (!error) return { staticDeps, dynamicDeps };
}
@@ -734,7 +744,8 @@ export class Generator {
);
} catch (err) {
// Most likely cause of a generation failure:
err.message += '\n\nIf you are linking locally against your node_modules folder, make sure that you have all the necessary dependencies installed.';
err.message +=
"\n\nIf you are linking locally against your node_modules folder, make sure that you have all the necessary dependencies installed.";
}

const preloadDeps =
@@ -938,7 +949,11 @@ export class Generator {

// Split the case of multiple install subpaths into multiple installs
// TODO: flatten all subpath installs here
if (!Array.isArray(install) && typeof install !== "string" && install.subpaths !== undefined) {
if (
!Array.isArray(install) &&
typeof install !== "string" &&
install.subpaths !== undefined
) {
install.subpaths.every((subpath) => {
if (
typeof subpath !== "string" ||
@@ -948,62 +963,71 @@ export class Generator {
`Install subpath "${subpath}" must be equal to "." or start with "./".`
);
});
return this._install(install.subpaths.map((subpath) => ({
target: (install as Install).target,
alias: (install as Install).alias,
subpath,
})));
return this._install(
install.subpaths.map((subpath) => ({
target: (install as Install).target,
alias: (install as Install).alias,
subpath,
}))
);
}

if (!Array.isArray(install))
install = [install];
if (!Array.isArray(install)) install = [install];

// Handle case of multiple install targets with at most one subpath:
await this.traceMap.processInputMap; // don't race input processing

const imports = await Promise.all(install.map(async install => {
// Resolve input information to a target package:
let alias, target, subpath;
if (typeof install === "string" || typeof install.target === "string") {
({ alias, target, subpath } = await installToTarget.call(
this,
install,
this.traceMap.installer.defaultRegistry
));
} else {
({ alias, target, subpath } = install);
validatePkgName(alias);
}
const imports = await Promise.all(
install.map(async (install) => {
// Resolve input information to a target package:
let alias, target, subpath;
if (typeof install === "string" || typeof install.target === "string") {
({ alias, target, subpath } = await installToTarget.call(
this,
install,
this.traceMap.installer.defaultRegistry
));
} else {
({ alias, target, subpath } = install);
validatePkgName(alias);
}

this.log(
"generator/install",
`Adding primary constraint for ${alias}: ${JSON.stringify(target)}`
);
this.log(
"generator/install",
`Adding primary constraint for ${alias}: ${JSON.stringify(target)}`
);

// By default, an install takes the latest compatible version for primary
// dependencies, and existing in-range versions for secondaries:
mode ??= "latest-primaries";
// By default, an install takes the latest compatible version for primary
// dependencies, and existing in-range versions for secondaries:
mode ??= "latest-primaries";

await this.traceMap.add(alias, target, mode);
await this.traceMap.add(alias, target, mode);

return alias + (subpath ? subpath.slice(1) : '');
}));
return alias + (subpath ? subpath.slice(1) : "");
})
);

await Promise.all(imports.map(async impt => {
await this.traceMap.visit(impt, {
installMode: mode,
toplevel: true,
},
this.mapUrl.href
);

// Add the target import as a top-level pin
// we do this after the trace, so failed installs don't pollute the map
if (!this.traceMap.pins.includes(impt))
this.traceMap.pins.push(impt);
}));

const { map, staticDeps, dynamicDeps } = await this.traceMap.extractMap(this.traceMap.pins, this.integrity);
await Promise.all(
imports.map(async (impt) => {
await this.traceMap.visit(
impt,
{
installMode: mode,
toplevel: true,
},
this.mapUrl.href
);

// Add the target import as a top-level pin
// we do this after the trace, so failed installs don't pollute the map
if (!this.traceMap.pins.includes(impt)) this.traceMap.pins.push(impt);
})
);

const { map, staticDeps, dynamicDeps } = await this.traceMap.extractMap(
this.traceMap.pins,
this.integrity
);
this.map = map;
return { staticDeps, dynamicDeps };
}
@@ -1014,8 +1038,10 @@ export class Generator {
*/
async reinstall() {
await this.traceMap.processInputMap;
const { map, staticDeps, dynamicDeps } =
await this.traceMap.extractMap(this.traceMap.pins, this.integrity);
const { map, staticDeps, dynamicDeps } = await this.traceMap.extractMap(
this.traceMap.pins,
this.integrity
);
this.map = map;
return { staticDeps, dynamicDeps };
}
@@ -1086,8 +1112,10 @@ export class Generator {
}

await this._install(installs, mode);
const { map, staticDeps, dynamicDeps } =
await this.traceMap.extractMap(this.traceMap.pins, this.integrity);
const { map, staticDeps, dynamicDeps } = await this.traceMap.extractMap(
this.traceMap.pins,
this.integrity
);
this.map = map;
return { staticDeps, dynamicDeps };
}
@@ -1113,8 +1141,10 @@ export class Generator {
);
}
this.traceMap.pins = pins;
const { staticDeps, dynamicDeps, map } =
await this.traceMap.extractMap(this.traceMap.pins, this.integrity);
const { staticDeps, dynamicDeps, map } = await this.traceMap.extractMap(
this.traceMap.pins,
this.integrity
);
this.map = map;
return { staticDeps, dynamicDeps };
}
@@ -1439,4 +1469,3 @@ function detectDefaultProvider(

return defaultProvider || winner || "jspm.io";
}

6 changes: 5 additions & 1 deletion src/providers/esmsh.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { ExactPackage, LatestPackageTarget, PackageConfig } from "../install/package.js";
import {
ExactPackage,
LatestPackageTarget,
PackageConfig,
} from "../install/package.js";
import { Resolver } from "../trace/resolver.js";
// @ts-ignore
import { fetch } from "../common/fetch.js";
5 changes: 4 additions & 1 deletion src/providers/index.ts
Original file line number Diff line number Diff line change
@@ -71,7 +71,10 @@ export function getProvider(name: string, providers: Record<string, Provider>) {
}

// Apply provider configurations
export function configureProviders(providerConfig: Record<string, any>, providers: Record<string, Provider>) {
export function configureProviders(
providerConfig: Record<string, any>,
providers: Record<string, Provider>
) {
for (const [providerName, provider] of Object.entries(providers)) {
if (provider.configure) {
provider.configure(providerConfig[providerName] || {});
48 changes: 30 additions & 18 deletions src/providers/jspm.ts
Original file line number Diff line number Diff line change
@@ -42,8 +42,7 @@ export async function pkgToUrl(
}

export function configure(config: any) {
if (config.cdnUrl)
cdnUrl = config.cdnUrl;
if (config.cdnUrl) cdnUrl = config.cdnUrl;
}

const exactPkgRegEx =
@@ -75,16 +74,16 @@ export function parseUrlPkg(url: string) {
}
}

function getJspmCache (resolver: Resolver): JspmCache {
function getJspmCache(resolver: Resolver): JspmCache {
const jspmCache = resolver.context.jspmCache;
if (!resolver.context.jspmCache) {
return resolver.context.jspmCache = {
return (resolver.context.jspmCache = {
lookupCache: new Map(),
versionsCacheMap: new Map(),
resolveCache: {},
cachedErrors: new Map(),
buildRequested: new Map(),
};
});
}
return jspmCache;
}
@@ -100,8 +99,7 @@ async function checkBuildOrError(
}
const { cachedErrors } = getJspmCache(resolver);
// no package.json! Check if there's a build error:
if (cachedErrors.has(pkgUrl))
return cachedErrors.get(pkgUrl);
if (cachedErrors.has(pkgUrl)) return cachedErrors.get(pkgUrl);

const cachedErrorPromise = (async () => {
try {
@@ -119,8 +117,14 @@ async function checkBuildOrError(
return cachedErrorPromise;
}

async function ensureBuild(resolver: Resolver, pkg: ExactPackage, fetchOpts: any) {
if (await checkBuildOrError(resolver, await pkgToUrl(pkg, "default"), fetchOpts))
async function ensureBuild(
resolver: Resolver,
pkg: ExactPackage,
fetchOpts: any
) {
if (
await checkBuildOrError(resolver, await pkgToUrl(pkg, "default"), fetchOpts)
)
return;

const fullName = `${pkg.name}@${pkg.version}`;
@@ -129,8 +133,7 @@ async function ensureBuild(resolver: Resolver, pkg: ExactPackage, fetchOpts: any

// no package.json AND no build error -> post a build request
// once the build request has been posted, try polling for up to 2 mins
if (buildRequested.has(fullName))
return buildRequested.get(fullName);
if (buildRequested.has(fullName)) return buildRequested.get(fullName);
const buildPromise = (async () => {
const buildRes = await fetch(`${apiUrl}build/${fullName}`, fetchOpts);
if (!buildRes.ok && buildRes.status !== 403) {
@@ -145,7 +148,13 @@ async function ensureBuild(resolver: Resolver, pkg: ExactPackage, fetchOpts: any
while (true) {
await new Promise((resolve) => setTimeout(resolve, BUILD_POLL_INTERVAL));

if (await checkBuildOrError(resolver, await pkgToUrl(pkg, "default"), fetchOpts))
if (
await checkBuildOrError(
resolver,
await pkgToUrl(pkg, "default"),
fetchOpts
)
)
return;

if (Date.now() - startTime >= BUILD_POLL_TIME)
@@ -302,8 +311,7 @@ async function lookupRange(
): Promise<ExactPackage | null> {
const { lookupCache } = getJspmCache(this);
const url = pkgToLookupUrl({ registry, name, version: range }, unstable);
if (lookupCache.has(url))
return lookupCache.get(url);
if (lookupCache.has(url)) return lookupCache.get(url);
const lookupPromise = (async () => {
const version = await fetch.text(url, this.fetchOpts);
if (version) {
@@ -328,14 +336,18 @@ async function lookupRange(
return lookupPromise;
}

export async function fetchVersions(this: Resolver, name: string): Promise<string[]> {
export async function fetchVersions(
this: Resolver,
name: string
): Promise<string[]> {
const { versionsCacheMap } = getJspmCache(this);
if (versionsCacheMap.has(name)) {
return versionsCacheMap.get(name);
}
const registryLookup = JSON.parse(await (
await fetch.text(`https://npmlookup.jspm.io/${encodeURI(name)}`, {})
)) || {};
const registryLookup =
JSON.parse(
await await fetch.text(`https://npmlookup.jspm.io/${encodeURI(name)}`, {})
) || {};
const versions = Object.keys(registryLookup.versions || {});
versionsCacheMap.set(name, versions);

3 changes: 2 additions & 1 deletion src/trace/analysis.ts
Original file line number Diff line number Diff line change
@@ -71,7 +71,8 @@ export async function createEsmAnalysis(
};
}
const leadingCommentRegex = /^\s*(\/\*[\s\S]*?\*\/|\s*\/\/[^\n]*)*/;
const registerRegex = /^\s*System\s*\.\s*register\s*\(\s*(\[[^\]]*\])\s*,\s*\(?function\s*\(\s*([^\),\s]+\s*(,\s*([^\),\s]+)\s*)?\s*)?\)/;
const registerRegex =
/^\s*System\s*\.\s*register\s*\(\s*(\[[^\]]*\])\s*,\s*\(?function\s*\(\s*([^\),\s]+\s*(,\s*([^\),\s]+)\s*)?\s*)?\)/;

function systemMatch(code) {
const commentMatch = code.match(leadingCommentRegex);
115 changes: 63 additions & 52 deletions src/trace/resolver.ts
Original file line number Diff line number Diff line change
@@ -84,7 +84,7 @@ export interface TraceEntry {

// network errors are stored on the traceEntryPromises promise, while parser
// errors are stored here. This allows for existence checks in resolver operations.
parseError: Error
parseError: Error;
}

export class Resolver {
@@ -363,7 +363,12 @@ export class Resolver {
provider,
this.providers
).resolveLatestTarget;
const pkg = await resolveLatestTarget.call(this, latestTarget, layer, parentUrl);
const pkg = await resolveLatestTarget.call(
this,
latestTarget,
layer,
parentUrl
);
if (pkg) return pkg;

if (provider === "nodemodules") {
@@ -424,7 +429,7 @@ export class Resolver {
// 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 + '/');
const urlUrl = new URL(url + "/");
if (this.env.includes("browser") && typeof pcfg.browser === "string")
return this.finalizeResolve(
await legacyMainResolve.call(this, pcfg.browser, urlUrl),
@@ -472,7 +477,8 @@ export class Resolver {
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 === false)
return this.resolveEmpty(parentIsCjs, url, pkgUrl);
if (!target.startsWith("./"))
throw new Error(
`TODO: External browser map for ${subpath} to ${target} in ${url}`
@@ -718,7 +724,11 @@ export class Resolver {
return null;
}

async resolveEmpty(cjsEnv: boolean, originalSpecifier: string, parentUrl: string) {
async resolveEmpty(
cjsEnv: boolean,
originalSpecifier: string,
parentUrl: string
) {
const stdlibTarget = {
registry: "npm",
name: "@jspm/core",
@@ -893,53 +903,54 @@ export class Resolver {

getAnalysis(resolvedUrl: string): TraceEntry | null | undefined {
const traceEntry = this.traceEntries[resolvedUrl];
if (traceEntry?.parseError)
throw traceEntry.parseError;
if (traceEntry?.parseError) throw traceEntry.parseError;
return traceEntry;
}

async analyze(resolvedUrl: string): Promise<TraceEntry | null> {
if (!this.traceEntryPromises[resolvedUrl]) this.traceEntryPromises[resolvedUrl] = (async () => {
let traceEntry: TraceEntry | null = 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;
if (!this.traceEntryPromises[resolvedUrl])
this.traceEntryPromises[resolvedUrl] = (async () => {
let traceEntry: TraceEntry | null = 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;

// wasCJS distinct from CJS because it applies to CJS transformed into ESM
// from the resolver perspective
const wasCJS =
format === "commonjs" || (await this.wasCommonJS(resolvedUrl));
if (wasCJS) traceEntry.wasCjs = true;
// wasCJS distinct from CJS because it applies to CJS transformed into ESM
// from the resolver perspective
const wasCJS =
format === "commonjs" || (await this.wasCommonJS(resolvedUrl));
if (wasCJS) traceEntry.wasCjs = true;
}
}
}
this.traceEntries[resolvedUrl] = traceEntry;
})();
this.traceEntries[resolvedUrl] = traceEntry;
})();
await this.traceEntryPromises[resolvedUrl];
const traceEntry = this.traceEntries[resolvedUrl];
if (traceEntry?.parseError)
throw traceEntry.parseError;
if (traceEntry?.parseError) throw traceEntry.parseError;
return traceEntry;
}

@@ -1127,11 +1138,13 @@ function allDotKeys(exports: Record<string, any>) {
}

// TODO: Refactor legacy intermediate Analysis type into TraceEntry directly
async function getAnalysis(resolver: Resolver, resolvedUrl: string): Promise<Analysis | null> {
async function getAnalysis(
resolver: Resolver,
resolvedUrl: string
): Promise<Analysis | null> {
const parentIsRequire = false;
const source = await fetch.arrayBuffer(resolvedUrl, resolver.fetchOpts);
if (!source)
return null;
if (!source) return null;
// TODO: headers over extensions for non-file URLs
try {
if (resolvedUrl.endsWith(".wasm")) {
@@ -1141,9 +1154,7 @@ async function getAnalysis(resolver: Resolver, resolvedUrl: string): Promise<Ana
throw e;
}
return {
deps: WebAssembly.Module.imports(compiled).map(
({ module }) => module
),
deps: WebAssembly.Module.imports(compiled).map(({ module }) => module),
dynamicDeps: [],
cjsLazyDeps: null,
size: source.byteLength,
@@ -1230,7 +1241,7 @@ async function getAnalysis(resolver: Resolver, resolvedUrl: string): Promise<Ana
} catch (e) {
if (!e.message || !e.message.startsWith("Parse error @:")) {
return {
parseError: e
parseError: e,
};
}
// TODO: better parser errors
@@ -1247,7 +1258,7 @@ async function getAnalysis(resolver: Resolver, resolvedUrl: string): Promise<Ana
return {
parseError: new JspmError(
`${errStack}\n\nError parsing ${resolvedUrl}:${pos}`
)
),
};
}
throw e;
34 changes: 26 additions & 8 deletions src/trace/tracemap.ts
Original file line number Diff line number Diff line change
@@ -20,7 +20,12 @@ import {
getMapMatch,
getScopeMatches,
} from "@jspm/import-map";
import { isBuiltinScheme, isMappableScheme, Resolver, TraceEntry } from "./resolver.js";
import {
isBuiltinScheme,
isMappableScheme,
Resolver,
TraceEntry,
} from "./resolver.js";
import { Log } from "../common/log.js";
import {
mergeConstraints,
@@ -74,8 +79,8 @@ function combineSubpaths(
installSubpath: "." | `./${string}` | null,
traceSubpath: "." | `./${string}`
): `./${string}` | "." {
if (traceSubpath.endsWith('/'))
throw new Error('Trailing slash subpaths unsupported');
if (traceSubpath.endsWith("/"))
throw new Error("Trailing slash subpaths unsupported");
return installSubpath === null ||
installSubpath === "." ||
traceSubpath === "."
@@ -198,12 +203,19 @@ export default class TraceMap {
var entry = await this.resolver.analyze(resolved);
} catch (e) {
if (e instanceof JspmError)
throw new Error(`Unable to analyze ${resolved} imported from ${parentUrl}: ${e.message}`);
throw new Error(
`Unable to analyze ${resolved} imported from ${parentUrl}: ${e.message}`
);
else
throw new Error(`Unable to analyze ${resolved} imported from ${parentUrl}`, { cause: e });
throw new Error(
`Unable to analyze ${resolved} imported from ${parentUrl}`,
{ cause: e }
);
}
if (entry === null) {
throw new Error(`Module not found ${resolved} imported from ${parentUrl}`);
throw new Error(
`Module not found ${resolved} imported from ${parentUrl}`
);
}
if (entry?.format === "commonjs" && entry.usesCjs && !this.opts.commonJS) {
throw new JspmError(
@@ -282,7 +294,8 @@ export default class TraceMap {
const existing = map.imports[specifier];
if (
!existing ||
(existing !== resolved && this.resolver.getAnalysis(parentUrl)?.wasCjs)
(existing !== resolved &&
this.resolver.getAnalysis(parentUrl)?.wasCjs)
) {
map.set(specifier, resolved);
}
@@ -562,7 +575,12 @@ export default class TraceMap {
const resolved = await this.resolver.realPath(
await this.resolver.resolveExport(
installUrl,
combineSubpaths(installSubpath, parentIsCjs && subpath.endsWith('/') ? subpath.slice(0, -1) as `./${string}` : subpath),
combineSubpaths(
installSubpath,
parentIsCjs && subpath.endsWith("/")
? (subpath.slice(0, -1) as `./${string}`)
: subpath
),
cjsEnv,
parentIsCjs,
specifier,
4 changes: 2 additions & 2 deletions src/trace/ts.ts
Original file line number Diff line number Diff line change
@@ -85,14 +85,14 @@ export async function createTsAnalysis(
return {
visitor: {
ExportAllDeclaration(path) {
if (path.node.exportKind !== 'type')
if (path.node.exportKind !== "type")
imports.add(path.node.source.value);
},
ExportNamedDeclaration(path) {
if (path.node.source) imports.add(path.node.source.value);
},
ImportDeclaration(path) {
if (path.node.exportKind !== 'type')
if (path.node.exportKind !== "type")
imports.add(path.node.source.value);
},
Import(path) {
12 changes: 10 additions & 2 deletions test/html/preload.test.js
Original file line number Diff line number Diff line change
@@ -27,8 +27,16 @@ let html = `
`;
let pins = await generator.addMappings(html);

const reactProductionIntegrity = await getIntegrity(await (await fetch('https://ga.jspm.io/npm:react@16.14.0/cjs/react.production.min.js')).text());
const reactIndexIntegrity = await getIntegrity(await (await fetch('https://ga.jspm.io/npm:react@16.14.0/index.js')).text());
const reactProductionIntegrity = await getIntegrity(
await (
await fetch(
"https://ga.jspm.io/npm:react@16.14.0/cjs/react.production.min.js"
)
).text()
);
const reactIndexIntegrity = await getIntegrity(
await (await fetch("https://ga.jspm.io/npm:react@16.14.0/index.js")).text()
);

assert.strictEqual(
await generator.htmlInject(html, { pins, integrity: true }),
24 changes: 15 additions & 9 deletions test/perf/perf.test.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import { Generator } from "@jspm/generator";
import assert from "assert";
import { fetch } from '../../lib/common/fetch.js';
import { fetch } from "../../lib/common/fetch.js";

const largeInstallSet = await (await fetch(new URL('./large-install-set.json', import.meta.url), {})).json();
const largeInstallSet = await (
await fetch(new URL("./large-install-set.json", import.meta.url), {})
).json();

// First, prime the fetch cache so we are not testing the network as much as possible
{
const generator = new Generator({
defaultProvider: "jspm.io",
resolutions: {
react: '16.14.0'
}
react: "16.14.0",
},
});
const installs = Object.entries(largeInstallSet).map(([name, versionRange]) => ({ target: name + '@' + versionRange }));
const installs = Object.entries(largeInstallSet).map(
([name, versionRange]) => ({ target: name + "@" + versionRange })
);
await generator.install(installs);
}

@@ -21,12 +25,14 @@ const largeInstallSet = await (await fetch(new URL('./large-install-set.json', i
const generator = new Generator({
defaultProvider: "jspm.io",
resolutions: {
react: '16.14.0'
}
react: "16.14.0",
},
});

const installs = Object.entries(largeInstallSet).map(([name, versionRange]) => ({ target: name + '@' + versionRange }));

const installs = Object.entries(largeInstallSet).map(
([name, versionRange]) => ({ target: name + "@" + versionRange })
);

const start = performance.now();
await generator.install(installs);

11 changes: 7 additions & 4 deletions test/providers/config.perf.test.js
Original file line number Diff line number Diff line change
@@ -2,7 +2,10 @@ import { Generator } from "@jspm/generator";
import assert from "assert";

// this private origin shouldn't really be shared publicly
const name = [111, 97, 107, 116, 105, 113].map(x => String.fromCharCode(x)).reverse().join('');
const name = [111, 97, 107, 116, 105, 113]
.map((x) => String.fromCharCode(x))
.reverse()
.join("");

// Test with custom CDN URL
{
@@ -11,9 +14,9 @@ const name = [111, 97, 107, 116, 105, 113].map(x => String.fromCharCode(x)).reve
defaultProvider: "jspm.io",
providerConfig: {
"jspm.io": {
cdnUrl: `https://${name}.com/`
}
}
cdnUrl: `https://${name}.com/`,
},
},
});

await generator.install("react@17.0.1");
2 changes: 1 addition & 1 deletion test/resolve/cjspkg/browser-dep-exclude.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
import 'excluded';
import "excluded";
2 changes: 1 addition & 1 deletion test/resolve/cjspkg/browser-dep-include.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
import 'jquery';
import "jquery";
4 changes: 2 additions & 2 deletions test/resolve/cjspkg/browser.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
import './browser-dep-exclude.js';
import './browser-dep-include.js';
import "./browser-dep-exclude.js";
import "./browser-dep-include.js";
17 changes: 10 additions & 7 deletions test/resolve/unused-cjs.test.js
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ import assert from "assert";

const generator = new Generator({
mapUrl: import.meta.url,
commonJS: true
commonJS: true,
});

// Should not throw, index file doesn't use CJS:
@@ -17,14 +17,17 @@ await (async () => {
} catch {}
})();

await generator.install({ target: './cjspkg', subpath: './browser.js' });
await generator.install({ target: "./cjspkg", subpath: "./browser.js" });
assert.deepStrictEqual(generator.getMap(), {
imports: {
'./cjspkg/browser-dep-exclude.js': 'https://ga.jspm.io/npm:@jspm/core@2.1.0/nodelibs/@empty.js',
'cjspkg/browser.js': './cjspkg/browser.js',
unusedcjspkg: './unusedcjspkg/index.js'
"./cjspkg/browser-dep-exclude.js":
"https://ga.jspm.io/npm:@jspm/core@2.1.0/nodelibs/@empty.js",
"cjspkg/browser.js": "./cjspkg/browser.js",
unusedcjspkg: "./unusedcjspkg/index.js",
},
scopes: {
'./cjspkg/': { jquery: 'https://ga.jspm.io/npm:jquery@3.7.1/dist/jquery.js' }
}
"./cjspkg/": {
jquery: "https://ga.jspm.io/npm:jquery@3.7.1/dist/jquery.js",
},
},
});