UNPKG

@jspm/generator

Version:

Package Import Map Generation Tool

130 lines (128 loc) 5.51 kB
// @ts-ignore import { fetch } from '../common/fetch.js'; import { JspmError } from '../common/err.js'; import { importedFrom } from '../common/url.js'; import { encodeBase64, decodeBase64 } from '../common/b64.js'; export function createProvider(baseUrl, ownsBaseUrl) { return { ownsUrl, pkgToUrl, parseUrlPkg, resolveLatestTarget, getPackageConfig }; function ownsUrl(url) { // The nodemodules provider owns the base URL when it is the default // provider so that it can link against a user's local installs, letting // us support "file:" dependencies: return ownsBaseUrl && url === baseUrl || url.includes('/node_modules/'); } async function pkgToUrl(pkg) { // The node_modules registry uses the base64-encoded URL of the package as // the package version, so we need to decode it to get the right copy. See // comments in the `resolveLatestTarget` function for details: if (pkg.registry === 'node_modules') { return `${decodeBase64(pkg.version)}`; } // If we don't have a URL in the package name, then we need to try and // resolve the package against the node_modules in the base package: const target = await nodeResolve.call(this, pkg.name, baseUrl); if (!target) throw new JspmError(`Failed to resolve ${pkg.name} against node_modules from ${baseUrl}`); return `${decodeBase64(target.version)}`; } function parseUrlPkg(url) { // We can only resolve packages in node_modules folders: const nodeModulesIndex = url.lastIndexOf('/node_modules/'); if (nodeModulesIndex === -1) return null; const nameAndSubpaths = url.slice(nodeModulesIndex + 14).split('/'); const name = nameAndSubpaths[0][0] === '@' ? `${nameAndSubpaths[0]}/${nameAndSubpaths[1]}` : nameAndSubpaths[0]; const pkgUrl = `${url.slice(0, nodeModulesIndex + 14)}${name}/`; const subpath = `./${url.slice(pkgUrl.length)}`; if (name && pkgUrl) { return { pkg: { name, registry: 'node_modules', version: encodeBase64(pkgUrl) }, subpath: subpath === './' ? null : subpath, layer: 'default' }; } } async function resolveLatestTarget(target, _layer, parentUrl) { return nodeResolve.call(this, target.name, parentUrl); } async function getPackageConfig(pkgUrl) { if (!ownsUrl.call(this, pkgUrl)) return null; const pkgJsonUrl = new URL('package.json', pkgUrl); const res = await fetch(pkgJsonUrl.href, this.fetchOpts); switch(res.status){ case 200: case 204: case 304: break; default: return null; } async function remap(deps) { if (!deps) return; for (const [name, dep] of Object.entries(deps)){ if (!isLocal(dep)) continue; const remappedUrl = new URL(`./node_modules/${name}`, pkgUrl); if (!await dirExists.call(this, remappedUrl)) continue; deps[name] = remappedUrl.href; } } const pcfg = await res.json(); await remap.call(this, pcfg.dependencies); await remap.call(this, pcfg.peerDependencies); await remap.call(this, pcfg.optionalDependencies); await remap.call(this, pcfg.devDependencies); return pcfg; } } /** * Mimics the node resolution algorithm: look for a node_modules in the * current directory with a package matching the target, and if you can't * find it then recurse through the parent directories until you do. * TODO: we don't currently handle the target's version constraints here */ let realpath, pathToFileURL; async function nodeResolve(name, parentUrl) { let curUrl = new URL(`node_modules/${name}`, parentUrl); const rootUrl = new URL(`/node_modules/${name}`, parentUrl).href; const isScoped = name[0] === '@'; while(!await dirExists.call(this, curUrl)){ if (curUrl.href === rootUrl) return null; // failed to resolve curUrl = new URL(`../../${isScoped ? '../' : ''}node_modules/${name}`, curUrl); } // Providers need to be able to translate between canonical package specs and // URLs in a one-to-one fashion. The nodemodules provider breaks this contract // as a node_modules folder may contain multiple copies of a given package // and version, and if the user has local packages installed then "identical" // packages may have different contents! To work around this use the // base64-encoded URL of the package as the package version in the local // registry, which we can decode to get the right copy: return { name, registry: 'node_modules', version: encodeBase64(`${curUrl.href}/`) }; } async function dirExists(url, parentUrl) { const res = await fetch(url, this.fetchOpts); switch(res.status){ case 304: case 200: case 204: return true; case 404: return false; default: throw new JspmError(`Invalid status code ${res.status} looking up "${url}" - ${res.statusText}${importedFrom(parentUrl)}`); } } function isLocal(dep) { return dep.startsWith('file:'); } //# sourceMappingURL=nodemodules.js.map