dependencies.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. from dataclasses import dataclass
  2. from pathlib import Path
  3. from typing import Dict, List, Set, Tuple
  4. import vbuild
  5. from . import __version__, globals
  6. from .helpers import KWONLY_SLOTS
  7. from .ids import IncrementingStringIds
  8. @dataclass(**KWONLY_SLOTS)
  9. class Component:
  10. name: str
  11. path: Path
  12. @property
  13. def import_path(self) -> str:
  14. return f'/_nicegui/{__version__}/components/{self.name}'
  15. @dataclass(**KWONLY_SLOTS)
  16. class Dependency:
  17. id: int
  18. path: Path
  19. dependents: Set[str]
  20. optional: bool
  21. @property
  22. def import_path(self) -> str:
  23. return f'/_nicegui/{__version__}/dependencies/{self.id}/{self.path.name}'
  24. dependency_ids = IncrementingStringIds()
  25. vue_components: Dict[str, Component] = {}
  26. js_components: Dict[str, Component] = {}
  27. js_dependencies: Dict[int, Dependency] = {}
  28. def register_component(name: str, py_filepath: str, component_filepath: str, dependencies: List[str] = [],
  29. optional_dependencies: List[str] = []) -> None:
  30. suffix = Path(component_filepath).suffix.lower()
  31. assert suffix in {'.vue', '.js'}, 'Only VUE and JS components are supported.'
  32. if suffix == '.vue':
  33. assert name not in vue_components, f'Duplicate VUE component name {name}'
  34. vue_components[name] = Component(name=name, path=Path(py_filepath).parent / component_filepath)
  35. elif suffix == '.js':
  36. assert name not in js_components, f'Duplicate JS component name {name}'
  37. js_components[name] = Component(name=name, path=Path(py_filepath).parent / component_filepath)
  38. for dependency in dependencies + optional_dependencies:
  39. path = Path(py_filepath).parent / dependency
  40. assert path.suffix == '.js', 'Only JS dependencies are supported.'
  41. id = dependency_ids.get(str(path.resolve()))
  42. if id not in js_dependencies:
  43. optional = dependency in optional_dependencies
  44. js_dependencies[id] = Dependency(id=id, path=path, dependents=set(), optional=optional)
  45. js_dependencies[id].dependents.add(name)
  46. def generate_vue_content() -> Tuple[str, str, str]:
  47. builds = [
  48. vbuild.VBuild(name, component.path.read_text())
  49. for name, component in vue_components.items()
  50. if name not in globals.excludes
  51. ]
  52. return (
  53. '\n'.join(v.html for v in builds),
  54. '<style>' + '\n'.join(v.style for v in builds) + '</style>',
  55. '\n'.join(v.script.replace('Vue.component', 'app.component', 1) for v in builds),
  56. )
  57. def generate_js_imports(prefix: str) -> str:
  58. result = ''
  59. for dependency in js_dependencies.values():
  60. if dependency.optional:
  61. continue
  62. if not dependency.dependents.difference(globals.excludes):
  63. continue
  64. result += f'import "{prefix}{dependency.import_path}";\n'
  65. for name, component in js_components.items():
  66. if name in globals.excludes:
  67. continue
  68. result += f'import {{ default as {name} }} from "{prefix}{component.import_path}";\n'
  69. result += f'app.component("{name}", {name});\n'
  70. return result