Source code for next.urls.dispatcher

"""Walk filesystem page trees once to emit routes and register components.

`FilesystemTreeDispatcher` performs a single depth-first walk per
page-tree root. It yields `(url_path, page_file)` pairs for every
discovered `page.py` (plus virtual `template.djx`-only pages), and
registers `_components` folders it encounters along the way.
"""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING

from next.components import register_components_folder_from_router_walk


if TYPE_CHECKING:
    from collections.abc import Generator, Iterable
    from pathlib import Path


logger = logging.getLogger(__name__)


[docs] class FilesystemTreeDispatcher: """Run one depth-first walk: routes per node or skip component folders."""
[docs] def __init__( self, skip_dir_names: Iterable[str], *, components_folder_name: str, register_components: bool, ) -> None: """Remember which dirs to skip and whether to register component roots.""" self._skip_set = frozenset(skip_dir_names) self._components_folder_name = components_folder_name self._register_components = register_components
[docs] def walk(self, pages_path: Path) -> Generator[tuple[str, Path], None, None]: """Yield `(url_path, page_file)`, where `url_path` is the route trail.""" yield from self._visit(pages_path, pages_path, "")
def _visit( self, current_path: Path, tree_root: Path, url_path: str, ) -> Generator[tuple[str, Path], None, None]: try: items = list(current_path.iterdir()) except OSError as e: logger.debug("Cannot list directory %s: %s", current_path, e) return for item in items: if item.is_dir(): if item.name in self._skip_set: if ( self._register_components and item.name == self._components_folder_name ): register_components_folder_from_router_walk( item, tree_root, url_path ) continue dir_name = item.name new_url_path = f"{url_path}/{dir_name}" if url_path else dir_name yield from self._visit(item, tree_root, new_url_path) elif item.name == "page.py": yield url_path, item if current_path.is_dir(): page_file = current_path / "page.py" template_file = current_path / "template.djx" if not page_file.exists() and template_file.exists(): yield url_path, current_path / "page.py"
[docs] def scan_pages_tree( pages_path: Path, skip_dir_names: Iterable[str] = (), *, components_folder_name: str = "_components", register_components: bool = False, ) -> Generator[tuple[str, Path], None, None]: """Walk a tree for `page.py` (and virtual pages) without a router instance.""" dispatcher = FilesystemTreeDispatcher( skip_dir_names, components_folder_name=components_folder_name, register_components=register_components, ) yield from dispatcher.walk(pages_path)
def _scan_pages_directory( pages_path: Path, skip_dir_names: Iterable[str] = (), *, components_folder_name: str = "_components", register_components: bool = False, ) -> Generator[tuple[str, Path], None, None]: """Yield the same pairs as `scan_pages_tree`.""" yield from scan_pages_tree( pages_path, skip_dir_names, components_folder_name=components_folder_name, register_components=register_components, ) __all__ = ["FilesystemTreeDispatcher", "scan_pages_tree"]