@bleed-believer/path-alias
Version:
Assign path alias using tsconfig.json file
126 lines (125 loc) • 5.68 kB
JavaScript
import { fileURLToPath, pathToFileURL } from "url";
import { isBuiltin } from "module";
import { transform } from "@swc/core";
import { resolve } from "path";
import { access } from "node:fs/promises";
import { isPackageInstalled } from "./tool/is-package-installed/index.js";
import { ExtParser } from "./tool/ext-parser/ext-parser.js";
import { TsConfig } from "./tool/ts-config/index.js";
export class HookManager {
#resolveCache = new Map();
#loadCache = new Map();
#tsConfig = TsConfig.load();
#swcrc;
#rootDir = resolve(this.#tsConfig.cwd, this.#tsConfig.rootDir);
#outDir = resolve(this.#tsConfig.cwd, this.#tsConfig.outDir);
async #fileExist(path) {
try {
await access(path);
return true;
} catch {
return false;
}
}
#isURL(input) {
try {
new URL(input);
return true;
} catch {
return false;
}
}
async resolve(specifier, context, defaultResolve) {
if (typeof context.parentURL === 'string') {
const cache = this.#resolveCache.get(specifier);
if (typeof cache === 'string') {
// Get the value stored in cache
specifier = cache;
} else if (isPackageInstalled(specifier, context.parentURL)) {
// Store the module specifier in cache
this.#resolveCache.set(specifier, specifier);
} else {
const path = resolve(fileURLToPath(context.parentURL), '..', specifier);
if (!await this.#fileExist(path)) {
// Recalculate using outDir and rootDir
// const tsPath = new ExtParser(path).toTs().replace(this.#outDir, this.#rootDir);
// const jsPath = new ExtParser(path).toJs().replace(this.#rootDir, this.#outDir);
const tsPath = typeof this.#outDir === 'string' && typeof this.#rootDir === 'string' ? new ExtParser(path).toTs().replace(this.#outDir, this.#rootDir) : new ExtParser(path).toTs();
const jsPath = typeof this.#outDir === 'string' && typeof this.#rootDir === 'string' ? new ExtParser(path).toJs().replace(this.#rootDir, this.#outDir) : new ExtParser(path).toJs();
if (await this.#fileExist(tsPath)) {
specifier = new ExtParser(specifier).toTs();
} else if (await this.#fileExist(jsPath)) {
specifier = new ExtParser(specifier).toJs();
} else if (jsPath.startsWith(this.#outDir)) {
// Resolve using tsconfig path alias
const promisePaths = this.#tsConfig.resolveAll(specifier, true).map(async (x)=>[
x,
await this.#fileExist(x)
]);
const resolvedPath = (await Promise.all(promisePaths))?.find(([_, exist])=>exist)?.[0];
if (typeof resolvedPath === 'string') {
const href = pathToFileURL(resolvedPath).href;
this.#resolveCache.set(specifier, href);
specifier = href;
}
}
}
}
}
return defaultResolve(specifier, context);
}
async load(url, context, defaultLoad) {
let out = this.#loadCache.get(url);
if (out) {
return out;
}
if (this.#isURL(url) && !isBuiltin(url)) {
const path = fileURLToPath(url);
const rootDir = resolve(this.#tsConfig.cwd, this.#tsConfig.rootDir);
if (path.startsWith(rootDir)) {
if (!this.#swcrc) {
this.#swcrc = this.#tsConfig.toSwcConfig();
this.#swcrc.inlineSourcesContent = true;
this.#swcrc.outputPath = resolve(this.#tsConfig.cwd, this.#tsConfig.outDir);
this.#swcrc.sourceMaps = 'inline';
}
const localSwcConfig = structuredClone(this.#swcrc);
localSwcConfig.sourceFileName = path;
localSwcConfig.filename = localSwcConfig.sourceFileName;
delete localSwcConfig.exclude;
const original = await defaultLoad(url, {
...context,
format: 'module'
});
const rawText = original.source.toString('utf-8');
const result = await transform(rawText, localSwcConfig);
out = {
format: 'module',
source: result.code,
shortCircuit: true
};
this.#loadCache.set(url, out);
return out;
}
}
if (context.format === 'json') {
// Added JSON compatibility
if (this.#tsConfig.config.compilerOptions?.resolveJsonModule) {
if (!context.importAttributes) {
context.importAttributes = {};
}
context.importAttributes = {
type: 'json'
};
} else {
throw new Error([
'To import a JSON file, first sets in your tsconfig.json configuration',
'file the property ["compilerOptions"]["resolveJsonModule"] into `true`'
].join(` `));
}
}
out = await defaultLoad(url, context);
this.#loadCache.set(url, out);
return out;
}
}