#!/usr/bin/env python3 """ Transpile Odoo ES module JS files to odoo.define() format. Uses Odoo's built-in js_transpiler.py to convert: import { X } from "@web/core/foo" → odoo.define("@web/...", [...], function(require) {...}) Usage: python3 tools/transpile_assets.py Example: python3 tools/transpile_assets.py ../odoo build/js """ import os import sys import shutil def main(): if len(sys.argv) < 3: print("Usage: transpile_assets.py ") sys.exit(1) odoo_path = sys.argv[1] output_dir = sys.argv[2] # Mock odoo.tools.misc.OrderedSet before importing transpiler import types odoo_mock = types.ModuleType('odoo') odoo_tools_mock = types.ModuleType('odoo.tools') odoo_misc_mock = types.ModuleType('odoo.tools.misc') class OrderedSet(dict): """Minimal OrderedSet replacement using dict keys.""" def __init__(self, iterable=None): super().__init__() if iterable: for item in iterable: self[item] = None def add(self, item): self[item] = None def __iter__(self): return iter(self.keys()) def __contains__(self, item): return dict.__contains__(self, item) odoo_misc_mock.OrderedSet = OrderedSet odoo_tools_mock.misc = odoo_misc_mock odoo_mock.tools = odoo_tools_mock sys.modules['odoo'] = odoo_mock sys.modules['odoo.tools'] = odoo_tools_mock sys.modules['odoo.tools.misc'] = odoo_misc_mock # Import the transpiler directly import importlib.util transpiler_path = os.path.join(odoo_path, 'odoo', 'tools', 'js_transpiler.py') spec = importlib.util.spec_from_file_location("js_transpiler", transpiler_path) mod = importlib.util.module_from_spec(spec) spec.loader.exec_module(mod) transpile_javascript = mod.transpile_javascript is_odoo_module = mod.is_odoo_module # Read the JS bundle file list bundle_file = os.path.join(os.path.dirname(__file__), '..', 'pkg', 'server', 'assets_js.txt') with open(bundle_file) as f: js_files = [line.strip() for line in f if line.strip()] addons_dirs = [ os.path.join(odoo_path, 'addons'), os.path.join(odoo_path, 'odoo', 'addons'), ] transpiled = 0 copied = 0 errors = 0 for url_path in js_files: # url_path is like /web/static/src/env.js # Find the real file rel_path = url_path.lstrip('/') source_file = None for addons_dir in addons_dirs: candidate = os.path.join(addons_dir, rel_path) if os.path.isfile(candidate): source_file = candidate break if not source_file: print(f" SKIP (not found): {url_path}") continue # Output path out_file = os.path.join(output_dir, rel_path) os.makedirs(os.path.dirname(out_file), exist_ok=True) # Read source with open(source_file, 'r', encoding='utf-8', errors='replace') as f: content = f.read() # Check if it's an odoo module (has import/export) if is_odoo_module(url_path, content): try: result = transpile_javascript(url_path, content) with open(out_file, 'w', encoding='utf-8') as f: f.write(result) transpiled += 1 except Exception as e: print(f" ERROR transpiling {url_path}: {e}") # Copy as-is on error shutil.copy2(source_file, out_file) errors += 1 else: # Not an ES module — copy as-is (e.g., libraries, legacy code) shutil.copy2(source_file, out_file) copied += 1 print(f"\nDone: {transpiled} transpiled, {copied} copied as-is, {errors} errors") print(f"Output: {output_dir}") if __name__ == '__main__': main()