UNPKG

@jspm/import-map

Version:
650 lines (648 loc) 30.6 kB
function _check_private_redeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } } function _class_apply_descriptor_get(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; } function _class_apply_descriptor_set(receiver, descriptor, value) { if (descriptor.set) { descriptor.set.call(receiver, value); } else { if (!descriptor.writable) { throw new TypeError("attempted to set read only private field"); } descriptor.value = value; } } function _class_extract_field_descriptor(receiver, privateMap, action) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to " + action + " private field on non-instance"); } return privateMap.get(receiver); } function _class_private_field_get(receiver, privateMap) { var descriptor = _class_extract_field_descriptor(receiver, privateMap, "get"); return _class_apply_descriptor_get(receiver, descriptor); } function _class_private_field_init(obj, privateMap, value) { _check_private_redeclaration(obj, privateMap); privateMap.set(obj, value); } function _class_private_field_set(receiver, privateMap, value) { var descriptor = _class_extract_field_descriptor(receiver, privateMap, "set"); _class_apply_descriptor_set(receiver, descriptor, value); return value; } function _class_private_method_get(receiver, privateSet, fn) { if (!privateSet.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return fn; } function _class_private_method_init(obj, privateSet) { _check_private_redeclaration(obj, privateSet); privateSet.add(obj); } function _define_property(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; } import { baseUrl, rebase, isPlain, isURL, getCommonBase, resolve, sameOrigin } from "./url.js"; import { alphabetize } from "./alphabetize.js"; class MapMatcher { constructor(map){ _define_property(this, "prefixMatches", void 0); _define_property(this, "results", new Map()); this.prefixMatches = Object.keys(map).filter((match)=>match.endsWith("/") || match.endsWith("*")).sort((matchA, matchB)=>matchA.length < matchB.length ? 1 : -1); } } var _importsMatcher = /*#__PURE__*/ new WeakMap(), _scopeMatchers = /*#__PURE__*/ new WeakMap(), _scopeCandidates = /*#__PURE__*/ new WeakMap(), _scopeMatchCache = /*#__PURE__*/ new WeakMap(), _resolveCache = /*#__PURE__*/ new WeakMap(), _dirty = /*#__PURE__*/ new WeakMap(), _refreshCaches = /*#__PURE__*/ new WeakSet(), _getScopeMatches = /*#__PURE__*/ new WeakSet(); export class ImportMap { /** * Clones the import map * @returns Cloned import map */ clone() { return new ImportMap({ mapUrl: this.mapUrl, rootUrl: this.rootUrl }).extend(this); } /** * Extends the import map mappings * @param map Import map to extend with * @param overrideScopes Set to true to have scopes be replacing instead of extending * @returns ImportMap for chaining */ extend(map, overrideScopes = false) { Object.assign(this.imports, map.imports); if (overrideScopes) { Object.assign(this.scopes, map.scopes); } else if (map.scopes) { for (const scope of Object.keys(map.scopes))Object.assign(this.scopes[scope] = this.scopes[scope] || Object.create(null), map.scopes[scope]); } Object.assign(this.integrity, map.integrity); this.rebase(); return this; } /** * Performs an alphanumerical sort on the import map imports and scopes * @returns ImportMap for chaining */ sort() { this.imports = alphabetize(this.imports); this.scopes = alphabetize(this.scopes); for (const scope of Object.keys(this.scopes))this.scopes[scope] = alphabetize(this.scopes[scope]); this.integrity = alphabetize(this.integrity); return this; } /** * Set a specific entry in the import map * * @param name Specifier to set * @param target Target of the map * @param parent Optional parent scope * @returns ImportMap for chaining */ set(name, target, parent) { if (!parent) { this.imports[name] = target; } else { this.scopes[parent] = this.scopes[parent] || Object.create(null); this.scopes[parent][name] = target; } _class_private_field_set(this, _dirty, true); return this; } /** * @param target URL target * @param integrity Integrity */ setIntegrity(target, integrity) { this.integrity[target] = integrity; const targetRebased = rebase(target, this.mapUrl, this.rootUrl); if (targetRebased !== target && this.integrity[targetRebased]) delete this.integrity[targetRebased]; if (targetRebased.startsWith('./') && target !== targetRebased.slice(2) && this.integrity[targetRebased.slice(2)]) delete this.integrity[targetRebased.slice(2)]; } /** * @param target URL target * @param integrity Integrity */ getIntegrity(target, integrity) { const targetResolved = resolve(target, this.mapUrl, this.rootUrl); if (this.integrity[targetResolved]) return this.integrity[targetResolved]; const targetRebased = rebase(targetResolved, this.mapUrl, this.rootUrl); if (this.integrity[targetRebased]) return this.integrity[targetRebased]; if (this.integrity[targetRebased.slice(2)]) return this.integrity[targetRebased.slice(2)]; } /** * Bulk replace URLs in the import map * Provide a URL ending in "/" to perform path replacements * * @param url {String} The URL to replace * @param newUrl {String} The URL to replace it with * @returns ImportMap for chaining */ replace(url, newUrl) { const replaceSubpaths = url.endsWith("/"); if (!isURL(url)) throw new Error("URL remapping only supports URLs"); const newRelPkgUrl = rebase(newUrl, this.mapUrl, this.rootUrl); if (this.imports[url]) { this.imports[newRelPkgUrl] = this.imports[url]; delete this.imports[url]; } if (replaceSubpaths) { for (const impt of Object.keys(this.imports)){ const target = this.imports[impt]; if (target.startsWith(url)) { this.imports[impt] = newRelPkgUrl + target.slice(url.length); } } } for (const scope of Object.keys(this.scopes)){ const scopeImports = this.scopes[scope]; const scopeUrl = resolve(scope, this.mapUrl, this.rootUrl); if (replaceSubpaths && scopeUrl.startsWith(url) || scopeUrl === url) { const newScope = newRelPkgUrl + scopeUrl.slice(url.length); delete this.scopes[scope]; this.scopes[newScope] = scopeImports; } for (const impt of Object.keys(scopeImports)){ const target = scopeImports[impt]; if (replaceSubpaths && target.startsWith(url) || target === url) scopeImports[impt] = newRelPkgUrl + target.slice(url.length); } } if (this.integrity[url]) { this.integrity[newRelPkgUrl] = this.integrity[url]; delete this.integrity[url]; } _class_private_field_set(this, _dirty, true); return this; } /** * Groups subpath mappings into path mappings when multiple exact subpaths * exist under the same path. * * For two mappings like { "base/a.js": "/a.js", "base/b.js": "/b.js" }, * these will be replaced with a single path mapping { "base/": "/" }. * Groupings are done throughout all import scopes individually. * * @param targets Which sections to combine: 'scopes' (default) or 'both' (includes top-level imports) * @returns ImportMap for chaining */ /** * Condense import entries sharing a common prefix into a single * trailing-slash entry. Entries with inconsistent URL bases (e.g. * shadowed exports) are left in place alongside the prefix entry. * * @param prefixes Import key prefixes to condense (e.g. "pkg/modules/") * @returns ImportMap for chaining */ condenseImports(prefixes) { const condenseMappings = (mappings, safePrefixes)=>{ for (const prefix of safePrefixes){ for (const key of Object.keys(mappings)){ var _mappings, _prefix; if (!key.startsWith(prefix) || key === prefix) continue; const suffix = key.slice(prefix.length); const value = mappings[key]; if (!value.endsWith(suffix)) continue; var _; (_ = (_mappings = mappings)[_prefix = prefix]) !== null && _ !== void 0 ? _ : _mappings[_prefix] = value.slice(0, value.length - suffix.length); if (value.startsWith(mappings[prefix])) { delete mappings[key]; } } } }; if (prefixes.imports) condenseMappings(this.imports, prefixes.imports); if (prefixes.scopes) { for (const scope of Object.keys(prefixes.scopes))if (this.scopes[scope]) condenseMappings(this.scopes[scope], prefixes.scopes[scope]); } _class_private_field_set(this, _dirty, true); return this; } combineSubpaths(targets = 'scopes') { // iterate possible bases and submappings, grouping bases greedily const combineSubpathMappings = (mappings)=>{ let outMappings = Object.create(null); for (let impt of Object.keys(mappings)){ const target = mappings[impt]; // Check if this import is already handled by an existing path mapping // If so, either merge with it or continue on let mapMatch; if (isPlain(impt)) { mapMatch = getMapMatch(impt, outMappings); } else { mapMatch = getMapMatch(impt = rebase(impt, this.mapUrl, this.rootUrl), outMappings) || this.rootUrl && getMapMatch(impt = rebase(impt, this.mapUrl, null), outMappings) || undefined; } if (mapMatch && impt.slice(mapMatch.length) === resolve(target, this.mapUrl, this.rootUrl).slice(resolve(outMappings[mapMatch], this.mapUrl, this.rootUrl).length)) { continue; } let newbase = false; const targetParts = mappings[impt].split("/"); const imptParts = impt.split("/"); for(let j = imptParts.length - 1; j > 0; j--){ const subpath = imptParts.slice(j).join("/"); const subpathTarget = targetParts.slice(targetParts.length - (imptParts.length - j)).join("/"); if (subpath !== subpathTarget) { outMappings[impt] = mappings[impt]; break; } const base = imptParts.slice(0, j).join("/") + "/"; if (outMappings[base]) continue; const baseTarget = targetParts.slice(0, targetParts.length - (imptParts.length - j)).join("/") + "/"; // Dedupe existing mappings against the new base to remove them // And if we dont dedupe against anything then dont perform this basing for (let impt of Object.keys(outMappings)){ const target = outMappings[impt]; let matches = false; if (isPlain(impt)) { matches = impt.startsWith(base); } else { matches = (impt = rebase(impt, this.mapUrl, this.rootUrl)).startsWith(base) || (impt = rebase(impt, this.mapUrl, this.rootUrl)).startsWith(base); } if (matches && impt.slice(base.length) === resolve(target, this.mapUrl, this.rootUrl).slice(resolve(baseTarget, this.mapUrl, this.rootUrl).length)) { newbase = true; delete outMappings[impt]; } } if (newbase) { outMappings[base] = baseTarget; break; } } if (!newbase) outMappings[impt] = target; } return outMappings; }; if (targets === 'both') { this.imports = combineSubpathMappings(this.imports); } if (targets === 'scopes' || targets === 'both') { for (const scope of Object.keys(this.scopes)){ this.scopes[scope] = combineSubpathMappings(this.scopes[scope]); } } _class_private_field_set(this, _dirty, true); return this; } /** * Groups the import map scopes to shared URLs to reduce duplicate mappings. * * For two given scopes, "https://site.com/x/" and "https://site.com/y/", * a single scope will be constructed for "https://site.com/" including * their shared mappings, only retaining the scopes if they have differences. * * In the case where the scope is on the same origin as the mapUrl, the grouped * scope is determined based on determining the common baseline over all local scopes * * @returns ImportMap for chaining */ flatten() { // Ensure resolve cache is fresh before we use it if (_class_private_field_get(this, _dirty)) { _class_private_method_get(this, _refreshCaches, refreshCaches).call(this); _class_private_field_set(this, _dirty, false); } const urlCache = new Map(); const cachedResolve = (url)=>{ let resolved = urlCache.get(url); if (resolved !== undefined) return resolved; resolved = resolve(url, this.mapUrl, this.rootUrl); urlCache.set(url, resolved); return resolved; }; // First, determine the common base for the local mappings if any let localScopemapUrl = null; for (const scope of Object.keys(this.scopes)){ const resolvedScope = cachedResolve(scope); if (isURL(resolvedScope)) { const scopeUrl = new URL(resolvedScope); if (sameOrigin(scopeUrl, this.mapUrl)) { if (!localScopemapUrl) localScopemapUrl = scopeUrl.href; else localScopemapUrl = getCommonBase(scopeUrl.href, localScopemapUrl); } } else { if (!localScopemapUrl) localScopemapUrl = resolvedScope; else localScopemapUrl = getCommonBase(resolvedScope, localScopemapUrl); } } // for each scope, update its mappings to be in the shared base where possible const relativeLocalScopemapUrl = localScopemapUrl ? rebase(localScopemapUrl, this.mapUrl, this.rootUrl) : null; for (const scope of Object.keys(this.scopes)){ const scopeImports = this.scopes[scope]; let scopemapUrl; const resolvedScope = cachedResolve(scope); if (isURL(resolvedScope)) { const scopeUrl = new URL(resolvedScope); if (sameOrigin(scopeUrl, this.mapUrl)) { scopemapUrl = relativeLocalScopemapUrl; } else { scopemapUrl = scopeUrl.protocol + "//" + scopeUrl.hostname + (scopeUrl.port ? ":" + scopeUrl.port : "") + "/"; } } else { scopemapUrl = relativeLocalScopemapUrl; } let scopeBase = this.scopes[scopemapUrl] || Object.create(null); if (scopeBase === scopeImports) scopeBase = null; let flattenedAll = true; for (const name of Object.keys(scopeImports)){ const target = scopeImports[name]; if (this.imports[name] && cachedResolve(this.imports[name]) === cachedResolve(target)) { delete scopeImports[name]; } else if (scopeBase && (!scopeBase[name] || cachedResolve(scopeBase[name]) === cachedResolve(target))) { scopeBase[name] = rebase(target, this.mapUrl, this.rootUrl); delete scopeImports[name]; this.scopes[scopemapUrl] = alphabetize(scopeBase); } else { flattenedAll = false; } } if (flattenedAll) delete this.scopes[scope]; } _class_private_field_set(this, _dirty, true); return this; } /** * Rebase the entire import map to a new mapUrl and rootUrl * * If the rootUrl is not provided, it will remain null if it was * already set to null. * * Otherwise, just like the constructor options, the rootUrl * will default to the mapUrl base if it is an http: or https: * scheme URL, and null otherwise keeping absolute URLs entirely * in-tact. * * @param mapUrl The new map URL to use * @param rootUrl The new root URL to use * @returns ImportMap for chaining */ rebase(mapUrl = this.mapUrl, rootUrl) { if (typeof mapUrl === "string") mapUrl = new URL(mapUrl); if (rootUrl === undefined) { if (mapUrl.href === this.mapUrl.href) rootUrl = this.rootUrl; else rootUrl = this.rootUrl === null || mapUrl.protocol !== "https:" && mapUrl.protocol !== "http:" ? null : new URL("/", mapUrl); } else if (typeof rootUrl === "string") rootUrl = new URL(rootUrl); let changedImportProps = false; for (const impt of Object.keys(this.imports)){ const target = this.imports[impt]; this.imports[impt] = rebase(resolve(target, this.mapUrl, this.rootUrl), mapUrl, rootUrl); if (!isPlain(impt)) { const newImpt = rebase(resolve(impt, this.mapUrl, this.rootUrl), mapUrl, rootUrl); if (newImpt !== impt) { changedImportProps = true; this.imports[newImpt] = this.imports[impt]; delete this.imports[impt]; } } } if (changedImportProps) this.imports = alphabetize(this.imports); let changedScopeProps = false; // Create a temporary map to collect scopes by their rebased URLs const rebasedScopes = Object.create(null); for (const scope of Object.keys(this.scopes)){ const scopeImports = this.scopes[scope]; let changedScopeImportProps = false; for (const impt of Object.keys(scopeImports)){ const target = scopeImports[impt]; scopeImports[impt] = rebase(resolve(target, this.mapUrl, this.rootUrl), mapUrl, rootUrl); if (!isPlain(impt)) { const newName = rebase(resolve(impt, this.mapUrl, this.rootUrl), mapUrl, rootUrl); if (newName !== impt) { changedScopeImportProps = true; scopeImports[newName] = scopeImports[impt]; delete scopeImports[impt]; } } } if (changedScopeImportProps) this.scopes[scope] = alphabetize(scopeImports); const newScope = rebase(resolve(scope, this.mapUrl, this.rootUrl), mapUrl, rootUrl); // Check if this scope URL already exists in our rebased collection if (rebasedScopes[newScope]) { // Merge the imports from this scope into the existing one Object.assign(rebasedScopes[newScope], scopeImports); changedScopeProps = true; } else { // First time seeing this rebased scope URL rebasedScopes[newScope] = scopeImports; if (scope !== newScope) { changedScopeProps = true; } } } // Replace the scopes with the unified rebased scopes this.scopes = rebasedScopes; if (changedScopeProps) this.scopes = alphabetize(this.scopes); let changedIntegrityProps = false; for (const target of Object.keys(this.integrity)){ const newTarget = rebase(resolve(target, this.mapUrl, this.rootUrl), mapUrl, rootUrl); if (target !== newTarget) { this.integrity[newTarget] = this.integrity[target]; delete this.integrity[target]; changedIntegrityProps = true; } } if (changedIntegrityProps) this.integrity = alphabetize(this.integrity); this.mapUrl = mapUrl; this.rootUrl = rootUrl; _class_private_field_set(this, _dirty, true); return this; } /** * Perform a module resolution against the import map * * @param specifier Specifier to resolve * @param parentUrl Parent URL to resolve against * @returns Resolved URL string */ resolve(specifier, parentUrl = this.mapUrl) { if (_class_private_field_get(this, _dirty)) { _class_private_method_get(this, _refreshCaches, refreshCaches).call(this); _class_private_field_set(this, _dirty, false); } if (typeof parentUrl !== "string") parentUrl = parentUrl.toString(); parentUrl = resolve(parentUrl, this.mapUrl, this.rootUrl); const cacheKey = `${parentUrl}\0${specifier}`; const cached = _class_private_field_get(this, _resolveCache).get(cacheKey); if (cached !== undefined) return cached; let specifierUrl; if (!isPlain(specifier)) { specifierUrl = new URL(specifier, parentUrl); specifier = specifierUrl.href; } const scopeMatches = _class_private_method_get(this, _getScopeMatches, getScopeMatches).call(this, parentUrl); for (const [scope] of scopeMatches){ const scopeMatcher = _class_private_field_get(this, _scopeMatchers)[scope]; let mapMatch = getMapMatch.call(scopeMatcher, specifier, this.scopes[scope]); if (!mapMatch && specifierUrl) { mapMatch = getMapMatch.call(scopeMatcher, specifier = rebase(specifier, this.mapUrl, this.rootUrl), this.scopes[scope]) || this.rootUrl && getMapMatch.call(scopeMatcher, specifier = rebase(specifier, this.mapUrl, null), this.scopes[scope]) || undefined; } if (mapMatch) { const target = this.scopes[scope][mapMatch]; const resolved = resolve(target + specifier.slice(mapMatch.length), this.mapUrl, this.rootUrl); _class_private_field_get(this, _resolveCache).set(cacheKey, resolved); return resolved; } } let mapMatch = getMapMatch.call(_class_private_field_get(this, _importsMatcher), specifier, this.imports); if (!mapMatch && specifierUrl) { mapMatch = getMapMatch.call(_class_private_field_get(this, _importsMatcher), specifier = rebase(specifier, this.mapUrl, this.rootUrl), this.imports) || this.rootUrl && getMapMatch.call(_class_private_field_get(this, _importsMatcher), specifier = rebase(specifier, this.mapUrl, null), this.imports) || undefined; } if (mapMatch) { const target = this.imports[mapMatch]; const resolved = resolve(target + specifier.slice(mapMatch.length), this.mapUrl, this.rootUrl); _class_private_field_get(this, _resolveCache).set(cacheKey, resolved); return resolved; } if (specifierUrl) { _class_private_field_get(this, _resolveCache).set(cacheKey, specifierUrl.href); return specifierUrl.href; } throw new Error(`Unable to resolve ${specifier} in ${parentUrl}`); } /** * Get the import map JSON data * * @returns Import map data */ toJSON() { const obj = {}; if (Object.keys(this.imports).length) obj.imports = this.imports; if (Object.keys(this.scopes).length) obj.scopes = this.scopes; if (Object.keys(this.integrity).length) obj.integrity = this.integrity; return JSON.parse(JSON.stringify(obj)); } /** * Create a new import map instance * * @param opts import map options, can be an optional bag of { map?, mapUrl?, rootUrl? } or just a direct mapUrl */ constructor(opts){ _class_private_method_init(this, _refreshCaches); _class_private_method_init(this, _getScopeMatches); _define_property(this, "imports", Object.create(null)); _define_property(this, "scopes", Object.create(null)); _define_property(this, "integrity", Object.create(null)); /** * The absolute URL of the import map, for determining relative resolutions * When using file:/// URLs this allows relative modules to be co-located */ _define_property(this, "mapUrl", void 0); /** * The URL to use for root-level resolutions in the import map * If null, root resolutions are not resolved and instead left as-is * * By default, rootUrl is null unless the mapUrl is an http or https URL, * in which case it is taken to be the root of the mapUrl. */ _define_property(this, "rootUrl", void 0); _class_private_field_init(this, _importsMatcher, { writable: true, value: void 0 }); _class_private_field_init(this, _scopeMatchers, { writable: true, value: void 0 }); _class_private_field_init(this, _scopeCandidates, { writable: true, value: void 0 }); _class_private_field_init(this, _scopeMatchCache, { writable: true, value: void 0 }); _class_private_field_init(this, _resolveCache, { writable: true, value: void 0 }); _class_private_field_init(this, _dirty, { writable: true, value: false }); let { map, mapUrl = baseUrl, rootUrl } = opts instanceof URL || typeof opts === "string" || typeof opts === "undefined" ? { mapUrl: opts, map: undefined, rootUrl: undefined } : opts; if (typeof mapUrl === "string") mapUrl = new URL(mapUrl); this.mapUrl = mapUrl; if (rootUrl === undefined && (this.mapUrl.protocol === "http:" || this.mapUrl.protocol === "https:")) rootUrl = new URL("/", this.mapUrl); else if (typeof rootUrl === "string") rootUrl = new URL(rootUrl); this.rootUrl = rootUrl || null; _class_private_field_set(this, _importsMatcher, new MapMatcher(this.imports)); _class_private_field_set(this, _scopeMatchers, Object.create(null)); _class_private_field_set(this, _scopeCandidates, []); _class_private_field_set(this, _scopeMatchCache, new Map()); _class_private_field_set(this, _resolveCache, new Map()); if (map) this.extend(map); } } function refreshCaches() { _class_private_field_set(this, _importsMatcher, new MapMatcher(this.imports)); _class_private_field_set(this, _scopeMatchers, Object.create(null)); for (const scope of Object.keys(this.scopes)){ _class_private_field_get(this, _scopeMatchers)[scope] = new MapMatcher(this.scopes[scope]); } _class_private_field_set(this, _scopeCandidates, Object.keys(this.scopes).map((scope)=>[ scope, resolve(scope, this.mapUrl, this.rootUrl) ]).sort(([, a], [, b])=>a.length < b.length ? 1 : -1)); _class_private_field_get(this, _scopeMatchCache).clear(); _class_private_field_get(this, _resolveCache).clear(); } function getScopeMatches(parentUrl) { const cached = _class_private_field_get(this, _scopeMatchCache).get(parentUrl); if (cached) return cached; const matches = _class_private_field_get(this, _scopeCandidates).filter(([, scopeUrl])=>{ return scopeUrl === parentUrl || scopeUrl.endsWith("/") && parentUrl.startsWith(scopeUrl); }); _class_private_field_get(this, _scopeMatchCache).set(parentUrl, matches); return matches; } const scopeCandidateCache = new WeakMap(); function getScopeMatches1(parentUrl, scopes, mapUrl, rootUrl) { const keys = Object.keys(scopes); let cached = scopeCandidateCache.get(scopes); if (!cached || cached.keyCount !== keys.length) { const candidates = keys.map((scope)=>[ scope, resolve(scope, mapUrl, rootUrl) ]).sort(([, a], [, b])=>a.length < b.length ? 1 : -1); cached = { keyCount: keys.length, candidates }; scopeCandidateCache.set(scopes, cached); } return cached.candidates.filter(([, scopeUrl])=>{ return scopeUrl === parentUrl || scopeUrl.endsWith("/") && parentUrl.startsWith(scopeUrl); }); } export { getScopeMatches1 as getScopeMatches }; export function getMapMatch(specifier, map) { if (specifier in map) return specifier; if (this instanceof MapMatcher) { const cached = this.results.get(specifier); if (cached !== undefined) return cached; let curMatch; for (const match of this.prefixMatches){ const wildcard = match.endsWith("*"); if (specifier.startsWith(wildcard ? match.slice(0, -1) : match)) { if (!curMatch || match.length > curMatch.length) curMatch = match; } } this.results.set(specifier, curMatch); return curMatch; } let curMatch; for (const match of Object.keys(map)){ const wildcard = match.endsWith("*"); if (!match.endsWith("/") && !wildcard) continue; if (specifier.startsWith(wildcard ? match.slice(0, -1) : match)) { if (!curMatch || match.length > curMatch.length) curMatch = match; } } return curMatch; } //# sourceMappingURL=map.js.map