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@ttsc/paths ships the ttsc.plugin auto-discovery marker in its package.json, so the install alone activates the rewrite for any project whose tsconfig declares compilerOptions.paths, no plugins entry needed. The explicit entry below is equivalent and takes precedence; to turn a discovered plugin off without uninstalling it, declare the entry with enabled: false (see Plugin protocol).
// 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. WhenrootDiris omitted, output paths anchor at the tsconfigโs directory โ exactly where TypeScript-Go anchors its own emit. - When several patterns match one specifier, an exact pattern wins, then the longest literal prefix: the same order tsc uses to resolve the import.
- 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. - Preserves the extension of assets the compiler copies rather than transpiles: an exact alias to a
.jsonsource pulled in byresolveJsonModulerewrites to./data.json, the file TypeScript-Go actually copies, keeping anyimport ... with { type: "json" }attribute intact โ never an invented./data.js. - 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