chore: import upstream snapshot with attribution
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

This commit is contained in:
wehub-resource-sync
2026-07-13 12:33:42 +08:00
commit a06f331eb8
3186 changed files with 689843 additions and 0 deletions
+129
View File
@@ -0,0 +1,129 @@
package languages
import (
"regexp"
"strings"
"github.com/zzet/gortex/internal/graph"
"github.com/zzet/gortex/internal/parser"
)
// CoffeeScript is indent-delimited and surfaces functions as
// `name = (args) ->` (thin arrow) or `name = (args) =>` (bound/fat
// arrow). `class Name [extends Parent]` defines a class. Top-level
// `name = value` at column 0 is a plain variable binding. Module
// loading uses CommonJS-style `require 'X'` or `require "X"`.
var (
coffeeFuncRe = regexp.MustCompile(`(?m)^\s*(\w+)\s*=\s*(?:\([^)]*\)\s*)?[-=]>`)
coffeeClassRe = regexp.MustCompile(`(?m)^\s*class\s+(\w+)`)
coffeeVarRe = regexp.MustCompile(`(?m)^(\w+)\s*=\s*[^=\->]`)
coffeeImportRe = regexp.MustCompile(`(?m)^\s*\w*\s*=?\s*require\s*\(?\s*['"]([^'"]+)['"]`)
coffeeCallRe = regexp.MustCompile(`\b([a-zA-Z_]\w*)\s*\(`)
)
// CoffeeScriptExtractor extracts CoffeeScript source using regex.
type CoffeeScriptExtractor struct{}
func NewCoffeeScriptExtractor() *CoffeeScriptExtractor { return &CoffeeScriptExtractor{} }
func (e *CoffeeScriptExtractor) Language() string { return "coffeescript" }
func (e *CoffeeScriptExtractor) Extensions() []string { return []string{".coffee"} }
func (e *CoffeeScriptExtractor) Extract(filePath string, src []byte) (*parser.ExtractionResult, error) {
lines := strings.Split(string(src), "\n")
result := &parser.ExtractionResult{}
fileNode := &graph.Node{
ID: filePath, Kind: graph.KindFile, Name: filePath,
FilePath: filePath, StartLine: 1, EndLine: len(lines),
Language: "coffeescript",
}
result.Nodes = append(result.Nodes, fileNode)
seen := make(map[string]bool)
add := func(name string, kind graph.NodeKind, start, end int) {
if name == "" || isCoffeeKeyword(name) {
return
}
id := filePath + "::" + name
if seen[id] {
return
}
seen[id] = true
result.Nodes = append(result.Nodes, &graph.Node{
ID: id, Kind: kind, Name: name,
FilePath: filePath, StartLine: start, EndLine: end,
Language: "coffeescript",
})
result.Edges = append(result.Edges, &graph.Edge{
From: fileNode.ID, To: id, Kind: graph.EdgeDefines,
FilePath: filePath, Line: start,
})
}
// Track which lines are functions so we don't re-emit them as bare
// variables via the fall-through `name = value` pattern.
funcLines := make(map[int]bool)
for _, m := range coffeeFuncRe.FindAllSubmatchIndex(src, -1) {
name := string(src[m[2]:m[3]])
line := lineAt(src, m[0])
funcLines[line] = true
add(name, graph.KindFunction, line, findIndentedBlockEnd(lines, line))
}
for _, m := range coffeeClassRe.FindAllSubmatchIndex(src, -1) {
name := string(src[m[2]:m[3]])
line := lineAt(src, m[0])
funcLines[line] = true
add(name, graph.KindType, line, findIndentedBlockEnd(lines, line))
}
for _, m := range coffeeVarRe.FindAllSubmatchIndex(src, -1) {
name := string(src[m[2]:m[3]])
line := lineAt(src, m[0])
if funcLines[line] {
continue
}
add(name, graph.KindVariable, line, line)
}
for _, m := range coffeeImportRe.FindAllSubmatchIndex(src, -1) {
mod := string(src[m[2]:m[3]])
line := lineAt(src, m[0])
result.Edges = append(result.Edges, &graph.Edge{
From: fileNode.ID, To: "unresolved::import::" + mod,
Kind: graph.EdgeImports, FilePath: filePath, Line: line,
})
}
funcRanges := buildFuncRanges(result)
for _, m := range coffeeCallRe.FindAllSubmatchIndex(src, -1) {
name := string(src[m[2]:m[3]])
if isCoffeeKeyword(name) {
continue
}
line := lineAt(src, m[0])
callerID := findEnclosingFunc(funcRanges, line)
if callerID == "" || strings.HasSuffix(callerID, "::"+name) {
continue
}
result.Edges = append(result.Edges, &graph.Edge{
From: callerID, To: "unresolved::" + name,
Kind: graph.EdgeCalls, FilePath: filePath, Line: line,
})
}
return result, nil
}
func isCoffeeKeyword(s string) bool {
switch s {
case "if", "else", "unless", "then", "for", "while", "until", "loop",
"switch", "when", "break", "continue", "return", "class", "extends",
"new", "this", "super", "null", "undefined", "true", "false",
"and", "or", "not", "is", "isnt", "in", "of", "by", "do", "yes", "no",
"on", "off", "try", "catch", "finally", "throw", "require":
return true
}
return false
}
var _ parser.Extractor = (*CoffeeScriptExtractor)(nil)