Design

Architecture: optimize the hot path without losing compiler boundaries

tscc's performance comes from doing less work and moving it through a small native pipeline—not from one isolated micro-optimization. The semantic campaign now proves binding, durable types and checking can grow without discarding the source-preserving emitter.

Major components

CompilationUnit durable per-file lifecycle + retained state
SourceFile      source bytes + line mapping
Lexer           lexical tokens
Parser          syntax recognition + erasure/replacements
Semantic model  durable source/token spans
Binder          scopes + stable symbol identity
Type model      canonical primitive facts
Checker         bounded expressions + assignments
Transpiler      runtime transforms + output application
Module graph    relative project discovery
Config          tsconfig subset
Compiler        preparation + explicit output policy
Diagnostics     attributed errors
CLI             tsc-like user boundary

Lex once where possible

An earlier project implementation lexed while building the graph and then lexed again while transpiling. Benchmarks caught the many-file regression. The current streaming queue loads and lexes a source once, discovers dependencies from that token stream, transpiles it and queues unseen modules.

Replacement-based emission

Much of TypeScript transpilation is deletion: type annotations, interfaces, aliases and modifiers disappear. Keeping source offsets allows tscc to preserve untouched JavaScript closely while applying explicit replacement ranges for runtime-bearing features.

Context is where complexity accumulates

Generics versus comparisons, TSX versus angle assertions, live imports versus shadowing, regex versus JSX punctuation and declaration boundaries are not solved by global string replacement. The project has steadily moved those decisions into contextual parser/transform helpers, with independent regressions protecting both sides of each ambiguity.

Performance as a regression property

Benchmarks run alongside feature work because compiler architecture can become slow gradually. A feature that is correct but doubles many-file latency is treated as an architectural signal, not an unavoidable tax.

The bounded semantic seam

CP3/TC1 adds a durable per-file CompilationUnit. It retains source, tokens, parsed program, semantic model, binding, type facts, diagnostics and emitted text through output commit. Emission still uses the proven source-edit path, now from retained prepared state rather than anonymous stage-local objects.

A lightweight semantic overlay retains declarations and lexical regions. The binder gives simple variables, functions, parameters, catch/loop identifiers and static imports stable identity. A program-lifetime type store attaches canonical primitive facts to those symbols, and a precedence-aware checker owns a deliberately small expression/assignment contract.

CommonJS live-import rewriting is a production consumer of the same identity: supported local shadows resolve to local symbols, while ordinary imported reads resolve positively to import symbols. Unsupported destructuring, arrows, classes and template interpolation still retain a documented compatibility bridge.

Future semantic architecture

CP5/TC3 now gives every structural syntax node a stable per-file ID, parent relationship, exact token/byte span and explicit recovery representation. These identities survive checking and emission; the source-edit path remains intact.

CP8/TC4 adds bounded deterministic parser entry and declaration-boundary recovery: malformed constructs that reach EOF no longer automatically consume valid following declarations. Recovery stays conservative and does not alter the source-edit emitter.

CP9/INT0 adds only an opt-in external test adapter: selected emitted files run under Node and JS++ through their public CLIs. No JS++ header, private source or library enters the compiler build.

CP10/TC5 expands binder-owned identity to arrow scopes and parameters, flat destructuring, class names, captures, shadowing and order-independent function lookup. Nested patterns and full member tables remain explicit gaps.

CP13/TC6 adds canonical function identity and symbol-indexed ordinary-function signatures. Parameter/result facts now drive direct argument, arity and annotated-return checks without claiming overloads, generics, contextual function types or callable variable inference.

CP31–CP32 move those signatures into canonical callable type identities. Annotated variables can now be called and can contextually type bounded arrow and function expressions; this remains a focused initializer-expression slice rather than full inference or overload resolution.

A genuine checker will require durable syntax identity, symbols, scopes, declarations, type objects, inference, assignability and control-flow facts. Those should grow as semantic layers over the syntax/project foundation rather than hide inside emitter heuristics. The likely architecture is hybrid: semantic structure owns meaning while validated source edits keep preserving unchanged JavaScript bytes.

CP19 adds canonical structural object shapes to that type layer. Each shape retains named property types plus optional and readonly flags, giving later checking one stable identity instead of reconstructing property facts ad hoc.

CP24 gives checked expressions stable compilation-unit identity, retained spans and one canonical significant-token sequence. Checker entry now goes through those interned nodes, creating a controlled migration seam for richer expression structure rather than multiplying anonymous token-range interpretations.

CP41 retains callable expression roots for standalone statements, branch and loop headers, and throw positions in the semantic model. The checker consumes those owned ranges directly instead of rediscovering calls from binding references.

Known architectural pressure

CP4/TC2 removes the parallel traversal paths: one production ProgramGraph owns canonical file identities, roots, dependency edges, durable units and graph diagnostics. Configuration extraction remains regex-based, some binding forms still rely on transform-local shadow logic, and there are no module export/type tables. Program preparation precedes explicit emit policy and individual files are staged before rename, but ordinary filesystems cannot make the whole rename sequence transactional.

Boundary with JS++

tscc owns TypeScript syntax, binding, checking, diagnostics, project/module compilation and JavaScript emission. JS++ owns executable ECMAScript parsing, bytecode and runtime semantics. CP1 makes that separation an aggregate repository check: the normal compiler cannot accidentally include js.h, link libjs or compile JS++ private sources.

The first integration will be test-only, running eligible tscc output under Node and JS++. CommonJS lowering remains a compiler transform in tscc; ECMAScript module execution belongs to JS++.

JS++ has now reached its bounded embedded-preview candidate. TCP4 runs a frozen module-free tscc output under both Node and JS++ at 7/7 with explicit expected results. This remains test-only: no JS++ header, library or private source enters normal compiler production.

Dedicated type-annotation parser

Object members, function annotations, index signatures, call signatures and unions enter the canonical type store through a decomposed annotation parser. This keeps grammar growth separate from type identity and checking.