vuejs/core
compiler-core
Active contributors: Evan You, edison, daiwei
Purpose
@vue/compiler-core is the platform-agnostic template compiler. It tokenizes, parses, transforms, and code-generates a Vue template into a JavaScript render function that calls the runtime helpers exported by vue/@vue/runtime-core. It is the largest compiler package by source size (~11,000 lines, 30 files) and the foundation that compiler-dom, compiler-ssr, and compiler-sfc extend.
Directory layout
packages/compiler-core/src/
├── index.ts # public API barrel
├── compile.ts # baseCompile + getBaseTransformPreset
├── options.ts # ParserOptions, TransformOptions, CodegenOptions
├── parser.ts # baseParse → AST (~30KB)
├── tokenizer.ts # streaming tokenizer state machine (~36KB)
├── ast.ts # AST node types and constructors
├── transform.ts # transform driver (TransformContext, traverseNode)
├── codegen.ts # generate() → JS source
├── runtimeHelpers.ts # symbolic ids for runtime imports
├── errors.ts # ErrorCodes + messages
├── utils.ts # AST helpers
├── babelUtils.ts # walk identifiers, scope analysis
├── validateExpression.ts # disallow certain JS constructs in templates
├── transforms/
│ ├── transformElement.ts # the core element-vnode codegen
│ ├── transformExpression.ts # rewrites identifiers (foo → _ctx.foo)
│ ├── transformText.ts # merges adjacent text nodes
│ ├── transformSlotOutlet.ts # <slot/> handling
│ ├── transformVBindShorthand.ts # :foo (shorthand) special-cases
│ ├── cacheStatic.ts # static hoisting / cache
│ ├── vIf.ts # v-if / v-else / v-else-if
│ ├── vFor.ts # v-for
│ ├── vSlot.ts # named/scoped slots
│ ├── vModel.ts # base v-model (compiler-dom overrides)
│ ├── vOn.ts # base v-on (compiler-dom overrides)
│ ├── vBind.ts # :prop / .modifier
│ ├── vMemo.ts # v-memo
│ ├── vOnce.ts # v-once
│ └── noopDirectiveTransform.ts # for v-cloak and similar
└── compat/
└── compatConfig.ts # Vue 2 compat warnings (gated by __COMPAT__)The pipeline
graph LR
src["template source"]
tok["tokenizer.ts<br/>state machine"]
parse["baseParse<br/>parser.ts"]
ast["AST"]
xform["transform<br/>transform.ts"]
cgast["AST + codegenNode"]
gen["generate<br/>codegen.ts"]
code["render(_ctx, _cache) {…}"]
src --> tok --> parse --> ast --> xform --> cgast --> gen --> codeThree substeps inside transform:
- Walk the tree depth-first, applying every
nodeTransformfromoptions.nodeTransformsand everydirectiveTransformmatching directives on each element. - cacheStatic runs before codegen and lifts unchanging subtrees to module-scope
_hoisted_*constants. This is the optimization that makes static parts of templates allocation-free. - createRootCodegen wraps the whole thing in a
Blockso the runtime can flat-walk dynamic descendants.
Key abstractions
| Symbol | File | Description |
|---|---|---|
baseParse(source, options) |
packages/compiler-core/src/parser.ts |
Tokenizer-driven recursive parser. Builds the RootNode AST. |
Tokenizer |
packages/compiler-core/src/tokenizer.ts |
A 36KB state machine; one of the most-tuned files in the codebase. Emits start tags, end tags, attributes, interpolation, comments, CDATA. |
RootNode, ElementNode, TextNode, InterpolationNode, DirectiveNode |
packages/compiler-core/src/ast.ts |
Discriminated AST union. Each node has a loc (source location) and an optional codegenNode. |
transform(root, options) |
packages/compiler-core/src/transform.ts |
Drives the visit. Maintains a TransformContext with currentNode, parent, helpers (which runtime helpers to import), hoists, cached. |
NodeTransform, DirectiveTransform, StructuralDirectiveTransform |
packages/compiler-core/src/transform.ts |
Plug-in shapes. StructuralDirectiveTransform is what v-if, v-for, v-slot use to short-circuit normal traversal. |
transformElement |
packages/compiler-core/src/transforms/transformElement.ts |
The biggest transform (~28KB). Builds createElementVNode/createBlock calls, computes PatchFlags, packs props arguments. |
transformExpression |
packages/compiler-core/src/transforms/transformExpression.ts |
Rewrites template identifiers using babelUtils.walkIdentifiers. With prefixIdentifiers: true (the default for ESM-bundler builds and SSR), it qualifies free identifiers as _ctx.foo so render functions are pure. |
cacheStatic |
packages/compiler-core/src/transforms/cacheStatic.ts |
Static-hoisting pass. Determines getConstantType(node) for every element and lifts those that are fully constant. |
generate(ast, options) |
packages/compiler-core/src/codegen.ts |
Walks codegenNode and writes function render(_ctx, _cache) { return (_openBlock(), _createElementBlock("div", null, [...])) }. |
RuntimeHelpers |
packages/compiler-core/src/runtimeHelpers.ts |
Symbol → name table (OPEN_BLOCK, CREATE_BLOCK, CREATE_ELEMENT_BLOCK, CREATE_VNODE, CREATE_ELEMENT_VNODE, CREATE_COMMENT, CREATE_TEXT, CREATE_STATIC, RESOLVE_COMPONENT, RESOLVE_DYNAMIC_COMPONENT, RESOLVE_DIRECTIVE, WITH_DIRECTIVES, RENDER_LIST, RENDER_SLOT, WITH_CTX, KEEP_ALIVE, TELEPORT, SUSPENSE, WITH_MEMO, …). The runtime imports are guaranteed to be re-exported by vue. |
Patch flag generation
The compiler's most important optimization is PatchFlags. transformElement decides, per element, which of these to set on the generated createElementVNode call:
TEXT— the only dynamic part is text interpolation.CLASS,STYLE,PROPS— only those bind types change.PROPScarries the changed-key list.FULL_PROPS— hasv-bind="object"so any key may change.HYDRATE_EVENTS— has events that need attaching during hydration.STABLE_FRAGMENT— children list never reorders. Used forv-forover a constant key.KEYED_FRAGMENT/UNKEYED_FRAGMENT— children may reorder.NEED_PATCH— non-render-output thing (ref, custom directive).DYNAMIC_SLOTS— slot content is dynamic.HOISTED— vnode itself is module-scope and never patches.
The patch flag list lives in packages/shared/src/patchFlags.ts; the compiler is the only producer, the runtime is the only consumer.
v-if, v-for, v-slot
These are structural directives and rewrite the AST rather than just transforming a single node:
vIf.tscollectsv-if/v-else-if/v-elsesiblings into anIfNodewith multiplebranches. Codegen emits a chain of conditional expressions wrapped in a block.vFor.tsproduces aForNodewhose codegen calls_renderList(source, (item, key, index) => …). It infers a stable key from:key="item.id"and choosesSTABLE_FRAGMENTvsKEYED_FRAGMENTaccordingly.vSlot.ts(buildSlots,trackVForSlotScopes,trackSlotScopes) handles<template #name>, scoped slots, and dynamic slot names. It produces a slots object literal whose entries are functions wrapped in_withCtx.
Errors
packages/compiler-core/src/errors.ts defines ErrorCodes and a stable message table. Errors are emitted via createCompilerError(code, loc) from any transform; the parser's own errors carry codes from the same enum. The hosting compiler (compiler-dom, compiler-sfc) usually surfaces these with a code frame produced by @vue/shared's generateCodeFrame.
Integration points
- Re-exported as the substrate of
@vue/compiler-dom, which wrapsbaseCompilewith DOM-flavored options. compiler-ssrreusestransformExpression,transformBind,transformOn,transformStyle,trackSlotScopes, and the parser, but installs its own SSR transforms on top.compiler-sfccallsbaseCompile(viacompiler-dom) for the template block, then post-processes the result.- The runtime helper names listed in
runtimeHelpers.tsmust match the names actually exported fromruntime-core/runtime-dom. Updating one without the other breaks the build.
Entry points for modification
- New directive? Add a
DirectiveTransform(orStructuralDirectiveTransform) intransforms/, register it ingetBaseTransformPreset(compile.ts). - New patch-flag opportunity? Edit
transformElement.tsto detect the case and set the flag; updateruntime-core/src/renderer.tsandruntime-core/src/vnode.tsto honor it. Don't forget the patch-flag table in@vue/shared. - Parser bug? Most fixes land in
tokenizer.ts(the state-machine table) orparser.ts(where the tokens are assembled). The tokenizer is heavily commented because it is not pleasant to read. - New cache-static heuristic?
cacheStatic.tsand thegetConstantTypefunction are the right place; be careful with the bail conditions.
For the SFC pipeline that wraps this compiler, see packages/compiler-sfc. For the SSR pipeline, see packages/compiler-ssr.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.