Strip debug code
@ttsc/strip removes configured function calls and debugger statements from emitted code.
Install
npm install -D @ttsc/strip@ttsc/strip ships the ttsc.plugin auto-discovery marker in its package.json, so the install alone activates it, no compilerOptions.plugins[] entry needed: the default patterns below start disappearing from your emit on the next build. The explicit entry 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": {
"plugins": [{ "transform": "@ttsc/strip" }]
}
}With no extra configuration, the defaults are:
console.logconsole.debugassert.*debugger
Custom patterns
Drop a strip.config.ts next to your tsconfig.json:
// strip.config.ts
import type { ITtscStripConfig } from "@ttsc/strip";
export default {
calls: ["logger.debug", "metrics.devOnly", "assert.*"],
statements: ["debugger"],
} satisfies ITtscStripConfig;calls: statement-level calls. Supports a.*wildcard at the end (e.g.assert.*matchesassert.equal,assert.deepStrictEqual, …).statements: bare statements. Currently supportsdebugger.
The config is a Node module and runs under the module format Node would give it: an explicit .cts/.mts extension decides on its own, and an ambiguous .ts or .js config follows the nearest package.json "type". So __dirname works in a CommonJS package and import.meta.dirname in a "type": "module" package, and Node’s globals need no /// <reference types="node" /> directive as long as @types/node is installed beside the config.
When the system temporary directory contains the TypeScript config directory, including through a link, the evaluator can use the already opened ttsc cache’s identities directory to avoid changing the config directory. On Windows, it also uses identities when TEMP is on another drive, provided the cache is on the config path’s drive. An independent temporary directory keeps its existing placement. If identities is unavailable, the evaluator uses its existing TEMP or nearest node_modules/.cache or config-directory fallback and removes its temporary files when it finishes.
@ttsc/strip discovers its config by walking upward from the tsconfig directory, looking for strip.config.{ts,cts,mts,js,cjs,mjs,json}. When a build integration compiles through a generated tsconfig outside the project (the bundler adapters do this whenever an alias or a compilerOptions overlay is set), the integration declares the real project root (pluginConfigDir on TtscCompiler) and the walk starts there instead of the temp directory. To point at a specific file, set configFile on the tsconfig entry (a relative path resolves against the same base directory):
// tsconfig.json
{
"compilerOptions": {
"plugins": [
{ "transform": "@ttsc/strip", "configFile": "./config/strip.config.ts" }
]
}
}What “statement-level” means
The plugin only strips calls that are the entire statement:
console.log("debug"); // ← removed
const result = compute();
console.log(result); // ← removed
const data = console.log("x"); // ← NOT removed (the call is an expression)Calls used within expressions are retained because removing them could change the expression’s value.
Use cases
- Production builds: strip
console.loganddebugger, keepconsole.warnandconsole.error. - Test code in published artifacts: strip
assert.*from the package emit if your library usesnode:assertinternally. - Feature-flag tooling: wrap dev-only code in
metrics.devOnly(...)calls that get stripped in prod.
When not to use this
If you want a real minifier (dead-code elimination, mangling, …), use Terser or esbuild via @ttsc/unplugin. @ttsc/strip is the targeted “remove these specific patterns” pass.
See also
- @ttsc/banner: another emit-time plugin.
- @ttsc/paths: rewrite
compilerOptions.pathsaliases in the emit. - Plugin Development: write your own.