Documentation
¶
Overview ¶
Package preproc extracts TypeScript-ish code blocks from preprocessor-language files (Svelte, Astro) into a "virtual source" buffer that can be fed to a tree-sitter TypeScript/TSX parser. The companion Builder type and per-extractor functions (ExtractSvelte, ExtractAstro) live in the same package.
Import cycle note: this package does NOT import internal/parser. The RemapSymbolLines helper that rewrites parse results lives in internal/parser/preproc_remap.go (parser package) to avoid a cycle.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ExtractAstroWithRefs ¶
func ExtractAstroWithRefs(src []byte) (*VirtualSource, []TemplateRef)
ExtractAstroWithRefs is the primary extractor. It returns both the virtual TypeScript/JS source (frontmatter + <script> blocks) and a list of capitalised JSX-style component tag references found in the template body.
TemplateRefs are emitted for each opening tag whose name starts with an uppercase ASCII letter. Closing tags, HTML-lowercase tags, and namespace-prefixed tags (<astro:fragment>, <svelte:head>) are skipped. Contents of <!-- comments -->, <script>, and <style> blocks are also skipped. Each usage occurrence is recorded (no deduplication — callers may deduplicate per their own requirements).
Resolution of tag names to file paths is NOT performed here — callers should join TemplateRefs against ParseResult.Imports for that purpose.
func ExtractSvelteWithRefs ¶ added in v1.22.0
func ExtractSvelteWithRefs(src []byte) (*VirtualSource, []TemplateRef)
ExtractSvelteWithRefs returns both the virtual TypeScript source (from the component's <script> blocks) and the capitalised JSX-style component tag references found in the template markup — the Svelte analogue of ExtractAstroWithRefs.
scanTemplateRefs is template-syntax agnostic: a Svelte component has no --- frontmatter, so findTemplateStart returns 0 and the whole file is walked. <script>/<style> blocks and HTML comments are skipped; every opening tag whose name starts with an uppercase ASCII letter is recorded. Closing tags and namespace-prefixed tags (<svelte:head>, <svelte:component>, …) are skipped, so Svelte's own special elements do not produce spurious refs.
Resolution of tag names to file paths is NOT performed here — callers should join TemplateRefs against the component's <script> imports for that purpose (see callgraph.ResolveTemplateRefs).
Types ¶
type Builder ¶
type Builder struct {
// contains filtered or unexported fields
}
Builder accumulates virtual code and a line-map from multiple source-block spans.
func NewBuilder ¶
NewBuilder creates a Builder for the given language label.
func (*Builder) AppendBlankLine ¶
func (b *Builder) AppendBlankLine()
AppendBlankLine appends a single "\n" with LineMap entry 0 (padding — maps to no original line). Use between blocks to keep virtual symbols separate.
func (*Builder) AppendBlock ¶
AppendBlock copies fullSrc[blockStart:blockEnd] into the virtual buffer and records line offsets so every virtual line in this block maps to the corresponding original 1-based line number in the full source.
Parameters:
- fullSrc: the entire original source file bytes
- blockStart: byte offset in fullSrc where the content begins (inclusive)
- blockEnd: byte offset in fullSrc where the content ends (exclusive)
Panics if blockEnd < blockStart or offsets are out of range.
func (*Builder) Build ¶
func (b *Builder) Build() *VirtualSource
Build finalises and returns the VirtualSource. The Builder must not be used after Build is called.
type GoTemplateDefine ¶ added in v1.20.0
GoTemplateDefine records a {{define "Name"}} ... {{end}} block found at the top level of a Go html/template file. StartLine and EndLine are 1-based.
func StripGoTemplate ¶ added in v1.20.0
func StripGoTemplate(src []byte) (cleaned []byte, defines []GoTemplateDefine)
StripGoTemplate scans src for Go html/template actions ({{...}}) and returns:
cleaned: a copy of src where every {{...}} action span is replaced with an equal number of space bytes. This preserves line/column positions for any downstream byte-level scanners (htmx attribute walker, etc.).
defines: the list of top-level {{define "Name"}} ... {{end}} blocks, with 1-based start/end line numbers derived from the original src.
Strategy: balanced-brace byte scanner.
- "{{" opens an action; find matching "}}". Account for trim markers "{{-" and "-}}".
- "{{/*" ... "*/}}" is a comment block; scan content without nesting.
- Block actions (define, range, if, with, block) push onto a stack; the matching "{{end}}" pops the stack.
- Top-level "{{define}}" blocks (depth 0 before pushing) are recorded.
Limitations (sufficient for Wave 1):
- Strings inside actions (e.g. `{{ printf "%s" "fmt" }}`) are skipped as part of the action span — their content does not affect brace counting because the outer {{...}} scanner does not recurse into Go expressions.
- Nested pipeline braces `{{ if eq .X "{" }}` are NOT handled; in practice Go template action bodies rarely contain literal braces and the scanner falls back to consuming until the next "}}" at the same depth.
type HtmxRef ¶ added in v1.20.0
type HtmxRef struct {
Method string // "GET", "POST", "PUT", "DELETE", or "PATCH"
URL string // raw attribute value including any {{...}} actions
StartLine int // 1-based line of the opening tag
EndLine int // 1-based line of the attribute value end (same as StartLine for single-line)
EnclosingTemplate string // name from the nearest enclosing {{define "X"}} block; "" if at top level
}
HtmxRef records a single URL-emitting hx-* attribute found in an HTML file. The URL field contains the ORIGINAL bytes from src (Go template actions such as {{.ID}} are preserved for downstream normalisation by the routes layer).
func ScanHtmxRefs ¶ added in v1.20.0
ScanHtmxRefs scans src for htmx URL-emitting attributes (hx-get, hx-post, hx-put, hx-delete, hx-patch) and returns one HtmxRef per occurrence.
Contract:
- src MUST be the original file bytes, NOT the cleaned output of StripGoTemplate. The walker needs raw bytes so that Go template actions ({{.ID}}, {{add .Page 1}}) inside attribute values are preserved for the route normaliser in the routes layer.
- <script> and <style> block contents are skipped (same as scanTemplateRefs).
- HTML comments <!-- ... --> are skipped.
- hx-on:* / hx-on::* attributes are not URL-emitting and are ignored.
- Both double-quoted and single-quoted attribute values are handled.
type TemplateRef ¶
TemplateRef records a single usage of a capitalised JSX-style component tag found in an Astro (or similar) template body.
Name is the bare tag name (e.g. "Breadcrumbs"), Line and Col are 1-based positions in the original source file. Closing tags (</Foo>) are not recorded — only opening tags.
type VirtualSource ¶
type VirtualSource struct {
// Code is the synthetic TypeScript buffer passed to the tree-sitter parser.
Code []byte
// Lang is the effective language label for Symbol.Language ("svelte", "astro", …).
Lang string
// LineMap[i] is the 1-based line number in the ORIGINAL source that corresponds
// to line (i+1) in Code. Length == number of \n in Code + 1. Lines that exist
// only in Code (blank padding between script blocks) map to 0.
LineMap []uint32
}
VirtualSource is TypeScript code extracted from a preprocessor-language file (Svelte, Astro, Vue) together with a per-virtual-line mapping back to the original source line numbers.
func ExtractAstro ¶
func ExtractAstro(src []byte) *VirtualSource
ExtractAstro extracts the Astro frontmatter block (between leading ---) and all <script> blocks (page-level <script> tags). Returns VirtualSource with lang="astro".
Handles:
- Leading frontmatter: starts with optional whitespace then ---\n, ends at next line starting with ---
- Frontmatter may be absent — just <script> blocks processed
- Multiple <script> blocks with blank-line padding
Does NOT handle:
- Template expressions {foo} in the HTML body
- Style tags
- Template literal strings containing <script> (backtick-quoted JS strings with embedded <script> markers may be misidentified as real script blocks)
func ExtractMarkupExprs ¶ added in v1.22.0
func ExtractMarkupExprs(src []byte) *VirtualSource
ExtractMarkupExprs batches the inner content of ALL top-level {expr} ranges in the Astro template body into ONE virtual source (see buildExprVirtualSource), offset-mapped back to original coordinates, so a downstream reparse pays a single tree-sitter invocation per file rather than one per expression.
The returned VirtualSource carries lang="astro"; it is meant to be reparsed with the TSX grammar (a superset that parses the JSX legally embedded in template expressions), not the plain-TypeScript grammar.
func ExtractSvelte ¶
func ExtractSvelte(src []byte) *VirtualSource
ExtractSvelte extracts all <script> and <script context="module"> / <script module> blocks from a Svelte component and returns a VirtualSource with lang="svelte".
Handles:
- <script> … </script>
- <script lang="ts"> … </script>
- <script context="module"> … </script> (Svelte 4)
- <script module> … </script> (Svelte 5)
- Multiple script tags — concatenated with blank-line padding
- CRLF and LF line endings
- Missing closing tag — treat as extending to EOF (best-effort)
Does NOT handle:
- Script content inside HTML comments
- <style> blocks
- Template markup
- Escaped <script> in string literals
- Template literal strings containing <script> (backtick-quoted JS strings with embedded <script> markers may be misidentified as real script blocks)
func ExtractSvelteMarkupExprs ¶ added in v1.22.0
func ExtractSvelteMarkupExprs(src []byte) *VirtualSource
ExtractSvelteMarkupExprs batches every Svelte template EXPR — plain mustaches and sigil-aware block-header expressions — into ONE reparsable virtual source (buildExprVirtualSource), reusing the same batching + line-map core as the Astro path. The result carries lang="svelte" and is reparsed with the TSX grammar by markupExprReparse.
func ExtractVue ¶ added in v1.22.0
func ExtractVue(src []byte) *VirtualSource
ExtractVue extracts <script setup> and <script> blocks from a Vue single-file component (SFC) and returns a VirtualSource with lang="vue".
Handles:
- <script setup> … </script>
- <script setup lang="ts"> … </script>
- <script> … </script>
- <script lang="ts"> … </script>
- Both blocks in the same SFC — concatenated with blank-line padding
- CRLF and LF line endings
- Missing </script> — treats content as extending to EOF (best-effort)
Does NOT handle:
- <template> block expressions or component references
- <style> blocks
- Script content inside HTML comments
- Escaped <script> inside string literals
Both blocks are appended in document (source) order: <script setup> first because appendScriptBlocks starts scanning from offset 0, so the earlier block in the file appears first in the virtual buffer. In practice Vue SFCs place <script setup> before <script>, which matches this behaviour.