Mercurial > libervia-web
view libervia/web/server/tasks/implicit/task_js_modules.py @ 1609:f3305832f3f6
server (resources): handle external Libervia apps by adding them to menu without using a Proxy:
When an external Libervia app is used, a link is added to the menu, and clicking on it
will open the app in an blank page instead of embedding it with a Proxy.
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 31 May 2024 11:10:04 +0200 |
parents | d07838fc9d99 |
children |
line wrap: on
line source
#!/ur/bin/env python3 import json from pathlib import Path from libervia.backend.core.i18n import _ from libervia.backend.core.log import getLogger from libervia.backend.core import exceptions from libervia.web.server.constants import Const as C from libervia.web.server.tasks import task log = getLogger(__name__) class Task(task.Task): async def prepare(self): if "js" not in self.resource.browser_modules: raise exceptions.CancelError("No JS module needed") async def start(self): js_data = self.resource.browser_modules['js'] package = js_data.get('package', {}) package_path = self.build_path / 'package.json' with package_path.open('w') as f: json.dump(package, f) cmd = self.find_command('yarnpkg', 'yarn') await self.runCommand(cmd, 'install', path=str(self.build_path)) try: brython_map = js_data['brython_map'] except KeyError: pass else: log.info(_("creating JS modules mapping for Brython")) js_modules_path = self.build_path / 'js_modules' js_modules_path.mkdir(exist_ok=True) init_path = js_modules_path / '__init__.py' init_path.touch() for module_name, module_data in brython_map.items(): log.debug(f"generating mapping for {module_name}") if ' ' in module_name: raise ValueError( f"module {module_name!r} has space(s), it must not!") module_python_name = module_name.replace(".", "_").replace("-", "_") if module_python_name != module_name: log.info(f"{module_python_name!r} will be used as python module name") module_path = js_modules_path / f"{module_python_name}.py" if isinstance(module_data, str): module_data = {'path': [module_data]} try: js_paths = module_data.pop('path') except KeyError: raise ValueError( f'module data for {module_name} must have a "path" key') if isinstance(js_paths, str): js_paths = [js_paths] module_data['path'] = [ Path('node_modules') / js_path.strip(' /') for js_path in js_paths ] to_export = module_data.get("export", [module_name]) if isinstance(to_export, str): to_export = to_export.split(",") to_export = [e.strip() for e in to_export] init_code = [] # CSS if any css_to_load = module_data.get("css") if css_to_load: if isinstance(css_to_load, str): css_to_load = [css_to_load] add_styles_lines = [] for css_path in css_to_load: normalized_css_path = Path("node_modules") / css_path.strip(" /") css_href = Path("/").joinpath(C.BUILD_DIR, normalized_css_path) style_tag = ( f'<link rel="stylesheet" type="text/css" href="{css_href}">' ) add_style_code = ( 'document.head.insertAdjacentHTML(' '"beforeend", ' f"'{style_tag}')" ) add_styles_lines.append(add_style_code) add_styles = "\n".join(add_styles_lines) else: add_styles = "" with module_path.open('w') as f: browser_imports = ["aio", "document"] modules_import = [] script_paths = [ str(Path('/').joinpath(C.BUILD_DIR, path)) for path in module_data["path"] ] import_type = module_data.get('import_type', "load") if import_type == 'module': modules_import.append('javascript') modules_import.append('proxy') callback_function_name = f"on_{module_python_name}_loaded" declare_obj = "\n".join(f"{e} = proxy.JSProxy()" for e in to_export) export_str = "\n ".join(f"{e}.js_module=module.{e}" for e in to_export) load_js_libraries = ( f"\n{declare_obj}\n\n" f"def {callback_function_name}(module):\n" f" {export_str}\n" f" loaded.set_result(True)\n\n" f"javascript.import_modules({script_paths}, {callback_function_name})\n" ) elif import_type == 'script': browser_imports.append("window") script_tags = [ f"<script src='{src}'></script>" for src in script_paths ] load_js_libraries = "\n".join( f'document.head.insertAdjacentHTML("beforeend", "{tag}")' for tag in script_tags ) init_code.append('\n'.join(f'{e} = window.{e}' for e in to_export)) init_code.append("loaded.set_result(True)") elif import_type == "load": browser_imports.append("load") browser_imports.append("window") load_calls = [f'load("{src}")' for src in script_paths] load_js_libraries = "\n".join(load_calls) init_code.append('\n'.join(f'{e} = window.{e}' for e in to_export)) init_code.append("loaded.set_result(True)") else: raise ValueError("Invalid import type: {import_type!r}") extra_init = module_data.get("extra_init") if extra_init is not None: if not isinstance(extra_init, str): raise ValueError(f"Invalid extra_init: {extra_init!r}") init_code.append( extra_init.format( build_dir = C.BUILD_DIR ) ) init_code_str = "\n".join(init_code) imports = [f"from browser import {', '.join(browser_imports)}"] for module in modules_import: imports.append(f"import {module}") imports_str = "\n".join(imports) f.write( "#!/usr/bin/env python3\n" f"{imports_str}\n\n" f"loaded = aio.Future()\n" f"{add_styles}\n" f"{load_js_libraries}\n" f"{init_code_str}" )