Coverage for webapp/vite_integration/impl/prod.py: 97%
71 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-09-17 22:07 +0000
« prev ^ index » next coverage.py v7.10.6, created at 2025-09-17 22:07 +0000
1from os import path
2import json
3from typing import List, Set
4from functools import cache
6from webapp.config import VITE_OUTPUT_DIR
7from webapp.vite_integration.impl.base import _AbstractViteIntegration
8from webapp.vite_integration.types import Manifest, ManifestChunk
9from webapp.vite_integration.exceptions import (
10 AssetPathException,
11 ManifestContentException,
12 ManifestPathException,
13)
16class ProdViteIntegration(_AbstractViteIntegration):
17 OUT_DIR = VITE_OUTPUT_DIR
18 BUILD_MANIFEST = ".vite/manifest.json"
19 manifest = None # we cache the manifest contents in a static attribute
21 def __init__(self):
22 if ProdViteIntegration.manifest:
23 # manifest has already been parsed
24 return
26 # print("Initializing Vite manifest")
27 manifest_path = path.join(
28 ProdViteIntegration.OUT_DIR, ProdViteIntegration.BUILD_MANIFEST
29 )
30 if not path.isfile(manifest_path):
31 raise ManifestPathException("Bad path to Vite manifest")
33 manifest: Manifest = {}
34 with open(manifest_path) as f:
35 manifest = json.load(f)
36 ProdViteIntegration.manifest = manifest
38 def get_dev_tools(self) -> str:
39 return ""
41 @cache
42 def get_asset_url(self, asset_name: str) -> str:
43 entry = ProdViteIntegration.manifest.get(asset_name)
44 if not entry:
45 raise ManifestContentException(
46 f'Asset "{asset_name}" not declared in Vite build manifest'
47 )
49 entry_path = path.join(ProdViteIntegration.OUT_DIR, entry["file"])
50 if not path.isfile(entry_path):
51 raise AssetPathException(
52 f'Path to asset file "{entry_path}" doesn\'t exist; check your'
53 "VITE_OUTPUT_DIR env variable"
54 )
56 return f"/{entry_path}"
58 @cache
59 def _recursive_get_chunks(self, asset_name: str) -> List[ManifestChunk]:
60 seen: Set[str] = set()
62 # recursively visit the manifest to build the import tree for the
63 # given `asset_name`
64 def __recursive_get_chunks(
65 chunk: ManifestChunk,
66 ) -> List[ManifestChunk]:
67 chunks: List[ManifestChunk] = []
68 for file in chunk.get("imports", []):
69 importee = ProdViteIntegration.manifest[file]
70 if file in seen:
71 continue
72 seen.add(file)
73 chunks.extend(__recursive_get_chunks(importee))
74 chunks.append(importee)
75 return chunks
77 entry = ProdViteIntegration.manifest.get(asset_name)
78 if not entry:
79 raise ManifestContentException(
80 f'Asset "{asset_name}" not declared in Vite build manifest'
81 )
83 return [entry] + __recursive_get_chunks(entry)
85 @cache
86 def get_imported_chunks(self, asset_name: str) -> List[str]:
87 chunks = self._recursive_get_chunks(asset_name)
88 files = [
89 path.join(ProdViteIntegration.OUT_DIR, f["file"])
90 for f in chunks[1:] # first chunk is asset_name
91 ]
93 for f in files:
94 if not path.isfile(f):
95 raise AssetPathException(
96 f'Path to asset file "{f}" doesn\'t exist; check your'
97 "VITE_OUTPUT_DIR env variable"
98 )
100 # only build filesystem paths for all chunks
101 urls = [f"/{f}" for f in files]
103 return urls
105 @cache
106 def get_imported_css(self, asset_name: str) -> List[str]:
107 chunks = self._recursive_get_chunks(asset_name)
108 files = []
109 for chunk in chunks:
110 for file in chunk.get("css", []):
111 files.append(path.join(ProdViteIntegration.OUT_DIR, file))
113 for f in files:
114 if not path.isfile(f):
115 raise AssetPathException(
116 f'Path to asset file "{f}" doesn\'t exist; check your'
117 "VITE_OUTPUT_DIR env variable"
118 )
120 # only build filesystem paths for all chunks
121 urls = [f"/{f}" for f in files]
123 return urls