microsoft/TypeScript
Node factory
The single canonical builder for AST nodes. Every parsed, transformed, or emitted node ultimately passes through the factory; this is what guarantees consistent node shape, parent pointers, position tracking, and emit-helper attachment across the compiler.
Source
| File | Lines | Role |
|---|---|---|
src/compiler/factory/nodeFactory.ts |
7,542 | The full factory — one createXxx per node kind |
src/compiler/factory/baseNodeFactory.ts |
60 | Allocator hook for node objects |
src/compiler/factory/nodeTests.ts |
~37k | isXxx predicates for every node kind |
src/compiler/factory/parenthesizerRules.ts |
~34k | Where to insert parentheses on emit to preserve precedence |
src/compiler/factory/emitHelpers.ts |
~69k | Standard helpers (__awaiter, __decorate, __spread, …) injected by transformers |
src/compiler/factory/emitNode.ts |
~13k | Per-node emit metadata (comments, source maps, helpers) |
src/compiler/factory/utilities.ts |
~76k | Higher-level builders used by transformers |
src/compiler/factory/nodeChildren.ts |
tiny | Lookup of named children for diagnostics |
Purpose
ts.factory is the single object exposed by the public API for building nodes. The parser uses an internal factory variant; the transformers use the public one; the emitter consumes whatever factory produced. Going through a single factory ensures:
pos/enddefaulting — synthesised nodes havepos === end === -1so the emitter knows to skip source-map entries.parentlinking —setParentandsetParentRecursivekeep parent pointers consistent.flags—NodeFlagsand emit-only flags (EmitFlags) are merged correctly when one node is built from another.- Helper requests — when a transformer creates a
__awaitercall, the factory records that the helper must be emitted at the file level. The emitter later prints it. - Comment and source-map preservation —
setOriginalNode,setTextRange,setSyntheticLeadingCommentsare factory-level concerns.
How it works
graph LR
Caller["parser / transformer / fix"] --> Factory["factory.createXxx"]
Factory --> Base["baseNodeFactory.allocate"]
Base --> Node["new Node"]
Factory --> Init["set kind, flags, modifiers, children"]
Factory --> Helpers["request emit helpers"]
Factory --> Tests["nodeTests.isXxx (consumer side)"]
Factory --> Pretty["parenthesizerRules (emitter side)"]Sub-factories
Different callers receive different factory variants:
- The
parserfactory tags created nodes with their position and disables expensive invariants like helper tracking. - The default
factory(used by the transformers) enables synthesis defaults (pos === end === -1) and helper tracking. - The
lazyTextNodeFactoryis used by some incremental paths.
createNodeFactory(flags, baseFactory) builds the variant. The NodeFactoryFlags enum controls invariants:
NoIndentationOnFreshPropertyAccess— emit hintNoOriginalNode— don't auto-linkoriginalNode
nodeTests.ts
The companion to nodeFactory.ts is a giant table of predicates: isVariableDeclaration(n), isClassExpression(n), etc. Together they let any consumer build new trees and check incoming trees without manual n.kind === SyntaxKind.X switching. The two files are the most heavily generated-feeling part of the codebase, and changes to either typically come together.
parenthesizerRules.ts
When an emitter prints a + b * c, it shouldn't add parens around b * c (precedence handles it), but when it prints (a + b) * c it must. The parenthesizer encodes operator precedence and associativity rules so the emitter can ask parenthesizer.parenthesizeBinaryOperand(...) and let the rules decide. Transformers also call the parenthesizer when they hoist an expression to a different precedence context.
emitHelpers.ts
When transformES2015 lowers async function f() {} to ES5, it emits a call to __awaiter. The helper itself isn't generated inline every time; instead the transformer requests it from the factory, and the emitter prints the canonical helper definition once at the top of the file (or imports it from tslib when --importHelpers is set). This file contains the canonical text of every helper.
Integration points
- Parser uses an internal factory variant.
- Every transformer under
src/compiler/transformers/callsfactoryextensively. - Code fixes and refactors in
src/services/codefixes/andsrc/services/refactors/use the factory to construct edits. - Emitter consults
emitNodemetadata andparenthesizerRulesto print correctly.
Entry points for modification
Adding a new node kind requires touching all of factory/:
- Add a
SyntaxKindandinterfaceinsrc/compiler/types.ts. - Add
createXxx,updateXxx, and anycloneXxxtonodeFactory.ts. - Add an
isXxxtonodeTests.ts. - Add precedence/associativity rules to
parenthesizerRules.tsif it's an expression. - Update
forEachChildinparser.tsandvisitEachChildinvisitorPublic.tsto walk children.
See systems/transformers and systems/emitter for the consumers.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.