# Vite Integration with Django BlockNote This guide explains how Django BlockNote integrates with Vite for modern asset management, including filename hashing for optimal caching performance. ## Overview Django BlockNote uses Vite to build and bundle JavaScript and CSS assets. The integration provides: - **Content-based filename hashing** for cache busting - **Development/production optimization** with different build strategies - **Automatic asset discovery** through manifest files - **Seamless Django template integration** ## How It Works The integration consists of three main components: 1. **Vite Build Process** - Compiles and hashes assets, generates manifest 2. **Widget Asset Resolution** - Reads manifest to find current asset paths 3. **Template Asset Loading** - Dynamically loads correct asset URLs ```mermaid graph LR A[Vite Build] --> B[manifest.json] B --> C[Widget.get_vite_assets()] C --> D[Template Context] D --> E[Dynamic URLs] E --> F[Browser Cache] ``` ## Vite Configuration ### Build Configuration The Vite configuration handles multiple entry points and generates production-optimized bundles: ```javascript // vite.config.js import { defineConfig } from 'vite' const isProduction = process.env.NODE_ENV === 'production' export default defineConfig({ build: { // Output to Django static directory outDir: '../django_blocknote/static/django_blocknote', emptyOutDir: true, // Multiple entry points for different components rollupOptions: { input: { blocknote: './src/editor.js', widget: './src/widget.js' }, output: { // Content hashing in production for cache busting entryFileNames: (chunkInfo) => { const name = chunkInfo.name if (isProduction) { return `js/${name}.[hash].min.js` } return `js/${name}.js` }, // CSS with hashing assetFileNames: (assetInfo) => { if (assetInfo.name?.endsWith('.css')) { const name = assetInfo.name.replace('.css', '') if (isProduction) { return `css/${name}.[hash].min.css` } return `css/${name}.css` } return isProduction ? 'assets/[name].[hash].[ext]' : 'assets/[name].[ext]' }, format: 'umd', name: 'DjangoBlockNote' } }, // Generate manifest for Django integration manifest: true, // Production optimizations minify: isProduction ? 'terser' : false, terserOptions: isProduction ? { compress: { dead_code: true, passes: 2 }, mangle: { keep_quoted: "strict" // Safe and predictable }, format: { comments: false // Clean output }, ecma: 2022, module: true } : undefined, // Single CSS bundle cssCodeSplit: false, // No source maps in production sourcemap: isProduction ? false : 'inline' }, // Modern ES target esbuild: { drop: isProduction ? ['console', 'debugger'] : [], target: 'es2022' } }) ``` ### Build Output Structure The build process creates the following structure: ``` django_blocknote/static/django_blocknote/ ├── manifest.json # Asset mapping file ├── js/ │ ├── blocknote.a1b2c3d4.min.js # Hashed main bundle │ └── widget.e5f6g7h8.min.js # Hashed widget bundle ├── css/ │ └── blocknote.i9j0k1l2.min.css # Hashed styles └── assets/ # Other static assets └── [fonts, images, etc.] ``` ### Manifest File Format The `manifest.json` file maps logical names to actual build outputs: ```json { "blocknote": { "file": "js/blocknote.a1b2c3d4.min.js", "src": "src/editor.js" }, "widget": { "file": "js/widget.e5f6g7h8.min.js", "src": "src/widget.js" }, "blocknote.css": { "file": "css/blocknote.i9j0k1l2.min.css" } } ``` ## Widget Integration ### Asset Resolution Method The `BlockNoteWidget` class includes a method to resolve current asset paths: ```python # django_blocknote/widgets.py import json from pathlib import Path from django.conf import settings class BlockNoteWidget(forms.Textarea): def get_vite_assets(self): """Get the current asset paths from Vite manifest""" try: # Try to find manifest in static files if hasattr(settings, 'STATIC_ROOT') and settings.STATIC_ROOT: manifest_path = Path(settings.STATIC_ROOT) / 'django_blocknote' / 'manifest.json' else: # Fallback for development from django.apps import apps app_config = apps.get_app_config('django_blocknote') manifest_path = Path(app_config.path) / 'static' / 'django_blocknote' / 'manifest.json' if manifest_path.exists(): with open(manifest_path, 'r') as f: manifest = json.load(f) # Extract asset paths from manifest assets = {} for key, data in manifest.items(): if isinstance(data, dict) and 'file' in data: assets[key] = data['file'] else: assets[key] = data return { 'js': { 'blocknote': assets.get('blocknote', 'js/blocknote.js'), 'widget': assets.get('widget', 'js/widget.js') }, 'css': { 'blocknote': assets.get('blocknote.css', 'css/blocknote.css') } } except (FileNotFoundError, json.JSONDecodeError, KeyError) as e: if getattr(settings, 'DEBUG', False): print(f"Warning: Could not load Vite manifest: {e}") # Fallback to non-hashed filenames for development return { 'js': { 'blocknote': 'js/blocknote.js', 'widget': 'js/widget.js' }, 'css': { 'blocknote': 'css/blocknote.css' } } ``` ### Context Integration The widget passes asset information to the template context: ```python def get_context(self, name, value, attrs): context = super().get_context(name, value, attrs) # Get current asset paths assets = self.get_vite_assets() # Add asset paths to context context["widget"]["assets"] = assets # Debug output in development if getattr(settings, 'DEBUG', False): print(f"BlockNote Widget Assets: {assets}") return context ``` ## Template Integration ### Dynamic Asset Loading The widget template uses the asset paths from the context to load resources: ```html {% load static %} {# Load CSS with dynamic asset paths #} {# Load JavaScript with dynamic asset paths #}