UNPKG

@jsenv/import-map

Version:
125 lines (114 loc) 3.46 kB
import { createDetailedMessage } from "@jsenv/logger" import { assertImportMap } from "./internal/assertImportMap.js" import { hasScheme } from "./internal/hasScheme.js" import { tryUrlResolution } from "./internal/tryUrlResolution.js" import { resolveSpecifier } from "./resolveSpecifier.js" export const applyImportMap = ({ importMap, specifier, importer, createBareSpecifierError = ({ specifier, importer }) => { return new Error(createDetailedMessage(`Unmapped bare specifier.`, { specifier, importer })) }, onImportMapping = () => {}, }) => { assertImportMap(importMap) if (typeof specifier !== "string") { throw new TypeError( createDetailedMessage("specifier must be a string.", { specifier, importer, }), ) } if (importer) { if (typeof importer !== "string") { throw new TypeError( createDetailedMessage("importer must be a string.", { importer, specifier, }), ) } if (!hasScheme(importer)) { throw new Error( createDetailedMessage(`importer must be an absolute url.`, { importer, specifier, }), ) } } const specifierUrl = resolveSpecifier(specifier, importer) const specifierNormalized = specifierUrl || specifier const { scopes } = importMap if (scopes && importer) { const scopeSpecifierMatching = Object.keys(scopes).find((scopeSpecifier) => { return scopeSpecifier === importer || specifierIsPrefixOf(scopeSpecifier, importer) }) if (scopeSpecifierMatching) { const scopeMappings = scopes[scopeSpecifierMatching] const mappingFromScopes = applyMappings( scopeMappings, specifierNormalized, scopeSpecifierMatching, onImportMapping, ) if (mappingFromScopes !== null) { return mappingFromScopes } } } const { imports } = importMap if (imports) { const mappingFromImports = applyMappings( imports, specifierNormalized, undefined, onImportMapping, ) if (mappingFromImports !== null) { return mappingFromImports } } if (specifierUrl) { return specifierUrl } throw createBareSpecifierError({ specifier, importer }) } const applyMappings = (mappings, specifierNormalized, scope, onImportMapping) => { const specifierCandidates = Object.keys(mappings) let i = 0 while (i < specifierCandidates.length) { const specifierCandidate = specifierCandidates[i] i++ if (specifierCandidate === specifierNormalized) { const address = mappings[specifierCandidate] onImportMapping({ scope, from: specifierCandidate, to: address, before: specifierNormalized, after: address, }) return address } if (specifierIsPrefixOf(specifierCandidate, specifierNormalized)) { const address = mappings[specifierCandidate] const afterSpecifier = specifierNormalized.slice(specifierCandidate.length) const addressFinal = tryUrlResolution(afterSpecifier, address) onImportMapping({ scope, from: specifierCandidate, to: address, before: specifierNormalized, after: addressFinal, }) return addressFinal } } return null } const specifierIsPrefixOf = (specifierHref, href) => { return specifierHref[specifierHref.length - 1] === "/" && href.startsWith(specifierHref) }