diff --git a/django_vite/core/asset_loader.py b/django_vite/core/asset_loader.py index f868ea0..316a825 100644 --- a/django_vite/core/asset_loader.py +++ b/django_vite/core/asset_loader.py @@ -1,4 +1,7 @@ +import requests +from requests.adapters import HTTPAdapter import json +import urllib3 from pathlib import Path from typing import Dict, List, Callable, NamedTuple, Optional, Union from urllib.parse import urljoin @@ -51,6 +54,32 @@ class DjangoViteConfig(NamedTuple): react_refresh_url: str = "@react-refresh" +def evaluate_dev_mode(config: DjangoViteConfig) -> bool: + """ + ==== + When running in "dev mode", check that an actual vite webserver is running + If not: fallback to serving the actual bundled version read from the manifest.json + ==== + When "dev mode" was false to begin with, this means that we're running in prod and should never + check for a running vite webserver instance to begin with + """ + if config.dev_mode: + print("Evaluating dev mode..") + # Hacky way to check if the vite webserver is serving something + vite_webserver_url = f"http://{config.dev_server_host}:{config.dev_server_port}/" + try: + session = requests.Session() + session.mount(vite_webserver_url, HTTPAdapter(max_retries=0)) + response = session.get(vite_webserver_url) + return response.status_code == 404 + except urllib3.exceptions.MaxRetryError: + return False + except requests.exceptions.ConnectionError: + return False + else: + return False + + class ManifestEntry(NamedTuple): """ Represent an entry for a file inside the "manifest.json". @@ -80,7 +109,6 @@ def __init__( self._config = config self.app_name = app_name - self.dev_mode = config.dev_mode self.manifest_path = self._clean_manifest_path() self.legacy_polyfills_motif = config.legacy_polyfills_motif @@ -90,7 +118,7 @@ def __init__( # Don't crash if there is an error while parsing manifest.json. # Running DjangoViteAssetLoader.instance().checks() on startup will log any # errors. - if not self.dev_mode: + if not evaluate_dev_mode(config): try: self._entries, self.legacy_polyfills_entry = self._parse_manifest() except DjangoViteManifestError: @@ -120,7 +148,7 @@ def _clean_manifest_path(self) -> Path: def check(self) -> List[Warning]: """Check that manifest files are valid when dev_mode=False.""" try: - if not self.dev_mode: + if not evaluate_dev_mode(self._config): self._parse_manifest() return [] except DjangoViteManifestError as exception: @@ -156,7 +184,7 @@ def _parse_manifest(self) -> ParsedManifestOutput: DjangoViteManifestError: if cannot load the file or JSON in file is malformed. """ - if self.dev_mode: + if evaluate_dev_mode(self._config): return self.ParsedManifestOutput() entries: Dict[str, ManifestEntry] = {} @@ -218,7 +246,6 @@ def __init__( self._config = config self.app_name = app_name - self.dev_mode = config.dev_mode self.dev_server_protocol = config.dev_server_protocol self.dev_server_host = config.dev_server_host self.dev_server_port = config.dev_server_port @@ -241,7 +268,9 @@ def _get_dev_server_url( Returns: str -- Full URL to the asset. """ - static_url_base = urljoin(settings.STATIC_URL, self.static_url_prefix) + # Override from default library, we don't want the static_url_base to be used as in our case + # this is an actual url and not a relative path + static_url_base = urljoin('', self.static_url_prefix) if not static_url_base.endswith("/"): static_url_base += "/" @@ -302,7 +331,7 @@ def generate_vite_asset( this asset in your page. """ - if self.dev_mode: + if evaluate_dev_mode(self._config): url = self._get_dev_server_url(path) return TagGenerator.script( url, @@ -367,7 +396,7 @@ def preload_vite_asset( str -- all tags to preload this asset. """ - if self.dev_mode: + if evaluate_dev_mode(self._config): return "" tags: List[Tag] = [] @@ -480,7 +509,7 @@ def generate_vite_asset_url(self, path: str) -> str: str -- The URL of this asset. """ - if self.dev_mode: + if evaluate_dev_mode(self._config): return self._get_dev_server_url(path) manifest_entry = self.manifest.get(path) @@ -509,7 +538,7 @@ def generate_vite_legacy_polyfills( str -- The script tag to the polyfills. """ - if self.dev_mode: + if evaluate_dev_mode(self._config): return "" polyfills_manifest_entry = self.manifest.legacy_polyfills_entry @@ -554,7 +583,7 @@ def generate_vite_legacy_asset( str -- The script tag of this legacy asset . """ - if self.dev_mode: + if evaluate_dev_mode(self._config): return "" manifest_entry = self.manifest.get(path) @@ -580,7 +609,7 @@ def generate_vite_ws_client(self, **kwargs: Dict[str, str]) -> str: script tags. """ - if not self.dev_mode: + if not evaluate_dev_mode(self._config): return "" url = self._get_dev_server_url(self.ws_client_url) @@ -605,7 +634,7 @@ def generate_vite_react_refresh_url(self, **kwargs: Dict[str, str]) -> str: config_key {str} -- Key of the configuration to use. """ - if not self.dev_mode: + if not evaluate_dev_mode(self._config): return "" url = self._get_dev_server_url(self.react_refresh_url) diff --git a/setup.py b/setup.py index dfebe06..2ef2745 100644 --- a/setup.py +++ b/setup.py @@ -24,6 +24,8 @@ ], install_requires=[ "Django>=3.2", + "requests>=2.31.0", + "urllib3>=1.26.18", ], classifiers=[ "License :: OSI Approved :: Apache Software License",