// 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 }