Files
goodie/cmd/transpile/main.go
Marc 9c444061fd Backend improvements: views, fields_get, session, RPC stubs
- Improved auto-generated list/form/search views with priority fields,
  two-column form layout, statusbar widget, notebook for O2M fields
- Enhanced fields_get with currency_field, compute, related metadata
- Fixed session handling: handleSessionInfo/handleSessionCheck use real
  session from cookie instead of hardcoded values
- Added read_progress_bar and activity_format RPC stubs
- Improved bootstrap translations with lang_parameters
- Added "contacts" to session modules list

Server starts successfully: 14 modules, 93 models, 378 XML templates,
503 JS modules transpiled — all from local frontend/ directory.

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

170 lines
4.1 KiB
Go

// Command transpile converts Odoo ES module JS files to odoo.define() format.
// This is a pure Go replacement for tools/transpile_assets.py, eliminating
// the Python dependency entirely.
//
// Usage:
//
// go run ./cmd/transpile -source ../odoo -output build/js
// go run ./cmd/transpile -source ../odoo -output build/js -list pkg/server/assets_js.txt
package main
import (
"bufio"
"flag"
"fmt"
"io"
"log"
"os"
"path/filepath"
"strings"
"odoo-go/pkg/server"
)
func main() {
log.SetFlags(0)
sourceDir := flag.String("source", "", "Path to Odoo source tree (e.g. ../odoo)")
outputDir := flag.String("output", "build/js", "Output directory for transpiled JS files")
listFile := flag.String("list", "pkg/server/assets_js.txt", "Path to the JS asset list file")
flag.Parse()
if *sourceDir == "" {
log.Fatal("error: -source flag is required (path to Odoo source tree)")
}
// Resolve paths
absSource, err := filepath.Abs(*sourceDir)
if err != nil {
log.Fatalf("error: invalid source path: %v", err)
}
absOutput, err := filepath.Abs(*outputDir)
if err != nil {
log.Fatalf("error: invalid output path: %v", err)
}
// Read asset list
jsFiles, err := readAssetList(*listFile)
if err != nil {
log.Fatalf("error: reading asset list %s: %v", *listFile, err)
}
log.Printf("transpile: %d JS files from %s", len(jsFiles), *listFile)
log.Printf("transpile: source: %s", absSource)
log.Printf("transpile: output: %s", absOutput)
// Addon directories to search (in order of priority)
addonsDirs := []string{
filepath.Join(absSource, "addons"),
filepath.Join(absSource, "odoo", "addons"),
}
var transpiled, copied, skipped, errors int
for _, urlPath := range jsFiles {
// Find the source file in one of the addons directories
relPath := strings.TrimPrefix(urlPath, "/")
sourceFile := findFile(addonsDirs, relPath)
if sourceFile == "" {
log.Printf(" SKIP (not found): %s", urlPath)
skipped++
continue
}
// Read source content
content, err := os.ReadFile(sourceFile)
if err != nil {
log.Printf(" ERROR reading %s: %v", sourceFile, err)
errors++
continue
}
// Determine output path
outFile := filepath.Join(absOutput, relPath)
// Ensure output directory exists
if err := os.MkdirAll(filepath.Dir(outFile), 0o755); err != nil {
log.Printf(" ERROR mkdir %s: %v", filepath.Dir(outFile), err)
errors++
continue
}
src := string(content)
if server.IsOdooModule(urlPath, src) {
// Transpile ES module to odoo.define() format
result := server.TranspileJS(urlPath, src)
if err := os.WriteFile(outFile, []byte(result), 0o644); err != nil {
log.Printf(" ERROR writing %s: %v", outFile, err)
errors++
continue
}
transpiled++
} else {
// Not an ES module: copy as-is
if err := copyFile(sourceFile, outFile); err != nil {
log.Printf(" ERROR copying %s: %v", urlPath, err)
errors++
continue
}
copied++
}
}
fmt.Printf("\nDone: %d transpiled, %d copied as-is, %d skipped, %d errors\n",
transpiled, copied, skipped, errors)
fmt.Printf("Output: %s\n", absOutput)
if errors > 0 {
os.Exit(1)
}
}
// readAssetList reads a newline-separated list of JS file paths.
func readAssetList(path string) ([]string, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
var files []string
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line != "" && !strings.HasPrefix(line, "#") {
files = append(files, line)
}
}
return files, scanner.Err()
}
// findFile searches for a relative path in the given directories.
func findFile(dirs []string, relPath string) string {
for _, dir := range dirs {
candidate := filepath.Join(dir, relPath)
if info, err := os.Stat(candidate); err == nil && !info.IsDir() {
return candidate
}
}
return ""
}
// copyFile copies a file from src to dst.
func copyFile(src, dst string) error {
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, in)
return err
}