Path aliases
If your tsconfig.json uses compilerOptions.paths (e.g. @/* β ./src/*), the emit will contain those alias paths verbatim, and they wonβt resolve at runtime. @ttsc/paths rewrites them into relative imports during compile.
The problem
// src/main.ts (source)
import { value } from "@lib/value";After a plain ttsc build:
// dist/main.js (broken)
import { value } from "@lib/value"; // β Node can't resolve thisThe fix
npm install -D @ttsc/paths// tsconfig.json
{
"compilerOptions": {
"rootDir": "src",
"outDir": "dist",
"paths": {
"@/*": ["./src/*"],
"@lib/*": ["./src/modules/*"]
},
"plugins": [{ "transform": "@ttsc/paths" }]
}
}Now the emit is:
// dist/main.js
import { value } from "./modules/value.js";What it does and doesnβt do
- Reads the same
compilerOptions.paths,rootDir, andoutDirthatttscalready uses. - Rewrites JavaScript-family emit (
.js,.mjs,.cjs,.jsx) and.d.tsdeclarations. - Resolves extensionless path targets against
.ts,.tsx,.mts,.cts, then.js,.jsx,.mjs, and.cjssource files. - No separate plugin config.
- Only rewrites specifiers that match
compilerOptions.paths; relative, absolute, and non-matching package specifiers are left alone. - Uses TypeScript-Goβs emitted runtime suffix for matched targets (
.js,.mjs,.cjs, or.jsxdepending on the source file andjsxmode). - Does not change source files: only the emit.
Common alternatives (and why this is simpler)
tsc-alias: post-build script. Works, but itβs a second pass.@ttsc/pathsruns inside the compile.- Path remapping at runtime via
tsconfig-paths/register: adds runtime cost and a startup step.@ttsc/pathsproduces normal relative imports. - Custom bundler config: if a bundler owns your build, thatβs fine.
@ttsc/pathsis for projects that ship as plain Node packages.
Inspired by
typescript-transform-paths. Same goal, different compiler.
See also
- @ttsc/banner: another emit-time plugin.
- @ttsc/strip: strip configured calls and
debuggerfrom the emit. - Plugin Development: write your own.
Last updated on