Programmatic API (ttsc JS class)
For tool authors that need to drive ttsc from JavaScript or TypeScript.
This page is for embedders, anyone wrapping ttsc inside a Node program (test harnesses, dev-server middleware, playground hosts, code-generation pipelines). Day-to-day users should keep using the ttsc, ttsx, and ttscserver CLIs instead.
For browser embedding (running ttsc inside a Web Worker / WebAssembly), see the WASM guide and the @ttsc/playground package built on top of it.
When to use
Reach for the programmatic API when you need to:
- Compile or type-check a project from inside another Node tool and consume the result as structured data, not parsed terminal output.
- Capture every emitted text artifact in memory without writing JS or
.d.tsfiles into the user’s project tree. - Pay the lazy plugin-build cost ahead of time in CI warmup steps.
- Manage the ttsc cache directory from your tool’s own lifecycle.
If you only need to run the same command a user would type, shell out to npx ttsc instead. The CLI is the public, stable surface for that.
Install
npm install ttsc @typescript/native-previewBoth packages are runtime dependencies. @typescript/native-preview ships the TypeScript-Go binary that ttsc drives.
Minimal example
import { TtscCompiler } from "ttsc";
const compiler = new TtscCompiler({
cwd: "/abs/path/to/project",
// tsconfig: "tsconfig.build.json", // optional; auto-discovered when omitted
});
const result = compiler.compile();
if (result.type === "success") {
for (const [path, text] of Object.entries(result.output)) {
// path is project-relative ("dist/index.js", "dist/index.d.ts", ...)
console.log(path, text.length);
}
} else if (result.type === "failure") {
for (const diag of result.diagnostics) {
console.error(`${diag.file}:${diag.line}:${diag.character} ${diag.messageText}`);
}
} else {
// type === "exception"
console.error("ttsc host failed:", result.error);
}Lifecycle
TtscCompiler is construct-once, reuse. The constructor captures one project context (working directory, tsconfig, plugin list, cache root, env) and every operation runs against that context. The context is intentionally not replaceable per call, create another instance to compile a different project.
// One compiler per project, kept around across calls.
const compiler = new TtscCompiler({ cwd: projectDir });
await ensurePluginsReady(compiler); // optional warmup
const first = compiler.compile();
const second = compiler.compile(); // safe to call againThe context is defensively copied at construction, so mutating the original options object after the fact has no effect on the instance.
Operations
compile()
Runs the configured project through the TypeScript-Go pipeline and returns the structured result as a discriminated union.
For projects with no plugins, ttsc uses its in-process TypeScript-Go host and captures every WriteFile callback into the output map. For projects with plugins, ttsc runs the plugin pipeline against a temporary output directory under the configured cache root, reads the generated text back into memory, and removes the temp directory before returning.
Result shape (ITtscCompilerResult):
type ITtscCompilerResult =
| { type: "success"; diagnostics?: ITtscCompilerDiagnostic[]; output: Record<string, string> }
| { type: "failure"; diagnostics: ITtscCompilerDiagnostic[]; output: Record<string, string> }
| { type: "exception"; error: unknown };success: TypeScript-Go completed with no error-class diagnostics. Theoutputmap contains every text artifact (JS,.d.ts, source maps, declaration maps).diagnosticsis omitted when empty; warnings appear here when present.failure: Compilation ran but produced error-class diagnostics.outputmay still be partial, TypeScript-Go can emit before erroring.exception: The host could not run normally (cache lockup, missing binary, plugin sidecar crash, etc.). Treat theerrorfield as opaque diagnostic material; do not assume it’s anErrorinstance.
transform()
Source-to-source. Returns a typescript map (project-relative path → TypeScript text), never JS or .d.ts.
const t = compiler.transform();
if (t.type === "success") {
// t.typescript["src/index.ts"] holds the post-transform source
}When no transform-stage plugin is configured, the map contains the unmodified files as TypeScript-Go loaded them. With a transform plugin (e.g. typia’s), the map contains the rewritten text.
prepare()
Builds every configured Go source plugin into the cache up front. Use this in CI warmup steps so the first user-triggered compile doesn’t pay the plugin build cost.
compiler.prepare(); // returns compiled binary paths (debug aid)prepare() does not run diagnostics, does not create a Program, and does not emit files. It is purely a cache warmer.
clean()
Removes the cache directories owned by this compiler’s context. Returns the list of paths that were actually removed (existing directories only).
compiler.clean();Cleanup target order:
- If
cacheDirwas passed to the constructor → remove exactly that. - Else if
TTSC_CACHE_DIRis set (incontext.envorprocess.env) → remove that override’s plugin cache plus legacy project-local caches. - Otherwise → remove the default global plugin cache plus legacy project-local caches.
Context options
| Field | Purpose |
|---|---|
cwd | Working directory. Defaults to process.cwd(). |
tsconfig | Path to a project config file. Relative paths resolve against cwd. Omit to auto-discover the nearest tsconfig. |
projectRoot | Override the project root used for plugin resolution and cache anchoring. Most callers leave this unset. |
binary | Pin a specific TypeScript-Go binary. For test rigs and embedded toolchains; normal callers omit it. |
env | Extra environment variables merged over process.env for child compiler processes. |
cacheDir | Root for compiled ttsc artifacts. Relative paths resolve against cwd. Defaults to TTSC_CACHE_DIR or the user cache. |
plugins | undefined = read project plugins. false = ignore project plugins for this instance. Array = explicit entries. |
Diagnostics
ITtscCompilerDiagnostic is a flat, JSON-friendly shape independent of TypeScript-Go’s internal Go types.
interface ITtscCompilerDiagnostic {
file: string | null; // null for global diagnostics
category: "error" | "warning" | "suggestion" | "message";
code: number | string; // numeric for TS, string for plugin codes
start?: number; // 0-based char offset
length?: number;
line?: number; // 1-based, display convention
character?: number; // 1-based, display convention
messageText: string; // chains are pre-flattened
}When the host process exits non-zero but produced no parsable diagnostics, ttsc synthesizes a { code: "TTSC_PROCESS", category: "error" } entry that carries the captured stderr/stdout. Embedders can branch on code to detect this synthesized case.
Plugins
Project plugins are taken from compilerOptions.plugins in the project’s tsconfig and from installed package markers. The constructor’s plugins field lets embedders override that resolution for one compiler instance:
// Run the project without any plugins at all.
const bare = new TtscCompiler({ cwd, plugins: false });
// Run the project with a different plugin set than the tsconfig requests.
const lintOnly = new TtscCompiler({
cwd,
plugins: [{ name: "@ttsc/lint" }],
});See ITtscProjectPluginConfig for the entry shape and Plugin authoring for how to ship a plugin package.
Patterns
Long-lived service
Build one TtscCompiler per watched project at startup. Call prepare() once, then compile() whenever you need a fresh build. There is no incremental mode today, every compile() reloads the project, so the construction cost is just descriptor wiring, not the compile itself.
CI warmup
const compiler = new TtscCompiler({ cwd });
compiler.prepare(); // pre-build native plugins into cachePair this with caching TTSC_CACHE_DIR between CI runs to skip the rebuild on subsequent invocations.
Driving a Web Worker / Browser
TtscCompiler is a Node API, it spawns native processes. For browser embedding, use @ttsc/wasm directly or the higher-level @ttsc/playground package.
Stability
TtscCompiler, the result types, and ITtscCompilerContext are the public programmatic surface. CLI launchers, binary resolution helpers, project config parsing utilities, and native build helpers stay internal, depending on them will break. The package exports field enforces this.