UNPKG

@jspm/generator

Version:

Package Import Map Generation Tool

222 lines (220 loc) 10.1 kB
import { JspmError } from "../common/err.js"; import { importedFrom } from "../common/url.js"; import { pkgToStr } from "../install/package.js"; // @ts-ignore import { SemverRange } from "sver"; // @ts-ignore import { fetch } from "../common/fetch.js"; let cdnUrl = "https://ga.jspm.io/"; const systemCdnUrl = "https://ga.system.jspm.io/"; const apiUrl = "https://api.jspm.io/"; const BUILD_POLL_TIME = 60 * 1000; const BUILD_POLL_INTERVAL = 5 * 1000; export const supportedLayers = [ "default", "system" ]; export async function pkgToUrl(pkg, layer) { return `${layer === "system" ? systemCdnUrl : cdnUrl}${pkgToStr(pkg)}/`; } export function configure(config) { if (config.cdnUrl) cdnUrl = config.cdnUrl; } const exactPkgRegEx = /^(([a-z]+):)?((?:@[^/\\%@]+\/)?[^./\\%@][^/\\%@]*)@([^\/]+)(\/.*)?$/; export function parseUrlPkg(url) { let subpath = null; let layer; if (url.startsWith(cdnUrl)) layer = "default"; else if (url.startsWith(systemCdnUrl)) layer = "system"; else return; const [, , registry, name, version] = url.slice((layer === "default" ? cdnUrl : systemCdnUrl).length).match(exactPkgRegEx) || []; if (registry && name && version) { if (registry === "npm" && name === "@jspm/core" && url.includes("/nodelibs/")) { subpath = `./nodelibs/${url.slice(url.indexOf("/nodelibs/") + 10).split("/")[1]}`; if (subpath && subpath.endsWith(".js")) subpath = subpath.slice(0, -3); else subpath = null; } return { pkg: { registry, name, version }, layer, subpath }; } } function getJspmCache(resolver) { const jspmCache = resolver.context.jspmCache; if (!resolver.context.jspmCache) { return resolver.context.jspmCache = { lookupCache: new Map(), versionsCacheMap: new Map(), resolveCache: {}, cachedErrors: new Map(), buildRequested: new Map() }; } return jspmCache; } async function checkBuildOrError(resolver, pkgUrl, fetchOpts) { const pcfg = await resolver.getPackageConfig(pkgUrl); if (pcfg) { return true; } const { cachedErrors } = getJspmCache(resolver); // no package.json! Check if there's a build error: if (cachedErrors.has(pkgUrl)) return cachedErrors.get(pkgUrl); const cachedErrorPromise = (async ()=>{ try { const errLog = await fetch.text(`${pkgUrl}/_error.log`, fetchOpts); throw new JspmError(`Resolved dependency ${pkgUrl} with error:\n\n${errLog}\nPlease post an issue at jspm/project on GitHub, or by following the link below:\n\nhttps://github.com/jspm/project/issues/new?title=CDN%20build%20error%20for%20${encodeURIComponent(pkgUrl)}&body=_Reporting%20CDN%20Build%20Error._%0A%0A%3C!--%20%20No%20further%20description%20necessary,%20just%20click%20%22Submit%20new%20issue%22%20--%3E`); } catch (e) { return false; } })(); cachedErrors.set(pkgUrl, cachedErrorPromise); return cachedErrorPromise; } async function ensureBuild(resolver, pkg, fetchOpts) { if (await checkBuildOrError(resolver, await pkgToUrl(pkg, "default"), fetchOpts)) return; const fullName = `${pkg.name}@${pkg.version}`; const { buildRequested } = getJspmCache(resolver); // 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); const buildPromise = (async ()=>{ const buildRes = await fetch(`${apiUrl}build/${fullName}`, fetchOpts); if (!buildRes.ok && buildRes.status !== 403) { const err = (await buildRes.json()).error; throw new JspmError(`Unable to request the JSPM API for a build of ${fullName}, with error: ${err}.`); } // build requested -> poll on that let startTime = Date.now(); while(true){ await new Promise((resolve)=>setTimeout(resolve, BUILD_POLL_INTERVAL)); if (await checkBuildOrError(resolver, await pkgToUrl(pkg, "default"), fetchOpts)) return; if (Date.now() - startTime >= BUILD_POLL_TIME) throw new JspmError(`Timed out waiting for the build of ${fullName} to be ready on the JSPM CDN. Try again later, or post a jspm.io project issue at https://github.com/jspm/project if the problem persists.`); } })(); buildRequested.set(fullName, buildPromise); return buildPromise; } export async function resolveLatestTarget(target, layer, parentUrl) { const { registry, name, range, unstable } = target; // exact version optimization if (range.isExact && !range.version.tag) { const pkg = { registry, name, version: range.version.toString() }; await ensureBuild(this, pkg, this.fetchOpts); return pkg; } const { resolveCache } = getJspmCache(this); const cache = resolveCache[target.registry + ":" + target.name] = resolveCache[target.registry + ":" + target.name] || { latest: null, majors: Object.create(null), minors: Object.create(null), tags: Object.create(null) }; if (range.isWildcard || range.isExact && range.version.tag === "latest") { let lookup = await (cache.latest || (cache.latest = lookupRange.call(this, registry, name, "", unstable, parentUrl))); // Deno wat? if (lookup instanceof Promise) lookup = await lookup; if (!lookup) return null; this.log("jspm/resolveLatestTarget", `${target.registry}:${target.name}@${range} -> WILDCARD ${lookup.version}${parentUrl ? " [" + parentUrl + "]" : ""}`); await ensureBuild(this, lookup, this.fetchOpts); return lookup; } if (range.isExact && range.version.tag) { const tag = range.version.tag; let lookup = await (cache.tags[tag] || (cache.tags[tag] = lookupRange.call(this, registry, name, tag, unstable, parentUrl))); // Deno wat? if (lookup instanceof Promise) lookup = await lookup; if (!lookup) return null; this.log("jspm/resolveLatestTarget", `${target.registry}:${target.name}@${range} -> TAG ${tag}${parentUrl ? " [" + parentUrl + "]" : ""}`); await ensureBuild(this, lookup, this.fetchOpts); return lookup; } let stableFallback = false; if (range.isMajor) { const major = range.version.major; let lookup = await (cache.majors[major] || (cache.majors[major] = lookupRange.call(this, registry, name, major, unstable, parentUrl))); // Deno wat? if (lookup instanceof Promise) lookup = await lookup; if (!lookup) return null; // if the latest major is actually a downgrade, use the latest minor version (fallthrough) // note this might miss later major prerelease versions, which should strictly be supported via a pkg@X@ unstable major lookup if (range.version.gt(lookup.version)) { stableFallback = true; } else { this.log("jspm/resolveLatestTarget", `${target.registry}:${target.name}@${range} -> MAJOR ${lookup.version}${parentUrl ? " [" + parentUrl + "]" : ""}`); await ensureBuild(this, lookup, this.fetchOpts); return lookup; } } if (stableFallback || range.isStable) { const minor = `${range.version.major}.${range.version.minor}`; let lookup = await (cache.minors[minor] || (cache.minors[minor] = lookupRange.call(this, registry, name, minor, unstable, parentUrl))); // in theory a similar downgrade to the above can happen for stable prerelease ranges ~1.2.3-pre being downgraded to 1.2.2 // this will be solved by the pkg@X.Y@ unstable minor lookup // Deno wat? if (lookup instanceof Promise) lookup = await lookup; if (!lookup) return null; this.log("jspm/resolveLatestTarget", `${target.registry}:${target.name}@${range} -> MINOR ${lookup.version}${parentUrl ? " [" + parentUrl + "]" : ""}`); await ensureBuild(this, lookup, this.fetchOpts); return lookup; } return null; } function pkgToLookupUrl(pkg, edge = false) { return `${cdnUrl}${pkg.registry}:${pkg.name}${pkg.version ? "@" + pkg.version : edge ? "@" : ""}`; } async function lookupRange(registry, name, range, unstable, parentUrl) { const { lookupCache } = getJspmCache(this); const url = pkgToLookupUrl({ registry, name, version: range }, unstable); if (lookupCache.has(url)) return lookupCache.get(url); const lookupPromise = (async ()=>{ const version = await fetch.text(url, this.fetchOpts); if (version) { return { registry, name, version: version.trim() }; } else { // not found const versions = await fetchVersions.call(this, name); const semverRange = new SemverRange(String(range) || "*", unstable); const version = semverRange.bestMatch(versions, unstable); if (version) { return { registry, name, version: version.toString() }; } throw new JspmError(`Unable to resolve ${registry}:${name}@${range} to a valid version${importedFrom(parentUrl)}`); } })(); lookupCache.set(url, lookupPromise); return lookupPromise; } export async function fetchVersions(name) { 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 versions = Object.keys(registryLookup.versions || {}); versionsCacheMap.set(name, versions); return versions; } //# sourceMappingURL=jspm.js.map