Internals: Build and Cache
This page is for debugging source plugin builds.
Cold Build Path
When ttsc sees a plugin source descriptor:
- Resolve
source. - Hash the plugin source and host versions.
- Reuse the cached binary on hit.
- Copy source into a scratch directory on miss.
- Generate a
go.workoverlay pointing atttscand its shims, plus (when the plugin’sgo.moddoes not declaregithub.com/samchon/ttsc/packages/ttsc) a synthesizedreplace github.com/samchon/ttsc/packages/ttsc v0.0.0 => <workspace overlay>block. Thereplaceblock is the seam that lets a plugin’s barerequire github.com/samchon/ttsc/packages/ttsc v0.0.0resolve to the workspacettscsource at build time. - Run
go build -o .ttsc-plugin <entry>in the scratch directory. The leading-dot placeholder name makes the in-progress binary easy to spot ingo buildstderr; step 7 renames it toplugin(orplugin.exeon Windows) as it moves into the cache. - Move the binary into the cache.
- Invoke the binary with
check,transform,build,fix, orformat.
Cache Key Inputs
The cache key for a built plugin binary is sha256 of, in order:
ttsc=${ttscVersion}tsgo=${tsgoVersion}— the resolved@typescript/native-previewversion.platform=${process.platform}/${process.arch}entry=${plugin entry package relative to its go.mod}go=${resolveGoCompilerIdentity(goBinary)}— the resolved Go binary’s file content hash andgo versiontext, not the install path.- Every effective Go build env value in
GO_BUILD_ENV_KEYS:GOOS,GOARCH,GOAMD64,GOARM,GOARM64,GO386,GOMIPS,GOMIPS64,GOPPC64,GORISCV64,GOWASM,GOFLAGS,GOEXPERIMENT,GOFIPS140,GO_EXTLINK_ENABLED,GCCGO,GCCGOTOOLDIR,CGO_ENABLED, the cgo compiler/linker variables,GOTOOLCHAIN, andGOROOTby toolchain-content fingerprint. Command-valued cgo settings also include the resolved tool binary identity. - Plugin source-directory hash. Skipped paths:
node_modules,.git,.ttsc,go.work,go.work.sum,*.tgz,*.tar.gz,.DS_Store,Thumbs.db, and editor backup files ending in~. - Overlay-directory hashes, sorted by directory name (
ttscand shim source under the host’snode_modules). - Contributor source-directory hashes, sorted by contributor name.
The Go-binary identity (5) and GO_BUILD_ENV_KEYS (6) are the load-bearing reason GOOS=linux GOARCH=arm64 ttsc does not poison the native-platform cache slot. Pathless Go identity also lets pnpm installs with different virtual-store paths share the same cached binary when the bundled compiler content is identical.
The cache key does not include:
- consumer TypeScript source files;
- plugin options such as lint
rules/extends,banner, orcalls; - CLI flags such as
--emitor--outDir.
README, JSON, schema, and other data files inside the plugin source directory do affect the cache key. This keeps //go:embed and other file-backed plugin data aligned with the built binary. Local workspace changes to ttsc’s Go host or shim overlay also invalidate the source plugin cache automatically.
Cache Locations
Default:
Linux: $XDG_CACHE_HOME/ttsc/plugins/<key>/plugin
Linux: ~/.cache/ttsc/plugins/<key>/plugin
macOS: ~/Library/Caches/ttsc/plugins/<key>/plugin
Windows: %LOCALAPPDATA%/ttsc/plugins/<key>/pluginOverride:
TTSC_CACHE_DIR=/tmp/ttsc-cache npx ttsc --emit--cache-dir and TTSC_CACHE_DIR remain project/test isolation escapes. Relative --cache-dir values resolve from the ttsc working directory and store plugin binaries below <cache-dir>/plugins.
Clean — removes the active cache location and legacy project-local caches (node_modules/.ttsc, .ttsc) so projects upgraded from older ttsc releases do not keep stale binaries:
npx ttsc cleanThe default cache is content-addressed and shared across projects on the same machine. It is not tied to a package manager layout: npm installs, pnpm virtual-store paths, and workspace packages share a binary whenever the source plugin, contributors, ttsc overlay, Go toolchain content, platform, and build environment produce the same cache key.
ttsc updates a small last-used marker whenever a binary is reused. Once per day it opportunistically prunes default-cache entries unused for 30 days and trims the cache by least-recently-used order when the cache grows beyond the size budget. Explicit --cache-dir and TTSC_CACHE_DIR caches are not garbage-collected automatically because callers usually use them for CI or test isolation.
Build environment
The plugin builder reads Go’s effective build environment for the cache key. It also injects the TTSC_*_BINARY trio into each plugin process so plugins can re-spawn ttsc helpers.
Cache-key inputs: GO_BUILD_ENV_KEYS
These effective Go build env values fold into the cache key when non-empty, so cross-compile targets, build-tag tweaks, cgo compiler/linker choices, FIPS mode, and toolchain roots never collide with the native build slot. ttsc reads them through go env, so go env -w settings are covered too. GOROOT is normalized to a toolchain-content fingerprint instead of the install path, preserving pnpm path sharing for identical bundled toolchains. Command-valued entries such as CC, CXX, AR, GCCGO, and PKG_CONFIG include the resolved executable content hash as well as the command string.
GOOS GOARCH GOAMD64 GOARM GOARM64 GO386 GOMIPS GOMIPS64 GOPPC64
GORISCV64 GOWASM GOFLAGS GOEXPERIMENT GOFIPS140 GO_EXTLINK_ENABLED
GCCGO GCCGOTOOLDIR CGO_ENABLED AR CC CXX FC PKG_CONFIG
CGO_CFLAGS CGO_CFLAGS_ALLOW CGO_CFLAGS_DISALLOW
CGO_CPPFLAGS CGO_CPPFLAGS_ALLOW CGO_CPPFLAGS_DISALLOW
CGO_CXXFLAGS CGO_CXXFLAGS_ALLOW CGO_CXXFLAGS_DISALLOW
CGO_FFLAGS CGO_FFLAGS_ALLOW CGO_FFLAGS_DISALLOW
CGO_LDFLAGS CGO_LDFLAGS_ALLOW CGO_LDFLAGS_DISALLOW
GOTOOLCHAIN GOROOTttsc also folds selected external cgo/pkg-config environment values into the key when they are set, including include/library search path variables (CPATH, LIBRARY_PATH, INCLUDE, LIB, LIBPATH), pkg-config search variables (PKG_CONFIG_PATH, PKG_CONFIG_LIBDIR, PKG_CONFIG_SYSROOT_DIR), and Apple SDK/deployment variables (SDKROOT, MACOSX_DEPLOYMENT_TARGET).
Source: packages/ttsc/src/plugin/internal/buildSourcePlugin.ts (GO_BUILD_ENV_KEYS and EXTERNAL_GO_BUILD_ENV_KEYS; the cache-key loop folds each non-empty effective value into the hash).
Injected into the plugin process
Before spawning a plugin, ttsc sets three env vars (TTSC_NODE_BINARY, TTSC_TSGO_BINARY, TTSC_TTSX_BINARY) in the child environment so the plugin can re-spawn the ttsc toolchain without resolving paths itself:
TTSC_NODE_BINARY— absolute path of the Node binary that launchedttsc.TTSC_TSGO_BINARY— resolved tsgo native binary used by thisttscinvocation.TTSC_TTSX_BINARY— resolvedttsxlauncher.
A plugin that wants to re-spawn ttsx or tsgo (for example to type-check a generated file) should call these vars rather than re-resolving from $PATH. They guarantee the same host the consumer ran. Sources: runBuild.ts and transformProjectInMemory.ts. See Recipes → Re-spawn ttsx or tsgo from a plugin for usage.
GOROOT is auto-injected only when unset
When the resolved Go binary is the bundled SDK shipped by @ttsc/{platform}-{arch}, ttsc infers GOROOT from the binary’s parent directory and injects it into the build env — but only if process.env.GOROOT is empty. A stale GOROOT exported by a shell rc file from a different Go install will be honored as-is and silently bind the bundled compiler to the wrong stdlib. If a freshly-installed plugin build fails with errors like runtime/runtime0.go: no such file or directory, unset GOROOT before re-running.
Go Toolchain Resolution
ttsc resolves the Go compiler in this order — first match wins:
TTSC_GO_BINARY, when set to a non-empty path.- The installed
@ttsc/{platform}-{arch}package’s bundled Go SDK, resolved viacreateRequireagainst@ttsc/{platform}-{arch}/bin/go/bin/go. - The workspace-development fallback
<repo>/packages/ttsc-{platform}-{arch}/bin/go/bin/go. - The legacy local-workspace fallback
<repo>/native/go/bin/go. $HOME/go-sdk/go/bin/go— the convention used byscripts/build-platform-package.cjsand by maintainers who bootstrap a bundled-Go layout without going through the platform package.- Literal
"go"— a$PATHlookup; the development fallback.
Published consumer installs should land on step 2. Steps 3–5 exist for development workflows; step 6 should only fire when none of the above are present.
Debugging Build Failures
If go build fails, ttsc prints the Go stderr:
ttsc: building plugin "my-plugin" via "go build" failed:
.ttsc-plugin/main.go:42:8: undefined: shimast.SomethingCheck:
- missing shim
requireingo.mod; - missing source files in the npm tarball;
- wrong source directory or package entry;
- stale cache, fixed with
npx ttsc clean; - pnpm local dev layout, fixed in Editor setup;
- on POSIX,
ttscchmod 0755s the bundledgobinary and every file underpkg/tool/before invoking it; a sandbox or read-only mount that blocks chmod will surface here as a Go spawn failure.
Concurrency
Concurrent ttsc processes may build the same missing key at the same time. Scratch directories are unique, and the final move is atomic. This is wasteful but safe.
See also
- Walkthroughs — shipped plugins as worked examples.
- Pitfalls — failure modes specific to plugin authoring (including the
goToolchainNotFoundMessageexact text). - Driver API — the Go façade plugin authors should call.
- Plugin Protocol — the wire contract.