- 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>
115 lines
3.3 KiB
Go
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))
|
|
}
|