From f15e3928062f7cab0b45965df6bd61e82a21ace5 Mon Sep 17 00:00:00 2001 From: Seth Trowbridge Date: Fri, 28 Feb 2025 13:47:21 -0500 Subject: [PATCH] cleanup 1 --- import_map_resolver.js | 83 ------------------------------------ mod.ts | 96 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 78 insertions(+), 101 deletions(-) delete mode 100644 import_map_resolver.js diff --git a/import_map_resolver.js b/import_map_resolver.js deleted file mode 100644 index d5aea4e..0000000 --- a/import_map_resolver.js +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Resolves a module specifier against an import map - * @param {string} specifier - The module specifier to resolve (bare specifier or absolute URL) - * @param {Object} importMap - The import map object containing imports and scopes - * @param {string} baseURL - The base URL for context (especially for scopes) - * @returns {string} The resolved URL - */ -export default function resolveImportMap(specifier, importMap, baseURL) { - - // Check for exact matches first (most common case for bare specifiers) - const lookup = importMap.imports[specifier]; - if (lookup) { return lookup; } - - const Scanner =(obj)=> - { - // Check for path prefix matches (longest first) - const keys = Object.keys(obj) - .filter(key => key.endsWith('/')) - .sort((a, b) => b.length - a.length); - for (const key of keys) - { - if (specifier.startsWith(key)) - { - const remainder = specifier.slice(key.length); - return obj[key] + remainder; - } - } - } - - const scan = Scanner(importMap.imports); - if (scan) {return scan} - - // Handle scopes if available - if (importMap.scopes) { - const scopeKeys = Object.keys(importMap.scopes).sort((a, b) => b.length - a.length); - for (const scopeKey of scopeKeys) - { - if (specifier.startsWith(scopeKey)) - { - const scopeImports = importMap.scopes[scopeKey]; - const scan = Scanner(scopeImports); - if (scan) {return scan} - } - } - } - - return specifier; - } - - // Example usage for bare specifiers: - function demonstrateOptimizedResolver() { - const importMap = { - "imports": { - "lodash": "https://cdn.skypack.dev/lodash", - "react": "https://cdn.skypack.dev/react", - "lib/": "/node_modules/lib/", - "components/": "/components/" - }, - "scopes": { - "/admin/": { - "components/": "/admin-components/", - "admin-utils": "scoped!/js/admin-utils.js" - } - } - }; - - // Examples with bare specifiers - console.log("Resolving 'lodash':", resolveImportMap("lodash", importMap, "https://example.com/admin/dashboard/")); - console.log("Resolving 'components/button':", resolveImportMap("components/button", importMap, "https://example.com/admin/dashboard/")); - - // Example with scope - console.log("Resolving 'admin-utils':", - resolveImportMap("admin-utils", importMap, "https://example.com/admin/dashboard/")); - - console.log("Resolving '/admin/admin-utils':", - resolveImportMap("admin-utils", importMap, "https://example.com/admin/dashboard/")); - - // Example with URL - console.log("Resolving 'https://example.com/external.js':", - resolveImportMap("https://example.com/external.js", importMap, "https://example.com/admin/dashboard/")); - } - - demonstrateOptimizedResolver(); diff --git a/mod.ts b/mod.ts index 629de59..edfc803 100644 --- a/mod.ts +++ b/mod.ts @@ -1,8 +1,65 @@ import * as ESBuild from "https://deno.land/x/esbuild@v0.25.0/wasm.js"; -import * as Mapper from "https://esm.sh/esbuild-plugin-importmaps@1.0.0"; // https://github.com/andstellar/esbuild-plugin-importmaps -import Introspect from "./introspect.ts"; -const resolvePlugin =(fullPathDir:string):ESBuild.Plugin=>({ +/** + * Resolves a module specifier against an import map + * @param {string} specifier - The module specifier to resolve (bare specifier or absolute URL) + * @param {Object} importMap - The import map object containing imports and scopes + * @param {string} baseURL - The base URL for context (especially for scopes) + * @returns {string} The resolved URL + */ +function resolveImportMap(specifier, importMap, baseURL) { + + // Check for prefix matches in the main imports + const result = checkPrefixMatch(specifier, importMap.imports); + if (result) { + return result + } + + // First check scopes that match the baseURL (scope applies based on baseURL, not specifier) + if (importMap.scopes) { + const scopeKeys = Object.keys(importMap.scopes).sort((a, b) => b.length - a.length); + + for (const scopeKey of scopeKeys) { + // Convert scope key to absolute URL and check if baseURL starts with it + const scopeURL = new URL(scopeKey, baseURL).href; + + if (baseURL.startsWith(scopeURL)) { + const scopeImports = importMap.scopes[scopeKey]; + + // Check for prefix match in the scope + const result = checkPrefixMatch(specifier, scopeImports); + if (result) { + return result; + } + } + } + } +} + +/** + * Helper function to check for prefix matches + * @param {string} specifier - The specifier to check + * @param {Object} mappings - Object with prefix mappings + * @returns {string|null} The resolved path or null if no match + */ +function checkPrefixMatch(specifier, mappings) { + + const check = mappings[specifier]; + if(check){ return check; } + + const prefixes = Object.keys(mappings) + .filter(key => key.endsWith('/') && specifier.startsWith(key)) + .sort((a, b) => b.length - a.length); + + for (const prefix of prefixes) { + const remainder = specifier.slice(prefix.length); + return mappings[prefix] + remainder; + } + + return null; +} + +const resolvePlugin =(fullPathDir:string, importMap):ESBuild.Plugin=>({ name: "resolve-plugin", setup(build) { @@ -16,11 +73,19 @@ const resolvePlugin =(fullPathDir:string):ESBuild.Plugin=>({ return out; } ); - build.onResolve( {/* `file://`, `http://`, and `https://` */ filter:/^(file:\/\/|http:\/\/|https:\/\/).*/}, args=>{ - const out = { path:args.path, namespace:"FULLPATH" }; - console.log(`FULLPATH RESOLVE`, {args, out}, "\n"); - return out; - } ); + build.onResolve({filter:/.*/}, args=>{ + + const check = resolveImportMap(args.path, importMap, args.importer||fullPathDir); + console.log("pth??", check); + if(check) + { + const resolveRoot = args.importer||fullPathDir; + const out = { path:new URL(check, resolveRoot).href, namespace:"FULLPATH" }; + console.log(`IMPORTMAP RESOLVE`, {args, out}, "\n"); + return out; + } + + }) build.onLoad( {/* `file://`, `http://`, and `https://` */ filter:/^(file:\/\/|http:\/\/|https:\/\/).*/}, @@ -35,22 +100,18 @@ const resolvePlugin =(fullPathDir:string):ESBuild.Plugin=>({ }); await ESBuild.initialize({ worker: false }); -export type ImportMap = Parameters[0]; +export type ImportMap = {imports?:Record, scopes?:Record>} export type BuildOptions = ESBuild.BuildOptions; /** * * @param {string} directory Full file:// or http(s):// path to the directory containing assets you want to build (needed to resolve relative imports) - * @param {ESBuild.BuildOptions} buildOptions ESBuild "build" options (will be merged with "reasonable defaults") for docs: https://esbuild.github.io/api/#general-options - * @param {ImportMap|null} importMap An object to act as the import map ({imports:Record}). If this is left blank, a configuration will be scanned for in the "directory" + * @param {ESBuild.BuildOptions} [buildOptions={}] ESBuild "build" options (will be merged with "reasonable defaults") for docs: https://esbuild.github.io/api/#general-options + * @param {ImportMap|null} [importMap={}] An object to act as the import map ({imports:Record}). If this is left blank, a configuration will be scanned for in the "directory" * @returns {Promise>} build result */ -export default async function(directory, buildOptions={}, importMap) +export default async function Build(directory, buildOptions={}, importMap = {}) { - if(!importMap) - { - importMap = await Introspect(directory) as ImportMap - } console.log("using import map", importMap); const configuration:ESBuild.BuildOptions = { entryPoints: ["entry"], @@ -61,8 +122,7 @@ export default async function(directory, buildOptions={}, importMap) jsxImportSource: "react", ...buildOptions, plugins: [ - Mapper.importmapPlugin(importMap) as ESBuild.Plugin, - resolvePlugin(directory), + resolvePlugin(directory, importMap), ...buildOptions.plugins||[] ] };