Source code for next.server.watcher

"""Watch-spec helpers for the development file reloader.

`FilesystemWatchContributor` is a Protocol for objects that contribute
`(path, glob)` pairs to Django's `StatReloader.watch_dir`. The module
exposes built-in defaults derived from `NEXT_FRAMEWORK` and a registry
for extra pairs contributed by third-party apps.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Protocol, runtime_checkable

from next.components import component_extra_roots_from_config
from next.conf import next_framework_settings
from next.pages.watch import (
    get_pages_directories_for_watch,
    iter_pages_roots_with_components_folder_names,
)

from .signals import watch_specs_ready


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


[docs] @runtime_checkable class FilesystemWatchContributor(Protocol): """Yield `(root, glob)` pairs for `StatReloader.watch_dir`. Each tuple is a filesystem root and a glob pattern relative to that root. """
[docs] def iter_watch_specs(self) -> Iterable[tuple[Path, str]]: """Yield `(root, glob)` pairs for `StatReloader.watch_dir`."""
_registered_extra_watch_specs: list[tuple[Path, str]] = []
[docs] def register_autoreload_watch_spec(path: Path, glob: str) -> None: """Register one extra directory and glob pair for the file watcher. Built-in globs already come from `NEXT_FRAMEWORK`. Call this from your own `AppConfig.ready` if you need more trees watched without changing the `next` package. """ _registered_extra_watch_specs.append((path, glob))
def _dedupe_watch_specs( specs: Iterable[tuple[Path, str]], ) -> list[tuple[Path, str]]: """Drop duplicate `(path, glob)` pairs keyed on resolved path.""" seen: set[tuple[Path, str]] = set() out: list[tuple[Path, str]] = [] for path, glob in specs: try: key = (path.resolve(), glob) except OSError: key = (path, glob) if key not in seen: seen.add(key) out.append((path, glob)) return out def _iter_default_autoreload_watch_specs() -> list[tuple[Path, str]]: """Return the default watch specs for pages and filesystem components. `.djx` is intentionally omitted. Template edits do not restart the process, but Python entrypoints (`page.py`, `component.py`) trigger reload when their mtimes change. """ specs: list[tuple[Path, str]] = [ (p, "**/page.py") for p in get_pages_directories_for_watch() ] specs.extend( (root, f"**/{comp_name}/**/component.py") for root, comp_name in iter_pages_roots_with_components_folder_names() ) comp_cfgs = next_framework_settings.DEFAULT_COMPONENT_BACKENDS if isinstance(comp_cfgs, list): for config in comp_cfgs: if not isinstance(config, dict): continue specs.extend( (root, "**/component.py") for root in component_extra_roots_from_config(config) ) return specs
[docs] def iter_all_autoreload_watch_specs() -> list[tuple[Path, str]]: """Return default watch specs plus registered extras, deduplicated.""" specs = _dedupe_watch_specs( (*_iter_default_autoreload_watch_specs(), *_registered_extra_watch_specs) ) watch_specs_ready.send(sender=iter_all_autoreload_watch_specs, specs=specs) return specs