/** * Custom ESM Loader that adds .js extensions to imports without extensions */ import { resolve as pathResolve, dirname, extname, isAbsolute } from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { existsSync } from 'node:fs'; /** * Resolve hook - adds .js extension to imports without extensions */ export async function resolve(specifier, context, nextResolve) { // Log everything unconditionally console.log(`[loader] resolve called: ${specifier}`); // Skip node built-ins and package imports if (specifier.startsWith('node:') || specifier.startsWith('@') || !specifier.includes('/')) { return nextResolve(specifier, context); } // Check if specifier needs .js extension added if (!extname(specifier)) { // Calculate the absolute path let absolutePath; if (isAbsolute(specifier)) { absolutePath = specifier; } else if (specifier.startsWith('./') || specifier.startsWith('../')) { // For relative imports, resolve from parent directory const { parentURL } = context; let parentDir; if (parentURL) { try { parentDir = dirname(fileURLToPath(parentURL)); } catch { parentDir = process.cwd(); } } else { parentDir = process.cwd(); } absolutePath = pathResolve(parentDir, specifier); } else { // Not a path we handle return nextResolve(specifier, context); } console.log(`[loader] absolute path: ${absolutePath}`); // Try adding .js extension const pathWithJs = absolutePath + '.js'; console.log(`[loader] trying: ${pathWithJs}, exists: ${existsSync(pathWithJs)}`); if (existsSync(pathWithJs)) { const url = pathToFileURL(pathWithJs).href; console.log(`[loader] resolved to: ${url}`); return { shortCircuit: true, url, }; } // Try as directory with index.js const pathAsIndex = pathResolve(absolutePath, 'index.js'); console.log(`[loader] trying index: ${pathAsIndex}, exists: ${existsSync(pathAsIndex)}`); if (existsSync(pathAsIndex)) { const url = pathToFileURL(pathAsIndex).href; console.log(`[loader] resolved to: ${url}`); return { shortCircuit: true, url, }; } } // Default resolution return nextResolve(specifier, context); }