import * as ESBuild from "https://deno.land/x/esbuild@v0.25.0/wasm.js"; /** * 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:string, importMap: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) { build.onResolve( {/* `/`, `./`, and `../` */ filter:/^(\/|\.\/|\.\.\/).*/}, args=>{ const resolveRoot = args.importer||fullPathDir; const url = new URL(args.path, resolveRoot).href; const out = { path:url, namespace:"FULLPATH" }; console.log(`SLASHPATH=>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:\/\/).*/}, async(args)=> { const contents = await fetch(args.path).then(r=>r.text()); const out = { contents, loader: `tsx` }; console.log(`FULLPATH LOAD`, {args, out:{...out, contents:contents.substring(0, 100)}}, "\n"); return out; } ); }, }); await ESBuild.initialize({ worker: false }); 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} [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 Build(directory, buildOptions={}, importMap = {}) { console.log("using import map", importMap); const configuration:ESBuild.BuildOptions = { entryPoints: ["entry"], bundle: true, minify: true, format: "esm", jsx: "automatic", jsxImportSource: "react", ...buildOptions, plugins: [ resolvePlugin(directory, importMap), ...buildOptions.plugins||[] ] }; const result = await ESBuild.build(configuration); return result; } export { ESBuild };