0
# Asset Loading
1
2
Core asset loading system that manages Vite assets programmatically, handles manifest parsing and URL generation, and provides the foundation for Django template tag functionality.
3
4
## Capabilities
5
6
### Asset Loader Singleton
7
8
Central singleton class managing all Vite app configurations and asset loading operations.
9
10
```python { .api }
11
class DjangoViteAssetLoader:
12
"""
13
Singleton class handling Vite asset loading across multiple app configurations.
14
15
Routes asset and URL generation to appropriate DjangoViteAppClient instances.
16
"""
17
18
@classmethod
19
def instance(cls) -> 'DjangoViteAssetLoader':
20
"""
21
Get singleton instance.
22
23
Returns:
24
DjangoViteAssetLoader: Only instance of the class
25
"""
26
27
def check(self, **kwargs) -> List[Warning]:
28
"""
29
Check manifest files validity for apps with dev_mode=False.
30
31
Returns:
32
List of Django system check warnings
33
"""
34
```
35
36
**Basic Usage:**
37
```python
38
from django_vite.core.asset_loader import DjangoViteAssetLoader
39
40
# Get singleton instance
41
loader = DjangoViteAssetLoader.instance()
42
43
# Generate asset HTML
44
asset_html = loader.generate_vite_asset('src/main.js')
45
46
# Get asset URL only
47
asset_url = loader.generate_vite_asset_url('src/logo.png')
48
```
49
50
### Asset Generation Methods
51
52
Generate HTML tags and URLs for Vite-managed assets.
53
54
```python { .api }
55
def generate_vite_asset(
56
self,
57
path: str,
58
app: str = "default",
59
**kwargs
60
) -> str:
61
"""
62
Generate script/link tags for JS/TS asset with dependencies.
63
64
Args:
65
path: Path to Vite JS/TS asset
66
app: App configuration name (default: "default")
67
**kwargs: Additional attributes for generated tags
68
69
Returns:
70
HTML string with all required tags
71
72
Raises:
73
DjangoViteConfigNotFoundError: If app config not found
74
DjangoViteAssetNotFoundError: If asset not found (production only)
75
"""
76
77
def generate_vite_asset_url(self, path: str, app: str = "default") -> str:
78
"""
79
Generate URL for Vite-managed asset.
80
81
Args:
82
path: Path to Vite asset
83
app: App configuration name (default: "default")
84
85
Returns:
86
Asset URL string
87
"""
88
89
def preload_vite_asset(self, path: str, app: str = "default") -> str:
90
"""
91
Generate modulepreload tags for JS/TS asset and dependencies.
92
93
Args:
94
path: Path to Vite JS/TS asset
95
app: App configuration name (default: "default")
96
97
Returns:
98
HTML string with preload link tags
99
"""
100
```
101
102
**Asset Generation Examples:**
103
```python
104
loader = DjangoViteAssetLoader.instance()
105
106
# Basic asset generation
107
main_js = loader.generate_vite_asset('src/main.js')
108
109
# With custom attributes
110
analytics_js = loader.generate_vite_asset(
111
'src/analytics.js',
112
defer=True,
113
data_module="analytics"
114
)
115
116
# Multi-app usage
117
admin_js = loader.generate_vite_asset('src/admin.js', app='admin')
118
119
# Asset URL only
120
logo_url = loader.generate_vite_asset_url('src/images/logo.png')
121
122
# Preload assets
123
preload_html = loader.preload_vite_asset('src/main.js')
124
```
125
126
### Development Support Methods
127
128
Generate HMR client and React refresh scripts for development mode.
129
130
```python { .api }
131
def generate_vite_ws_client(self, app: str = "default", **kwargs) -> str:
132
"""
133
Generate Vite WebSocket client for HMR.
134
135
Args:
136
app: App configuration name (default: "default")
137
**kwargs: Additional attributes for script tag
138
139
Returns:
140
Script tag HTML (empty in production)
141
"""
142
143
def generate_vite_react_refresh_url(self, app: str = "default", **kwargs) -> str:
144
"""
145
Generate Vite React Refresh runtime script.
146
147
Args:
148
app: App configuration name (default: "default")
149
**kwargs: Additional attributes for script tags
150
151
Returns:
152
Inline script HTML (empty in production)
153
"""
154
```
155
156
**Development Examples:**
157
```python
158
loader = DjangoViteAssetLoader.instance()
159
160
# HMR client
161
hmr_client = loader.generate_vite_ws_client()
162
163
# React refresh runtime
164
react_refresh = loader.generate_vite_react_refresh_url()
165
166
# With custom attributes
167
hmr_with_attrs = loader.generate_vite_ws_client(data_turbo_track="reload")
168
```
169
170
### Legacy Browser Support Methods
171
172
Generate legacy browser polyfills and asset versions.
173
174
```python { .api }
175
def generate_vite_legacy_polyfills(
176
self,
177
app: str = "default",
178
nomodule: bool = True,
179
**kwargs
180
) -> str:
181
"""
182
Generate legacy browser polyfills script tag.
183
184
Args:
185
app: App configuration name (default: "default")
186
nomodule: Set nomodule attribute (default: True)
187
**kwargs: Additional attributes for script tag
188
189
Returns:
190
Script tag HTML (empty in development)
191
192
Raises:
193
DjangoViteAssetNotFoundError: If polyfills not found in manifest
194
"""
195
196
def generate_vite_legacy_asset(
197
self,
198
path: str,
199
app: str = "default",
200
**kwargs
201
) -> str:
202
"""
203
Generate legacy asset script tag.
204
205
Args:
206
path: Path to legacy asset (must contain '-legacy')
207
app: App configuration name (default: "default")
208
**kwargs: Additional attributes for script tag
209
210
Returns:
211
Script tag HTML (empty in development)
212
"""
213
```
214
215
**Legacy Support Examples:**
216
```python
217
loader = DjangoViteAssetLoader.instance()
218
219
# Legacy polyfills
220
polyfills = loader.generate_vite_legacy_polyfills()
221
222
# Legacy asset
223
legacy_main = loader.generate_vite_legacy_asset('src/main-legacy.js')
224
225
# Without nomodule attribute
226
polyfills_no_nomodule = loader.generate_vite_legacy_polyfills(nomodule=False)
227
```
228
229
### App Client Management
230
231
Individual app client for managing assets within a specific Vite app configuration.
232
233
```python { .api }
234
class DjangoViteAppClient:
235
"""
236
Interface for generating assets and URLs from one Vite app configuration.
237
"""
238
239
def __init__(self, config: DjangoViteConfig, app_name: str = "default"):
240
"""
241
Initialize app client with configuration.
242
243
Args:
244
config: DjangoViteConfig instance
245
app_name: Name of the app configuration
246
"""
247
248
def generate_vite_asset(self, path: str, **kwargs) -> str:
249
"""Generate script/link tags for JS/TS asset with dependencies."""
250
251
def preload_vite_asset(self, path: str) -> str:
252
"""Generate modulepreload tags for JS/TS asset."""
253
254
def generate_vite_asset_url(self, path: str) -> str:
255
"""Generate URL for Vite-managed asset."""
256
```
257
258
**Direct App Client Usage:**
259
```python
260
from django_vite.core.asset_loader import DjangoViteAppClient
261
from django_vite import DjangoViteConfig
262
263
# Create custom configuration
264
config = DjangoViteConfig(
265
dev_mode=False,
266
dev_server_port=3000,
267
static_url_prefix="custom/"
268
)
269
270
# Create app client
271
client = DjangoViteAppClient(config, app_name="custom")
272
273
# Generate assets
274
asset_html = client.generate_vite_asset('src/main.js')
275
asset_url = client.generate_vite_asset_url('src/logo.png')
276
```
277
278
### URL Generation Methods
279
280
Low-level URL generation for development and production servers.
281
282
```python { .api }
283
def get_dev_server_url(self, path: str) -> str:
284
"""
285
Generate URL to asset served by Vite development server.
286
287
Args:
288
path: Path to asset
289
290
Returns:
291
Full URL to development server asset
292
"""
293
294
def get_production_server_url(self, path: str) -> str:
295
"""
296
Generate URL to asset served during production.
297
298
Args:
299
path: Path to asset
300
301
Returns:
302
Production server URL (may use Django staticfiles)
303
"""
304
```
305
306
**URL Generation Examples:**
307
```python
308
# Get app client
309
loader = DjangoViteAssetLoader.instance()
310
client = loader._get_app_client("default")
311
312
# Development URL
313
dev_url = client.get_dev_server_url('src/main.js')
314
# Result: "http://localhost:5173/static/src/main.js"
315
316
# Production URL
317
prod_url = client.get_production_server_url('main.abc123.js')
318
# Result: "/static/main.abc123.js" or CDN URL if using staticfiles storage
319
```
320
321
### Manifest Client
322
323
Handles parsing and accessing entries from Vite's manifest.json file.
324
325
```python { .api }
326
class ManifestClient:
327
"""
328
Client for accessing entries in compiled Vite config's manifest.json.
329
Only parses manifest.json when dev_mode=False.
330
"""
331
332
def __init__(self, config: DjangoViteConfig, app_name: str = "default"):
333
"""
334
Initialize manifest client.
335
336
Args:
337
config: DjangoViteConfig instance
338
app_name: Name of the app configuration
339
"""
340
341
def get(self, path: str) -> ManifestEntry:
342
"""
343
Get ManifestEntry for given path.
344
345
Args:
346
path: Asset path in manifest
347
348
Returns:
349
ManifestEntry with file info and dependencies
350
351
Raises:
352
DjangoViteAssetNotFoundError: If path not found in manifest
353
"""
354
355
def check(self) -> List[Warning]:
356
"""
357
Check manifest file validity.
358
359
Returns:
360
List of Django system check warnings
361
"""
362
363
def load_manifest(self) -> dict:
364
"""
365
Read and parse manifest.json file.
366
367
Returns:
368
Dictionary with manifest contents
369
370
Raises:
371
DjangoViteManifestError: If manifest cannot be loaded or parsed
372
"""
373
```
374
375
**Manifest Client Usage:**
376
```python
377
from django_vite.core.asset_loader import DjangoViteAssetLoader
378
379
loader = DjangoViteAssetLoader.instance()
380
client = loader._get_app_client("default")
381
manifest = client.manifest
382
383
# Get manifest entry
384
try:
385
entry = manifest.get('src/main.js')
386
print(f"Compiled file: {entry.file}")
387
print(f"CSS dependencies: {entry.css}")
388
print(f"JS imports: {entry.imports}")
389
except DjangoViteAssetNotFoundError:
390
print("Asset not found in manifest")
391
392
# Check manifest validity
393
warnings = manifest.check()
394
for warning in warnings:
395
print(f"Manifest issue: {warning.msg}")
396
397
# Load raw manifest data
398
try:
399
raw_manifest = manifest.load_manifest()
400
print(f"Found {len(raw_manifest)} entries")
401
except DjangoViteManifestError as e:
402
print(f"Manifest error: {e}")
403
```
404
405
### Manifest Parsing Utilities
406
407
Internal utilities for parsing and processing Vite manifest files.
408
409
```python { .api }
410
class ParsedManifestOutput(NamedTuple):
411
"""Output from manifest parsing operations."""
412
entries: Dict[str, ManifestEntry] = {}
413
legacy_polyfills_entry: Optional[ManifestEntry] = None
414
415
def _parse_manifest(self) -> ParsedManifestOutput:
416
"""
417
Read and parse the Vite manifest file.
418
419
Returns:
420
ParsedManifestOutput with entries and legacy polyfills entry
421
422
Raises:
423
DjangoViteManifestError: If cannot load file or JSON is malformed
424
"""
425
```
426
427
### Manifest Entry Data
428
429
Data structure representing an entry in Vite's manifest.json.
430
431
```python { .api }
432
class ManifestEntry(NamedTuple):
433
"""
434
Represents an entry for a file inside the manifest.json.
435
"""
436
437
file: str # Compiled output file path
438
src: Optional[str] = None # Original source file path
439
isEntry: Optional[bool] = False # Whether it's an entry point
440
isDynamicEntry: Optional[bool] = False # Whether it's a dynamic entry
441
css: Optional[List[str]] = [] # CSS file dependencies
442
imports: Optional[List[str]] = [] # JavaScript import dependencies
443
dynamicImports: Optional[List[str]] = [] # Dynamic import dependencies
444
```
445
446
**Manifest Entry Example:**
447
```python
448
# Example manifest entry for 'src/main.js'
449
entry = ManifestEntry(
450
file='assets/main.abc123.js',
451
src='src/main.js',
452
isEntry=True,
453
css=['assets/main.def456.css'],
454
imports=['src/vendor.js'],
455
dynamicImports=['src/lazy-component.js']
456
)
457
458
# Access entry data
459
print(f"Output file: {entry.file}")
460
print(f"CSS files: {', '.join(entry.css)}")
461
print(f"Dependencies: {', '.join(entry.imports)}")
462
```
463
464
## Error Handling and Debugging
465
466
### System Checks
467
468
Django-vite includes Django system checks for validating configurations and manifest files.
469
470
```python
471
# Manual system check
472
from django_vite.core.asset_loader import DjangoViteAssetLoader
473
474
loader = DjangoViteAssetLoader.instance()
475
warnings = loader.check()
476
477
for warning in warnings:
478
print(f"{warning.id}: {warning.msg}")
479
if warning.hint:
480
print(f"Hint: {warning.hint}")
481
```
482
483
### Exception Handling
484
485
```python
486
from django_vite.core.exceptions import (
487
DjangoViteAssetNotFoundError,
488
DjangoViteManifestError,
489
DjangoViteConfigNotFoundError
490
)
491
492
loader = DjangoViteAssetLoader.instance()
493
494
try:
495
asset_html = loader.generate_vite_asset('src/missing.js')
496
except DjangoViteAssetNotFoundError as e:
497
print(f"Asset not found: {e}")
498
# Check manifest.json or Vite build configuration
499
500
except DjangoViteConfigNotFoundError as e:
501
print(f"Configuration error: {e}")
502
# Check DJANGO_VITE settings
503
504
except DjangoViteManifestError as e:
505
print(f"Manifest error: {e}")
506
# Check manifest.json file and path
507
```
508
509
### Debug Asset Loading
510
511
```python
512
def debug_asset_loading():
513
"""Debug django-vite asset loading configuration."""
514
loader = DjangoViteAssetLoader.instance()
515
516
# Check all app configurations
517
for app_name, client in loader._apps.items():
518
print(f"\nApp: {app_name}")
519
print(f"Dev mode: {client.dev_mode}")
520
521
if client.dev_mode:
522
print(f"Dev server: {client.dev_server_protocol}://{client.dev_server_host}:{client.dev_server_port}")
523
else:
524
print(f"Manifest path: {client.manifest.manifest_path}")
525
526
# Check manifest validity
527
warnings = client.manifest.check()
528
if warnings:
529
for warning in warnings:
530
print(f"Warning: {warning.msg}")
531
else:
532
print("Manifest is valid")
533
```
534
535
### HTML Tag Generation Utilities
536
537
Low-level utilities for generating HTML tags used by the asset loading system.
538
539
```python { .api }
540
class TagGenerator:
541
"""Static methods for generating HTML tags."""
542
543
@staticmethod
544
def script(src: str, attrs: Dict[str, str]) -> str:
545
"""
546
Generate HTML script tag.
547
548
Args:
549
src: Source URL for the script
550
attrs: Dictionary of HTML attributes
551
552
Returns:
553
HTML script tag string
554
"""
555
556
@staticmethod
557
def stylesheet(href: str, attrs: Optional[Dict[str, str]] = None) -> str:
558
"""
559
Generate HTML link tag for CSS stylesheets.
560
561
Args:
562
href: CSS file URL
563
attrs: Optional dictionary of HTML attributes
564
565
Returns:
566
HTML link tag string
567
"""
568
569
@staticmethod
570
def stylesheet_preload(href: str, attrs: Optional[Dict[str, str]] = None) -> str:
571
"""
572
Generate HTML link preload tag for CSS.
573
574
Args:
575
href: CSS file URL
576
attrs: Optional dictionary of HTML attributes
577
578
Returns:
579
HTML link preload tag string
580
"""
581
582
@staticmethod
583
def preload(href: str, attrs: Dict[str, str]) -> str:
584
"""
585
Generate HTML link preload tag.
586
587
Args:
588
href: Resource URL
589
attrs: Dictionary of HTML attributes
590
591
Returns:
592
HTML link tag string
593
"""
594
595
def attrs_to_str(attrs: Dict[str, str]) -> str:
596
"""
597
Convert dictionary of attributes to HTML attribute string.
598
599
Args:
600
attrs: Dictionary of attribute name-value pairs
601
602
Returns:
603
Formatted HTML attributes string
604
"""
605
```
606
607
**Tag Generation Examples:**
608
```python
609
from django_vite.core.tag_generator import TagGenerator, attrs_to_str
610
611
# Generate script tag
612
script = TagGenerator.script(
613
'https://example.com/script.js',
614
{'type': 'module', 'defer': ''}
615
)
616
# Result: '<script type="module" defer="" src="https://example.com/script.js"></script>'
617
618
# Generate stylesheet link
619
css = TagGenerator.stylesheet(
620
'https://example.com/styles.css',
621
{'media': 'print'}
622
)
623
# Result: '<link media="print" rel="stylesheet" href="https://example.com/styles.css" />'
624
625
# Generate preload link
626
preload = TagGenerator.preload(
627
'https://example.com/font.woff2',
628
{'as': 'font', 'type': 'font/woff2', 'crossorigin': ''}
629
)
630
# Result: '<link href="https://example.com/font.woff2" as="font" type="font/woff2" crossorigin="" />'
631
632
# Convert attributes dictionary to string
633
attrs_str = attrs_to_str({'data-module': 'app', 'defer': ''})
634
# Result: 'data-module="app" defer=""'
635
```
636
637
## Performance Considerations
638
639
### Singleton Benefits
640
641
The DjangoViteAssetLoader singleton pattern ensures:
642
- Manifest files are parsed once and cached in memory
643
- Configuration is loaded once at startup
644
- Multiple asset requests reuse parsed data
645
646
### Manifest Parsing
647
648
- Manifest parsing happens once when dev_mode=False
649
- Parsing errors are cached to avoid repeated file system access
650
- System checks validate manifests at startup
651
652
### Development vs Production
653
654
**Development Mode:**
655
- No manifest parsing required
656
- Direct URLs to Vite dev server
657
- Minimal overhead per request
658
659
**Production Mode:**
660
- Manifest parsed once at startup
661
- Asset dependencies resolved from cached manifest data
662
- Static file URLs generated using Django's staticfiles system