esbuild-fix-imports-plugin
Version:
An ESBuild plugin that fixes import paths by applying fixAliasPlugin, fixFolderImportsPlugin, and fixExtensionsPlugin. It ensures correct file extensions, resolves path aliases, and fixes directory imports in your build output when using 'tsup' with 'bund
160 lines • 5.8 kB
JavaScript
import { dirname, join, parse, relative, resolve } from "path";
import { loadTsConfig } from "load-tsconfig";
import { existsSync } from "fs";
const originAbsolutePath = process.cwd();
const fixAliasPlugin = () => ({
name: "fixAliasPlugin",
setup: (build) => {
const outDir = build.initialOptions.outdir ?? "dist";
const outDirAbsolutePath = resolve(originAbsolutePath, outDir);
const outExtension = build.initialOptions.outExtension?.[".js"] ?? ".js";
const tsConfig = loadTsConfig(
originAbsolutePath,
build.initialOptions.tsconfig ?? "./tsConfig.ts"
);
const alias = tsConfig?.data.compilerOptions?.paths ?? {};
const baseUrl = tsConfig?.data.compilerOptions?.baseUrl;
build.onEnd((result) => {
if (result.errors.length > 0) {
return;
}
const entryFiles = build.initialOptions.entryPoints ?? [];
const outputFiles = (result.outputFiles ?? []).filter(
(outputFile) => outputFile.path.endsWith(outExtension)
);
for (const outputFile of outputFiles) {
const relativeOutputPath = relative(
// '/Users/user/Documents/app/dist/esm/',
outDirAbsolutePath,
// '/Users/user/Documents/app/dist/esm/folder/index.mjs',
outputFile.path
);
const relativeOutputPathWithoutExt = getPathWithoutExtension(relativeOutputPath);
const entryFile = entryFiles.find(
(filePath) => filePath.includes(relativeOutputPathWithoutExt)
) ?? "";
const entryFilePathWithoutExt = getPathWithoutExtension(entryFile);
const ignoredPath = entryFilePathWithoutExt.replace(
relativeOutputPathWithoutExt,
""
);
const relativePath = relative(
// '/Users/user/Documents/app/dist/esm/folder',
dirname(outputFile.path),
// '/Users/user/Documents/app/dist/esm/',
outDirAbsolutePath
);
const fileContents = outputFile.text;
const nextFileContents = modifyAlias(
fileContents,
relativePath,
alias,
ignoredPath,
baseUrl,
originAbsolutePath
);
outputFile.contents = Buffer.from(nextFileContents);
}
});
}
});
const getPathWithoutExtension = (path) => {
const parsedPath = parse(path);
return join(parsedPath.dir, parsedPath.name);
};
const ESM_IMPORT_EXP = /from\s*['"]([^'"]+)['"]/g;
const CJS_REQUIRE_EXP = /require\s*\(\s*['"]([^'"]+?)['"]\s*\)/g;
const modifyAlias = (contents, relativePath, alias, ignoredPath, baseUrl, originAbsolutePath2) => {
let result = contents;
result = result.replace(ESM_IMPORT_EXP, (match, importPath) => {
const newImportPath = replaceAliasInPath(
importPath,
relativePath,
alias,
ignoredPath,
baseUrl,
originAbsolutePath2
);
return match.replace(importPath, newImportPath);
});
result = result.replace(CJS_REQUIRE_EXP, (match, importPath) => {
const newImportPath = replaceAliasInPath(
importPath,
relativePath,
alias,
ignoredPath,
baseUrl,
originAbsolutePath2
);
return match.replace(importPath, newImportPath);
});
return result;
};
const cleanPath = (path) => path.replace("/./", "/").replace("//", "/");
const replaceAliasInPath = (importPath, relativePath, alias, ignoredPath, baseUrl, originAbsolutePath2) => {
for (const aliasKey in alias) {
const aliasPatterns = alias[aliasKey];
for (const aliasPattern of aliasPatterns) {
const fixedAliasPattern = aliasPattern.replace(ignoredPath, "");
if (aliasKey.endsWith("*")) {
const aliasKeyBase = aliasKey.slice(0, -1);
const aliasPatternBase = fixedAliasPattern.slice(0, -1);
if (importPath.startsWith(aliasKeyBase)) {
const restOfPath = importPath.slice(aliasKeyBase.length);
const newRelativePath = `./${relativePath}/${aliasPatternBase}${restOfPath}`;
let cleanedNewRelativePath = cleanPath(newRelativePath);
return cleanedNewRelativePath;
}
} else if (importPath === aliasKey) {
const newRelativePath = `./${relativePath}/${fixedAliasPattern}`;
let cleanedNewRelativePath = cleanPath(newRelativePath);
return cleanedNewRelativePath;
}
}
}
if (baseUrl && !isRelativeImport(importPath) && !isNodeModuleImport(importPath)) {
const resolvedBaseUrl = resolve(originAbsolutePath2, baseUrl);
const possibleExtensions = ["", ".ts", ".tsx", ".js", ".jsx", ".json"];
let foundPath = null;
for (const ext of possibleExtensions) {
const fullPath = resolve(resolvedBaseUrl, importPath + ext);
if (existsSync(fullPath)) {
foundPath = fullPath;
break;
}
const indexPath = resolve(resolvedBaseUrl, importPath, "index" + ext);
if (existsSync(indexPath)) {
foundPath = indexPath;
break;
}
}
if (foundPath) {
const relativeToBase = relative(resolvedBaseUrl, foundPath);
const cleanedRelativeToBase = getPathWithoutExtension(relativeToBase);
const newRelativePath = `./${relativePath}/${cleanedRelativeToBase}`;
const cleanedNewRelativePath = cleanPath(newRelativePath);
return cleanedNewRelativePath;
}
}
return importPath;
};
const isRelativeImport = (importPath) => {
return importPath.startsWith("./") || importPath.startsWith("../");
};
const isNodeModuleImport = (importPath) => {
if (importPath.startsWith(".") || importPath.startsWith("/")) {
return false;
}
if (!importPath.includes("/")) {
return true;
}
if (importPath.startsWith("@")) {
return true;
}
return false;
};
export {
fixAliasPlugin,
getPathWithoutExtension
};
//# sourceMappingURL=fixAliasPlugin.mjs.map