Rust Hygiene Audit
tsz-org/tsz
Run a deep DRY + code-hygiene audit of the Rust workspace and turn the findings into verified, deduplicated, hierarchical GitHub tech-debt issues.
Rust compiler pipeline, template codegen (VDOM/IDE), CodeTransform, cached directives, strict slots, IDE error recovery, style preprocessing, CompileTarget, compiler authority/policy/demand/admission
$ npx skills add pikax/verter --skill compiler-codegen -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install pikax/verter compiler-codegen --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/pikax/verter.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/compiler-codegen .claude/skills/compiler-codegen && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "compiler-codegen" agent skill from https://github.com/pikax/verter/tree/main/.claude/skills/compiler-codegen into .claude/skills/compiler-codegen/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "compiler-codegen", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/pikax/verter/tree/main/.claude/skills/compiler-codegenType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add pikax/verter --skill compiler-codegen -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install pikax/verter compiler-codegen --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/pikax/verter.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.claude/skills/compiler-codegen .agents/skills/compiler-codegen && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "compiler-codegen" agent skill from https://github.com/pikax/verter/tree/main/.claude/skills/compiler-codegen into .agents/skills/compiler-codegen/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "compiler-codegen", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add pikax/verter --skill compiler-codegen -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install pikax/verter compiler-codegen --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/pikax/verter.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.claude/skills/compiler-codegen .cursor/skills/compiler-codegen && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "compiler-codegen" agent skill from https://github.com/pikax/verter/tree/main/.claude/skills/compiler-codegen into .cursor/skills/compiler-codegen/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "compiler-codegen", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/pikax/verter.git --path .claude/skills/compiler-codegen--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add pikax/verter --skill compiler-codegen -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install pikax/verter compiler-codegen --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/pikax/verter.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.claude/skills/compiler-codegen .gemini/skills/compiler-codegen && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "compiler-codegen" agent skill from https://github.com/pikax/verter/tree/main/.claude/skills/compiler-codegen into .gemini/skills/compiler-codegen/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "compiler-codegen", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install pikax/verter compiler-codegenInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add pikax/verter --skill compiler-codegen -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/pikax/verter.git skills-src && mkdir -p .github/skills && cp -r skills-src/.claude/skills/compiler-codegen .github/skills/compiler-codegen && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "compiler-codegen" agent skill from https://github.com/pikax/verter/tree/main/.claude/skills/compiler-codegen into .github/skills/compiler-codegen/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "compiler-codegen", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add pikax/verter --skill compiler-codegen -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install pikax/verter compiler-codegen --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/pikax/verter.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.claude/skills/compiler-codegen .opencode/skills/compiler-codegen && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "compiler-codegen" agent skill from https://github.com/pikax/verter/tree/main/.claude/skills/compiler-codegen into .opencode/skills/compiler-codegen/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "compiler-codegen", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
compiler-codegenRust compiler pipeline, template codegen (VDOM/IDE), CodeTransform, cached directives, strict slots, IDE error recovery, style preprocessing, CompileTarget, compiler authority/policy/demand/admission
Compiler Codegen is an agent skill from pikax/verter. Rust compiler pipeline, template codegen (VDOM/IDE), CodeTransform, cached directives, strict slots, IDE error recovery, style preprocessing, CompileTarget, compiler authority/policy/demand/admission
Its SKILL.md is about 21k tokens, which your agent loads only when the skill is triggered. The skill folder holds 2 other files, including reference files (for example `references/authority-policy-demand.md`).
It sits in Development, covering Project scaffolding. It works with Rust. The repository describes itself as: Fast Rust-powered compiler, semantic extraction, and LSP for component frameworks. The licence is MIT.
4 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit e4f9d26. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
cargonodeFrom the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
github.comFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Compiler Codegen loads about 21k tokens when it runs, and up to ~23k if it reads all its reference files. Until then it costs about 54 tokens; SKILL.md has 9,292 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from pikax/verter at commit e4f9d26, republished under its MIT licence (© pikax). 9,292 words, ~21,282 tokens.
.claude/skills/compiler-codegen/SKILL.md (or your agent's skills folder). This skill also uses 1 other file; get the full folder from GitHub.Compiler authority, policy, demand, and admission: normative text is
the compiler-architecture.md contract, a DAG asset owned by the TAMA controller (not a file in this repository).
The skill file
references/authority-policy-demand.md
is an operational pointer only. The combined carrier-compiler registry remains
a live migration seam, not the ratified owner. DefaultCompilationContractId
is 1:1 with live ProductKind (no facts dump). compile_bundle is a
combined product pass, not a third bus. Cheap Default facts are
FrameworkSemanticAuthority over admitted parse only.
AST-based pipeline. compile() orchestrator drives a linear 5-phase pipeline:
Vue SFC Source
|
[Tokenizer] byte-level SFC tokenization (tokenizer/byte.rs)
|
[Parser] builds arena-based template AST + extracts script/style blocks (parser/)
|
[Style] typed Vue/Svelte rewrite planning over StyleSyntaxIr
|
[Script] macro expansion, binding extraction, component wrapper (script/)
|
[Template] render function codegen -- VDOM or Vapor backends (template/)
|
[Compile] orchestrates the above, applies CodeTransform, emits output (compile.rs)Module overview:
compile.rs # Pipeline orchestrator, options, result types
tokenizer/
+-- byte.rs # Zero-copy byte-level SFC tokenizer (production)
+-- helpers.rs # Tokenizer utility functions
+-- types.rs # Event, QuoteType
parser/
+-- mod.rs # Syntax state machine (tokenizer events -> AST)
+-- types.rs # RootNodeScript, RootNodeStyle, RootNodeTemplate
ast/
+-- mod.rs # TemplateAst (flat arena with O(1) navigation)
+-- builder.rs # TemplateAstBuilder (incremental AST construction)
+-- types.rs # AstNode, ElementNode, NodeId, pre-computed flags
script/
+-- mod.rs # generate_script() entry point
+-- process.rs # Script setup processing, companion script merging
+-- macros.rs # defineProps/Emits/Model/Slots/Expose/Options
+-- css_vars.rs # _useCssVars() injection for v-bind() in styles
template/
+-- oxc/ # OXC expression parsing for template bindings
| +-- mod.rs # parse_template_expressions()
| +-- scope.rs # LexicalScopes frames, LexicalScopeId handles, ActiveScope
| +-- types.rs # OxcParsedAst, OxcParsedElement, OxcParsedExpression
+-- code_gen/ # Render function codegen
+-- mod.rs # generate_template() entry point
+-- walker.rs # DFS tree walker (shared by all backends)
+-- types.rs # TemplateCodeGen trait, CodeGenOutput
+-- binding.rs # BindingResolver (_ctx./$setup. prefix resolution)
+-- shared/ # Shared codegen helpers
+-- vdom/ # VDOM render function output (_createElementVNode, etc.)
+-- vapor/ # Vapor mode output (_template, _renderEffect, etc.)
ide/ # IDE codegen: TSX or JSX+JSDoc (for LSP/TSGO type checking)
+-- mod.rs # generate_ide_template() -- Vue template -> valid JSX; IdeScriptOptions, IdeTemplateOptions
+-- script.rs # generate_ide_script() -- script block -> TS or JS+JSDoc wrapper
+-- script_recover.rs # Token scanner for macro binding recovery from broken script tails
+-- condition.rs # v-if/v-else-if/v-else condition chain codegen
+-- template/
+-- mod.rs # walk_element/walk_node, cached directive removal, ref conversion
+-- directives.rs # v-if -> ternary, v-for -> .map(), v-show -> style
+-- props.rs # :prop -> prop={}, @event -> onEvent={}, v-bind spread
style_planner.rs # Vue authored-v-bind, CSS Modules, and plain-CSS scoping stages
css/
+-- mod.rs # legacy processStyle/CSS-modules compatibility surface
+-- prepass.rs # retained until the NAPI/provider cutover
+-- scoped.rs # retained until the NAPI/provider cutover
+-- modules.rs # CSS Modules: hash class names
+-- walk.rs # String-level CSS selector walking
+-- types.rs # ProcessStyleOptions, ProcessStyleResult
code_transform/
+-- code_transform.rs # Chunk-based deferred mutation engine (MagicString equivalent)
+-- chunk.rs # Chunk types (Original, Overwritten, Inserted, InsertedMapped)
+-- source_map.rs # Source map generation from chunk positions
utils/
+-- oxc/ # OXC parser utilities
| +-- bindings/ # Expression binding extraction
| +-- vue/ # Vue-specific OXC helpers (macro syntax, v-for, v-slot)
+-- vue/ # Vue runtime helpers (tag detection, patch flags)Parser builds a flat Vec<AstNode> arena with O(1) navigation:
pub struct TemplateAst {
nodes: Vec<AstNode>, // flat arena
root: RootNodeTemplate,
}
pub struct AstNode {
kind: AstNodeKind, // Element | Text | Comment | Interpolation
parent: Option<NodeId>, // O(1) parent lookup
index_in_parent: usize, // O(1) sibling lookup
}ElementNode pre-computes metadata during parsing to avoid re-scanning in codegen:
tag_type: Element / Component / SlotOutlet / Templateprop_flag: Bitset of prop characteristics (has class, style, spread, etc.)children_flag: Bitset of children characteristics (has text, elements, v-if, etc.)children_mode: Enum for codegen branching (Empty, TextOnly, SingleElement, Mixed, etc.)v_condition, v_for, v_slot, v_once, v_reftemplate/oxc/scope.rs)parse_template_expressions is the ONE producer of template lexical scope, built
in its existing forward pass over the node arena:
v-for alias list and each v-slot parameter list opens one persistent
frame in OxcParsedAst::scopes (LexicalScopes) holding ONLY the names that
list declares, linked to its enclosing frame. A list that declares nothing
opens no frame.LexicalScopeId handle: OxcParsedAst::children_scope(id)
is what the node's children see; scope_of(id, ast) (the parent's children
scope) is what the node itself sits in; OxcParsedElement::props_scope is the
element's props / dynamic slot name / v-slot value scope (own v-for aliases
visible, own slot params NOT). ide_recovery_scope on a broken IDE expression
is a handle too.ActiveScope, a multiset the
pass moves between frames incrementally (leave/enter only the frames that
differ). BindingContext::within and the v-for / v-slot binding helpers
read it in place through verter_parser's EnclosingScope trait.Receiving rules: query names through a handle (LexicalScopes::declares,
declares_completion_of, own_names) or the shared EnclosingScope; never
flatten a handle's inherited names into a per-element / per-expression list, never
copy a parent's aliases into a nested v-for scope, and never walk ancestors to
rediscover a node's scope. Exact lexical identity and order are the frames'
(source order within a frame, innermost frame first along a chain).
All codegen phases use CodeTransform -- a chunk-based deferred mutation engine:
let mut ct = CodeTransform::new(input, &allocator);
ct.overwrite(start, end, replacement); // deferred
ct.prepend_left(pos, content); // deferred
let output = ct.build_string(); // single-pass concatenationKey features:
cursor_hint: Accelerates forward-progressing access patterns to amortized O(1)output_delta: Incremental length tracking avoids full scansource_len / 13 (empirically tuned)All modifications to generated code MUST go through CodeTransform operations (overwrite, prepend_left, append_left, move_with_suffix, etc.). Never apply string replacements, regex transforms, or manual splicing to the output of build_string() or to content produced by a CodeTransform.
Post-hoc string manipulation breaks sourcemap accuracy: CodeTransform generates source maps by tracking chunks (Original, Inserted, Moved, Overwritten). Modifying the string after the transform makes byte offsets in the source map no longer match the content, causing position mismatches in the LSP (e.g. hover landing on the wrong token, go-to-definition jumping to wrong locations).
Correct: Use ct.prepend_left(pos, ".ts") to insert text at a known position -- chunk list and source map stay consistent.
Wrong: Call content.replace(".vue'", ".vue.ts'") on the built string -- the source map still reflects the pre-replace byte offsets.
The rule has NO scoped exceptions: the Svelte scoped-CSS renderer (crates/verter_compiler/src/svelte/runtime/css/render.rs) edits the original component source through the shared CodeTransform's checked (try_*) operations -- whose insertion-affinity chunk model carries the magic-string semantics the official svelte@5.56.10 render_stylesheet depends on (content-only try_update preserving the replaced range's first-chunk boundary insertions, left/right insertion affinity with per-affinity stacking, try_remove clearing interior insertions; pinned by code_transform/edit_semantics_tests.rs) -- and generates the css source map (css.map) from the SAME transform that built css.code. The guard svelte_css_renderer_uses_code_transform (crates/verter_compiler/tests/) asserts the renderer stays on the shared transform and bans any private edit buffer from the css matcher/render tree.
assembly::CompileArtifactSet owns the immutable terminal schema for
already-produced compiler artifacts. Its constructor validates facts only;
it does not parse, compile, assemble, or publish. Artifact rows keep the
caller-supplied contribution/plan order (identity bytes are not an ordering
key); terminal JSON still sorts artifacts by id so the wire stays
identity-canonical. CompileArtifact inputs are mutable builders, while a
validated set exposes immutable accessors and is the only schema container
implementing Serialize.
Artifact lineage is the canonical tuple (SourceUnitId, ProductKind, LanguageId, producer-owned slot name). Revision, source content, output availability, and
provenance are neighbouring facts. Each artifact names its primary source unit,
an InputBasisId, a producer ResultContractId, and all contributing units.
Typed artifact relations must target another artifact in the same set. Duplicate
artifact/source identities, missing references, and inconsistent revisions of
one source fail closed. Empty available text differs from unavailable content.
ArtifactSourceUnit binds each input to its registered SFC-absolute byte Span;
external inputs carry their own source identity and absolute extent.
QualifiedArtifactMap distinguishes source-projection and runtime-source-map
families, names its generated artifact and nonempty input-space set, and carries
the exact generated ContentId and observed InputBasisId so maps from older
output bytes or inputs fail closed even when artifact lineage is unchanged.
It carries typed generated ranges separately from authored Spans. Map sources
must be part of artifact provenance; ranges must fit their spaces, generated endpoints
must be UTF-8 boundaries, and ambiguous overlapping generated mappings fail.
Unmapped generated text has no authored segment. An absent family is not an
implicit identity map. These mappings describe authored anchors and do not
assert byte-for-byte fidelity or interpolate between differently sized ranges.
Terminal JSON uses schema version 1, explicit utf8-bytes coordinates,
hex-encoded full canonical identities, sorted objects/sets and generated-order
segments. JSON/V3/LSP adapters own UTF-16 conversion using source documents.
The schema does not replace the live ArtifactSet publication boundary or
VerterCompileResult routes.
Vue custom-block parsing (compile/mod.rs, extract_block_ranges /
the unknown_nodes() walk) retains one VerterCustomBlock per source
block: block_type, content, attrs (exact authored order — attrs
are never sorted or deduped, since CustomBlockDescriptorId::mint
hashes them in order), region (SFC-absolute; a self-closing tag with
no content span anchors at tag_open.end, a zero-length region),
source_order, lang, src (matched by exact attribute name, as Vue's
createBlock does), and source_content — the ContentId of the bytes
the facts were read from, hashed once per compile and only when a custom
block exists. The facts travel opaquely on
RuntimeCompileOutput.custom_block_facts to the Vue bridge, which mints
CustomBlockDescriptors from them directly — no re-parse, no re-scan of
source text — and refuses facts whose source_content differs from the
registered carrier bytes.
assembly::StagedCompileArtifacts is the request's ONE handoff from a
framework host-integration backend into session lifecycle/publication code:
a validated CompileArtifactSet, the ArtifactId of the runtime-module
artifact inside it, the FragmentDialect those bytes are written in, and
the runtime source map produced with them. stage is the only mint site
and refuses a root the set does not contain (UnknownRootArtifact) or one
that produced no content (UnavailableRootArtifact); an empty map payload
stages as absent. vue_main_compile_artifacts and
svelte_main_compile_artifacts return it, and it is what
RuntimeCompileOutput.main carries — the module bytes live ON the staged
root artifact, so a published body without its typed set is not
representable and no consumer re-derives the module's language or locates
its artifact by name.
Runtime and IDE output blocks carry a RuntimeOutputDescriptor naming the generated destination space, emitted content artifact, declared input spaces, raw map, and honest Exact/Approximate fidelity. A separately lowered template receives TemplateBindingMetadata from its script pass (bindings, has_script, const props, and ref-bindable imports), matching Vue's official bindingMetadata mechanism.
When an output genuinely merges spaces, each source is parsed and lowered independently. The compiler exposes typed generated-template holes/chunks, and framework_common/generated_chunk.rs assembles those generated bytes while rebuilding a multi-source V3 map. Do not concatenate authored inputs, synthesize carrier markup, or reparse a fabricated whole carrier. A missing chunk boundary or uncomposable map is a typed unavailable result.
Generated-hole geometry is registered through CodeTransform chunk identity and resolved during the authoritative chunk walk. Never locate a generated hole by scanning built output for marker text; authored source may contain identical text.
Current Vue capability gates:
css: None and no located rows until LSP consumers understand the declared source space. Native SCSS/Sass/Less/Stylus does not mint an optional supplied-output request.BlockContentRuntimeUnavailable.BlockContentIdeUnavailable because its registered template hole is mid-module. Any projected plain or setup script also remains BlockContentIdeUnavailable, including the simultaneous case.node --check, with both gates carrying invalid-output and non-execution controls. A compiler Ok result alone is not proof of capability.VueProjectionBackend::component_resolution combines the admitted ProjectionPlan, component-use witnesses, ScriptProjectionFacts binding inventory and the parse-selected custom-element prefixes. Configured custom elements do not enter component resolution. ComponentResolutionObservation distinguishes local/namespace, filename-recursive, global and dynamic uses without answering their types in Rust. Global names use the existing script fallback emitter, including its navigation probe; missing PascalCase members remain unknown. Non-Pascal authored tags retain the existing GlobalComponentKebabType route, and any Pascal-authored occurrence upgrades the shared fallback to Pascal intent.
DynamicComponentUseContract checks a pure :is="choice.component" plus v-bind="choice.props" pair over each arm of choice. A branch containing a constructor union must satisfy every possible constructor; a mismatched or never arm is refused. Other dynamic expressions keep the ordinary component-use witness. Resolution specialization includes the plan snapshot and binding route alongside the witness key, so source shifts and binding changes invalidate the observation. The product is dormant until the Vue checking composer adopts it; TypeScript remains the type authority.
Svelte block projectors consume parser-owned structural spans. In particular, {#snippet ...} uses SvelteBlock.head_span, whose end is the grammar-balanced outer brace; downstream code must not rediscover the head with a first-} scan because destructured/defaulted parameters can contain nested braces. Element-owned snippets lower into a lexical IIFE so same-name snippets in sibling elements do not collide and forward, mutual, and recursive references remain valid. Unchanged snippet names, parameter lists, and bodies move as original CodeTransform chunks; only punctuation, annotations, or parameter text that genuinely requires a store/await-default rewrite is synthetic.
Private component-call checks may map only byte-identical authored tokens. Synthetic scaffolding, quoted/escaped property spellings, rewritten spreads, and transformed directive names stay unmapped. Legacy intrinsic on:event|modifier={handler} projects to the lowercase Svelte DOM attribute (onevent={handler}); modifiers are runtime listener behavior and never survive as TSX attribute syntax.
The Svelte IDE carrier's public facade inlines the syntactic $props() annotation directly into Component<Props, Exports, Bindings>. Facade scaffolding stays unmapped; a byte-identical annotation is emitted as one mapped insertion per authored line because V3 source-map state does not carry across generated newlines. This lets tsgo definitions on a public prop land on the authored annotation member without assigning provenance to synthetic facade text. The higher-layer Svelte public-API projector prefers the resolved semantic contract when available (captured syntax is the fallback) and records prop-name anchors from typed local declaration origins, so tsserver targets in .svelte.verter.ts map through that surface's own source map to the same authored member.
Vue IDE self-instance declarations reference the public API as InstanceType<typeof import('./Foo.vue.verter')['default']> (and the JSDoc equivalent). The relative specifier is basename-only and omits the physical .ts extension, avoiding allowImportingTsExtensions diagnostics while resolving the exact virtual .verter.ts surface. Do not use InstanceType<import(...)['default']>: import(...) there is not a value query and forces the old @ts-ignore workaround.
script/process.rs parses <script setup> -> walks AST -> classifies bindings as BindingType (SetupConst, SetupRef, Props, etc.)template/code_gen/ via generate_template() parameterBindingResolver determines correct accessor prefix (_ctx., $setup., __props.) and suffix (.value for refs)CodeGenOutput, batch-applied to CodeTransformThe compiler owns Vue macro syntax and code emission, not typed macro
resolution. Parser macro facts are limited to authored spans, runtime
object/array constructors, defaults-object shape, model names/options, and
other syntax needed to preserve the source. Typed defineProps,
defineEmits, and defineModel surfaces arrive from TypeInfo through the
explicit VueMacroSemanticInput compile argument, staged once at entry on
the sealed CompileAttempt (stage_vue_macro_semantics / read via
vue_macro_semantics()) — the transaction's staged handoff is the
projection's SOLE carrier; no route threads a bundle on
VueExecutionInputs/VueRuntimeInputs beside it. Staging is one-shot:
a second stage_vue_macro_semantics on the same transaction panics
(misuse-loud); an explicitly staged Unavailable is a valid single stage:
UnavailableRuntime(Arc<MacroRuntimeBundle>)Tsc(Arc<MacroTscBundle>)RuntimeAndTsc { runtime, tsc }Runtime and TSC are independent demands. Bundler script emission consumes only
MacroRuntimeBundle; declaration emission consumes only MacroTscBundle.
Entries join macro syntax by stable syntax_index. Runtime entries contain
the normalized props/emits/model shapes. TSC entries contain terminal splice
text and are emitted directly; the compiler does not parse or reinterpret the
splice. A property-form emits tuple remains one terminal rest-tuple parameter
(...args: [value: T]) in both TscEmitRow.emit_parameters and
handler_parameters; flattening it to value: T loses the authored tuple
shape and is forbidden.
Profile-aware public-API projection treats a script/content block override as
an immutable one-file session overlay. The batch fixed view, TypeInfo macro
producer (SessionResolverContext), syntax extraction, and whole-hash revision
fence must all observe that exact overlay source. Override extraction is
request-local and must not populate the raw-source cached_tsc_extract slot.
Resolved invalid roots cross this boundary only as closed
MacroInvalidReason facts. The compiler renders their public diagnostic once,
using the typed reason plus the parser-owned macro role and authored type span;
authored type text is presentation data and must never be used to reclassify
the semantic outcome. Runtime and TSC invalid outcomes share this renderer.
Local declaration carriers preserve TypeInfo refusal detail through
TscDependencyDeclaration.declaration_failure: structural inference budgets
remain the closed depth/work variants, while deterministic unsupported and
unresolved declaration shapes remain distinct. The compiler forwards that
typed detail in TscDeclarationShapeReason; it never collapses the carrier to
a generic semantic-inference failure or a diagnostic string.
The compiler must never resolve a typed macro parameter, build a companion
type environment, accept a compiler-owned external-type map, or merge
host-resolved types into parser state. PreparedScript parses setup and
companion blocks once for syntax reuse only. Typed prop bindings are registered
from the runtime DTO; runtime-form object/array bindings remain parser-owned
syntax facts.
A target that encounters a typed macro without its required bundle, with a
degraded entry, or with a projection for the wrong macro role fails closed at
the authored macro/type anchor using XMissingMacroSemanticBundle or
XUnavailableMacroSemanticResult. Before runtime codegen, the compiler
structurally validates the whole bundle: syntax/effective macro identities,
roles, withDefaults association, public names, authored-member ordinals, and
synthesized model-row anchors must all match parser-owned syntax. Any invalid
row suppresses the entire runtime bundle; a Complete row with a degraded
member remains usable, emits type: null, and reports the typed reason/detail
at the exact authored key (or model-name/type) span.
Parser model-name facts carry both an OXC-decoded semantic value and the exact authored literal span. Runtime/TSC joins compare the decoded value, retain the span only for mappings and diagnostics, and serialize typed emit/model public names with the canonical JavaScript string escaper.
withDefaults syntax remains parser-owned. A statically eligible object
(supported keys, no spread) is folded into each DTO-derived prop row, preserving
the first duplicate and method/default expression syntax. Dynamic, spread, or
unsupported-key defaults preserve the whole authored expression and emit
exactly one _mergeDefaults. Runtime prop rendering follows three independent
profiles: development emits type, required: true|false, skipCheck, then a
static default; production retains only Vue-required Boolean/Function types and
defaults; production custom-element mode retains every type field, including
type: null. CodegenOptions.custom_element selects the script policy and is
independent of template tag matching in custom_elements. Model props use
Vue's separate model policy (no synthesized required; custom-element mode
does not widen production model types).
Guards: vmrs_boundary_missing_runtime_semantic_bundle_fails_closed,
vmrs_runtime_bundle_is_the_only_type_based_props_authority,
vmrs_invalid_macro_shapes_render_role_specific_diagnostics_on_both_rails,
vmrs_runtime_failures_preserve_typed_reason_detail_and_absolute_anchor,
vmrs_tsc_unavailable_diagnostics_preserve_exact_outcome_reason_and_detail,
pinned_vue_macro_oracle_carries_provenance_and_discriminating_runtime_facts.
ide/template/emit.rs)IDE template codegen emits a Vue binding value as JSX through the typed EmitOp vocabulary so the user expression keeps an exact source-map mapping while synthetic JSX scaffolding stays unmapped. EmitText (Static/Borrowed/Owned) is the text payload; EmitOp variants: InsertUnmapped (order-preserving unmapped insert, lowers via prepend_ordered_unmapped), InsertMapped (InsertedMapped chunk, mapped at source_start+content_offset), PreserveOriginal (pure no-op — bytes stay an Original 1:1 chunk), OverwriteSyntheticBoundary (delete + unmapped insert; NEVER a mapped out.overwrite), MoveOriginal. emit_op is the single lowering point. emit_jsx_binding_value emits a JsxBindingValue (source_expr/prefix/suffix/occurrences/bindings) occurrences times for RELOCATED emission (native v-model emits the expression 2-3x); in-place sites (v-html, v-text, :[key], .foo=, v-bind="obj", static :prop) preserve the bytes and emit OverwriteSyntheticBoundary + collect_binding_patches around them. A function-typed :prop under a v-if scope (e.g. <div v-if="ok" :onX="() => handle()">) gets a type-narrowing guard: compute_function_guard_injection (props.rs) locates the injection point in SOURCE coordinates from the OXC AST (arrow-EXPRESSION body start → ternary !((cond))?undefined:; arrow-BLOCK / function body {+1 → block if(!((cond))) return;), then the value is kept IN PLACE (boundary split + collect_binding_patches) and the guard is an UNMAPPED prepend_alloc spliced into the middle — emitted BEFORE collect_binding_patches so an arrow-expr body identifier at the injection offset stable-sorts as <guard><accessor-prefix><identifier>. The guard is never baked into a mapped overwrite. The v-on inline-handler guard (von.rs) is likewise a synthetic PREFIX inside out.overwrite(prop.start, trimmed_vs, …) with the handler body preserved in place — it never bakes the resolved value, so it is not migrated.
Bug this replaces: baking prefix + identifier into one out.overwrite(prop.start, prop_end, &format!(...)) produced a Chunk::Overwritten mapping the whole run back to the prop start (identifier hover/go-to-definition landed on the prop name). The flat-string IDE producers resolve_prefixed_expr/resolve_prefixed_dynamic_arg were deleted; wrapped/transformed flat-string consumers (v-on spreads, dynamic event-name keys, v-show) call the shared build_prefixed_expr directly. Guard: crates/verter_compiler/tests/cases/ide_no_baked_prefix_overwrite.rs — scans ide/template/** for both the INLINE bake (out.overwrite(.., &format!(..<resolver-var>..))) and the let-INDIRECTION (let v = …format!(..<resolver-var>..)… / build_prefixed_expr(..) / resolve_simple_expr(..); out.overwrite(.., &v)), EXCLUDING self-anchored overwrites (out.overwrite(base + node.start, base + node.end, &v) replaces one node's own span → navigable; partial-interpolation recovery path is the canonical example). The allowlist is EMPTY.
Three backends implement the TemplateCodeGen trait, called by walker::walk_template() in DFS order:
vdom/): In-place source overwrites producing _createElementVNode() callsvapor/): Replaces entire template block with direct DOM manipulation codeThe Rust compiler has two separate template codegen paths. Modifying one does NOT affect the other:
| Path | Module | Purpose | Output |
|---|---|---|---|
| VDOM/Vapor | template/code_gen/vdom/ | Runtime render functions for bundler output | _createElementVNode(...) calls |
| IDE | ide/template/ | Valid JSX/TSX for LSP/TSGO type checking | <div prop={expr}> JSX elements |
The LSP uses the IDE path via host.ensure_compiled() with CompileTarget::IDE. TSGO type-checks this output. Changes to VDOM codegen do NOT affect LSP hover/completions. IDE codegen auto-detects the script language: TS SFCs produce .tsx (TypeScript + JSX); JS SFCs (no lang or lang="js") produce .jsx (JavaScript + JSDoc annotations).
Guards: the compile_audit_sourcemap suite (crates/verter_session/tests/cases/g_compile/compile_audit_sourcemap.rs) plus the compile-output snapshots. An IDE-side regression caused by a VDOM change (or the reverse) surfaces as a snapshot diff or a sourcemap byte-offset mismatch.
Official-framework compiler conformance is behavioral plus structural/helper-topology parity, not raw-byte identity. For Vue VDOM/Vapor, Svelte svelte/internal/*, SSR/client, and future runtime backends, compare emitted output by observable behavior plus parsed/token-normalized structure: imports, helper families, helper call sequence where order is semantic, memoization/reactivity/effect topology, DOM/hydration template topology, class/style/attribute normalization, prop/property routing, event delegation, and diagnostic/reject ordering.
Cosmetic JS carrier formatting is not a finding: indentation, line breaks, non-semantic comments, intra-expression whitespace outside literals, and behavior-preserving redundant parentheses may differ from the official compiler. Directive, pragma, license/preserve, source-map/sourceURL, TS-directive, JSDoc, and other tool-consumed or framework-significant comments remain in contract. Generated local identifier spellings are waived only when the backend oracle implements scope-aware alpha-equivalence for private, non-observable bindings; otherwise identifiers are structural. Literal payload bytes, static HTML/CSS/SSR strings, public/exported or source-authored names, sourcemap mappings, diagnostic text/codes/order, and any framework-defined observable format remain in contract.
Do not build or route production compiled-output emission through JS printers, re-printers, redundant-paren canonicalizers, or any machinery whose role includes mimicking the official compiler's cosmetic JS carrier formatting. Direct-emission helpers may emit syntax-required tokens, including required parentheses for valid JavaScript expression/statement shape, but they must be scoped to semantic/syntactic correctness and covered by behavioral/structural tests rather than official cosmetic byte parity. Emit correct code directly and make conformance oracles structural for cosmetic categories: a cosmetic-only diff passes; a behavioral or structural divergence fails.
Byte-equality tests remain valid only where bytes are the actual contract, such as generated binding freshness, source-map exactness, or self-characterization during a refactor; they are not official-compiler conformance oracles.
Tracked guard gap: the positive structural-discriminator guard currently covers Svelte client only. Add backend-owned positive structural conformance oracles for Vue VDOM/Vapor and SSR/client outputs before those backends are considered fully guard-covered by this rule; the re-printer guard is cross-backend negative coverage.
Default is parity with official's observable-correct behavior. A deviation is a DELIBERATE final-state choice to differ from official's correct behavior, recorded with a deviation record, durable code comment, and landed note; silent divergence is never a deviation. The native Svelte client backend currently has no deliberate deviations. This does not mean zero divergences: known structural/helper-topology divergences, behavior-equivalent topology differences, and unconverged SSR/unimplemented surfaces remain tracked in crates/verter_compiler/src/svelte/runtime/diff_oracle_divergences.rs or their owning tests and must be converged or kept fail-closed before promotion. <svelte:head> attributes fail closed matching official's svelte_head_illegal_attribute; that is reject-parity, not a deviation.
Guards: svelte_structural_conformance_discriminates_cosmetic_from_behavioral_diffs, no_compiled_output_cosmetic_reprinter_path.
Template text expressions are classified from the canonical retained OXC AST. Supported roots are identifier, member/optional-member, call/optional-call, binary, logical, conditional, template, new, and primitive literals. Rewriting, call memoization, binding impurity, D-14 constant evaluation, and nullish-coalescing analysis must consume that retained carrier rather than reparsing or scanning source text. Exact static runs use textContent (sole element child) or nodeValue (reached sibling text node) without an effect; mixed static/live chunks share one text update, and call-bearing values use the official deps-array $.template_effect topology. Each/await aliases retain their signal-root rewrite. Unsupported nested constructs preserve their precise typed refusal.
resolve_svelte_compile_options(source, parsed, opts) -> Result<ResolvedSvelteCompileOptions, UnsupportedSvelteRuntimeSurface> (svelte/runtime/compile_options.rs) is the SINGLE fold point for Svelte compile options. It runs ONCE per compile request from the single guarded call site at the top of compile_client (svelte/runtime/client_compile.rs) — every downstream consumer reads the resolved struct, never the raw SvelteRuntimeOptions.
The fold — compile-option side (SvelteRuntimeOptions) ∪ the inline <svelte:options> attributes, INLINE WINS per admitted key (matching svelte@5.56.10 precedence). Inline values are read through the typed AST via the shared parser value authority (options_namespace_value / options_boolean_value), never a raw rescan. Only the keys the inline syntax admits (namespace, preserveWhitespace) fold; the resolver runs AFTER the official-reject gate, so it only ever sees official-accepted <svelte:options> shapes. The folded namespace is used ONLY to fail closed (see below) — the backend emits HTML-namespace roots ONLY, so no namespace value is threaded to codegen.
Resolved struct ResolvedSvelteCompileOptions { fragments: SvelteFragments{Html,Tree}, preserve_whitespace: bool (default false), preserve_comments: bool (default false), disclose_version: bool (default true) } — HTML-only, four fields. There is NO resolved namespace field (svg/mathml fail closed, so the emitted root is always html-namespaced), NO component_name field, and NO css_hash_override field: the component name is derived during LOWERING (derive_component_name in naming.rs, reading opts.name ?? filename, then Scope.generate sanitization + deconfliction against the canonical ComponentScopeFacts binder — component_scope_facts.rs, source_declarations ∪ free_references from one lexical pass over the module/instance scripts plus the template's authored declarations and stored expression references; the single scope authority, replacing the earlier selective all_declared_names + reparse approximations) and fed onto ComponentIr, and the cssHash override rides the carrier channel into the single style-plan scope point (see /host-session for the cache-identity seam).
Namespace fail-close (html-only). A namespace: 'svg' | 'mathml' selection (compile-option OR inline) fails closed at the resolver with a typed UnsupportedSvelteRuntimeSurface::NamespaceUnsupported { namespace: SvelteNamespace, origin: CompileOptionOrigin{CompileProfile,Inline}, span: Option<Span> } (stable code svelte-runtime-unsupported-namespace) → NO runtime module; an inline namespace="html" masks a compile-option svg/mathml (inline wins). svg/mathml ELEMENT emission (the $.from_svg / $.from_mathml root-helper family, the TEMPLATE_USE_SVG / TEMPLATE_USE_MATHML flag bits) is a separate deferred element-emission surface — see the svelte-native-compiler-plan D-62 row. There is NO ns×fragments matrix: every supported root, in every fragments mode, is html-namespaced.
Per-option codegen consumers (all read the resolved struct):
fragments → the root template factory. emit_root_hoist (client_module_frame.rs) picks $.from_html (the backtick clone) or $.from_tree (the array-literal objectifier) under fragments: 'tree'; the root is always html-namespaced.preserve_whitespace → seeds the root CleanContext { preserve_ws } threaded through region synthesis.preserve_comments → a drop-set gate on retained comments, which serialize as <!--data--> (bare <!> for empty) in template_serialize.rs with the node-path shift applied.disclose_version → ImportPlan.disclose_version (helpers.rs), toggling the import 'svelte/internal/disclose-version' side-effect import.Fail-closed unsupported carrier. Four officially-accepted options this backend does not support — compatibility.componentApi (any explicit value other than 5), hmr, accessors, immutable — are demoted out of the essential surface. Any EXPLICIT presence (including a false / default-equivalent value, from EITHER the compile-option origin OR an inline <svelte:options> origin, even a value later masked by inline) fails closed with a typed UnsupportedSvelteRuntimeSurface::CompileOptionUnsupported { option: UnsupportedSvelteCompileOption, origin: CompileOptionOrigin{CompileProfile,Inline}, span: Option<Span> } (distinct stable codes svelte-runtime-unsupported-{compatibility-component-api,hmr,accessors,immutable}) → NO runtime module. This is a FEATURE refusal, NOT an official compile-error. The deprecated inline tag key stays the parser-first svelte_options_deprecated_tag HARD error, with a defensive unreachable resolver arm. runes is NOT folded here — it flows through the existing mode-inference plumbing (forced_runes_option + opts.runes); css / customElement / dev / generate / experimental.async stay delegated to their owners.
conformance-trace feature)verter_compiler's conformance-trace Cargo feature (default OFF) enables the typed conformance-observability side channel verter_compiler::svelte::runtime::conformance_trace — CONFORMANCE-TOOLING-ONLY, consumed by the verter_svelte_conformance crate (which dev-deps verter_compiler with the feature on). It is not a production API surface: the default build compiles the module, its producer hooks, and every trace collection site out entirely, and production IR structs carry no trace state under either setting.
API surface (feature-gated): compile_client_with_conformance_trace(...) runs the production compile_client pipeline under a capture and returns the compile outcome together with the trace (a refused/rejected fixture still returns what was observed up to the failure); capture(f) installs a thread-local trace around a closure (captures nest, unwind-safe restore); ConformanceTrace { static_attrs, style_matches } carries static-attribute lexical provenance (quoting + HTML entity source representation, folded from the attribute-lowering producer boundary's single decode pass — never a second source scan) plus per-<style> matcher facts (per-selector tri-state certainty rows, used/scoped selector spans, scoped element identities); MatchCertainty is re-exported.
MatchCertainty tri-state (svelte/runtime/css/match.rs, always-on — NOT feature-gated): No < Maybe < Yes, and = min, or = max. Production projects through might_match(): Yes | Maybe ⇒ true, No ⇒ false — byte-identical to the pre-tri-state boolean matcher (Maybe was true; it is never treated as No). The per-selector certainty rows on the match sink exist only under cfg(any(test, feature = "conformance-trace")).
Zero cost when off: by #[cfg] gating plus a monomorphized no-op entity-decode observer that compiles away in the default path. Guarded by crates/verter_compiler/tests/svelte_conformance_trace_zero_cost_guard.rs (prod-IR trace-mention ban, feature-gated module declaration, closed AttrIr/match-sink field inventories, decoder-mention ban, manifest keeps the default build feature-off with no dev-dependency re-enable channel) and an isolated feature-off CI gate (cargo build/cargo test -p verter_compiler --lib with no conformance crate in the dependency graph, so workspace feature unification cannot mask the default build).
Every authored import is hoisted to module scope beside the generated helper
preamble, so a generated import must never rebind a name an authored import
already binds. unbound_builtin_components drops a Vue built-in
(Teleport, Suspense, KeepAlive, …) from the generated vue import when
either script block already imports that local name; an authored ALIAS
(Suspense as Pending) leaves the built-in's own name unbound, so it is still
imported.
A setup binding whose WHOLE initializer is a proven call to Vue's runtime
defineAsyncComponent (a non-type-only named vue import under any local
alias, or that member read off a runtime vue namespace import — see
proven_vue_async_component_bindings) is read into the template through
asyncComponent(name) instead of name as unknown as typeof name. Vue types
the call as whatever the loader resolves to, so a loader resolving to a raw
options object has no construct/call signature and direct JSX rejects a valid
tag. AsyncComponent<T> returns constructors and functional components
EXACTLY (generics included) and turns a raw options object into a constructor
over the contract its own props/emits declare. The authored declaration,
the destructured template local and the JSX tag identifier are unchanged. The
helper import is emitted only by files that use it, so no other file's
preamble moves. A same-name local function, a non-Vue import and a type-only
import prove nothing and keep the ordinary entry. The declaration lives in all
four shipped surfaces (packages/types/src/components/components.ts,
packages/types/index.d.ts, crates/verter_lsp/src/verter_types_stub.d.ts,
packages/typescript-plugin/src/helpers/verterTypesStub.ts).
Scoped slot inference captures a component instance in the parent's lexical scope
using new (componentConstructor(Component))(props). Preserve generic constructor
identity until the authored props are applied; extracting a generic return type
first erases its type parameters. ide/template/slot_inference.rs emits the
unmapped inference inputs using the same bindings as JSX props. Capture before
slot parameters and named-slot loops can shadow those inputs, including paired
components with an empty slot body. Incomplete/self-closing recovery must not
reference an instance whose capture wrapper was never emitted.
When strict_slots: true (VS Code: verter.experimental.strictSlots), the IDE template codegen emits strictRenderSlot calls after the JSX tree, enforcing that slot children match the parent component's defineSlots() type signature (RFC #733).
Generated pattern (inside the block scope, after JSX):
___VERTER___strictRenderSlot({} as NonNullable<ReturnType<typeof ___VERTER___Comp{offset}>['$slots']['{slot}']>, [TabItem, {} as HTMLElementTagNameMap["input"], "" as string]);Child type references: Component -> constructor name, HTML element -> HTMLElementTagNameMap["tag"], text/interpolation -> "" as string. Each child is a sourcemapped InsertedMapped chunk pointing to its template position.
Skipped cases: self-closing components (no children), is_jsx mode, <component :is> (deferred), whitespace-only text, comments.
Key files: ide/template/mod.rs (StrictSlotEntry, collect_strict_slot_children, emit_strict_slot_checks), ide/script.rs (ambient strictRenderSlot type declarations).
Parser extracts structural directives from el.props via prop.take() and caches them as dedicated fields on ElementNode (ast/types.rs):
| Field | Directive | In el.props? | Notes |
|---|---|---|---|
v_condition | v-if, v-else-if, v-else | No (taken) | Contains ElementNodeCondition with kind + prop |
v_for | v-for | No (taken) | Contains the full NodeProp |
v_slot | v-slot, #name | No (taken) | Contains the full NodeProp |
v_once | v-once | No (taken) | Contains the full NodeProp |
v_ref | ref, :ref | No (taken) | Contains the full NodeProp |
Consequence: Code iterating el.props will never see these directives. Both codegen paths must handle them explicitly. The IDE module removes v-if/v-for/v-slot/v-once attributes (they become JSX wrappers/removals) and converts ref to JSX expression syntax (ref={"name"}).
Globally-registered components (registered only through a GlobalComponents augmentation, never imported) type in the template via per-tag fallback consts emitted into every script arm (ide/script/wrapper.rs):
collect_global_component_fallbacks) walks the template once per arm: every non-builtin, non-member-expression, non-<component> component tag that is NOT already bound and NOT a configured custom element yields one GlobalComponentFallback { pascal, authored_non_pascal }, deduplicated by Pascal name in first-seen order. A <component is="Name"> STATIC target contributes too. The same list feeds the emitted consts AND the TemplateComponentBindings inventory (tag rewrite, @event spread payloads, simple-handler param inference) so one component types identically everywhere.const Pascal = {} as ___VERTER___GlobalComponentType<'Pascal'>; — an unregistered name types unknown and produces a real TS2604 at the tag (never silent any).const Pascal = {} as ___VERTER___GlobalComponentKebabType<'Pascal', 'authored-tag'>; — a registered member (Pascal key, then the authored key) resolves the component type; an UNREGISTERED tag degrades to a function component over JSX.IntrinsicElements['authored-tag'] (a user's web-component IntrinsicElements augmentation keeps typing it; Vue's [name: string]: any index otherwise yields any) — never a false TS2604 on a web-component tag.void ___VERTER___globalComponentsNav().Pascal;); global_component_nav_probe_offset byte-verifies BOTH emission shapes and fails closed on any mismatch.CompileOptions::custom_elements prefix match, threaded as IdeScriptOptions::custom_elements / IdeTemplateOptions::custom_elements, shared predicate ide::matches_custom_element) are native by contract: excluded from collection AND from the kebab rewrite — the tag stays authored even when a same-name local binding exists.ide/template/mod.rs): a dashed component tag with an inventory/local resolution rewrites to its Pascal identifier via emit_mapped_kebab_pascal_rewrite — PER-SEGMENT mapped edits (delete each -, overwrite only case-changing segment heads; every other byte stays an Original chunk). A whole-name overwrite mapped only up to the generated (Pascal) length, leaving the authored tag TAIL unmapped (dead hover/definition/rename); per-segment keeps every LETTER column mapped, including the last — only the removed - separators stay unmapped. Composition mismatch falls back to the whole-span mapped overwrite.The conditional types live in @verter/types — five synchronized copies: packages/types/index.d.ts, packages/types/src/components/components.ts, packages/typescript-plugin/src/helpers/verterTypesStub.ts, crates/verter_lsp/src/verter_types_stub.d.ts, and both constants in crates/verter_compiler/src/ide/script/type_constructs.rs (VERTER_TYPES_AMBIENT_MODULE + VERTER_TYPES_STANDALONE_DTS). The shipped empty declare module "vue" { interface GlobalComponents {} } augmentation guarantees the surface exists on every Vue version (introduce-on-absence + user-augmentation merge proven by verterTypesStub.spec.ts's ≤3.4 leg and its discrimination control).
Declaration-surface parity. Hand-maintained copies drift: the runCustomDirective fix that carried the directive's Arg type parameter (Directive<HostElement, Value, Modifiers, Arg>) into arg landed in the two type_constructs.rs constants and stayed missing from the three copies a real editor actually serves. verterTypesStub.spec.ts → declaration-surface parity now compiles ONE contract against every copy a TypeScript test can read as a whole artifact (VERTER_TYPES_STUB, crates/verter_lsp/src/verter_types_stub.d.ts, packages/types/index.d.ts) plus a deliberately reverted pre-fix copy, all in a single program: the TypeScript compiler is the oracle, so formatting and Directive vs import("vue").Directive spelling differences are invisible and only BEHAVIOURAL divergence fails. The published @verter/types source is covered by its own type test (packages/types/src/directives/directives.spec.ts); the two type_constructs.rs constants keep verter_types_surface_carries_the_directive_arg_type_parameter. Generating all copies from one source of truth would delete the drift class outright and remains the durable fix.
Design + deferred-debt rows: the LSO5 charter in the TAMA controller DAG.
OXC parses the original <script setup> content exactly ONCE (ide/script/setup.rs). There is a single recovery surface — no truncate-and-reparse, no clean-prefix reparse authority, no file-scope error mode.
___VERTER___TemplateBindingFN wrapper).rewrite_ts_type_assertions) → a single token scan of the REAL source produces a ScriptSetupRecoveryPlan (ide/script_recover.rs, ScriptTokenScanner::recover_plan). The plan carries top-level (bracket depth 0) original-span imports / macros / functions / variables (reused for hoisting + binding registration) plus OUTPUT-ONLY recovery chunks (detected over the WHOLE source at any depth):a. / a?. gets a universal member placeholder (valueOf) right after the operator so the dot cannot absorb the following token;(undefined));</script> overwrite) — close the brackets the user left open so the generated scaffolding starts cleanly. A delimiter that requires a non-empty body but was left empty (a grouping/arrow-body paren const x = (, a computed-member bracket foo[) gets a placeholder operand BEFORE its closer (undefined), undefined]); call args foo(), array literals [], and blocks/objects {} are valid empty and get a bare closer.Top-level fact gate. Recovered facts are gated to bracket depth 0, mirroring the clean top-level parser's block_depth == 0 rule. A block-local declaration (function f(){ const inner = 1; }) is NEVER recovered as a setup binding/import; only the whole-source holes/closers fire inside nested scopes.
Recovered macro = clean-lowering parity. A recovered defineProps/withDefaults binding is registered Props AND emits the same const __props = <binding>; alias as clean macro lowering, so a template props.x (lowered to __props.x) resolves instead of dangling against a __props that was never declared.
The user's body STAYS inside the ___VERTER___TemplateBindingFN wrapper in both cases; the broken-tail member access (count.) keeps hover/completion/go-to-definition working for declarations above the cursor.
No synthesize-then-reparse. Synthetic recovery chunks are output-only and unmapped; they are NEVER bindings, macros, imports, or any other source fact. Recovery metadata comes only from the original clean OXC AST or from original-span token recovery over the real source — a reparsed synthetic view is never an authority.
Guard: crates/verter_compiler/tests/cases/ide_script_recovery_guard.rs (scans ide/script/setup.rs for the deleted dual-recovery identifiers and the synthesize-then-reparse anti-pattern), plus crates/verter_compiler/tests/cases/repro_member_access_ide_codegen.rs (recovery shapes + clean-path preservation) and the negative-metadata tests in script_recover.rs.
Framework style rewrites use verter_css_syntax::StyleSyntaxIr and are deliberately split. Public bump-backed CSS syntax nodes (StyleBlock, StyleRule, SelectorCompound, …) are borrowed from that IR and are not independently Clone/Copy; clone the IR (Arc) when an owned handle is needed.
transform_vue_v_bind(AuthoredStyleInput) is the production v-bind-only entry: it runs on authored CSS, SCSS, indented Sass, Less, or Stylus and returns the same dialect. It never preprocesses or evaluates.PlainCssInput::try_new typed-refuses every non-CSS dialect before that cascade stage is admitted. There is no shipped isolated parse/materialize entry for this stage.:global, selector matching/pruning, scope insertion, and keyframe rewriting. Authored preprocessor dialects are typed-refused unless the host supplied a completed external preprocessing result for the block: the host hands it over once as a SvelteSuppliedStyle (produced bytes, producer, the host-minted source-space/artifact identity of those bytes, the host's parse of them, and the content identity of the authored bytes it validated — all read off the projection the block-content capture fence stamped, never re-derived from the live carrier), bind_svelte_style_continuations (svelte/carrier.rs) binds it to its block through ExternalStyleContinuation::admit, and the admitted continuation's produced bytes are then the block's only body for every stage (official-reject css probe, analysis, matcher, render) via AdmittedStyleIrs. A result that cannot be bound (stale basis, missing basis, unknown authored dialect, no block at its slot, boundary refusal) refuses the compile with svelte-runtime-style-continuation-refused; it never falls back to the authored block. Svelte's external css artifact publishes as a stage-qualified QualifiedRuntimeStyle (RuntimeCompileOutput.qualified_styles / DirectCompileOutput.qualified_styles), declared over the byte space the render consumed: the carrier source for an authored block, and for a continued block the HOST-minted space/artifact pair carried on BoundStyleContinuation — never a carrier space minted over the produced bytes, which would name an identity the host's own produced-to-authored map cannot be joined to. That host map (SvelteSuppliedStyle.source_map → BoundStyleContinuation.source_map) is transported with the continuation, and a continued block's css map is published chained through it (chain_generated_map_json, as for a supplied Vue block) — or absent when the host holds no map or the chain cannot be built — never the bare render map, whose produced-space positions would be labelled with the carrier file. The diagnostics the producing tool reported (SvelteSuppliedStyle.diagnostics, projected from the admitted override at the severity the tool stated, addressing the authored stage with no fabricated position) ride the continuation's result and then the published style's result in production order; they are never replaced by an empty list at the boundary.Only complete trusted nodes publish edits. Rewrite uncertainty fails closed; style-liveness uncertainty fails open. Every emitted edit and its source map comes from the same CodeTransform. Supplied or external inputs retain their host-minted source-space/artifact identity in RuntimeOutputDescriptor; carrier-absolute positions are never fabricated.
The sealed applyBlockOverrides() handoff remains the input channel for caller-preprocessed content. External/supplied semantic analysis continues to publish css: None until its source-space-aware analysis consumer lands; compiler rewrite code must not route around that gate.
verter_css_syntax::stage owns the vocabulary every style consumer shares: StyleStage (Authored / Preprocessed / FrameworkRewritten), StyleProducer (Verter / External(ExternalStyleProducer) / ExternalAnonymous), StyleDiagnostic (a message plus a span qualified by the stage whose space it addresses), StyleDependency (the parse-minted inclusion inventory), and QualifiedStyleResult (bytes + stage + dialect + producer + diagnostics).
A diagnostic's stage always names the space its span is IN, never the authority that reported it. There is deliberately no authority or severity axis on StyleDiagnostic: every style diagnostic a live route produces is a rewrite refusing, and a refusal is an error. A closed taxonomy whose other members name routes that do not exist claims a generality the pipeline does not have — the route that needs one adds it together with its producer.
VueStyleCascadeOutcome.result is that carrier, and outcome.code() is the only way to the bytes. A run that changed nothing stays at its input stage; a run that produced output is FrameworkRewritten and keeps its input dialect (only preprocessing fixes the dialect to CSS).CodeTransform, so no stage ever inspects bytes another stage rewrote. finish_vue_style_cascade therefore projects every diagnostic at input.stage() — a single answer, not a per-stage table. Later Vue rewrite refusals are Authored (or admitted Preprocessed) and token-precise; they are not FrameworkRewritten and do not use a rewritten-space span. A refusing plain-CSS-only stage still clears the output after reporting.CascadeOutput (Passthrough / Rewritten / ClearedByRefusal), and a wiped output mints QualifiedStyleResult::refused(...) — is_refused() true, no bytes, and no claim that a rewrite produced them. Deriving "was this rewritten?" from "are there owned bytes?" labelled those zero bytes FrameworkRewritten + StyleProducer::Verter. cascade_output_is_publishable reads result.is_refused(), not code().is_empty() and not the failing stage's identity. An authored-v-bind() rewrite failure that left the input intact is not a refusal. A parse miss is recorded once, by the parse that ran, and clears only a request whose meaning depended on a rewrite it could not plan: with module or scoped set, unrewritten bytes would ship unhashed class names or component styles applied document-wide, so the output is cleared; with neither, the only work was v-bind() lowering, nothing was rewritten, and the authored bytes publish beside the diagnostic. That second case is byte-for-byte the answer run_vue_style_authored_only gives, and it is the same answer by construction rather than by agreement: RuntimeStyleProcessing::AuthoredOnly and a Complete request with neither attribute are the same request, so run_vue_style_authored_only IS run_vue_style_cascade(input, scope_id, false, false, …) and holds no route of its own. It used to, and the two diverged on exactly one recorded fact — whether a parse that completed but whose v-bind() planning refused had surveyed the block's inclusions — which no signature could have caught. Every dialect now takes one route: the plain-CSS gate (CssStageRequest::gated, whose sole predicate is PlainCssInput::try_new) drops the CSS-only stages for a non-CSS <style module>/<style scoped> and records the refusal that dropped them, so "which stages ran" and "was a refusal recorded" are one value and cannot disagree. run_vue_style_cascade_verified passes CssStageRequest::admitted, because VerifiedPlainCss already carries the proof the gate exists to establish.outcome.result.diagnostics(). facts.refusals and stage_failures are each authority's own record of what it reported; the carrier is where those records are projected into the shared vocabulary exactly once. StyleRewriteFailure::to_diagnostic is crate-private, which removes the correctly-spaced projection (and its space argument) from the outside vocabulary so no consumer accidentally re-derives one and reports the same refusal twice. Read what that buys exactly: it is a convention backed by where the correct answer lives, NOT a structural bar — StyleDiagnostic::new and Display are public and facts.refusals/stage_failures stay pub, so a consumer that sets out to mint its own shape can. verter_napi's transform_vue_style therefore reads result.diagnostics() rather than re-formatting facts.refusals.compile::push_style_diagnostic shifts a span by the block start only for an Authored span; a later-space span (Preprocessed) anchors on the block, because no later-space → SFC map exists at that boundary. Shared Vue rewrite refusals stay Authored, so they take the token-precise arm. The VS Code client's preprocessorDiagnostics applies the same rule.run_vue_style_cascade_verified / transform_vue_style take a CascadeInput (Authored, or Preprocessed(PreprocessorIdentity)): preprocessed plain CSS and authored plain CSS are the same bytes, so only the caller knows. standalone::apply_selected_runtime_styles reads it from RuntimeBlockContentInput.producer, which the host fills from the supplied artifact's processor identity — never from a dialect comparison, which missed every tool that ran over an already-plain-CSS block. The authored-v-bind() stage still runs on preprocessed bytes: a preprocessor leaves v-bind() in its output for exactly that stage.prepare_supplied_style(PreprocessedStyle<'_>) is the sole admission point for caller-preprocessed bytes. The witness borrows the bytes and carries PreprocessorIdentity, which can name only an external tool or explicit anonymity. It is minted by QualifiedStyleResult::as_preprocessed or PreprocessedStyle::admitted. as_preprocessed gates on stage Preprocessed, an external producer, and not-a-refusal. What the signature enforces is that bytes cannot arrive without a stated space and external producer; &str is rejected by prepare_supplied_style_rejects_unqualified_bytes.rs. It cannot prove the bytes left SCSS behind because plain CSS is a subset of every supported dialect. That assertion belongs to the admitting boundary; richer tool provenance stays on SuppliedContentArtifact.CssDialect::from_lang is the single authority for exact lang="…" spelling → CssDialect identity, and CssDialect::requires_external_preprocessing() answers "is this already CSS". LANG_SPELLINGS is byte-exact for scss/sass/less/stylus/styl; lang="SCSS" therefore does not become SCSS through case folding. This lookup does not decide framework fallback: Vue's reference compiler passes a missing processor-table entry through as plain CSS. css is matched case-insensitively because it already names that fallback grammar. Every route reaches the identity owner with authored bytes; other block roles retain their owner-specific normalized spelling. carrier_style_dialect is the matching carrier-level owner shared by Vue and Svelte. The carrier parser's universe is deliberately wider because it also names postcss, which the editor serves as CSS-shaped content while the rewrite pipeline has no native dialect for it.StyleSyntaxIr::dependencies() replaces the former imports_unresolved boolean, and StyleSyntaxIr::dependency_pulls_in_unparsed_bytes is the same owner's answer for ONE inclusion. StyleDependencyKind is closed over every inclusion keyword the five dialects spell — @import/@use/@forward/@plugin/@require — plus Stylus's bare require 'x' / import 'x' statement form, which carries no at-keyword and is recognised at the IR sink instead. A missing member is a wrong-complete defect, not a cosmetic gap: an unrecognised inclusion keyword records nothing, so the block publishes an exhaustive v-bind() surface while its bindings sit in the included sheet. The list lives on the IR alone — QualifiedStyleResult carries no copy, because a StyleDependency's spans address the minting parse's space, reading a specifier needs that same IR, and an empty list there could not distinguish "no inclusions" from "nothing ever parsed". Sass's NON-EMITTING built-in modules bring in nothing (function libraries: no rules, no classes, no v-bind()), and that exemption is the exact closed set SASS_NON_EMITTING_BUILTIN_MODULES, not the sass: prefix — sass:meta emits another module's CSS through load-css(), so it answers like any other sheet. Everything else does too, including an inclusion whose target the parse could not address exactly.StyleSyntaxIr::pulls_in_unparsed_bytes(), NOT a fold over dependencies(). A recover-mode parse returns a usable IR for a sheet it could not read end to end, and its inclusion list is then a LOWER BOUND: an @import inside an unterminated block never reaches the at-rule frame and never enters the list. Folding over what the parse DID record therefore answers "nothing foreign here" exactly where it saw least — the wrong-complete direction, publishing an exhaustive v-bind() liveness surface for a block whose bindings live in a sheet nothing parsed. The whole-sheet answer adds the parse's own recovery record (RecoveryKind::discarded_input, an exhaustive match so a new recovery strategy must state its side) to the per-inclusion answers. It keys on recovery, not on diagnostic kinds or on "are there diagnostics": an ambiguity reported without dropping anything still parsed and still inventoried the surrounding structure. extract_style_v_bind_usage_* marks its result incomplete on that one fact and callers fail open. Every entry that parses the input records the completeness answer through the one record_input_dependencies recorder, so it does not depend on which entry the caller took. The recorded state is tri-state (Option<bool>, private) and read through VueStyleFacts::pulls_in_unparsed_bytes(), which fails closed on the unrecorded state: false is the STRONG claim ("nothing outside these bytes contributes to this block's surface") and no parse has earned it until one has run. The reachable unsurveyed state is a cascade whose parse of the input never completed, with neither modules nor scoping requested to clear the output; a derived bool default answers "exhaustive" there, which is the wrong-complete direction. The answer is recorded from the parse BEFORE any stage plans an edit, so a stage that then refuses to plan (an untrusted v-bind() target, an indented-layout mutation) does not un-survey a parse that completed, and a recovered parse still fails closed through the owner's own discarded_input check rather than through an absent recording. Pinned by a_cascade_that_surveyed_nothing_never_claims_an_exhaustive_surface (both entry points, with its surveyed control) and a_refusing_stage_does_not_un_survey_a_completed_parse (both entry points, completed and recovered parses).Later Vue rewrite refusals remain in the cascade input space, so compile::push_style_diagnostic applies authored-block arithmetic and consumers receive token-precise ranges. The former whole-block FrameworkRewritten bound does not apply to this planner.
Tests: crates/verter_compiler/tests/cases/style_stage_identity.rs, crates/verter_compiler/src/direct_result_tests/style_planner.rs, crates/verter_css_syntax/tests/cases/dialects.rs, crates/verter_session/tests/cases/style_native_analysis_preprocessor_boundary.rs.
Style blocks with lang="scss", lang="sass", lang="less", or lang="stylus" require caller-owned preprocessing before the plain-CSS module/scoping stages.
Vite mode: the unplugin caches raw authored style content and preserves its authored lang in the style request. Its Vue RuntimeRender Main request selects the typed RuntimeStyleProcessing::AuthoredOnly plan so authored v-bind() facts still drive runtime codegen without claiming modules/scoping before CSS exists. Vite's CSS pipeline performs preprocessing; Vue-specific post-processing then consumes the resulting plain CSS. Host-backed, non-Vite, and direct compiles retain the default Complete plan and therefore remain fail-closed for scoped non-CSS without supplied preprocessing.
Non-Vite mode: preprocessBlock() / preprocessStyle() use Vite's preprocessCSS() when configured. The result returns through the sealed applyBlockOverrides() channel with validated artifact, revision, source-space, and content hashes before the compiler runs the plain-CSS stages.
vue/compiler-sfc is resolved once per plugin instance from the project root and is shared by SFC parsing and bundler-side style post-processing. The relevant owners are packages/unplugin/src/index.ts, packages/unplugin/src/core/preprocessor.ts, crates/verter_session/src/block_content.rs, and crates/verter_session/src/host_resolve/virtual_file_pipeline.rs.
Compatible Vue rewrite stages share one StyleSyntaxIr and one terminal CodeTransform. Later-stage refusals stay in the cascade input space (Authored or admitted Preprocessed) and keep the refused token's span.
authored or admitted-preprocessed bytes
| one StyleSyntaxIr
| v-bind + CSS Modules + scoped plans (authored coordinates)
| one terminal CodeTransform
rewritten CSS, passthrough, or ClearedByRefusalA non-CSS <style module> / <style scoped> request refuses at PlainCssInput without parsing the bytes as CSS.
Isolated CSS-Modules / scoped parse-and-materialize entries are forbidden on the shipped crate: they would be a second CSS parse path. The crate-test comparison instruments (transform_vue_css_modules, transform_vue_scoped_css) exist only under #[cfg(test)]. They must never be compiled through the test-support Cargo feature: that feature is an ordinary library feature and can compile in a non-test build. Compiling either instrument into any non-test library build reintroduces the second CSS parse and the staged coordinate spaces the shared plan removed. Per-stage allocation and source-map benches measure the shared cascade with the unused stage flags off. transform_vue_v_bind remains a real production entry for the v-bind-only surface. Giving either isolated instrument a production caller, or compiling it outside #[cfg(test)], is forbidden; route new work through run_vue_style_cascade.
Each stage's plan is merged into the previous one in authored coordinates (merge_shared_stage_edits): a later edit strictly inside an earlier overwrite targets bytes the earlier stage already replaced and is discarded; a later deletion may subsume earlier work inside the bytes it removes; every other intersection refuses the whole plan rather than materialize a half-ordered rewrite.
Order and pairwise disjointness are one type invariant, PlanDisjointEdits, not a precondition on the merge. The merge's Insert arm inspects only the nearest earlier edit by start offset, and that single candidate is the unique possible container exactly when the stream it scans is disjoint. Carrying that in the type is what stops a future caller from handing the arm an overlapping stream it would silently mis-read rather than refuse. The invariant is established once, over the first stage's own edits (from_planned sorts then checks), and re-established over each merge's composition (from_ordered — later carries no invariant of its own, and two disjoint streams can compose into an overlapping one). There is no terminal disjointness gate on the finished plan: holding the witness IS the proof. build_transform_output keeps its own independent sort-and-check as the backstop for every route, including a test-only staged comparison that builds no shared plan.
That merge is equivalent to running the stages one after the other only while no stage's REPLACEMENT bytes contain something a later stage would have acted on — nothing structurally enforces it, so a_shared_plan_matches_running_the_stages_one_after_the_other compares the two models across the construct corpus. The merge itself catches only the SAME-SPAN case, where two stages emit coincident non-empty overwrites and it refuses. The likelier hazard is DIFFERENT-SPAN and silent: if a stage ever renamed an @keyframes name, the matching animation-name: rewrite a later stage owes lives in another span entirely, the merge sees no intersection and refuses nothing, and the shared model emits an animation reference pointing at the old name where the staged model was correct — wrong-complete output, not a refusal.
This one is a test-and-review mitigation, not a guard, and it is the planner's live residual risk. The mitigation is the corpus sweep (both models over every fixture, with its own non-vacuity legs so a mutual refusal cannot pass for agreement) plus the shared_families == 11 size pin. The pin detects a change in the shared generator family's COUNT, not new planner behavior omitted from that corpus: a stage that starts emitting a new replacement construct while the family list stays at 11 will not trip it. Any change to a stage's replacement vocabulary MUST extend a_shared_plan_matches_running_the_stages_one_after_the_other in the same change. Read the corpus honestly: the shared generators produce TOP-LEVEL rules only, and they cannot be extended cheaply because that same category list is the allocation ceiling's universe of recaptured legacy counts. The nested and overlap-prone shapes — a class rule inside an @media body, @keyframes renaming beside a class hash, a later rewrite landing inside a v-bind() replacement — are therefore carried as that same test's own fixtures.
CompileTarget (bitflags in verter_compiler::compile::types) controls which compilation steps run:
| Flag | Controls | Used By |
|---|---|---|
STYLE | Style codegen (CSS scoping, modules, v-bind) | Bundler |
SCRIPT | Script codegen (macro expansion, binding extraction) | Bundler, Analysis |
TEMPLATE | Template VDOM/Vapor render function codegen | Bundler |
TSX | TSX template codegen for type checking | LSP/IDE |
TSC | TSC declaration file generation | TSC |
TEMPLATE_DATA | Template data extraction (binding occurrences) | LSP, Analysis |
Presets:
| Preset | Flags | Consumer |
|---|---|---|
BUNDLER | STYLE | SCRIPT | TEMPLATE | @verter/unplugin, default |
IDE | TSX | LSP, TSGO |
ANALYSIS | SCRIPT | TEMPLATE_DATA | MCP analysis |
Key API: VerterHost::ensure_compiled(canonical_id, profile) compiles with the given profile's target. Used by LSP and MCP to populate the cache. get_virtual_file() still exists for retrieving specific virtual file outputs.
Empty SFC = valid empty component. A completely block-less .vue file (0 bytes / whitespace / comments only) compiles to a minimal synthetic shell — defineComponent({ __name: "<Filename>" }) + export default — through a dedicated synthetic-script branch (empty_sfc_script_block in compile/helpers.rs) adjacent to the scoped-style/vapor/SSR one, so the host assembles a Main virtual node instead of erroring MissingVirtualNode, and the imported public surface is empty ($props: {}, no slots). Zero-block files also count the whole input as one inter-block gap (remove_inter_block_gaps), so stray top-level comments never leak into generated module output. Template-only SFCs keep their existing no-synthetic-script shape.
The OXC worker and the semantic-lowering surface produce owned TypeExpr IR (and worker-local OXC AST) ONLY — they never emit a session semantic-graph node (SemanticNodeData / SemanticNodeId / HotTypeRef); that crate barrier (verter_semantic never depends on verter_session) is locked from the worker side by the oxc_worker_emits_no_session_graph_node guard. Downstream, an engine-owned, query-free structural lowerer (crates/verter_type_engine/src/structural_carrier_producer/macro_arg_producer.rs, entry lower_type_expr_structural) consumes that owned TypeExpr and emits the dormant semantic-graph carriers (BareRef / ImportType / RawFallback / SyntheticBinding, with a construct-signature type lowered to Signature { kind: Construct } and tuple rest preserved on TupleElement.rest) plus the structural shells, NodeScopeId-rooted, performing NO name / import / type resolution: Foo<Arg> becomes a BareRef whose type_args are structurally lowered (never an InstantiationRef), and keyof / indexed-access / conditional / mapped / typeof stay deferred shells even where the eager path would reduce them. It is intern-only — it makes no host / dispatch query (session_graph_lowerer_makes_no_query) and never materializes a carrier back to TypeExpr during emission (unresolved_carriers_not_materialized_during_emission). It stays dormant / demand-time (never pulled into publish or indexing). Carrier RESOLUTION is a separate demand-time engine — see the type-resolution skill.
| File | Purpose |
|---|---|
crates/verter_compiler/src/compile.rs | Pipeline orchestrator (tokenize -> parse -> style -> script -> template) |
crates/verter_compiler/src/parser/mod.rs | SFC parser: tokenizer events -> root nodes + template AST |
crates/verter_compiler/src/ast/types.rs | AstNode, ElementNode, NodeId, PropFlags |
crates/verter_compiler/src/script/macros.rs | defineProps/Emits/Model/Slots/Expose/Options |
crates/verter_compiler/src/script/process.rs | Script setup processing, companion script merging |
crates/verter_compiler/src/template/code_gen/mod.rs | Template codegen entry point |
crates/verter_compiler/src/template/code_gen/walker.rs | DFS tree walker (shared by VDOM/Vapor backends) |
crates/verter_compiler/src/template/code_gen/binding.rs | BindingResolver (_ctx./$setup. prefix resolution) |
crates/verter_compiler/src/template/code_gen/vdom/ | VDOM render function codegen |
crates/verter_compiler/src/template/code_gen/vapor/ | Vapor mode codegen |
crates/verter_compiler/src/ide/mod.rs | IDE codegen entry: TSX (TS SFCs) or JSX+JSDoc (JS SFCs) |
crates/verter_compiler/src/ide/script.rs | IDE script codegen: TS annotations or JSDoc equivalents |
crates/verter_compiler/src/ide/script_recover.rs | Token scanner for macro binding recovery from broken tails |
crates/verter_compiler/src/ide/condition.rs | v-if/v-else-if/v-else condition chain codegen |
crates/verter_compiler/src/ide/template/mod.rs | IDE template codegen: Vue -> JSX, StrictSlotEntry, emit_strict_slot_checks |
crates/verter_compiler/src/ide/template/directives.rs | IDE: v-if -> ternary, v-for -> .map(), v-show -> style |
crates/verter_compiler/src/ide/template/props.rs | IDE: :prop -> prop={}, @event -> onEvent={} |
crates/verter_compiler/src/ide/template/emit.rs | IDE typed prefixed-expression emit substrate (EmitOp, emit_jsx_binding_value) |
crates/verter_compiler/src/style_planner.rs | Typed Vue stage-1/stage-2 planners over shared style IR |
crates/verter_compiler/src/css/mod.rs | Legacy NAPI/CSS-modules compatibility owner pending provider cutover |
crates/verter_compiler/src/css/modules.rs | CSS Modules: hash class names |
crates/verter_compiler/src/code_transform/code_transform.rs | Chunk-based deferred mutation engine |
crates/verter_compiler/src/code_transform/chunk.rs | Chunk types (Original, Overwritten, Inserted, InsertedMapped) |
crates/verter_compiler/src/code_transform/source_map.rs | Source map generation from chunk positions |
crates/verter_compiler/src/framework_common/generated_chunk.rs | Generated-chunk assembly and multi-source map composition |
© pikax, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 1 other file (references) in .claude/skills/compiler-codegen of pikax/verter.
Open the folder on GitHubat commit e4f9d26
Compiler Codegen next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Compiler Codegen this skillpikax/verter | 112 | — | ~21k | Automated safety check: Pass | MIT | |
| Rust Hygiene Audittsz-org/tsz | 577 | — | ~1.5k | Automated safety check: Pass | Apache-2.0 | |
| New CrateDevolutions/IronRDP | 3.2k | — | ~236 | Automated safety check: Pass | Apache-2.0 | |
| Frb Code Generationfzyzcjy/flutter_rust_bridge | 5.4k | — | ~1.5k | Automated safety check: Pass | MIT | |
| Jinja Codegenxberg-io/alef | 100 | — | ~735 | Automated safety check: Pass | MIT | |
| Rocky Codegenrocky-data/rocky | 304 | — | ~1.9k | Automated safety check: Pass | Apache-2.0 |
tsz-org/tsz
Run a deep DRY + code-hygiene audit of the Rust workspace and turn the findings into verified, deduplicated, hierarchical GitHub tech-debt issues.
Devolutions/IronRDP
Create and integrate a new Rust crate in the IronRDP workspace.
fzyzcjy/flutter_rust_bridge
A skill your agent uses when modifying Rust APIs, codegen, generated examples, or platform scaffolds in flutterrustbridge to select generation commands and preserve source-of-truth and convergence…
xberg-io/alef
Mechanics of alef's Minijinja template system: which templateenv module to call, how to register a template, inline-template rules, and engine settings.
rocky-data/rocky
Rocky CLI JSON-output schema cascade. An agent skill from rocky-data/rocky.
s3s-project/s3s
Change generated code in this repository. An agent skill from s3s-project/s3s.
pikax/verter
In-process backtrace watchdog + LLDB attach wrapper + release-dbg profile for diagnosing hangs and slow paths in Verter benches and binaries on Windows / macOS / Linux.
pikax/verter
Generate copy-pasteable prompts for driving separate Claude Code sessions through refactor, review, or migration work.
pikax/verter
Build dependency chains, rebuild sequences, profiling with MCP, and Analysis MCP server setup for Verter
pikax/verter
CTO/manager-of-managers methodology for autonomous multi-train plans where the user says "you are the MoM/CTO", "orchestrate the whole plan", "drive the migration end-to-end", "manager-of-managers"…
pikax/verter
Rust performance optimization patterns: batch operations, allocation hierarchy, object pooling, CodeTransform API for vertercompiler
pikax/verter
Verter semantic signature kernel — signature records/descriptors, epoch-safe interned storage and retirement, request-pinned borrowed reads, positional matching, call substitution, ordered…
Works with
Categories
Rust compiler pipeline, template codegen (VDOM/IDE), CodeTransform, cached directives, strict slots, IDE error recovery, style preprocessing, CompileTarget, compiler authority/policy/demand/admission. Compiler Codegen is an agent skill from pikax/verter.
Compiler Codegen fits situations like: tasks that involve Project scaffolding.
Run `npx skills add pikax/verter --skill compiler-codegen -a claude-code`. Or copy the skill folder (.claude/skills/compiler-codegen in pikax/verter) into .claude/skills/compiler-codegen in your project. Claude Code loads it when a task matches its description.
Run `npx skills add pikax/verter --skill compiler-codegen -a codex`. Or copy the skill folder (.claude/skills/compiler-codegen in pikax/verter) into .agents/skills/compiler-codegen in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add pikax/verter --skill compiler-codegen -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/compiler-codegen, .gemini/skills/compiler-codegen, .github/skills/compiler-codegen and .opencode/skills/compiler-codegen in your project.
Going by SKILL.md and its folder, Compiler Codegen needs the command-line tools its instructions call (cargo and node).
SKILL.md names 1 domain. As links in the text: github.com. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Compiler Codegen is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 21k tokens (SKILL.md is roughly 85k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 1.5k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Compiler Codegen: Rust Hygiene Audit (tsz-org/tsz, 577 stars), New Crate (Devolutions/IronRDP, 3.2k stars), Frb Code Generation (fzyzcjy/flutter_rust_bridge, 5.4k stars) and Jinja Codegen (xberg-io/alef, 100 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
pikax (a GitHub user) maintains it in pikax/verter, which has 112 GitHub stars. The repository holds 14 skills in this directory. The repository was last updated on October 8, 2026.
Source: pikax/verter on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.