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>
This commit is contained in:
114
pkg/server/bundle.go
Normal file
114
pkg/server/bundle.go
Normal file
@@ -0,0 +1,114 @@
|
||||
// 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))
|
||||
}
|
||||
Reference in New Issue
Block a user