Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature]: handle ${configDir} TS template var in compilerOptions.paths #4050

Open
mister-good-deal opened this issue Jan 25, 2025 · 1 comment
Labels

Comments

@mister-good-deal
Copy link

🚀 Feature Proposal

In TS v5.5, there is a new template var ${configDir} in compilerOptions.paths definition see official doc

Esbuild should handle it and replace this var with the correct path value.

Motivation

Keep being updated with TS standard

Example

The following project monorepo structure

monorepo/
├── tsconfig.base.json    # Base TypeScript config
├── jest.config.base.ts   # Base Jest config
└── packages/
    ├── <package_1>/
    │     ├── package.json    # Extends package-base.json
    │     ├── tsconfig.json   # Extends tsconfig.base.json
    │     └── jest.config.ts  # Extends jest.config.base.ts
    └── <package_2>/
            ├── package.json    # Extends package-base.json
            ├── tsconfig.json   # Extends tsconfig.base.json
            └── jest.config.ts  # Extends jest.config.base.ts

with a defined tsconfig.base.json

{
    "compilerOptions": {
        "rootDir": "./",
        "paths": {
            "@monorepo/*": ["./packages/*"],
            "@database/*": ["${configDir}/prisma/*"]
        },
    "include": [ "jest.config.base.ts" ]
}

would resolve @database as '^@database/(.*)$': '/path/to/monorepo/packages/package_1/prisma/$1' when loading from package_1/tsconfig.json.

I know it would be much more easier to just defined @database at tsconfig.json package level but unfortunately TS does not let extend the compilerOptions.paths property with the root one, it overwrites it...

@mister-good-deal mister-good-deal changed the title [Feature]: handle ${configDir} TS template var in pathsToModuleNameMapper [Feature]: handle ${configDir} TS template var in compilerOptions.paths Jan 25, 2025
@mister-good-deal
Copy link
Author

mister-good-deal commented Jan 25, 2025

As a workaround, if someone encounter this issue, here is a build script i'm using to replace ${configDir} using esbuild tsconfigRaw property with custom made tsconfig.json.

build.mjs

import esbuild from "esbuild";
import path from "node:path";
import { fileURLToPath } from "node:url";

import baseTsConfig from "../../../tsconfig.base.json" with { type: "json" };
import projectTsConfig from "../tsconfig.json" with { type: "json" };

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const PROJECT_ROOT = path.resolve(__dirname, "..");
const MONOREPO_ROOT = path.resolve(PROJECT_ROOT, "../..");

// Process paths to use absolute paths
const processedPaths = Object.fromEntries(
    Object.entries(baseTsConfig.compilerOptions.paths).map(([key, value]) => [
        key,
        value.map(configPath => {
            // Handle ${configDir} replacement
            if (configPath.includes("${configDir}")) return configPath.replace("${configDir}", PROJECT_ROOT);
            // Handle @monorepo/* paths
            if (key.startsWith("@monorepo/")) return path.resolve(MONOREPO_ROOT, configPath);

            return configPath;
        })
    ])
);

// Merge configurations
const mergedConfig = {
    ...baseTsConfig,
    ...projectTsConfig,
    compilerOptions: {
        ...baseTsConfig.compilerOptions,
        ...projectTsConfig.compilerOptions,
        paths: processedPaths,
        baseUrl: MONOREPO_ROOT
    },
    extends: undefined // Remove the extends field as it's not needed in the final configuration
};

await esbuild.build({
    entryPoints: [`${PROJECT_ROOT}/src/index.ts`],
    bundle: true,
    platform: "node",
    target: "node23",
    format: "esm",
    outdir: `${PROJECT_ROOT}/dist`,
    tsconfigRaw: JSON.stringify(mergedConfig),
    resolveExtensions: [".ts", ".js", ".mjs", ".json"],
    nodePaths: [MONOREPO_ROOT],
    banner: {
        js: `
            import { createRequire } from 'module';
            import { fileURLToPath } from 'url';
            import { dirname } from 'path';
            const require = createRequire(import.meta.url);
            const __filename = fileURLToPath(import.meta.url);
            const __dirname = dirname(__filename);
        `
    }
});

@evanw evanw added the tsconfig label Feb 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants