git commit -m "feat: initial release of Strata framework v0.1.0
- Static compiler with STRC pattern (Static Template Resolution with
Compartmentalized Layers)
- Template syntax: { } interpolation, { s-for }, { s-if/s-elif/s-else
}
- File types: .strata, .compiler.sts, .service.sts, .api.sts, .sts,
.scss
- CLI tools: strata dev, strata build, strata g (generators)
- create-strata scaffolding CLI with Pokemon API example
- Dev server with WebSocket HMR (Hot Module Replacement)
- Documentation: README, ARCHITECTURE, CHANGELOG, CONTRIBUTING,
LICENSE
This commit is contained in:
181
compiler/internal/generator/injector.go
Normal file
181
compiler/internal/generator/injector.go
Normal file
@@ -0,0 +1,181 @@
|
||||
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 <head>
|
||||
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 <body>
|
||||
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 </head>
|
||||
headScripts := si.GetHeadScripts()
|
||||
if len(headScripts) > 0 {
|
||||
var headContent strings.Builder
|
||||
for _, s := range headScripts {
|
||||
headContent.WriteString("<!-- Injected: ")
|
||||
headContent.WriteString(s.Name)
|
||||
headContent.WriteString(" -->\n")
|
||||
headContent.WriteString(s.Content)
|
||||
headContent.WriteString("\n")
|
||||
}
|
||||
html = strings.Replace(html, "</head>", headContent.String()+"</head>", 1)
|
||||
}
|
||||
|
||||
// Inject body scripts before </body>
|
||||
bodyScripts := si.GetBodyScripts()
|
||||
if len(bodyScripts) > 0 {
|
||||
var bodyContent strings.Builder
|
||||
for _, s := range bodyScripts {
|
||||
bodyContent.WriteString("<!-- Injected: ")
|
||||
bodyContent.WriteString(s.Name)
|
||||
bodyContent.WriteString(" -->\n")
|
||||
bodyContent.WriteString(s.Content)
|
||||
bodyContent.WriteString("\n")
|
||||
}
|
||||
html = strings.Replace(html, "</body>", bodyContent.String()+"</body>", 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
|
||||
}
|
||||
Reference in New Issue
Block a user