Procházet zdrojové kódy

use hash of parent path as dependency identifier

Falko Schindler před 1 rokem
rodič
revize
14a0dda98b
3 změnil soubory, kde provedl 47 přidání a 42 odebrání
  1. 33 34
      nicegui/dependencies.py
  2. 7 4
      nicegui/element.py
  3. 7 4
      nicegui/templates/index.html

+ 33 - 34
nicegui/dependencies.py

@@ -1,5 +1,6 @@
 from __future__ import annotations
 from __future__ import annotations
 
 
+import hashlib
 from dataclasses import dataclass
 from dataclasses import dataclass
 from pathlib import Path
 from pathlib import Path
 from typing import TYPE_CHECKING, Dict, List, Set, Tuple
 from typing import TYPE_CHECKING, Dict, List, Set, Tuple
@@ -49,59 +50,57 @@ js_components: Dict[str, JsComponent] = {}
 libraries: Dict[str, Library] = {}
 libraries: Dict[str, Library] = {}
 
 
 
 
-def register_vue_component(location: Path, base_path: Path = Path(__file__).parent / 'elements') -> Component:
+def register_vue_component(path: Path) -> Component:
     """Register a .vue or .js Vue component.
     """Register a .vue or .js Vue component.
 
 
     Single-file components (.vue) are built right away
     Single-file components (.vue) are built right away
     to delegate this "long" process to the bootstrap phase
     to delegate this "long" process to the bootstrap phase
     and to avoid building the component on every single request.
     and to avoid building the component on every single request.
-
-    :param location: location to the library relative to the base_path (used as the resource identifier, must be URL-safe)
-    :param base_path: base path where your libraries are located
-    :return: registered component
     """
     """
-    path, key, name, suffix = deconstruct_location(location, base_path)
-    if suffix == '.vue':
+    key = compute_key(path)
+    name = get_name(path)
+    if path.suffix == '.vue':
         if key in vue_components and vue_components[key].path == path:
         if key in vue_components and vue_components[key].path == path:
             return vue_components[key]
             return vue_components[key]
         assert key not in vue_components, f'Duplicate VUE component {key}'
         assert key not in vue_components, f'Duplicate VUE component {key}'
-        build = vbuild.VBuild(name, path.read_text())
-        vue_components[key] = VueComponent(key=key, name=name, path=path,
-                                           html=build.html, script=build.script, style=build.style)
+        v = vbuild.VBuild(name, path.read_text())
+        vue_components[key] = VueComponent(key=key, name=name, path=path, html=v.html, script=v.script, style=v.style)
         return vue_components[key]
         return vue_components[key]
-    if suffix == '.js':
+    if path.suffix == '.js':
         if key in js_components and js_components[key].path == path:
         if key in js_components and js_components[key].path == path:
             return js_components[key]
             return js_components[key]
         assert key not in js_components, f'Duplicate JS component {key}'
         assert key not in js_components, f'Duplicate JS component {key}'
         js_components[key] = JsComponent(key=key, name=name, path=path)
         js_components[key] = JsComponent(key=key, name=name, path=path)
         return js_components[key]
         return js_components[key]
-    raise ValueError(f'Unsupported component type "{suffix}"')
-
+    raise ValueError(f'Unsupported component type "{path.suffix}"')
 
 
-def register_library(location: Path, base_path: Path = Path(__file__).parent / 'elements' / 'lib', *,
-                     expose: bool = False) -> Library:
-    """Register a *.js library.
 
 
-    :param location: location to the library relative to the base_path (used as the resource identifier, must be URL-safe)
-    :param base_path: base path where your libraries are located
-    :param expose: whether to expose library as an ESM module (exposed modules will NOT be imported)
-    :return: registered library
-    """
-    path, key, name, suffix = deconstruct_location(location, base_path)
-    if suffix in {'.js', '.mjs'}:
+def register_library(path: Path, *, expose: bool = False) -> Library:
+    """Register a *.js library."""
+    key = compute_key(path)
+    name = get_name(path)
+    if path.suffix in {'.js', '.mjs'}:
         if key in libraries and libraries[key].path == path:
         if key in libraries and libraries[key].path == path:
             return libraries[key]
             return libraries[key]
         assert key not in libraries, f'Duplicate js library {key}'
         assert key not in libraries, f'Duplicate js library {key}'
         libraries[key] = Library(key=key, name=name, path=path, expose=expose)
         libraries[key] = Library(key=key, name=name, path=path, expose=expose)
         return libraries[key]
         return libraries[key]
