UNPKG

@jspm/generator

Version:

Package Import Map Generation Tool

1,264 lines (1,252 loc) 293 kB
import sver, { SemverRange as SemverRange$2, Semver as Semver$1 } from 'sver'; import { setNetworkFetch, fetch as fetch$1, isVirtualUrl, setVirtualSourceData, setRetryCount, clearCache as clearCache$1 } from '@jspm/fetch'; import { ImportMap, getScopeMatches, getMapMatch as getMapMatch$1 } from '@jspm/import-map'; import { parse } from 'es-module-lexer/js'; import { init, parse as parse$1 } from 'es-module-lexer'; import { minimatch } from 'minimatch'; let _nodeCrypto; async function getIntegrityNodeLegacy(buf) { const hash = (_nodeCrypto || (_nodeCrypto = await (0, eval)('import("node:crypto")'))).createHash('sha384'); hash.update(buf); return `sha384-${hash.digest('base64')}`; } let getIntegrity = async function getIntegrity(buf) { 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; // See: https://nodejs.org/docs/latest/api/modules.html#the-module-scope const cjsGlobals = [ '__dirname', '__filename', 'exports', 'module', 'require' ]; let babel$1; function setBabel$1(_babel) { babel$1 = _babel; } async function createCjsAnalysis(imports, source, url) { if (!babel$1) babel$1 = await import('@babel/core'); const requires = new Set(); const lazy = new Set(); const unboundGlobals = new Set(); babel$1.transform(source, { ast: false, sourceMaps: false, inputSourceMap: false, babelrc: false, babelrcRoots: false, configFile: false, highlightCode: false, compact: false, sourceType: 'script', parserOpts: { allowReturnOutsideFunction: true, // plugins: stage3Syntax, errorRecovery: true }, plugins: [ ({ types: t })=>{ return { visitor: { Program (path, state) { state.functionDepth = 0; }, CallExpression (path, state) { if (t.isIdentifier(path.node.callee, { name: 'require' }) || t.isIdentifier(path.node.callee.object, { name: 'require' }) && t.isIdentifier(path.node.callee.property, { name: 'resolve' }) || t.isMemberExpression(path.node.callee) && t.isIdentifier(path.node.callee.object, { name: 'module' }) && t.isIdentifier(path.node.callee.property, { name: 'require' })) { const req = buildDynamicString$1(path.get('arguments.0').node, url); requires.add(req); if (state.functionDepth > 0) lazy.add(req); } }, ReferencedIdentifier (path) { let identifierName = path.node.name; if (!path.scope.hasBinding(identifierName)) { unboundGlobals.add(identifierName); } }, Scope: { enter (path, state) { if (t.isFunction(path.scope.block)) state.functionDepth++; }, exit (path, state) { if (t.isFunction(path.scope.block)) state.functionDepth--; } } } }; } ] }); // Check if the module actually uses any CJS-specific globals, as otherwise // other host runtimes like browser/deno can run this module anyway: let usesCjs = false; for (let g of cjsGlobals){ if (unboundGlobals.has(g)) { usesCjs = true; break; } } return { deps: [ ...requires ], dynamicDeps: imports.filter((impt)=>impt.n).map((impt)=>impt.n), cjsLazyDeps: [ ...lazy ], size: source.length, format: 'commonjs', usesCjs, integrity: await getIntegrity(source) }; } function buildDynamicString$1(node, fileName, isEsm = false, lastIsWildcard = false) { if (node.type === 'StringLiteral') { return node.value; } if (node.type === 'TemplateLiteral') { let str = ''; for(let i = 0; i < node.quasis.length; i++){ const quasiStr = node.quasis[i].value.cooked; if (quasiStr.length) { str += quasiStr; lastIsWildcard = false; } const nextNode = node.expressions[i]; if (nextNode) { const nextStr = buildDynamicString$1(nextNode, fileName, isEsm, lastIsWildcard); if (nextStr.length) { lastIsWildcard = nextStr.endsWith('*'); str += nextStr; } } } return str; } if (node.type === 'BinaryExpression' && node.operator === '+') { const leftResolved = buildDynamicString$1(node.left, fileName, isEsm, lastIsWildcard); if (leftResolved.length) lastIsWildcard = leftResolved.endsWith('*'); const rightResolved = buildDynamicString$1(node.right, fileName, isEsm, lastIsWildcard); return leftResolved + rightResolved; } if (node.type === 'Identifier') { if (node.name === '__dirname') return '.'; if (node.name === '__filename') return './' + fileName; } // TODO: proper expression support // new URL('...', import.meta.url).href | new URL('...', import.meta.url).toString() | new URL('...', import.meta.url).pathname // import.meta.X /*if (isEsm && node.type === 'MemberExpression' && node.object.type === 'MetaProperty' && node.object.meta.type === 'Identifier' && node.object.meta.name === 'import' && node.object.property.type === 'Identifier' && node.object.property.name === 'meta') { if (node.property.type === 'Identifier' && node.property.name === 'url') { return './' + fileName; } }*/ return lastIsWildcard ? '' : '\x10'; } let babel, babelPresetTs, babelPluginImportAttributes; function setBabel(_babel, _babelPresetTs, _babelPluginImportAttributes) { babel = _babel, babelPresetTs = _babelPresetTs, babelPluginImportAttributes = _babelPluginImportAttributes; } const globalConsole = globalThis.console; const dummyConsole = { log () {}, warn () {}, memory () {}, assert () {}, clear () {}, count () {}, countReset () {}, debug () {}, dir () {}, dirxml () {}, error () {}, exception () {}, group () {}, groupCollapsed () {}, groupEnd () {}, info () {}, table () {}, time () {}, timeEnd () {}, timeLog () {}, timeStamp () {}, trace () {} }; async function createTsAnalysis(source, url) { if (!babel) [babel, { default: babelPresetTs }, { default: babelPluginImportAttributes }] = await Promise.all([ import('@babel/core'), import('@babel/preset-typescript'), import('@babel/plugin-syntax-import-attributes') ]); const imports = new Set(); const dynamicImports = new Set(); // @ts-ignore globalThis.console = dummyConsole; try { babel.transform(source, { filename: '/' + url, ast: false, sourceMaps: false, inputSourceMap: false, babelrc: false, babelrcRoots: false, configFile: false, highlightCode: false, compact: false, sourceType: 'module', parserOpts: { plugins: [ 'jsx' ], errorRecovery: true }, presets: [ [ babelPresetTs, { onlyRemoveTypeImports: true } ] ], plugins: [ babelPluginImportAttributes, ()=>{ return { visitor: { ExportAllDeclaration (path) { 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') imports.add(path.node.source.value); }, Import (path) { dynamicImports.add(buildDynamicString(path.parentPath.get('arguments.0').node, url, true)); } } }; } ] }); } finally{ globalThis.console = globalConsole; } return { deps: [ ...imports ], dynamicDeps: [ ...dynamicImports ], cjsLazyDeps: null, size: source.length, format: 'typescript', integrity: await getIntegrity(source) }; } // We use the special character \x10 as a "wildcard symbol" function buildDynamicString(node, fileName, isEsm = false, lastIsWildcard = false) { if (node.type === 'StringLiteral') { return node.value; } if (node.type === 'TemplateLiteral') { let str = ''; for(let i = 0; i < node.quasis.length; i++){ const quasiStr = node.quasis[i].value.cooked; if (quasiStr.length) { str += quasiStr; lastIsWildcard = false; } const nextNode = node.expressions[i]; if (nextNode) { const nextStr = buildDynamicString(nextNode, fileName, isEsm, lastIsWildcard); if (nextStr.length) { lastIsWildcard = nextStr.endsWith('\x10'); str += nextStr; } } } return str; } if (node.type === 'BinaryExpression' && node.operator === '+') { const leftResolved = buildDynamicString(node.left, fileName, isEsm, lastIsWildcard); if (leftResolved.length) lastIsWildcard = leftResolved.endsWith('\x10'); const rightResolved = buildDynamicString(node.right, fileName, isEsm, lastIsWildcard); return leftResolved + rightResolved; } if (isEsm && node.type === 'Identifier') { if (node.name === '__dirname') return '.'; if (node.name === '__filename') return './' + fileName; } // TODO: proper expression support // new URL('...', import.meta.url).href | new URL('...', import.meta.url).toString() | new URL('...', import.meta.url).pathname // import.meta.X /*if (isEsm && node.type === 'MemberExpression' && node.object.type === 'MetaProperty' && node.object.meta.type === 'Identifier' && node.object.meta.name === 'import' && node.object.property.type === 'Identifier' && node.object.property.name === 'meta') { if (node.property.type === 'Identifier' && node.property.name === 'url') { return './' + fileName; } }*/ return lastIsWildcard ? '' : '\x10'; } function _define_property$6(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; } class JspmError extends Error { constructor(msg, code){ super(msg), _define_property$6(this, "jspmError", true), _define_property$6(this, "code", void 0); this.code = code; } } function throwInternalError(...args) { throw new Error('Internal Error' + (args.length ? ' ' + args.join(', ') : '')); } function isFetchProtocol(protocol) { return protocol === 'file:' || protocol === 'https:' || protocol === 'http:' || protocol === 'data:'; } let baseUrl = undefined; // @ts-ignore if (typeof Deno !== 'undefined') { // @ts-ignore const denoCwd = Deno.cwd(); baseUrl = new URL('file://' + (denoCwd[0] === '/' ? '' : '/') + denoCwd + '/'); } else if (typeof process !== 'undefined' && process.versions.node) { baseUrl = new URL('file://' + process.cwd() + '/'); } else if (typeof document !== 'undefined') { baseUrl = new URL(document.baseURI); } if (!baseUrl && typeof location !== 'undefined') { baseUrl = new URL(location.href); } baseUrl.search = baseUrl.hash = ''; function resolveUrl(url, mapUrl, rootUrl) { if (url.startsWith('/') && rootUrl) return new URL('.' + url.slice(url[1] === '/' ? 1 : 0), rootUrl).href; return new URL(url, mapUrl).href; } function importedFrom(parentUrl) { if (!parentUrl) return ''; return ` imported from ${parentUrl}`; } function matchesRoot(url, baseUrl) { return url.protocol === baseUrl.protocol && url.host === baseUrl.host && url.port === baseUrl.port && url.username === baseUrl.username && url.password === baseUrl.password; } function relativeUrl(url, baseUrl, absolute = false) { const href = url.href; let baseUrlHref = baseUrl.href; if (!baseUrlHref.endsWith('/')) baseUrlHref += '/'; if (href.startsWith(baseUrlHref)) return (absolute ? '/' : './') + href.slice(baseUrlHref.length); if (!matchesRoot(url, baseUrl)) return url.href; if (absolute) return url.href; const baseUrlPath = baseUrl.pathname; const urlPath = url.pathname; const minLen = Math.min(baseUrlPath.length, urlPath.length); let sharedBaseIndex = -1; for(let i = 0; i < minLen; i++){ if (baseUrlPath[i] !== urlPath[i]) break; if (urlPath[i] === '/') sharedBaseIndex = i; } return '../'.repeat(baseUrlPath.slice(sharedBaseIndex + 1).split('/').length - 1) + urlPath.slice(sharedBaseIndex + 1) + url.search + url.hash; } function isURL(specifier) { try { if (specifier[0] === '#') return false; new URL(specifier); } catch { return false; } return true; } function isPlain(specifier) { return !isRelative(specifier) && !isURL(specifier); } function isRelative(specifier) { return specifier.startsWith('./') || specifier.startsWith('../') || specifier.startsWith('/'); } /** * Replace the underlying network fetch. Virtual sources, protocol handlers, * pool, retry, in-flight dedup, and caching continue to wrap it. */ function setFetch(fn) { setNetworkFetch(fn); } const cdnUrl$4 = 'https://deno.land/x/'; const stdlibUrl = 'https://deno.land/std'; let denoStdVersion; function resolveBuiltin$1(specifier, env) { // Bare npm:XXX imports are supported by Deno: if (env.includes('deno') && specifier.startsWith('npm:')) return specifier; if (specifier.startsWith('deno:')) { let name = specifier.slice(5); if (name.endsWith('.ts')) name = name.slice(0, -3); let subpath = '.'; const slashIndex = name.indexOf('/'); if (slashIndex !== -1) { name.slice(0, slashIndex); subpath = `./${name.slice(slashIndex + 1)}`; } return { target: { registry: 'deno', name: 'std', range: new SemverRange$2('*'), unstable: true }, subpath }; } } function pkgToUrl$6(pkg) { if (pkg.registry === 'deno') return `${stdlibUrl}@${pkg.version}/`; if (pkg.registry === 'denoland') return `${cdnUrl$4}${pkg.name}@${vCache[pkg.name] ? 'v' : ''}${pkg.version}/`; throw new Error(`Deno provider does not support the ${pkg.registry} registry for package "${pkg.name}" - perhaps you mean to install "denoland:${pkg.name}"?`); } async function getPackageConfig$4(pkgUrl) { if (pkgUrl.startsWith('https://deno.land/std@')) { return { exports: { './archive': './archive/mod.ts', './archive/*.ts': './archive/*.ts', './archive/*': './archive/*.ts', './async': './async/mod.ts', './async/*.ts': './async/*.ts', './async/*': './async/*.ts', './bytes': './bytes/mod.ts', './bytes/*.ts': './bytes/*.ts', './bytes/*': './bytes/*.ts', './collection': './collection/mod.ts', './collection/*.ts': './collection/*.ts', './collection/*': './collection/*.ts', './crypto': './crypto/mod.ts', './crypto/*.ts': './crypto/*.ts', './crypto/*': './crypto/*.ts', './datetime': './datetime/mod.ts', './datetime/*.ts': './datetime/*.ts', './datetime/*': './datetime/*.ts', './dotenv': './dotenv/mod.ts', './dotenv/*.ts': './dotenv/*.ts', './dotenv/*': './dotenv/*.ts', './encoding': './encoding/mod.ts', './encoding/*.ts': './encoding/*.ts', './encoding/*': './encoding/*.ts', './examples': './examples/mod.ts', './examples/*.ts': './examples/*.ts', './examples/*': './examples/*.ts', './flags': './flags/mod.ts', './flags/*.ts': './flags/*.ts', './flags/*': './flags/*.ts', './fmt': './fmt/mod.ts', './fmt/*.ts': './fmt/*.ts', './fmt/*': './fmt/*.ts', './fs': './fs/mod.ts', './fs/*.ts': './fs/*.ts', './fs/*': './fs/*.ts', './hash': './hash/mod.ts', './hash/*.ts': './hash/*.ts', './hash/*': './hash/*.ts', './http': './http/mod.ts', './http/*.ts': './http/*.ts', './http/*': './http/*.ts', './io': './io/mod.ts', './io/*.ts': './io/*.ts', './io/*': './io/*.ts', './log': './log/mod.ts', './log/*.ts': './log/*.ts', './log/*': './log/*.ts', './media_types': './media_types/mod.ts', './media_types/*.ts': './media_types/*.ts', './media_types/*': './media_types/*.ts', './node': './node/mod.ts', './node/*.ts': './node/*.ts', './node/*': './node/*.ts', './path': './path/mod.ts', './path/*.ts': './path/*.ts', './path/*': './path/*.ts', './permissions': './permissions/mod.ts', './permissions/*.ts': './permissions/*.ts', './permissions/*': './permissions/*.ts', './signal': './signal/mod.ts', './signal/*.ts': './signal/*.ts', './signal/*': './signal/*.ts', './streams': './streams/mod.ts', './streams/*.ts': './streams/*.ts', './streams/*': './streams/*.ts', './testing': './testing/mod.ts', './testing/*.ts': './testing/*.ts', './testing/*': './testing/*.ts', './textproto': './textproto/mod.ts', './textproto/*.ts': './textproto/*.ts', './textproto/*': './textproto/*.ts', './uuid': './uuid/mod.ts', './uuid/*.ts': './uuid/*.ts', './uuid/*': './uuid/*.ts', './version': './version.ts', './version.ts': './version.ts', './wasi': './wasi/mod.ts', './wasi/*.ts': './wasi/*.ts', './wasi/*': './wasi*.ts' } }; } // If there's a package.json, return that: const pkgJsonUrl = new URL('package.json', pkgUrl); const pkgRes = await fetch$1(pkgJsonUrl.href, this.fetchOpts); switch(pkgRes.status){ case 200: case 304: return await pkgRes.json(); } return null; } const vCache = {}; function parseUrlPkg$7(url) { let subpath = null; if (url.startsWith(stdlibUrl) && url[stdlibUrl.length] === '@') { const version = url.slice(stdlibUrl.length + 1, url.indexOf('/', stdlibUrl.length + 1)); subpath = url.slice(stdlibUrl.length + version.length + 2); if (subpath.endsWith('/mod.ts')) subpath = subpath.slice(0, -7); else if (subpath.endsWith('.ts')) subpath = subpath.slice(0, -3); const name = subpath.indexOf('/') === -1 ? subpath : subpath.slice(0, subpath.indexOf('/')); return { pkg: { registry: 'deno', name: 'std', version }, layer: 'default', builtin: `deno:${name}/${subpath}` }; } else if (url.startsWith(cdnUrl$4)) { const path = url.slice(cdnUrl$4.length); const versionIndex = path.indexOf('@'); if (versionIndex === -1) return; const sepIndex = path.indexOf('/', versionIndex); const name = path.slice(0, versionIndex); const version = path.slice(versionIndex + ((vCache[name] = path[versionIndex + 1] === 'v') ? 2 : 1), sepIndex === -1 ? path.length : sepIndex); return { pkg: { registry: 'denoland', name, version }, builtin: null, layer: 'default' }; } } async function resolveLatestTarget$6(target, _layer, parentUrl) { let { registry, name, range } = target; if (denoStdVersion && registry === 'deno') return { registry, name, version: denoStdVersion }; if (range.isExact) return { registry, name, version: range.version.toString() }; // convert all Denoland ranges into wildcards // since we don't have an actual semver lookup at the moment if (!range.isWildcard) range = new SemverRange$2(range.version.toString()); const fetchOpts = { ...this.fetchOpts, headers: Object.assign({}, this.fetchOpts.headers || {}, { // For some reason, Deno provides different redirect behaviour for the server // Which requires us to use the text/html accept accept: typeof document === 'undefined' ? 'text/html' : 'text/javascript' }) }; // "mod.ts" addition is necessary for the browser otherwise not resolving an exact module gives a CORS error const fetchUrl = registry === 'denoland' ? cdnUrl$4 + name + '/mod.ts' : stdlibUrl + '/version.ts'; const res = await fetch$1(fetchUrl, fetchOpts); if (!res.ok) throw new Error(`Deno: Unable to lookup ${fetchUrl}`); const { version } = (await parseUrlPkg$7(res.url)).pkg; if (registry === 'deno') denoStdVersion = version; return { registry, name, version }; } var deno = /*#__PURE__*/Object.freeze({ __proto__: null, getPackageConfig: getPackageConfig$4, parseUrlPkg: parseUrlPkg$7, pkgToUrl: pkgToUrl$6, resolveBuiltin: resolveBuiltin$1, resolveLatestTarget: resolveLatestTarget$6 }); let gaUrl = 'https://ga.jspm.io/'; const systemCdnUrl = 'https://ga.system.jspm.io/'; let apiUrl = 'https://api.jspm.io/'; // the URL we PUT the jspm.io publish to let publishUrl = 'https://dev.qitkao.com/'; // the URL the published jspm.io packages can be seen let rawUrl = 'https://jspm.io/'; let authToken; let versionsCacheMap = {}; const BUILD_POLL_TIME = 60 * 1000; const BUILD_POLL_INTERVAL = 5 * 1000; const AUTH_POLL_INTERVAL = 5 * 1000; // 5 seconds between auth polls // Instance caches — reset on configure() let lookupCache = {}; let lookupInflight = {}; let cachedErrorsInflight = {}; let buildRequestedInflight = {}; const supportedLayers = [ 'default', 'system' ]; function withTrailer(url) { return url.endsWith('/') ? url : url + '/'; } function pkgToUrl$5(pkg, layer = 'default') { if (pkg.registry === 'app') return `${rawUrl}${pkgToStr(pkg)}/`; return `${layer === 'system' ? systemCdnUrl : gaUrl}${pkgToStr(pkg)}/`; } async function getPackageConfig$3(pkgUrl) { var _res_headers_get; if (pkgUrl === gaUrl) return null; try { var res = await fetch$1(`${pkgUrl}package.json`, this.fetchOpts); } catch (e) { return null; } switch(res.status){ case 200: case 204: case 304: break; case 400: case 401: case 403: case 404: let err; try { // if it is a build error, try surface the build error let errRes = await fetch$1(`${pkgUrl}_error.log`); if (errRes.ok) err = await errRes.text(); else err = `Unable to fetch ${pkgUrl}package.json`; } catch {} if (err) throw new JspmError(err); case 406: case 500: return null; default: throw new JspmError(`Invalid status code ${res.status} reading package config for ${pkgUrl}. ${res.statusText}`); } 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 null; } else { try { return await res.json(); } catch (e) { return null; } } } function configure$1(config) { versionsCacheMap = {}; lookupCache = {}; lookupInflight = {}; cachedErrorsInflight = {}; buildRequestedInflight = {}; if (config.authToken) authToken = config.authToken; if (config.cdnUrl) { gaUrl = withTrailer(config.cdnUrl); clearParseUrlPkgCache(); // Clear cache when CDN URL changes } if (config.publishUrl) publishUrl = withTrailer(config.publishUrl); if (config.rawUrl) rawUrl = withTrailer(config.rawUrl); if (config.apiUrl) apiUrl = withTrailer(config.apiUrl); } const exactPkgRegEx$4 = /^(([a-z]+):)?((?:@[^/\\%@]+\/)?[^./\\%@][^/\\%@]*)@([^\/]+)(\/.*)?$/; // Cache for parseUrlPkg results - significantly reduces regex and string operations let parseUrlPkgCache = new Map(); function clearParseUrlPkgCache() { parseUrlPkgCache = new Map(); } function parseUrlPkg$6(url) { const cached = parseUrlPkgCache.get(url); if (cached !== undefined) return cached; // Check for cache miss with undefined stored (url doesn't match) if (parseUrlPkgCache.has(url)) return undefined; const result = parseUrlPkgUncached(url); parseUrlPkgCache.set(url, result); return result; } function parseUrlPkgUncached(url) { let builtin = null; let layer; if (url.startsWith(gaUrl)) layer = 'default'; else if (url.startsWith(systemCdnUrl)) layer = 'system'; else return; const [, , registry, name, version] = url.slice((layer === 'default' ? gaUrl : systemCdnUrl).length).match(exactPkgRegEx$4) || []; if (registry && name && version) { if (registry === 'npm' && name === '@jspm/core' && url.includes('/nodelibs/')) { builtin = url.slice(url.indexOf('/nodelibs/') + 10); if (builtin.endsWith('.js')) builtin = builtin.slice(0, -3); } return { pkg: { registry, name, version }, layer, builtin }; } } function getJspmCache(context) { const jspmCache = context.context.jspmCache; if (!context.context.jspmCache) { return context.context.jspmCache = { cachedErrors: {}, buildRequested: {} }; } return jspmCache; } async function checkBuildOrError(context, pkgUrl, fetchOpts, resolver) { // For backward compatibility, assuming we have an outer resolver that can handle this // In a fully refactored system, this would likely come from a different method const pcfg = await (resolver === null || resolver === void 0 ? void 0 : resolver.getPackageConfig(pkgUrl)) || await fetch$1(pkgUrl + 'package.json'); if (pcfg) { return true; } const { cachedErrors } = getJspmCache(context); // no package.json! Check if there's a build error: if (pkgUrl in cachedErrors) return cachedErrors[pkgUrl]; if (pkgUrl in cachedErrorsInflight) return cachedErrorsInflight[pkgUrl]; const cachedErrorPromise = (async ()=>{ try { const errLog = await getTextIfOk(`${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) { cachedErrors[pkgUrl] = false; delete cachedErrorsInflight[pkgUrl]; return false; } })(); cachedErrorsInflight[pkgUrl] = cachedErrorPromise; return cachedErrorPromise; } async function ensureBuild(context, pkg, fetchOpts, resolver) { if (await checkBuildOrError(context, pkgToUrl$5(pkg, 'default'), fetchOpts, resolver)) return; const fullName = `${pkg.name}@${pkg.version}`; const { buildRequested } = getJspmCache(context); // 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 (fullName in buildRequested) return; if (fullName in buildRequestedInflight) return buildRequestedInflight[fullName]; const buildPromise = (async ()=>{ const buildRes = await fetch$1(`${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(context, pkgToUrl$5(pkg, 'default'), fetchOpts, resolver)) { buildRequested[fullName] = true; delete buildRequestedInflight[fullName]; 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.`); } })(); buildRequestedInflight[fullName] = buildPromise; return buildPromise; } async function resolveLatestTarget$5(target, layer, parentUrl, resolver) { 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, resolver); return pkg; } if (range.isWildcard || range.isExact && range.version.tag === 'latest') { const lookup = await lookupRange.call(this, registry, name, '', unstable, parentUrl); if (!lookup) return null; this.log('jspm/resolveLatestTarget', `${target.registry}:${target.name}@${range} -> WILDCARD ${lookup.version}${parentUrl ? ' [' + parentUrl + ']' : ''}`); await ensureBuild(this, lookup, this.fetchOpts, resolver); return lookup; } if (range.isExact && range.version.tag) { const lookup = await lookupRange.call(this, registry, name, range.version.tag, unstable, parentUrl); if (!lookup) return null; this.log('jspm/resolveLatestTarget', `${target.registry}:${target.name}@${range} -> TAG ${range.version.tag}${parentUrl ? ' [' + parentUrl + ']' : ''}`); await ensureBuild(this, lookup, this.fetchOpts, resolver); return lookup; } let stableFallback = false; if (range.isMajor) { const lookup = await lookupRange.call(this, registry, name, range.version.major, unstable, parentUrl); 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, resolver); return lookup; } } if (stableFallback || range.isStable) { const minor = `${range.version.major}.${range.version.minor}`; const lookup = await 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 if (!lookup) return null; this.log('jspm/resolveLatestTarget', `${target.registry}:${target.name}@${range} -> MINOR ${lookup.version}${parentUrl ? ' [' + parentUrl + ']' : ''}`); await ensureBuild(this, lookup, this.fetchOpts, resolver); return lookup; } // Fallback for compound ranges (union, intersection, comparators) that // don't map to a single JSPM lookup — resolve via full version list: { const versions = await fetchVersions.call(this, name); const version = range.bestMatch(versions, unstable); if (version) { const pkg = { registry, name, version: version.toString() }; this.log('jspm/resolveLatestTarget', `${target.registry}:${target.name}@${range} -> BEST_MATCH ${pkg.version}${parentUrl ? ' [' + parentUrl + ']' : ''}`); await ensureBuild(this, pkg, this.fetchOpts, resolver); return pkg; } } return null; } function pkgToLookupUrl(pkg, edge = false) { return `${gaUrl}${pkg.registry}:${pkg.name}${pkg.version ? '@' + pkg.version : edge ? '@' : ''}`; } async function lookupRange(registry, name, range, unstable, parentUrl) { const url = pkgToLookupUrl({ registry, name, version: range }, unstable); if (url in lookupCache) return lookupCache[url]; if (url in lookupInflight) return lookupInflight[url]; const lookupPromise = (async ()=>{ const version = await getTextIfOk(url, this.fetchOpts); let result; if (version) { result = { registry, name, version: version.trim() }; } else { // not found const versions = await fetchVersions.call(this, name); const semverRange = new SemverRange$2(String(range) || '*'); const version = semverRange.bestMatch(versions, unstable); if (version) { result = { registry, name, version: version.toString() }; } else { throw new JspmError(`Unable to resolve ${registry}:${name}@${range} to a valid version${importedFrom(parentUrl)}`); } } lookupCache[url] = result; delete lookupInflight[url]; return result; })(); lookupInflight[url] = lookupPromise; return lookupPromise; } async function getTextIfOk(url, fetchOpts) { const res = await fetch$1(url, fetchOpts); switch(res.status){ case 200: case 304: return await res.text(); // not found = null case 404: return null; default: throw new Error(`Invalid status code ${res.status}`); } } async function fetchVersions(name) { if (name in versionsCacheMap) { return versionsCacheMap[name]; } const registryLookup = JSON.parse(await getTextIfOk(`https://npmlookup.jspm.io/${encodeURI(name)}`, { cache: 'no-cache' })) || {}; const versions = Object.keys(registryLookup.versions || {}); versionsCacheMap[name] = versions; return versions; } async function download(pkg) { const tar = await import('tar-stream'); const { default: pako } = await import('pako'); const { name, version, registry } = pkg; if (registry !== 'app') throw new JspmError(`The JSPM provider currently only supports downloading from the jspm.io "app:" registry`); let tarball; try { const tarballRes = await fetch$1(`${rawUrl}tarball/app:${name}@${version}`); if (tarballRes.ok) { tarball = await tarballRes.arrayBuffer(); } else { try { throw (await tarballRes.json()).error; } catch {} throw tarballRes.statusText || tarballRes.status; } } catch (e) { throw new JspmError(`Unable to fetch tarball for ${name}@${version} from ${rawUrl}: ${e}`); } const output = pako.inflate(tarball, { gzip: true }); const extract = tar.extract(); const fileData = {}; await new Promise((resolve, reject)=>{ extract.on('entry', async function(header, stream, next) { try { if (header.type === 'file') { if (header.name.indexOf('/') === -1) { next(); return; } const name = header.name.slice(header.name.indexOf('/') + 1); const target = new Uint8Array(header.size); let offset = 0; for await (const chunk of stream){ target.set(chunk, offset); offset += chunk.byteLength; } fileData[name] = target; } stream.once('end', next); stream.resume(); next(); } catch (e) { extract.emit('error', e); } }); extract.once('error', reject); extract.once('finish', resolve); extract.end(output); }); return fileData; } /** * Publishes a package to the jspm.io app registry * * Input package is already validated by JSPM * * @returns Promise that resolves with the package URL */ async function publish(pkg, files, importMap, imports, timeout = 30000) { const { registry, name, version } = pkg; if (registry !== 'app') { throw new JspmError(`Invalid registry ${registry}, JSPM can only publish to the jspm.io "app:" registry currently`); } // Prepare the URL for the request const packageUrl = `${publishUrl}app:${name}@${version}`; // Prepare the package data using tar-stream const tarball = await createTarball(files, importMap); // Get or create token const token = await createPublishToken(name, version); // Upload the package const response = await globalThis.fetch(packageUrl, { method: 'PUT', headers: { 'Content-Type': 'application/gzip', Authorization: `Bearer ${token}` }, // @ts-ignore body: tarball }); if (!response.ok) { let errorMessage; switch(response.status){ case 413: errorMessage = `Publish failed - package size of ${tarball.byteLength}B is too large`; break; default: errorMessage = `Publish failed with status ${response.status}`; } try { const errorJson = await response.json(); errorMessage = errorJson.message || errorJson.error || `Publish failed with status ${response.status}`; } catch {} throw new JspmError(errorMessage); } const result = await response.json(); if (!result.success) { throw new JspmError(result.message || 'Publish failed'); } const publicPackageUrl = pkgToUrl$5.call(this, pkg, 'default'); const mapUrl = publicPackageUrl + 'importmap.json'; return { packageUrl: publicPackageUrl, mapUrl, codeSnippet: `<!-- jspm.io import map injection (change to ".hot.js" for hot reloading) --> <script src="${mapUrl.slice(0, -2)}" crossorigin="anoymous"></script> <!-- Polyfill for older browsers --> <script async src="${await latestEsms.call(this, rawUrl)}" crossorigin="anonymous"></script> ${imports.length ? ` <!-- Import entrypoint${imports.length > 1 ? 's' : ''} --> <script type="module" crossorigin="anonymous">${imports.length > 1 ? '\n' : ''}${imports.map((impt, idx)=>`${idx === 0 ? '' : idx === 1 ? '// Further available import map entrypoints - import as needed:\n// ' : '// '}import '${impt}';`).join('\n')}${imports.length > 1 ? '\n' : ''}</script>` : ''} ` }; } async function latestEsms(forUrl) { // Obtain the latest ES Module Shims version const esmsPkg = await resolveLatestTarget$5.call(this, { name: 'es-module-shims', registry: 'npm', range: new SemverRange$2('*'), unstable: false }, 'default', forUrl, null); return pkgToUrl$5.call(this, esmsPkg, 'default') + 'dist/es-module-shims.js'; } /** * Authenticate with JSPM API to obtain a token * * @param options Authentication options * @returns Promise resolving to an authentication token */ async function auth(options) { // Start token request const deviceCodeResponse = await globalThis.fetch(`${apiUrl}v1/auth/cli`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ client_id: 'jspm-cli', scope: 'deployments' }) }); if (!deviceCodeResponse.ok) { throw new JspmError(`Failed to start authentication flow: ${deviceCodeResponse.status} ${deviceCodeResponse.statusText}`); } const deviceCodeData = await deviceCodeResponse.json(); const { device_code: deviceCode, user_code: userCode, verification_uri_complete: verificationUri, interval = 5 } = deviceCodeData; // Prepare instructions for the user const instructions = `Login or signup, using the code: ${userCode}`; // If a verification callback is provided, use it options.verify(verificationUri, instructions); while(true){ // Wait for the polling interval await new Promise((resolve)=>setTimeout(resolve, AUTH_POLL_INTERVAL)); try { // Poll the token endpoint const tokenResponse = await globalThis.fetch(`${apiUrl}v1/auth/cli/token`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ grant_type: 'urn:ietf:params:oauth:grant-type:device_code', device_code: deviceCode, client_id: 'jspm-cli' }) }); const tokenData = await tokenResponse.json(); // Check for errors if (tokenResponse.status !== 200) { // If authorization is pending, continue polling if (tokenData.error === 'authorization_pending') { continue; } // If another error occurred, stop polling throw new JspmError(tokenData.error_description || 'Authentication failed'); } // Success! Store and return the token authToken = tokenData.access_token; return { token: authToken }; } catch (error) { // Handle network errors, continue polling if (error.name === 'FetchError') { continue; } throw error; } } } /** * Creates a JWT token for package publishing * * @param packageName Name of the package * @param packageVersion Version of the package * @returns JWT token for publish authorization */ async function createPublishToken(packageName, packageVersion) { if (!authToken) { throw new JspmError(`No auth token has been generated for jspm.io. Either set providers['jspm.io'].authToken, or first run "jspm auth jspm.io"`); } try { const response = await globalThis.fetch(`${apiUrl}v1/package/token`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${authToken}` }, body: JSON.stringify({ package_name: packageName, package_version: packageVersion }) }); if (!response.ok) { let errJson; try { errJson = await response.json(); } catch { throw new Error(response.status.toString()); } throw new Error(errJson.error ? errJson.error : JSON.stringify(errJson, null, 2)); } const data = await response.json(); return data.token; } catch (error) { // Fall back to the placeholder token if there's an error if (error.message.includes('Invalid or expired token')) throw new JspmError(`Invalid or expired token, run "jspm provider auth jspm.io" to regenerate an authentication token.`); throw new JspmError(error.message); } } /** * Creates a gzipped tarball from the provided files * Following npm conventions with a "package" folder at the root * * @param files Record of file paths to content * @param importMap Optional import map to include * @returns Promise that resolves with the tarball */ async function createTarball(files, map) { const tar = await import('tar-stream'); const { default: pako } = await import('pako'); // Create pack stream const pack = tar.pack(); // Collect chunks const deflator = new pako.Deflate({ gzip: true }); const result = new Promise((resolve, reject)=>{ pack.on('data', (chunk)=>{ deflator.push(chunk, false); }); pack.on('finish', ()=>{ console.log('wat'); }); pack.on('end', async ()=>{ deflator.push(new Uint8Array([]), true); if (deflator.err) reject(deflator.err); else resolve(deflator.result); }); pack.on('error', (err)=>{ reject(err); }); }); if (map) { pack.entry({ name: 'package/importmap.json' }, new TextEncoder().encode(JSON.stringify(map.toJSON()))); } // Add files to the tarball, placing them inside a "package" directory // to follow npm's convention if (files) { for (const [path, content] of Object.entries(files)){ let contentBuffer; if (typeof content === 'string') { contentBuffer = new TextEncoder().encode(content); } else { contentBuffer = new Uint8Array(content); } // Prefix with "package/" to follow npm convention const tarPath = `package/${path}`; pack.entry({ name: tarPath }, contentBuffer); } } // Finalize the pack pack.finalize(); return result; } var jspm = /*#__PURE__*/Object.freeze({ __proto__: null, auth: auth, clearParseUrlPkgCache: clearParseUrlPkgCache, configure: configure$1, download: download, fetchVersions: fetchVersions, getPackageConfig: getPackageConfig$3, parseUrlPkg: parseUrlPkg$6, pkgToUrl: pkgToUrl$5, publish: publish, resolveLatestTarget: resolveLatestTarget$5, supportedLayers: supportedLayers }); const cdnUrl$3 = 'https://cdn.skypack.dev/'; function pkgToUrl$4(pkg) { return `${cdnUrl$3}${pkg.name}@${pkg.version}/`; } const exactPkgRegEx$3 = /^((?:@[^/\\%@]+\/)?[^./\\%@][^/\\%@]*)@([^\/]+)(\/.*)?$/; function parseUrlPkg$5(url) { if (!url.startsWith(cdnUrl$3)) return; const [, name, version] = url.slice(cdnUrl$3.length).match(exactPkgRegEx$3) || []; if (!name || !version) return; return { registry: 'npm', name, version }; } async function resolveLatestTarget$4(target, layer, parentUrl) { const { registry, name, range, unstable } = target; const versions = await fetchVersions.call(this, name); const semverRange = new SemverRange$2(String(range) || '*'); const version = semverRange.bestMatch(versions, unstable); if (version) { return { registry, name, version: version.toString()