comparison libervia/web/server/tasks/implicit/task_js_modules.py @ 1570:038d4bfdd967

server (tasks/JS modules): add new way to generate modules + support of CSS files
author Goffi <goffi@goffi.org>
date Wed, 22 Nov 2023 16:31:32 +0100
parents eb00d593801d
children d07838fc9d99
comparison
equal deleted inserted replaced
1569:54ba0f74a488 1570:038d4bfdd967
42 for module_name, module_data in brython_map.items(): 42 for module_name, module_data in brython_map.items():
43 log.debug(f"generating mapping for {module_name}") 43 log.debug(f"generating mapping for {module_name}")
44 if ' ' in module_name: 44 if ' ' in module_name:
45 raise ValueError( 45 raise ValueError(
46 f"module {module_name!r} has space(s), it must not!") 46 f"module {module_name!r} has space(s), it must not!")
47 module_path = js_modules_path / f"{module_name}.py" 47 module_python_name = module_name.replace(".", "_").replace("-", "_")
48 if module_python_name != module_name:
49 log.info("{module_python_name!r} will be used as python module name")
50 module_path = js_modules_path / f"{module_python_name}.py"
48 if isinstance(module_data, str): 51 if isinstance(module_data, str):
49 module_data = {'path': module_data} 52 module_data = {'path': [module_data]}
50 try: 53 try:
51 js_path = module_data.pop('path') 54 js_paths = module_data.pop('path')
52 except KeyError: 55 except KeyError:
53 raise ValueError( 56 raise ValueError(
54 f'module data for {module_name} must have a "path" key') 57 f'module data for {module_name} must have a "path" key')
55 module_data['path'] = Path('node_modules') / js_path.strip(' /') 58 if isinstance(js_paths, str):
56 export = module_data.get('export') or [module_name] 59 js_paths = [js_paths]
57 export_objects = '\n'.join(f'{e} = window.{e}' for e in export) 60 module_data['path'] = [
58 extra_kwargs = {"build_dir": C.BUILD_DIR} 61 Path('node_modules') / js_path.strip(' /') for js_path in js_paths
62 ]
63 to_export = module_data.get("export", [module_name])
64 if isinstance(to_export, str):
65 to_export = to_export.split(",")
66 to_export = [e.strip() for e in to_export]
67
68 init_code = []
69
70 # CSS if any
71 css_to_load = module_data.get("css")
72 if css_to_load:
73 if isinstance(css_to_load, str):
74 css_to_load = [css_to_load]
75
76 add_styles_lines = []
77 for css_path in css_to_load:
78 normalized_css_path = Path("node_modules") / css_path.strip(" /")
79 css_href = Path("/").joinpath(C.BUILD_DIR, normalized_css_path)
80 style_tag = (
81 f'<link rel="stylesheet" type="text/css" href="{css_href}">'
82 )
83 add_style_code = (
84 'document.head.insertAdjacentHTML('
85 '"beforeend", '
86 f"'{style_tag}')"
87 )
88 add_styles_lines.append(add_style_code)
89
90 add_styles = "\n".join(add_styles_lines)
91 else:
92 add_styles = ""
59 93
60 with module_path.open('w') as f: 94 with module_path.open('w') as f:
61 f.write(f"""\ 95 browser_imports = ["aio", "document"]
62 #!/usr/bin/env python3 96 modules_import = []
63 from browser import window, load 97 script_paths = [
64 {module_data.get('extra_import', '')} 98 str(Path('/').joinpath(C.BUILD_DIR, path))
99 for path in module_data["path"]
100 ]
101 import_type = module_data.get('import_type', "load")
102 if import_type == 'module':
103 modules_import.append('javascript')
104 modules_import.append('proxy')
105 callback_function_name = f"on_{module_python_name}_loaded"
106 declare_obj = "\n".join(f"{e} = proxy.JSProxy()" for e in to_export)
107 export_str = "\n ".join(f"{e}.js_module=module.{e}" for e in to_export)
108 load_js_libraries = (
109 f"\n{declare_obj}\n\n"
110 f"def {callback_function_name}(module):\n"
111 f" {export_str}\n"
112 f" loaded.set_result(True)\n\n"
113 f"javascript.import_modules({script_paths}, {callback_function_name})\n"
114 )
115 elif import_type == 'script':
116 browser_imports.append("window")
117 script_tags = [
118 f"<script src='{src}'></script>" for src in script_paths
119 ]
120 load_js_libraries = "\n".join(
121 f'document.head.insertAdjacentHTML("beforeend", "{tag}")'
122 for tag in script_tags
123 )
124 init_code.append('\n'.join(f'{e} = window.{e}' for e in to_export))
125 init_code.append("loaded.set_result(True)")
126 elif import_type == "load":
127 browser_imports.append("load")
128 browser_imports.append("window")
129 load_calls = [f'load("{src}")' for src in script_paths]
130 load_js_libraries = "\n".join(load_calls)
131 init_code.append('\n'.join(f'{e} = window.{e}' for e in to_export))
132 init_code.append("loaded.set_result(True)")
133 else:
134 raise ValueError("Invalid import type: {import_type!r}")
65 135
66 load("{Path('/').joinpath(C.BUILD_DIR, module_data['path'])}") 136 extra_init = module_data.get("extra_init")
67 {export_objects} 137 if extra_init is not None:
68 {module_data.get('extra_init', '').format(**extra_kwargs)} 138 if not isinstance(extra_init, str):
69 """) 139 raise ValueError(f"Invalid extra_init: {extra_init!r}")
140 init_code.append(
141 extra_init.format(
142 build_dir = C.BUILD_DIR
143 )
144 )
145
146 init_code_str = "\n".join(init_code)
147 imports = [f"from browser import {', '.join(browser_imports)}"]
148 for module in modules_import:
149 imports.append(f"import {module}")
150 imports_str = "\n".join(imports)
151
152 f.write(
153 "#!/usr/bin/env python3\n"
154 f"{imports_str}\n\n"
155 f"loaded = aio.Future()\n"
156 f"{add_styles}\n"
157 f"{load_js_libraries}\n"
158 f"{init_code_str}"
159 )