package generator import ( "os" "path/filepath" "sort" "strings" ) // InjectedScript represents a script file to be injected type InjectedScript struct { Name string Content string Position string // "head" or "body" Priority int // Lower = earlier injection } // ScriptInjector handles the injectedscripts/ directory type ScriptInjector struct { scriptsDir string scripts []InjectedScript } // NewScriptInjector creates a new script injector func NewScriptInjector(projectDir string) *ScriptInjector { return &ScriptInjector{ scriptsDir: filepath.Join(projectDir, "src", "injectedscripts"), scripts: make([]InjectedScript, 0), } } // LoadScripts loads all scripts from injectedscripts/ directory func (si *ScriptInjector) LoadScripts() error { if _, err := os.Stat(si.scriptsDir); os.IsNotExist(err) { return nil // No injectedscripts directory, that's fine } entries, err := os.ReadDir(si.scriptsDir) if err != nil { return err } for _, entry := range entries { if entry.IsDir() { continue } name := entry.Name() if !strings.HasSuffix(name, ".js") { continue } content, err := os.ReadFile(filepath.Join(si.scriptsDir, name)) if err != nil { return err } script := si.parseScript(name, string(content)) si.scripts = append(si.scripts, script) } // Sort by priority sort.Slice(si.scripts, func(i, j int) bool { return si.scripts[i].Priority < si.scripts[j].Priority }) return nil } // parseScript parses a script file and extracts metadata from comments func (si *ScriptInjector) parseScript(name string, content string) InjectedScript { script := InjectedScript{ Name: strings.TrimSuffix(name, ".js"), Content: content, Position: "head", // default to head Priority: 100, // default priority } // Parse special comments at the top // /* @position: body */ // /* @priority: 10 */ lines := strings.Split(content, "\n") for _, line := range lines { line = strings.TrimSpace(line) if !strings.HasPrefix(line, "/*") && !strings.HasPrefix(line, "//") { break } if strings.Contains(line, "@position:") { if strings.Contains(line, "body") { script.Position = "body" } else { script.Position = "head" } } if strings.Contains(line, "@priority:") { // Extract priority number parts := strings.Split(line, "@priority:") if len(parts) > 1 { priority := strings.TrimSpace(parts[1]) priority = strings.TrimSuffix(priority, "*/") priority = strings.TrimSpace(priority) var p int if _, err := parseIntFromString(priority, &p); err == nil { script.Priority = p } } } } return script } // GetHeadScripts returns scripts for func (si *ScriptInjector) GetHeadScripts() []InjectedScript { var result []InjectedScript for _, s := range si.scripts { if s.Position == "head" { result = append(result, s) } } return result } // GetBodyScripts returns scripts for end of func (si *ScriptInjector) GetBodyScripts() []InjectedScript { var result []InjectedScript for _, s := range si.scripts { if s.Position == "body" { result = append(result, s) } } return result } // InjectIntoHTML injects scripts into HTML content func (si *ScriptInjector) InjectIntoHTML(html string) string { // Inject head scripts before headScripts := si.GetHeadScripts() if len(headScripts) > 0 { var headContent strings.Builder for _, s := range headScripts { headContent.WriteString("\n") headContent.WriteString(s.Content) headContent.WriteString("\n") } html = strings.Replace(html, "", headContent.String()+"", 1) } // Inject body scripts before bodyScripts := si.GetBodyScripts() if len(bodyScripts) > 0 { var bodyContent strings.Builder for _, s := range bodyScripts { bodyContent.WriteString("\n") bodyContent.WriteString(s.Content) bodyContent.WriteString("\n") } html = strings.Replace(html, "", bodyContent.String()+"", 1) } return html } func parseIntFromString(s string, target *int) (int, error) { var n int for _, c := range s { if c >= '0' && c <= '9' { n = n*10 + int(c-'0') } else { break } } *target = n return n, nil }