-    raise ValueError(f'Unsupported library type "{suffix}"')
+    raise ValueError(f'Unsupported library type "{path.suffix}"')
+
+
+def compute_key(path: Path) -> str:
+    """Compute a key for a given path using a hash function.
+
+    If the path is relative to the NiceGUI base directory, the key is computed from the relative path.
+    """
+    nicegui_base = Path(__file__).parent
+    if path.is_relative_to(nicegui_base):
+        path = path.relative_to(nicegui_base)
+    return f'{hashlib.sha256(str(path.parent).encode()).hexdigest()}/{path.name}'
 
 
 
 
-def deconstruct_location(location: Path, base_path: Path) -> Tuple[Path, str, str, str]:
-    """Deconstruct a location into its parts: full path, relative path, name, suffix."""
-    abs_path = location if location.is_absolute() else base_path / location
-    rel_path = location if not location.is_absolute() else location.relative_to(base_path)
-    return abs_path, str(rel_path), location.name.split('.', 1)[0], location.suffix.lower()
+def get_name(path: Path) -> str:
+    return path.name.split('.', 1)[0]
 
 
 
 
 def generate_resources(prefix: str, elements: List[Element]) -> Tuple[List[str],
 def generate_resources(prefix: str, elements: List[Element]) -> Tuple[List[str],
@@ -137,14 +136,14 @@ def generate_resources(prefix: str, elements: List[Element]) -> Tuple[List[str],
         for library in element.libraries:
         for library in element.libraries:
             if library.key not in done_libraries:
             if library.key not in done_libraries:
                 if not library.expose:
                 if not library.expose:
-                    js_imports.append(f'import "{prefix}/_nicegui/{__version__}/libraries/{library.key}";')
+                    url = f'{prefix}/_nicegui/{__version__}/libraries/{library.key}'
+                    js_imports.append(f'import "{url}";')
                 done_libraries.add(library.key)
                 done_libraries.add(library.key)
         if element.component:
         if element.component:
             component = element.component
             component = element.component
             if component.key not in done_components and component.path.suffix.lower() == '.js':
             if component.key not in done_components and component.path.suffix.lower() == '.js':
-                js_imports.extend([
-                    f'import {{ default as {component.name} }} from "{prefix}/_nicegui/{__version__}/components/{component.key}";',
-                    f'app.component("{component.tag}", {component.name});',
-                ])
+                url = f'{prefix}/_nicegui/{__version__}/components/{component.key}'
+                js_imports.append(f'import {{ default as {component.name} }} from "{url}";')
+                js_imports.append(f'app.component("{component.tag}", {component.name});')
                 done_components.add(component.key)
                 done_components.add(component.key)
     return vue_html, vue_styles, vue_scripts, imports, js_imports
     return vue_html, vue_styles, vue_scripts, imports, js_imports

+ 7 - 4
nicegui/element.py

@@ -70,12 +70,15 @@ class Element(Visibility):
                           exposed_libraries: List[Union[str, Path]] = [],
                           exposed_libraries: List[Union[str, Path]] = [],
                           extra_libraries: List[Union[str, Path]] = [],
                           extra_libraries: List[Union[str, Path]] = [],
                           ) -> None:
                           ) -> None:
+        def abs_path(file: Union[str, Path]) -> Path:
+            p = Path(file)
+            return p if p.is_absolute() else base / p
         super().__init_subclass__()
         super().__init_subclass__()
         base = Path(inspect.getfile(cls)).parent
         base = Path(inspect.getfile(cls)).parent
-        cls.component = register_vue_component(Path(component), base) if component else None
-        cls.libraries = [register_library(Path(library), base) for library in libraries]
-        cls.extra_libraries = [register_library(Path(library), base) for library in extra_libraries]
-        cls.exposed_libraries = [register_library(Path(library), base, expose=True) for library in exposed_libraries]
+        cls.component = register_vue_component(abs_path(component)) if component else None
+        cls.libraries = [register_library(abs_path(library)) for library in libraries]
+        cls.extra_libraries = [register_library(abs_path(library)) for library in extra_libraries]
+        cls.exposed_libraries = [register_library(abs_path(library), expose=True) for library in exposed_libraries]
 
 
     def add_slot(self, name: str, template: Optional[str] = None) -> Slot:
     def add_slot(self, name: str, template: Optional[str] = None) -> Slot:
         """Add a slot to the element.
         """Add a slot to the element.

+ 7 - 4
nicegui/templates/index.html

@@ -190,10 +190,13 @@
       }
       }
 
 
       async function loadDependencies(element) {
       async function loadDependencies(element) {
-        if (element.component && !loaded_components.has(element.component.name) && !element.component.key.endsWith('.vue')) {
-          const component = (await import(`{{ prefix | safe }}/_nicegui/{{version}}/components/${element.component.key}`)).default;
-          app = app.component(element.component.tag, component);
-          loaded_components.add(element.component.name);
+        if (element.component) {
+          const {name, key, tag} = element.component;
+          if (!loaded_components.has(name) && !key.endsWith('.vue')) {
+            const component = await import(`{{ prefix | safe }}/_nicegui/{{version}}/components/${key}`);
+            app = app.component(tag, component.default);
+            loaded_components.add(name);
+          }
         }
         }
         for (const {name, key} of element.libraries) {
         for (const {name, key} of element.libraries) {
           if (loaded_libraries.has(name)) continue;
           if (loaded_libraries.has(name)) continue;