Files
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

104 lines
3.3 KiB
Go

package languages
import (
"regexp"
"strconv"
"strings"
"github.com/zzet/gortex/internal/parser"
)
var (
markupScriptRe = regexp.MustCompile(`(?is)<script\b([^>]*)>(.*?)</script>`)
markupLangAttrRe = regexp.MustCompile(`(?i)\blang\s*=\s*["']([a-z]+)["']`)
)
// scriptTagIsTypeScript reports whether a <script> tag's attribute string
// declares a TypeScript dialect.
func scriptTagIsTypeScript(attrs []byte) bool {
m := markupLangAttrRe.FindSubmatch(attrs)
if m == nil {
return false
}
switch strings.ToLower(string(m[1])) {
case "ts", "tsx", "typescript":
return true
}
return false
}
// carveAndDelegateScripts runs every <script>/<script setup> block in src through
// the TypeScript or JavaScript extractor (per its lang attribute) and merges the
// result rebased into host-file coordinates. It is the shared carving loop behind
// the Svelte and Astro extractors.
func carveAndDelegateScripts(src []byte, filePath, fileID, language string, ts, js parser.Extractor, result *parser.ExtractionResult) {
for _, m := range markupScriptRe.FindAllSubmatchIndex(src, -1) {
contentStart, contentEnd := m[4], m[5]
if contentStart < 0 {
continue
}
delegate := js
if scriptTagIsTypeScript(src[m[2]:m[3]]) {
delegate = ts
}
lineOffset := strings.Count(string(src[:contentStart]), "\n")
delegateInlineScriptSlice(delegate, src[contentStart:contentEnd], lineOffset, filePath, fileID, language, result)
}
}
// delegateInlineScriptSlice runs delegate over a carved inline-script slice and
// merges its symbols/edges into result, rebased into the host file's coordinate
// space. content is the raw script body; lineOffset is the 0-based line in the
// host file where the slice begins. Every delegated node/edge line is shifted by
// lineOffset, the synthetic file node is dropped, file-defines edges are
// repointed at fileID, nodes are relabeled to the host filePath (and language,
// when non-empty), and Meta["inline_script"]=true is stamped so downstream
// passes can tell a carved symbol from a natively-parsed one.
//
// It is the shared spine behind every markup extractor that embeds another
// language (HTML/Vue/Svelte/Astro <script>, Razor @code), so the error-prone
// offset-rebase math lives — and is tested — in exactly one place.
func delegateInlineScriptSlice(delegate parser.Extractor, content []byte, lineOffset int, filePath, fileID, language string, result *parser.ExtractionResult) {
if delegate == nil || result == nil || strings.TrimSpace(string(content)) == "" {
return
}
virtual := filePath + "#script:" + strconv.Itoa(lineOffset+1)
sub, err := delegate.Extract(virtual, content)
if err != nil || sub == nil {
return
}
for _, n := range sub.Nodes {
if n == nil || n.ID == virtual { // drop the synthetic file node
continue
}
n.FilePath = filePath
if language != "" {
n.Language = language
}
if n.StartLine > 0 {
n.StartLine += lineOffset
}
if n.EndLine > 0 {
n.EndLine += lineOffset
}
if n.Meta == nil {
n.Meta = map[string]any{}
}
n.Meta["inline_script"] = true
result.Nodes = append(result.Nodes, n)
}
for _, ed := range sub.Edges {
if ed == nil {
continue
}
if ed.From == virtual { // "file defines symbol" → the host file owns it
ed.From = fileID
}
ed.FilePath = filePath
if ed.Line > 0 {
ed.Line += lineOffset
}
result.Edges = append(result.Edges, ed)
}
}