Files
goodie/pkg/server/bundle.go
Marc 8741282322 Eliminate Python dependency: embed frontend assets in odoo-go
- Copy all OWL frontend assets (JS/CSS/XML/fonts/images) into frontend/
  directory (2925 files, 43MB) — no more runtime reads from Python Odoo
- Replace OdooAddonsPath config with FrontendDir pointing to local frontend/
- Rewire bundle.go, static.go, templates.go, webclient.go to read from
  frontend/ instead of external Python Odoo addons directory
- Auto-detect frontend/ and build/ dirs relative to binary in main.go
- Delete obsolete Python helper scripts (tools/*.py)

The Go server is now fully self-contained: single binary + frontend/ folder.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 23:09:12 +02:00

115 lines
3.3 KiB
Go

// Package server — JS bundle builder.
//
// Reads original Odoo JS source files from the addon directories, transpiles
// ES modules to odoo.define() format using the Go transpiler, and
// concatenates everything into a single JS bundle served at startup.
//
// This replaces the separate `go run ./cmd/transpile` build step — no
// pre-transpiled build/js directory is needed.
package server
import (
"log"
"net/http"
"os"
"path/filepath"
"strings"
"time"
"odoo-go/pkg/tools"
)
// buildJSBundle reads all JS files listed in assets_js.txt from the original
// Odoo source directories, transpiles ES modules on-the-fly, and returns the
// concatenated bundle as a single string.
//
// xmlTemplateBundle is the compiled XML templates JS (from compileXMLTemplates),
// injected where xml_templates_bundle.js appears in the asset list.
//
// The first entry (module_loader.js) is excluded from the bundle because it
// must load before the bundle via its own <script> tag.
//
// The bundle is built once at server startup and cached in memory.
func buildJSBundle(config *tools.Config, xmlTemplateBundle string) string {
start := time.Now()
var buf strings.Builder
transpiled := 0
copied := 0
skipped := 0
for _, src := range jsFiles {
if strings.HasSuffix(src, ".scss") {
continue
}
// module_loader.js is served via its own <script> tag before the
// bundle (it defines odoo.define/odoo.loader). Skip it here.
if src == "/web/static/src/module_loader.js" {
continue
}
// The XML templates bundle is generated in-memory by
// compileXMLTemplates. Inject it where the placeholder appears
// in the asset list instead of reading a file from disk.
if src == "/web/static/src/xml_templates_bundle.js" {
if xmlTemplateBundle != "" {
buf.WriteString(";\n")
buf.WriteString(xmlTemplateBundle)
buf.WriteString("\n")
}
continue
}
// Read source from frontend directory
content := ""
rel := strings.TrimPrefix(src, "/")
if config.FrontendDir != "" {
fullPath := filepath.Join(config.FrontendDir, rel)
if data, err := os.ReadFile(fullPath); err == nil {
content = string(data)
}
}
// Also try build dir as fallback (for pre-compiled assets)
if content == "" && config.BuildDir != "" {
buildPath := filepath.Join(config.BuildDir, rel)
if data, err := os.ReadFile(buildPath); err == nil {
content = string(data)
}
}
if content == "" {
skipped++
continue
}
// Transpile ES modules to odoo.define() format
if IsOdooModule(src, content) {
content = TranspileJS(src, content)
transpiled++
} else {
copied++
}
buf.WriteString(";\n")
buf.WriteString(content)
buf.WriteString("\n")
}
log.Printf("bundle: %d transpiled + %d copied + %d skipped in %s (%d KB)",
transpiled, copied, skipped, time.Since(start).Round(time.Millisecond), buf.Len()/1024)
return buf.String()
}
// handleJSBundle serves the concatenated JS bundle from memory.
// The bundle is built once at startup and cached in s.jsBundle.
func (s *Server) handleJSBundle(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet && r.Method != http.MethodHead {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
w.Header().Set("Content-Type", "application/javascript; charset=utf-8")
w.Header().Set("Cache-Control", "public, max-age=3600")
w.Write([]byte(s.jsBundle))
}