diff options
Diffstat (limited to 'internal/builder/components.go')
| -rw-r--r-- | internal/builder/components.go | 97 |
1 files changed, 0 insertions, 97 deletions
diff --git a/internal/builder/components.go b/internal/builder/components.go deleted file mode 100644 index 54a226a..0000000 --- a/internal/builder/components.go +++ /dev/null @@ -1,97 +0,0 @@ -package builder - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - "regexp" - "sort" - "strings" -) - -// componentRe matches <!-- component:tag-name { ...json... } --> -// Props JSON is optional. -var componentRe = regexp.MustCompile( - `<!--\s*component:([a-z][a-z0-9-]*)\s*(\{[^}]*\})?\s*-->`) - -// customElementRe matches opening tags for custom elements (name must contain a hyphen). -var customElementRe = regexp.MustCompile(`<([a-z][a-z0-9]*(?:-[a-z0-9]+)+)[\s/>]`) - -// ProcessComponents replaces HTML comment component directives with custom element tags. -// -// <!-- component:my-counter {"start": 5, "label": "Count"} --> -// → <my-counter start="5" label="Count"></my-counter> -func ProcessComponents(html string) string { - return componentRe.ReplaceAllStringFunc(html, func(match string) string { - subs := componentRe.FindStringSubmatch(match) - if len(subs) < 2 { - return match - } - tagName := subs[1] - attrs := "" - if len(subs) > 2 && subs[2] != "" { - var props map[string]any - if err := json.Unmarshal([]byte(subs[2]), &props); err == nil { - attrs = propsToAttrs(props) - } - } - if attrs != "" { - return fmt.Sprintf(`<%s %s></%s>`, tagName, attrs, tagName) - } - return fmt.Sprintf(`<%s></%s>`, tagName, tagName) - }) -} - -// propsToAttrs converts a JSON props map to an HTML attribute string. -// Keys are emitted in sorted order for deterministic output. -func propsToAttrs(props map[string]any) string { - keys := make([]string, 0, len(props)) - for k := range props { - keys = append(keys, k) - } - sort.Strings(keys) - - var parts []string - for _, k := range keys { - v := props[k] - switch val := v.(type) { - case string: - parts = append(parts, fmt.Sprintf(`%s="%s"`, k, strings.ReplaceAll(val, `"`, `"`))) - case bool: - if val { - parts = append(parts, k) // boolean attribute, no value - } - case float64: - if val == float64(int64(val)) { - parts = append(parts, fmt.Sprintf(`%s="%d"`, k, int64(val))) - } else { - parts = append(parts, fmt.Sprintf(`%s="%g"`, k, val)) - } - default: - // Complex value → JSON-encode into single-quoted attribute. - b, _ := json.Marshal(v) - parts = append(parts, fmt.Sprintf(`%s='%s'`, k, string(b))) - } - } - return strings.Join(parts, " ") -} - -// FindComponentScripts scans HTML for used custom elements and returns -// /components/<name>.js paths for any that exist on disk. -func FindComponentScripts(html, componentsDir string) []string { - matches := customElementRe.FindAllStringSubmatch(html, -1) - seen := make(map[string]bool) - var scripts []string - for _, m := range matches { - if len(m) < 2 || seen[m[1]] { - continue - } - seen[m[1]] = true - jsPath := filepath.Join(componentsDir, m[1]+".js") - if _, err := os.Stat(jsPath); err == nil { - scripts = append(scripts, "/components/"+m[1]+".js") - } - } - return scripts -} |
