deno-importmap
Version:
Resolve specifiers with import maps.
273 lines (269 loc) • 8.83 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// mod.ts
var mod_exports = {};
__export(mod_exports, {
resolveImportMap: () => resolveImportMap,
resolveModuleSpecifier: () => resolveModuleSpecifier
});
module.exports = __toCommonJS(mod_exports);
// _util.ts
function isObject(object) {
return typeof object == "object" && object !== null && object.constructor === Object;
}
function sortObject(normalized) {
const sorted = {};
const sortedKeys = Object.keys(normalized).sort((a, b) => b.length - a.length);
for (const key of sortedKeys) {
sorted[key] = normalized[key];
}
return sorted;
}
function isImportMap(importMap) {
return isObject(importMap) && (importMap.imports !== void 0 ? isImports(importMap.imports) : true) && (importMap.scopes !== void 0 ? isScopes(importMap.scopes) : true);
}
function isImports(importsMap) {
return isObject(importsMap);
}
function isScopes(scopes) {
return isObject(scopes) && Object.values(scopes).every((value) => isSpecifierMap(value));
}
function isSpecifierMap(specifierMap) {
return isObject(specifierMap);
}
function isURL(url) {
try {
new URL(url);
return true;
} catch {
return false;
}
}
// mod.ts
function sortAndNormalizeSpecifierMap(originalMap, baseURL) {
const normalized = {};
for (const [specifierKey, value] of Object.entries(originalMap)) {
const normalizedSpecifierKey = normalizeSpecifierKey(specifierKey, baseURL);
if (normalizedSpecifierKey === null)
continue;
if (typeof value !== "string") {
console.warn(`addresses need to be strings.`);
normalized[normalizedSpecifierKey] = null;
continue;
}
const addressURL = parseUrlLikeImportSpecifier(value, baseURL);
if (addressURL === null) {
console.warn(`the address was invalid.`);
normalized[normalizedSpecifierKey] = null;
continue;
}
if (specifierKey.endsWith("/") && !serializeURL(addressURL).endsWith("/")) {
console.warn(
`an invalid address was given for the specifier key specifierKey; since specifierKey ended in a slash, the address needs to as well.`
);
normalized[normalizedSpecifierKey] = null;
continue;
}
normalized[normalizedSpecifierKey] = serializeURL(addressURL);
}
return sortObject(normalized);
}
function serializeURL(url) {
return url.href;
}
function sortAndNormalizeScopes(originalMap, baseURL) {
const normalized = {};
for (const [scopePrefix, potentialSpecifierMap] of Object.entries(originalMap)) {
if (!isSpecifierMap(potentialSpecifierMap)) {
throw new TypeError(
`the value of the scope with prefix scopePrefix needs to be an object.`
);
}
let scopePrefixURL;
try {
scopePrefixURL = new URL(scopePrefix, baseURL);
} catch {
console.warn(`the scope prefix URL was not parseable.`);
continue;
}
const normalizedScopePrefix = serializeURL(scopePrefixURL);
normalized[normalizedScopePrefix] = sortAndNormalizeSpecifierMap(
potentialSpecifierMap,
baseURL
);
}
const sorted = {};
for (const key of Object.keys(normalized)) {
sorted[key] = sortObject(normalized[key]);
}
return sortObject(sorted);
}
function normalizeSpecifierKey(specifierKey, baseURL) {
if (!specifierKey.length) {
console.warn("specifier key cannot be an empty string.");
return null;
}
const url = parseUrlLikeImportSpecifier(specifierKey, baseURL);
if (url !== null) {
return serializeURL(url);
}
return specifierKey;
}
function parseUrlLikeImportSpecifier(specifier, baseURL) {
if (baseURL && (specifier.startsWith("/") || specifier.startsWith("./") || specifier.startsWith("../"))) {
try {
const url = new URL(specifier, baseURL);
return url;
} catch {
return null;
}
}
try {
const url = new URL(specifier);
return url;
} catch {
return null;
}
}
var specialSchemes = [
"ftp",
"file",
"http",
"https",
"ws",
"wss"
];
function isSpecial(asURL) {
return specialSchemes.some(
(scheme) => serializeURL(asURL).startsWith(scheme)
);
}
function resolveImportsMatch(normalizedSpecifier, asURL, specifierMap) {
for (const [specifierKey, resolutionResult] of Object.entries(specifierMap)) {
if (specifierKey === normalizedSpecifier) {
if (resolutionResult === null) {
throw new TypeError(
`resolution of specifierKey was blocked by a null entry.`
);
}
if (!isURL(resolutionResult)) {
throw new TypeError(`resolutionResult must be an URL.`);
}
return resolutionResult;
} else if (specifierKey.endsWith("/") && normalizedSpecifier.startsWith(specifierKey) && (asURL === null || isSpecial(asURL))) {
if (resolutionResult === null) {
throw new TypeError(
`resolution of specifierKey was blocked by a null entry.`
);
}
if (!isURL(resolutionResult)) {
throw new TypeError(`resolutionResult must be an URL.`);
}
const afterPrefix = normalizedSpecifier.slice(specifierKey.length);
if (!resolutionResult.endsWith("/")) {
throw new TypeError(`resolutionResult does not end with "/"`);
}
try {
const url = new URL(afterPrefix, resolutionResult);
if (!isURL(url)) {
throw new TypeError(`url must be an URL.`);
}
if (!serializeURL(url).startsWith(resolutionResult)) {
throw new TypeError(
`resolution of normalizedSpecifier was blocked due to it backtracking above its prefix specifierKey.`
);
}
return serializeURL(url);
} catch {
throw new TypeError(
`resolution of normalizedSpecifier was blocked since the afterPrefix portion could not be URL-parsed relative to the resolutionResult mapped to by the specifierKey prefix.`
);
}
}
}
return null;
}
function resolveImportMap(importMap, baseURL) {
let sortedAndNormalizedImports = {};
if (!isImportMap(importMap)) {
throw new TypeError(`the top-level value needs to be a JSON object.`);
}
const { imports, scopes } = importMap;
if (imports !== void 0) {
if (!isImports(imports)) {
throw new TypeError(`"imports" top-level key needs to be an object.`);
}
sortedAndNormalizedImports = sortAndNormalizeSpecifierMap(
imports,
baseURL
);
}
let sortedAndNormalizedScopes = {};
if (scopes !== void 0) {
if (!isScopes(scopes)) {
throw new TypeError(`"scopes" top-level key needs to be an object.`);
}
sortedAndNormalizedScopes = sortAndNormalizeScopes(
scopes,
baseURL
);
}
if (Object.keys(importMap).find((key) => key !== "imports" && key !== "scopes")) {
console.warn(`an invalid top-level key was present in the import map.`);
}
return {
imports: sortedAndNormalizedImports,
scopes: sortedAndNormalizedScopes
};
}
function resolveModuleSpecifier(specifier, { imports = {}, scopes = {} }, baseURL) {
const baseURLString = serializeURL(baseURL);
const asURL = parseUrlLikeImportSpecifier(specifier, baseURL);
const normalizedSpecifier = asURL !== null ? serializeURL(asURL) : specifier;
for (const [scopePrefix, scopeImports] of Object.entries(scopes)) {
if (scopePrefix === baseURLString || scopePrefix.endsWith("/") && baseURLString.startsWith(scopePrefix)) {
const scopeImportsMatch = resolveImportsMatch(
normalizedSpecifier,
asURL,
scopeImports
);
if (scopeImportsMatch !== null) {
return scopeImportsMatch;
}
}
}
const topLevelImportsMatch = resolveImportsMatch(
normalizedSpecifier,
asURL,
imports
);
if (topLevelImportsMatch !== null) {
return topLevelImportsMatch;
}
if (asURL !== null) {
return serializeURL(asURL);
}
throw new TypeError(
`specifier was a bare specifier, but was not remapped to anything by importMap.`
);
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
resolveImportMap,
resolveModuleSpecifier
});