- 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>
170 lines
4.1 KiB
Go
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
|
|
}
|