- 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
165 lines
3.7 KiB
Go
165 lines
3.7 KiB
Go
package generator
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"html/template"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
// HTMLGenerator generates index.html only in dist folder
|
|
type HTMLGenerator struct {
|
|
projectDir string
|
|
distDir string
|
|
injector *ScriptInjector
|
|
encryptionKey []byte
|
|
}
|
|
|
|
// NewHTMLGenerator creates a new HTML generator
|
|
func NewHTMLGenerator(projectDir, distDir string) *HTMLGenerator {
|
|
return &HTMLGenerator{
|
|
projectDir: projectDir,
|
|
distDir: distDir,
|
|
injector: NewScriptInjector(projectDir),
|
|
}
|
|
}
|
|
|
|
// Generate creates the index.html in dist folder
|
|
func (hg *HTMLGenerator) Generate(config *BuildConfig) error {
|
|
// Load injected scripts
|
|
if err := hg.injector.LoadScripts(); err != nil {
|
|
return fmt.Errorf("failed to load injected scripts: %w", err)
|
|
}
|
|
|
|
// Generate encryption key at build time
|
|
hg.encryptionKey = make([]byte, 32)
|
|
if _, err := rand.Read(hg.encryptionKey); err != nil {
|
|
return fmt.Errorf("failed to generate encryption key: %w", err)
|
|
}
|
|
|
|
// Create dist directory
|
|
if err := os.MkdirAll(hg.distDir, 0755); err != nil {
|
|
return fmt.Errorf("failed to create dist directory: %w", err)
|
|
}
|
|
|
|
// Generate HTML
|
|
html, err := hg.buildHTML(config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Inject scripts
|
|
html = hg.injector.InjectIntoHTML(html)
|
|
|
|
// Write to dist
|
|
indexPath := filepath.Join(hg.distDir, "index.html")
|
|
if err := os.WriteFile(indexPath, []byte(html), 0644); err != nil {
|
|
return fmt.Errorf("failed to write index.html: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// BuildConfig holds build configuration
|
|
type BuildConfig struct {
|
|
Title string
|
|
Description string
|
|
BaseURL string
|
|
APIBaseURL string
|
|
DevMode bool
|
|
Assets AssetManifest
|
|
}
|
|
|
|
// AssetManifest tracks generated assets
|
|
type AssetManifest struct {
|
|
JS []string
|
|
CSS []string
|
|
}
|
|
|
|
func (hg *HTMLGenerator) buildHTML(config *BuildConfig) (string, error) {
|
|
tmpl := `<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<meta name="description" content="{{.Description}}">
|
|
<title>{{.Title}}</title>
|
|
<base href="{{.BaseURL}}">
|
|
|
|
{{range .Assets.CSS}}
|
|
<link rel="stylesheet" href="{{.}}">
|
|
{{end}}
|
|
|
|
<script>
|
|
// Strata config (build-time injected)
|
|
window.__STRATA_CONFIG__ = {
|
|
apiBaseUrl: "{{.APIBaseURL}}",
|
|
devMode: {{.DevMode}},
|
|
encryptionKey: [{{.EncryptionKeyArray}}]
|
|
};
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<div id="app"></div>
|
|
|
|
<!-- Strata Runtime -->
|
|
<script type="module" src="/assets/js/runtime.js"></script>
|
|
|
|
{{range .Assets.JS}}
|
|
<script type="module" src="{{.}}"></script>
|
|
{{end}}
|
|
</body>
|
|
</html>`
|
|
|
|
// Convert encryption key to array format for JS
|
|
keyArray := make([]string, len(hg.encryptionKey))
|
|
for i, b := range hg.encryptionKey {
|
|
keyArray[i] = fmt.Sprintf("%d", b)
|
|
}
|
|
keyArrayStr := ""
|
|
for i, k := range keyArray {
|
|
if i > 0 {
|
|
keyArrayStr += ","
|
|
}
|
|
keyArrayStr += k
|
|
}
|
|
|
|
data := struct {
|
|
Title string
|
|
Description string
|
|
BaseURL string
|
|
APIBaseURL string
|
|
DevMode bool
|
|
Assets AssetManifest
|
|
EncryptionKeyArray string
|
|
}{
|
|
Title: config.Title,
|
|
Description: config.Description,
|
|
BaseURL: config.BaseURL,
|
|
APIBaseURL: config.APIBaseURL,
|
|
DevMode: config.DevMode,
|
|
Assets: config.Assets,
|
|
EncryptionKeyArray: keyArrayStr,
|
|
}
|
|
|
|
t, err := template.New("html").Parse(tmpl)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
if err := t.Execute(&buf, data); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return buf.String(), nil
|
|
}
|
|
|
|
// GetEncryptionKeyHex returns the encryption key as hex for debugging
|
|
func (hg *HTMLGenerator) GetEncryptionKeyHex() string {
|
|
return hex.EncodeToString(hg.encryptionKey)
|
|
}
|