UNPKG

modulepreload-link-relations

Version:

Utility for generating modulepreload link relations based on a JavaScript module import graph.

103 lines (85 loc) 2.94 kB
import assert from "node:assert/strict"; import tryURLLikeSpecifierParse from "./tryURLLikeSpecifierParse.mjs"; import isSpecial from "./isSpecial.mjs"; import tryURLParse from "./tryURLParse.mjs"; export default function resolveImportMap( specifier, parsedImportMap, scriptURL, ) { const asURL = tryURLLikeSpecifierParse(specifier, scriptURL); const normalizedSpecifier = asURL ? asURL.href : specifier; const scriptURLString = scriptURL.href; for (const [scopePrefix, scopeImports] of Object.entries( parsedImportMap.scopes, )) { if ( scopePrefix === scriptURLString || (scopePrefix.endsWith("/") && scriptURLString.startsWith(scopePrefix)) ) { const scopeImportsMatch = resolveImportsMatch( normalizedSpecifier, asURL, scopeImports, ); if (scopeImportsMatch !== null) { return scopeImportsMatch; } } } const topLevelImportsMatch = resolveImportsMatch( normalizedSpecifier, asURL, parsedImportMap.imports, ); if (topLevelImportsMatch !== null) { return topLevelImportsMatch; } // The specifier was able to be turned into a URL, but wasn't remapped into anything. if (asURL) { return asURL; } throw new TypeError(`Unmapped bare specifier "${specifier}"`); } function resolveImportsMatch(normalizedSpecifier, asURL, specifierMap) { for (const [specifierKey, resolutionResult] of Object.entries(specifierMap)) { // Exact-match case if (specifierKey === normalizedSpecifier) { if (resolutionResult === null) { throw new TypeError(`Blocked by a null entry for "${specifierKey}"`); } assert(resolutionResult instanceof URL); return resolutionResult; } // Package prefix-match case if ( specifierKey.endsWith("/") && normalizedSpecifier.startsWith(specifierKey) && (!asURL || isSpecial(asURL)) ) { if (resolutionResult === null) { throw new TypeError(`Blocked by a null entry for "${specifierKey}"`); } assert(resolutionResult instanceof URL); const afterPrefix = normalizedSpecifier.substring(specifierKey.length); // Enforced by parsing assert(resolutionResult.href.endsWith("/")); const url = tryURLParse(afterPrefix, resolutionResult); if (url === null) { throw new TypeError( `Failed to resolve the specifier "${normalizedSpecifier}" as its after-prefix portion ` + `"${afterPrefix}" could not be URL-parsed relative to the URL prefix ` + `"${resolutionResult.href}" mapped to by the prefix "${specifierKey}"`, ); } if (!url.href.startsWith(resolutionResult.href)) { throw new TypeError( `The specifier "${normalizedSpecifier}" backtracks above its prefix "${specifierKey}"`, ); } assert(url instanceof URL); return url; } } return null; }