79 lines
2.3 KiB
JavaScript
79 lines
2.3 KiB
JavaScript
|
|
/**
|
||
|
|
* 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);
|
||
|
|
}
|