Bläddra i källkod

use hash of parent path as dependency identifier

Falko Schindler 1 år sedan
förälder
incheckning
14a0dda98b
3 ändrade filer med 47 tillägg och 42 borttagningar
  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
 
+import hashlib
 from dataclasses import dataclass
 from pathlib import Path
 from typing import TYPE_CHECKING, Dict, List, Set, Tuple
@@ -49,59 +50,57 @@ js_components: Dict[str, JsComponent] = {}
 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.
 
     Single-file components (.vue) are built right away
     to delegate this "long" process to the bootstrap phase
     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:
             return vue_components[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]
-    if suffix == '.js':
+    if path.suffix == '.js':
         if key in js_components and js_components[key].path == path:
             return js_components[key]
         assert key not in js_components, f'Duplicate JS component {key}'
         js_components[key] = JsComponent(key=key, name=name, path=path)
         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:
             return libraries[key]
         assert key not in libraries, f'Duplicate js library {key}'
         libraries[key] = Library(key=key, name=name, path=path, expose=expose)
         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],
@@ -137,14 +136,14 @@ def generate_resources(prefix: str, elements: List[Element]) -> Tuple[List[str],
         for library in element.libraries:
             if library.key not in done_libraries:
                 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)
         if element.component:
             component = element.component
             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)
     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]] = [],
                           extra_libraries: List[Union[str, Path]] = [],
                           ) -> None:
+        def abs_path(file: Union[str, Path]) -> Path:
+            p = Path(file)
+            return p if p.is_absolute() else base / p
         super().__init_subclass__()
         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:
         """Add a slot to the element.

+ 7 - 4
nicegui/templates/index.html

@@ -190,10 +190,13 @@
       }
 
       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) {
           if (loaded_libraries.has(name)) continue;