From 9be231af5dcca19484849b2d4567d88f2fd660eb Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Thu, 30 Oct 2025 17:09:14 -0400 Subject: [PATCH 01/60] add microscope form create test, isolate snapshot plugin logic (#324) * add test and isolate plugin * fix side effect * skipif * bump --- backend/tests_e2e/test_e2e.py | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/tests_e2e/test_e2e.py b/backend/tests_e2e/test_e2e.py index 53998453..66205d8f 100644 --- a/backend/tests_e2e/test_e2e.py +++ b/backend/tests_e2e/test_e2e.py @@ -19,6 +19,7 @@ from favit.models import Favorite from proteins.factories import FilterFactory, MicroscopeFactory, OpticalConfigWithFiltersFactory, ProteinFactory from proteins.models import Filter, Microscope, Protein, Spectrum +from proteins.models.protein import Protein from proteins.util.blast import _get_binary if TYPE_CHECKING: From 2c95d23404696a63a858b2db6207404161b2533a Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Wed, 29 Oct 2025 19:33:22 -0400 Subject: [PATCH 02/60] plan --- VITE_MIGRATION_PLAN.md | 1520 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1520 insertions(+) create mode 100644 VITE_MIGRATION_PLAN.md diff --git a/VITE_MIGRATION_PLAN.md b/VITE_MIGRATION_PLAN.md new file mode 100644 index 00000000..210234a3 --- /dev/null +++ b/VITE_MIGRATION_PLAN.md @@ -0,0 +1,1520 @@ +# Webpack to Vite 7 Migration Plan for FPbase + +## GENERAL INSTRUCTIONS FOR CLAUDE CODE + +### Migration Overview + +Migrate FPbase's frontend build system from Webpack 5 + Babel to Vite 7, leveraging django-vite for Django integration. + +### Decision Summary + +- ✅ **Migrate to Vite 7:** YES (Vite is the modern standard, significant performance gains) +- ✅ **Keep 7 Entry Points:** YES (Required for D3 v3/v7 isolation) +- ✅ **Template Migration:** Big Bang (All at once, cleaner approach) + +### Tips and instructions + +- work step-by-step, and when you are happy with a specific step, commit it to the `use-vite` branch. +- do NOT push to remote, just commit locally. +- run tests frequently with `just test` (which will test both backend and frontend). +- if you need to reinstall dependencies, run `just setup`. (which doed `pnpm install` and `uv sync`) +- if you need to start a dev server, run `pnpm dev` from the root of the monorepo (it will start both vite and django dev servers). +- if you want to interact the dev site in a browser, use playwright mcp and open `http://localhost:8000`. +- prefer RAG and websearch over guess and check +- refer frequently to the official documentation with questions: + - Vite: and + - django-vite: + +--- + +## Table of Contents + +1. [Executive Summary](#executive-summary) +2. [Current Architecture Analysis](#current-architecture-analysis) +3. [Target Architecture](#target-architecture) +4. [Migration Strategy](#migration-strategy) +5. [Detailed Implementation Plan](#detailed-implementation-plan) +6. [Risk Assessment & Mitigation](#risk-assessment--mitigation) +7. [Testing Strategy](#testing-strategy) +8. [Reference Information](#reference-information) + +--- + +## Current Architecture Analysis + +### 1. Webpack Configuration + +**File:** `frontend/webpack.config.js` + +#### Entry Points (7 total) + +```javascript +entry: { + main: './src/index.js', // Core site functionality + embedscope: './src/embedscope.js', // Microscope embed (uses D3 v3 CDN) + litemol: './src/my-litemol.js', // Protein structure viewer + spectraViewer: './src/spectra-viewer.js', // Full spectra viewer + simpleSpectraViewer: './src/simple-spectra-viewer.js', // Simple spectra + microscopeForm: './src/microscope-form.js', // Microscope config form + blast: './src/blast-app.js', // BLAST search + proteinTable: './src/protein-table.js', // Protein table +} +``` + +#### Code Splitting Strategy + +**6-tier manual chunking:** + +```javascript +splitChunks: { + cacheGroups: { + sentry: { priority: 30 }, // Shared error tracking + react: { priority: 25 }, // React + ReactDOM + scheduler + jquery: { priority: 20 }, // jQuery (used across bundles) + d3: { priority: 15 }, // D3 v7 (NOT in embedscope) + vendors: { priority: 10 }, // Other node_modules + common: { priority: 5 }, // Shared app code + } +} +``` + +**CRITICAL:** `embedscope` must NOT include D3 v7 (uses CDN D3 v3 instead). + +#### Asset Processing + +- **Sass/SCSS** → CSS with autoprefixer + cssnano +- **JavaScript** → Babel (preset-env, preset-react) +- **Assets** → Copy plugin for `microscope.js` +- **Source Maps** → Sentry webpack plugin uploads + +#### Dev Server + +- Port: 8080 +- Hot Reload: `HOT_RELOAD=1` env var +- CORS enabled for Django backend +- Public path: `http://localhost:8080/static/` + +### 2. Django Integration + +#### Settings Configuration + +**File:** `backend/config/settings/base.py` (lines 226-237) + +```python +INSTALLED_APPS.append("webpack_loader") + +WEBPACK_LOADER = { + "DEFAULT": { + "CACHE": not DEBUG, + "BUNDLE_DIR_NAME": "/", + "STATS_FILE": str(ROOT_DIR.parent / "frontend" / "dist" / "webpack-stats.json"), + "POLL_INTERVAL": 0.1, + "TIMEOUT": None, + "IGNORE": [r".*\.hot-update.js", r".+\.map"], + } +} +``` + +**File:** `backend/config/settings/test.py` (lines 100-105) + +```python +class MockWebpackLoader(FakeWebpackLoader): + def get_assets(self): + return {} + +WEBPACK_LOADER["DEFAULT"]["LOADER_CLASS"] = "config.settings.test.MockWebpackLoader" +``` + +#### Template Usage + +**10 templates use `render_bundle`:** + +| Template | Bundles Used | Notes | +|----------|-------------|-------| +| `fpbase/templates/base.html` | main (css + js) | Base template for all pages | +| `proteins/templates/spectra.html` | spectraViewer | Full spectra viewer page | +| `proteins/templates/spectra_graph.html` | simpleSpectraViewer | Embedded spectra widget | +| `proteins/templates/table.html` | proteinTable | Protein comparison table | +| `proteins/templates/compare.html` | simpleSpectraViewer | Protein comparison | +| `proteins/templates/proteins/microscope_form.html` | microscopeForm | Microscope config | +| `proteins/templates/proteins/microscope_embed.html` | main (css) + embedscope (js) | **CRITICAL: D3 v3** | +| `proteins/templates/proteins/protein_detail.html` | simpleSpectraViewer + litemol | Protein page | +| `proteins/templates/proteins/blast.html` | blast | BLAST search | +| `fpbase/templates/500.html` | main (css) | Error page | + +**Pattern Examples:** + +```django +{% load render_bundle from webpack_loader %} +{% render_bundle 'main' 'css' %} +{% render_bundle 'main' 'js' %} +{% render_bundle 'embedscope' 'js' %} +{% render_bundle 'litemol' attrs='defer' %} +``` + +### 3. E2E Test Infrastructure + +**File:** `backend/tests_e2e/conftest.py` (lines 121-142) + +```python +@pytest.fixture(scope="module", autouse=True) +def _setup_frontend_assets() -> None: + """Build webpack assets once per test module if needed.""" + stats_file = Path(django.conf.settings.WEBPACK_LOADER["DEFAULT"]["STATS_FILE"]) + + if _frontend_assets_need_rebuild(stats_file): + print("Building frontend assets for e2e tests...") + subprocess.check_output(["pnpm", "--filter", "fpbase", "build"], stderr=subprocess.PIPE) + + # UNDO MockWebpackLoader for e2e tests + utils.get_loader = _get_real_get_loader +``` + +**Logic:** + +- Checks if `webpack-stats.json` exists and is valid +- Checks if source files are newer than stats file +- Rebuilds frontend if needed +- Reverts MockWebpackLoader to real loader + +### 4. Package Structure + +**Root monorepo:** pnpm workspaces + +``` +fpbase/ +├── frontend/ # Main webpack bundle (this migration) +│ ├── dist/ # Build output +│ ├── src/ +│ │ ├── index.js # main entry +│ │ ├── embedscope.js # embedscope entry (D3 v3 CDN) +│ │ ├── blast-app.js # blast entry +│ │ └── ... +│ ├── package.json +│ └── webpack.config.js +├── packages/ +│ ├── spectra/ # ✅ Already uses Vite 4 +│ │ ├── vite.config.js +│ │ └── package.json +│ ├── blast/ # ✅ Already uses Vite 4 +│ │ ├── vite.config.js +│ │ └── package.json +│ └── protein-table/ # ✅ Already uses Vite 4 +└── package.json +``` + +**Note:** `spectra`, `blast`, and `protein-table` are consumed by webpack as workspace packages. In Vite, they'll be directly importable. + +### 5. Key Dependencies + +**From `frontend/package.json`:** + +```json +{ + "dependencies": { + "@fpbase/blast": "workspace:*", + "@fpbase/spectra": "workspace:*", + "@sentry/browser": "^10.22.0", + "jquery": "^3.7.0", + "d3": "^7.9.0", + "react": "^19.2.0", + "react-dom": "^19.2.0", + // ... bootstrap, select2, etc. + }, + "devDependencies": { + "webpack": "^5.102.1", + "webpack-bundle-tracker": "3.2.1", + "babel-loader": "^10.0.0", + // ... all webpack/babel plugins + } +} +``` + +--- + +## Target Architecture + +### 1. Vite Configuration + +**New file:** `frontend/vite.config.js` + +```javascript +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import { viteStaticCopy } from 'vite-plugin-static-copy'; +import { sentryVitePlugin } from '@sentry/vite-plugin'; + +export default defineConfig(({ mode }) => { + const isDev = mode === 'development'; + + return { + // Match Django STATIC_URL + base: '/static/', + + // Build configuration + build: { + // Output to frontend/dist/ + outDir: 'dist', + emptyOutDir: true, + + // Generate manifest.json for django-vite + manifest: 'manifest.json', + + // Source maps for Sentry + sourcemap: true, + + // Multi-page app configuration + rollupOptions: { + input: { + main: './src/index.js', + embedscope: './src/embedscope.js', + litemol: './src/my-litemol.js', + spectraViewer: './src/spectra-viewer.js', + simpleSpectraViewer: './src/simple-spectra-viewer.js', + microscopeForm: './src/microscope-form.js', + blast: './src/blast-app.js', + proteinTable: './src/protein-table.js', + }, + + // Manual code splitting (hybrid approach) + output: { + manualChunks(id) { + // Sentry (shared across all) + if (id.includes('node_modules/@sentry')) { + return 'vendor-sentry'; + } + + // React (shared, but NOT in embedscope) + if (id.includes('node_modules/react')) { + return 'vendor-react'; + } + + // jQuery (shared across multiple) + if (id.includes('node_modules/jquery')) { + return 'vendor-jquery'; + } + + // D3 v7 (EXCLUDE from embedscope - it uses CDN D3 v3) + if (id.includes('node_modules/d3')) { + // Check if this is being imported by embedscope + // If so, throw error to prevent bundling + return 'vendor-d3'; + } + }, + }, + }, + }, + + // Development server + server: { + port: 5173, // Vite default (change if needed) + strictPort: true, + origin: 'http://localhost:5173', + cors: true, + hmr: { + protocol: 'ws', + host: 'localhost', + }, + }, + + // Resolve aliases (match webpack) + resolve: { + alias: { + '@fpbase/spectra': '/packages/spectra/src/index.jsx', + '@fpbase/blast': '/packages/blast/src/index.js', + '@fpbase/protein-table': '/packages/protein-table/src/index.jsx', + }, + }, + + // Plugins + plugins: [ + // React with Fast Refresh + react(), + + // Copy static files (microscope.js) + viteStaticCopy({ + targets: [ + { + src: '../backend/fpbase/static/js/microscope.js', + dest: 'js', + }, + ], + }), + + // Sentry source map upload (production only) + !isDev && sentryVitePlugin({ + org: 'talley-lambert', + project: 'fpbase', + authToken: process.env.SENTRY_AUTH_TOKEN, + release: process.env.HEROKU_SLUG_COMMIT, + }), + ].filter(Boolean), + + // CSS configuration + css: { + postcss: { + plugins: [ + require('autoprefixer'), + require('cssnano'), + ], + }, + }, + + // Define environment variables + define: { + 'process.env.NODE_ENV': JSON.stringify(mode), + 'process.env.SENTRY_DSN': JSON.stringify(process.env.SENTRY_DSN || ''), + 'process.env.HEROKU_SLUG_COMMIT': JSON.stringify(process.env.HEROKU_SLUG_COMMIT || ''), + }, + }; +}); +``` + +### 2. Django Settings Updates + +**File:** `backend/config/settings/base.py` + +```python +# Remove webpack_loader, add django_vite +INSTALLED_APPS = [ + 'django_vite', # Add BEFORE other apps + # ... rest of INSTALLED_APPS +] + +# Remove WEBPACK_LOADER, add DJANGO_VITE +DJANGO_VITE = { + "default": { + "dev_mode": DEBUG, # Match DEBUG setting + "dev_server_protocol": "http", + "dev_server_host": "localhost", + "dev_server_port": 5173, + "static_url_prefix": "", + "manifest_path": str(ROOT_DIR.parent / "frontend" / "dist" / "manifest.json"), + } +} +``` + +**File:** `backend/config/settings/test.py` + +```python +# Remove MockWebpackLoader + +# django-vite in test mode uses manifest +DJANGO_VITE = { + "default": { + "dev_mode": False, # Always use manifest in tests + "manifest_path": str(ROOT_DIR.parent / "frontend" / "dist" / "manifest.json"), + } +} +``` + +### 3. Template Migration + +**Pattern changes:** + +| Old (webpack_loader) | New (django_vite) | +|---------------------|-------------------| +| `{% load render_bundle from webpack_loader %}` | `{% load django_vite %}` | +| `{% render_bundle 'main' 'css' %}` | `{% vite_asset 'src/index.js' %}` (auto-includes CSS) | +| `{% render_bundle 'main' 'js' %}` | `{% vite_asset 'src/index.js' %}` | +| `{% render_bundle 'embedscope' 'js' %}` | `{% vite_asset 'src/embedscope.js' %}` | +| `{% render_bundle 'litemol' attrs='defer' %}` | `{% vite_asset 'src/my-litemol.js' defer %}` | + +**New addition (for HMR in dev):** + +Add to `` in base.html: + +```django +{% load django_vite %} +{% vite_hmr_client %} +``` + +**Complete migration list:** + +1. `backend/fpbase/templates/base.html` + - Add `{% vite_hmr_client %}` in `` + - Replace `{% render_bundle 'main' 'css' %}` → `{% vite_asset 'src/index.js' %}` + - Replace `{% render_bundle 'main' 'js' %}` → (already included above) + +2. `backend/proteins/templates/spectra.html` + - Replace `{% render_bundle 'spectraViewer' %}` → `{% vite_asset 'src/spectra-viewer.js' %}` + +3. `backend/proteins/templates/spectra_graph.html` + - Replace `{% render_bundle 'simpleSpectraViewer' %}` → `{% vite_asset 'src/simple-spectra-viewer.js' %}` + +4. `backend/proteins/templates/table.html` + - Replace `{% render_bundle 'proteinTable' %}` → `{% vite_asset 'src/protein-table.js' %}` + +5. `backend/proteins/templates/compare.html` + - Replace `{% render_bundle 'simpleSpectraViewer' %}` → `{% vite_asset 'src/simple-spectra-viewer.js' %}` + +6. `backend/proteins/templates/proteins/microscope_form.html` + - Replace `{% render_bundle 'microscopeForm' %}` → `{% vite_asset 'src/microscope-form.js' %}` + +7. `backend/proteins/templates/proteins/microscope_embed.html` **[CRITICAL: D3 v3]** + - Replace `{% render_bundle 'main' 'css' %}` → `{% vite_asset 'src/index.js' %}` + - Replace `{% render_bundle 'embedscope' 'js' %}` → `{% vite_asset 'src/embedscope.js' %}` + - **VERIFY:** embedscope.js must NOT import D3 v7 + +8. `backend/proteins/templates/proteins/protein_detail.html` + - Replace `{% render_bundle 'simpleSpectraViewer' %}` → `{% vite_asset 'src/simple-spectra-viewer.js' %}` + - Replace `{% render_bundle 'litemol' attrs='defer' %}` → `{% vite_asset 'src/my-litemol.js' defer %}` + +9. `backend/proteins/templates/proteins/blast.html` + - Replace `{% render_bundle 'blast' %}` → `{% vite_asset 'src/blast-app.js' %}` + +10. `backend/fpbase/templates/500.html` + - Replace `{% render_bundle 'main' 'css' %}` → `{% vite_asset 'src/index.js' %}` + +### 4. E2E Test Updates + +**File:** `backend/tests_e2e/conftest.py` + +```python +@pytest.fixture(scope="module", autouse=True) +def _setup_frontend_assets() -> None: + """Build Vite assets once per test module if needed.""" + # Change from webpack-stats.json to manifest.json + manifest_file = Path(django.conf.settings.DJANGO_VITE["default"]["manifest_path"]) + + if _frontend_assets_need_rebuild(manifest_file): + print("Building frontend assets for e2e tests...") + # Change from webpack build to vite build + subprocess.check_output(["pnpm", "--filter", "fpbase", "build"], stderr=subprocess.PIPE) + + # No need to revert MockWebpackLoader (django-vite doesn't need mocking) +``` + +Update `_frontend_assets_need_rebuild()`: + +```python +def _frontend_assets_need_rebuild(manifest_file) -> bool: + """Check if frontend assets need to be rebuilt.""" + if not manifest_file.is_file(): + return True + + # Check if manifest is valid JSON + try: + manifest = json.loads(manifest_file.read_bytes()) + if not manifest: + return True + except (json.JSONDecodeError, ValueError): + return True + + # Check if source files are newer + manifest_mtime = manifest_file.stat().st_mtime + frontend_src = Path(__file__).parent.parent.parent / "frontend" / "src" + if any( + f.stat().st_mtime > manifest_mtime for f in frontend_src.rglob("*") + if f.is_file() and not f.name.startswith(".") + ): + return True + + return False +``` + +--- + +## Migration Strategy + +### Phase Overview + +``` +Phase 1: Preparation +├─ Install dependencies +├─ Create vite.config.js +├─ Test standalone build +└─ Verify manifest.json + +Phase 2: Vite Configuration +├─ Configure all 7 entry points +├─ Verify D3 v3/v7 isolation +├─ Test dev server + HMR +└─ Profile bundle sizes + +Phase 3: Django Integration +├─ Update settings (base.py, test.py) +├─ Migrate 10 templates +├─ Test each page loads +└─ Verify no console errors + +Phase 4: Testing & Validation +├─ Update E2E test setup +├─ Run full test suite +├─ Manual QA on staging +└─ Fix any issues + +Phase 5: Production Deployment +├─ Remove webpack dependencies +├─ Update documentation +├─ Deploy to production +└─ Monitor for issues + +``` + +## Detailed Implementation Plan + +### Phase 1: Preparation + +#### Step 1.1: Install Vite Dependencies + +```bash +cd frontend + +# Remove webpack dependencies (will be done in Phase 5) +# For now, keep both to allow rollback + +# Install Vite and plugins +pnpm add -D vite@^7.1.12 \ + @vitejs/plugin-react@^5.1.0 \ + vite-plugin-static-copy@^3.1.4 + +# Install Sentry Vite plugin +pnpm add -D @sentry/vite-plugin@^4.6.0 + +# Verify Node.js version (Vite 7 requires Node 20.19+ or 22.12+) +node --version # Should be 20.19+ or 22.12+ +``` + +#### Step 1.2: Create vite.config.js + +Create `frontend/vite.config.js` with the configuration from [Target Architecture](#1-vite-configuration). + +#### Step 1.3: Update package.json Scripts + +```json +{ + "scripts": { + "dev": "vite", + "dev:old": "HOT_RELOAD=1 webpack-dev-server --mode development", + "build": "vite build", + "build:old": "NODE_ENV=production webpack --mode production", + "preview": "vite preview" + } +} +``` + +#### Step 1.4: Add Modulepreload Polyfill + +**Edit `frontend/src/index.js`** (and other entry points): + +```javascript +// Add as first import +import 'vite/modulepreload-polyfill'; + +// Rest of imports... +``` + +Do this for all 8 entry points: + +- `src/index.js` +- `src/embedscope.js` +- `src/blast-app.js` +- `src/spectra-viewer.js` +- `src/simple-spectra-viewer.js` +- `src/microscope-form.js` +- `src/protein-table.js` +- `src/my-litemol.js` + +#### Step 1.5: Test Standalone Vite Build + +```bash +cd frontend + +# Clean old build +rm -rf dist + +# Run Vite build +pnpm run build + +# Verify output +ls -lah dist/ + +# Expected files: +# - manifest.json +# - assets/main-[hash].js +# - assets/main-[hash].css +# - assets/embedscope-[hash].js +# - ... (all entry points) +# - assets/vendor-sentry-[hash].js +# - assets/vendor-react-[hash].js +# - assets/vendor-jquery-[hash].js +# - assets/vendor-d3-[hash].js +# - js/microscope.js (static copy) + +# Verify manifest.json structure +cat dist/manifest.json | jq . +``` + +**Success Criteria:** + +- ✅ Build completes without errors +- ✅ `manifest.json` exists and is valid JSON +- ✅ All 8 entry points have generated JS files +- ✅ CSS files are extracted +- ✅ Vendor chunks are created +- ✅ `microscope.js` is copied to `js/` + +--- + +### Phase 2: Vite Configuration + +#### Step 2.1: Verify D3 v3/v7 Isolation + +**CRITICAL:** `embedscope.js` uses CDN D3 v3, must NOT bundle D3 v7. + +**Check embedscope imports:** + +```bash +cd frontend +grep -r "from 'd3'" src/embedscope.js +grep -r "import.*d3" src/embedscope.js +``` + +**Expected:** NO matches. Embedscope should not import D3. + +**If D3 is imported:** + +1. Refactor embedscope to remove D3 v7 dependency +2. Ensure it only uses `window.d3` (CDN D3 v3) + +#### Step 2.2: Profile Bundle Sizes + +```bash +cd frontend + +# Build with analysis +pnpm run build + +# Analyze output +du -sh dist/assets/*.js | sort -h + +# Compare to webpack baseline +# Expected: ±10% variance is acceptable +# If >20% larger, investigate and optimize +``` + +**Baseline (webpack):** (record actual sizes from current build) + +``` +main: ~XXX KB +embedscope: ~XXX KB +blast: ~XXX KB +... +``` + +#### Step 2.3: Test Dev Server + HMR + +```bash +cd frontend + +# Start Vite dev server +pnpm run dev + +# Server should start on http://localhost:5173 +# Open browser to http://localhost:5173/src/index.js +# (Will NOT work yet - Django integration needed) + +# Test HMR by editing a file +# Edit src/index.js, save +# Check terminal for HMR update message +``` + +**Success Criteria:** + +- ✅ Dev server starts successfully +- ✅ HMR updates reflect in browser (<1s) +- ✅ No errors in browser console +- ✅ No errors in terminal + +#### Step 2.4: Test Sentry Integration + +**Verify source maps:** + +```bash +cd frontend + +# Build +pnpm run build + +# Check for .map files +ls -lah dist/assets/*.map + +# Verify Sentry plugin ran (if SENTRY_AUTH_TOKEN set) +# Check build output for "Sentry: Uploading source maps" +``` + +--- + +### Phase 3: Django Integration + +#### Step 3.1: Install django-vite + +```bash +cd /Users/talley/dev/self/FPbase +uv add django-vite +``` + +#### Step 3.2: Update Django Settings + +**Edit `backend/config/settings/base.py`:** + +```python +# Line 226: Change from webpack_loader to django_vite +INSTALLED_APPS = [ + # ... existing apps + 'django_vite', # ADD THIS (before apps that use it) + # 'webpack_loader', # REMOVE THIS +] + +# Lines 228-237: Replace WEBPACK_LOADER with DJANGO_VITE +DJANGO_VITE = { + "default": { + "dev_mode": DEBUG, + "dev_server_protocol": "http", + "dev_server_host": "localhost", + "dev_server_port": 5173, + "static_url_prefix": "", + "manifest_path": str(ROOT_DIR.parent / "frontend" / "dist" / "manifest.json"), + } +} + +# Remove old WEBPACK_LOADER config +``` + +**Edit `backend/config/settings/test.py`:** + +```python +# Remove lines 10, 100-105 (MockWebpackLoader) + +# Add DJANGO_VITE override +DJANGO_VITE = { + "default": { + "dev_mode": False, + "manifest_path": str(ROOT_DIR.parent / "frontend" / "dist" / "manifest.json"), + } +} +``` + +#### Step 3.3: Migrate Templates + +**Semi-automated approach using sed:** + +```bash +cd backend + +# Backup templates first +tar -czf templates_backup_$(date +%Y%m%d).tar.gz */templates/ + +# Step 1: Replace load statement +find . -name "*.html" -exec sed -i '' \ + 's/{%\s*load\s*render_bundle\s*from\s*webpack_loader\s*%}/{%\ load\ django_vite\ %}/g' {} \; + +# Step 2: Replace render_bundle calls +# Note: These are approximate patterns, verify each manually + +# main bundle (most common) +find . -name "*.html" -exec sed -i '' \ + "s/{%\s*render_bundle\s*'main'\s*'css'\s*%}/{%\ vite_asset\ 'src\/index.js'\ %}/g" {} \; +find . -name "*.html" -exec sed -i '' \ + "s/{%\s*render_bundle\s*'main'\s*'js'\s*%}//g" {} \; # Remove (auto-included) +find . -name "*.html" -exec sed -i '' \ + "s/{%\s*render_bundle\s*'main'\s*%}/{%\ vite_asset\ 'src\/index.js'\ %}/g" {} \; + +# Other bundles +find . -name "*.html" -exec sed -i '' \ + "s/{%\s*render_bundle\s*'embedscope'\s*'js'\s*%}/{%\ vite_asset\ 'src\/embedscope.js'\ %}/g" {} \; +find . -name "*.html" -exec sed -i '' \ + "s/{%\s*render_bundle\s*'blast'\s*%}/{%\ vite_asset\ 'src\/blast-app.js'\ %}/g" {} \; +find . -name "*.html" -exec sed -i '' \ + "s/{%\s*render_bundle\s*'spectraViewer'\s*%}/{%\ vite_asset\ 'src\/spectra-viewer.js'\ %}/g" {} \; +find . -name "*.html" -exec sed -i '' \ + "s/{%\s*render_bundle\s*'simpleSpectraViewer'\s*%}/{%\ vite_asset\ 'src\/simple-spectra-viewer.js'\ %}/g" {} \; +find . -name "*.html" -exec sed -i '' \ + "s/{%\s*render_bundle\s*'microscopeForm'\s*%}/{%\ vite_asset\ 'src\/microscope-form.js'\ %}/g" {} \; +find . -name "*.html" -exec sed -i '' \ + "s/{%\s*render_bundle\s*'proteinTable'\s*%}/{%\ vite_asset\ 'src\/protein-table.js'\ %}/g" {} \; +find . -name "*.html" -exec sed -i '' \ + "s/{%\s*render_bundle\s*'litemol'\s*attrs='defer'\s*%}/{%\ vite_asset\ 'src\/my-litemol.js'\ defer\ %}/g" {} \; + +# Verify no old tags remain +grep -r "render_bundle" . --include="*.html" +# Expected: No matches (or only in backups/docs) +``` + +**Manual verification required for each template!** + +#### Step 3.4: Add HMR Client to Base Template + +**Edit `backend/fpbase/templates/base.html`:** + +```django +{% load i18n %} +{% load static %} +{% load django_vite %} {# Changed from webpack_loader #} +{% load webp_picture from protein_tags %} + + + + + {% vite_hmr_client %} {# ADD THIS - enables HMR in dev #} + + {% block ga %} + ... + {% endblock ga %} + + ... + + {% vite_asset 'src/index.js' %} {# Changed from render_bundle 'main' #} + + ... + + ... + +``` + +#### Step 3.5: Test Each Page + +For this, you can use the dev server and playwright mcp + +**With Django dev server running:** + +```bash +# Terminal 1: Both django and vite dev concurrently +pnpm dev + +# Open browser to http://localhost:8000 +# Test each page: +``` + +**Checklist:** + +- [ ] Homepage loads (main bundle) +- [ ] Protein detail page (simpleSpectraViewer + litemol) +- [ ] Spectra viewer page (spectraViewer) +- [ ] BLAST page (blast) +- [ ] Protein table page (proteinTable) +- [ ] Microscope form page (microscopeForm) +- [ ] Microscope embed page **[CRITICAL: D3 v3]** (embedscope) +- [ ] Compare page (simpleSpectraViewer) +- [ ] Spectra graph widget (simpleSpectraViewer) +- [ ] Error page (500.html) + +**For each page, verify:** + +- ✅ Page loads without errors +- ✅ JavaScript functionality works +- ✅ Styles are applied correctly +- ✅ No console errors (F12 → Console) +- ✅ HMR works (edit JS file, save, see instant update) + +--- + +### Phase 4: Testing & Validation + +#### Step 4.1: Update E2E Test Setup + +**Edit `backend/tests_e2e/conftest.py`:** + +```python +# Line 131: Update stats_file to manifest_file +manifest_file = Path(django.conf.settings.DJANGO_VITE["default"]["manifest_path"]) + +# Line 134: Update condition check +if _frontend_assets_need_rebuild(manifest_file): + print("Building frontend assets for e2e tests...") + subprocess.check_output(["pnpm", "--filter", "fpbase", "build"], stderr=subprocess.PIPE) + +# Lines 138-142: Remove webpack-specific code +# No need to undo MockWebpackLoader (django-vite doesn't mock) + +# Update _frontend_assets_need_rebuild function (lines 100-118) +def _frontend_assets_need_rebuild(manifest_file) -> bool: + """Check if frontend assets need to be rebuilt.""" + if not manifest_file.is_file(): + return True + + # Check if manifest is valid JSON + try: + manifest = json.loads(manifest_file.read_bytes()) + if not manifest: + return True + except (json.JSONDecodeError, ValueError): + return True + + # Check if source files are newer than manifest + manifest_mtime = manifest_file.stat().st_mtime + frontend_src = Path(__file__).parent.parent.parent / "frontend" / "src" + if any( + f.stat().st_mtime > manifest_mtime for f in frontend_src.rglob("*") + if f.is_file() and not f.name.startswith(".") + ): + return True + + return False +``` + +#### Step 4.2: Run E2E Test Suite + +```bash +cd backend + +# Run e2e tests +uv run pytest backend/tests_e2e/ -v + +# Expected: All tests pass +# If failures, investigate: +# - Are assets being built correctly? +# - Are pages loading correctly? +# - Are JavaScript interactions working? +``` + +#### Step 4.3: Run Unit Test Suite + +```bash +cd backend + +# Run unit tests +uv run pytest + +# Expected: All tests pass +# Coverage should remain similar to baseline +``` + +--- + +### Phase 5: Production Deployment + +#### Step 5.1: Remove Webpack Dependencies + +**Edit `frontend/package.json`:** + +```bash +cd frontend + +# Remove webpack dependencies +pnpm remove webpack webpack-cli webpack-dev-server \ + webpack-bundle-tracker webpack-bundle-analyzer \ + clean-webpack-plugin copy-webpack-plugin \ + css-loader css-minimizer-webpack-plugin \ + mini-css-extract-plugin postcss-loader \ + sass-loader style-loader \ + babel-loader @babel/core @babel/preset-env @babel/preset-react \ + @babel/plugin-syntax-dynamic-import + +# Remove webpack.config.js +rm webpack.config.js + +# Update scripts (remove :old variants) +# Edit package.json: +{ + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + } +} +``` + +#### Step 5.2: Update CI/CD + +**If using GitHub Actions, update `.github/workflows/*.yml`:** + +```yaml +# Change build command +- name: Build frontend + run: | + cd frontend + pnpm install + pnpm run build # Now uses Vite + +# No changes needed for Django tests (they auto-detect manifest.json) +``` + +**If using Heroku, update `Procfile` (if needed):** + +``` +# Usually no changes needed +# Heroku buildpack should auto-detect pnpm and run build script +``` + +#### Step 5.3: Test Production Build + +```bash +cd frontend + +# Clean build +rm -rf dist + +# Production build +NODE_ENV=production pnpm run build + +# Verify output +ls -lah dist/ + +# Test with Django production settings +cd ../backend +DJANGO_DEBUG=False uv run python manage.py runserver + +# Open http://localhost:8000 +# Verify all pages load correctly +``` + +#### Step 5.4: Verify Sentry Source Maps + +**After deploying to staging/production:** + +1. Trigger an error (e.g., call undefined function in console) +2. Check Sentry for the error +3. Verify stack trace shows original source code (not minified) +4. Verify file paths are correct + +#### Step 5.5: Update Documentation + +**Edit `README.md`:** + +```markdown +## Frontend Development + +### Tech Stack + +- **Build Tool:** Vite 7 +- **Framework:** React 19 +- **Languages:** JavaScript, Sass/SCSS + +### Setup + +```bash +cd frontend +pnpm install +``` + +### Development + +```bash +# Start both Vite and Django dev servers concurrently +pnpm dev +``` + +Visit + +### Production Build + +```bash +pnpm run build +``` + +Output: `frontend/dist/` + +### Entry Points + +- `main`: Core site functionality (index.js) +- `embedscope`: Microscope embed viewer (embedscope.js) +- `blast`: BLAST search (blast-app.js) +- `spectraViewer`: Full spectra viewer (spectra-viewer.js) +- `simpleSpectraViewer`: Simple spectra widget (simple-spectra-viewer.js) +- `microscopeForm`: Microscope config form (microscope-form.js) +- `proteinTable`: Protein comparison table (protein-table.js) +- `litemol`: Protein structure viewer (my-litemol.js) + +## Risk Assessment & Mitigation + +### High Priority Risks + +#### Risk 1: D3 v3/v7 Conflict in Embedscope + +**Risk Level:** CRITICAL +**Probability:** Medium +**Impact:** High (chart breaks) + +**Description:** +`embedscope.js` uses CDN D3 v3, but other bundles use D3 v7. If D3 v7 is accidentally bundled into embedscope, it will conflict with CDN D3 v3 and break charts. + +**Mitigation:** + +1. **Pre-migration:** Audit `src/embedscope.js` for D3 imports + + ```bash + grep -r "from 'd3'" src/embedscope.js + ``` + + Expected: No matches + +2. **Build-time check:** Add ESLint rule (see Phase 2.1) + +3. **Manual verification:** After each build, inspect embedscope bundle: + + ```bash + grep -i "d3" dist/assets/embedscope-*.js + ``` + + Expected: Only references to `window.d3` (CDN) + +4. **Runtime test:** Load microscope embed page, check for console errors + +**Rollback:** Revert vite.config.js changes, rebuild with webpack + +--- + +#### Risk 2: Template Rendering Failures + +**Risk Level:** HIGH +**Probability:** Medium +**Impact:** High (pages don't load) + +**Description:** +10+ templates need migration from `{% render_bundle %}` to `{% vite_asset %}`. Incorrect paths or syntax will cause template errors. + +**Mitigation:** + +1. **Automated migration:** Use sed scripts (Phase 3.3) + +2. **Manual verification:** Review each template change via git diff + +3. **Comprehensive testing:** Test every page manually (Phase 3.5) + +4. **E2E tests:** Run full test suite (Phase 4.2) + +**Rollback:** Restore from `templates_backup_*.tar.gz` + +--- + +#### Risk 3: Bundle Size Regression + +**Risk Level:** MEDIUM +**Probability:** Low +**Impact:** Medium (slower page loads) + +**Description:** +Vite's automatic code splitting may produce larger bundles than webpack's manual configuration. + +**Mitigation:** + +1. **Baseline measurement:** Record webpack bundle sizes before migration + +2. **Vite profiling:** Compare Vite bundle sizes (Phase 2.2) + +3. **Acceptance criteria:** ±10% variance acceptable, >20% requires investigation + +4. **Optimization:** Adjust `manualChunks` in vite.config.js if needed + +**Rollback:** Revert to webpack if bundles are >30% larger and optimization fails + +--- + +### Medium Priority Risks + +#### Risk 4: jQuery Global Injection + +**Risk Level:** MEDIUM +**Probability:** Low +**Impact:** Medium (jQuery plugins break) + +**Description:** +Webpack uses `ProvidePlugin` to auto-inject `$` and `jQuery`. Vite doesn't have this by default. Legacy code may expect global jQuery. + +**Mitigation:** + +1. **Manual injection:** In `src/index.js`: + + ```javascript + import $ from 'jquery'; + window.$ = window.jQuery = $; + ``` + +2. **Alternative:** Use `vite-plugin-inject` for auto-injection: + + ```javascript + // vite.config.js + import inject from '@rollup/plugin-inject'; + + plugins: [ + inject({ + $: 'jquery', + jQuery: 'jquery', + }), + ] + ``` + +**Rollback:** None needed (fix is quick) + +--- + +#### Risk 5: E2E Test Rebuild Detection + +**Risk Level:** MEDIUM +**Probability:** Low +**Impact:** Medium (stale assets in tests) + +**Description:** +E2E tests currently check `webpack-stats.json` freshness. Migration to `manifest.json` requires updating this logic. + +**Mitigation:** + +1. **Update `conftest.py`:** Change stats_file to manifest_file (Phase 4.1) + +2. **Update `_frontend_assets_need_rebuild()`:** Check manifest.json validity + +3. **Test:** Run e2e suite and verify rebuild triggers correctly + +**Rollback:** Revert conftest.py changes + +--- + +### Low Priority Risks + +#### Risk 6: Sentry Source Map Upload + +**Risk Level:** LOW +**Probability:** Low +**Impact:** Medium (degraded error tracking) + +**Description:** +Sentry webpack plugin is replaced with Sentry Vite plugin. Configuration differences may break source map uploads. + +**Mitigation:** + +1. **Use official plugin:** `@sentry/vite-plugin` + +2. **Test after deployment:** Trigger error, check Sentry for source-mapped stack + +3. **Verify release:** Check Sentry releases for uploaded artifacts + +**Rollback:** None needed (source maps are optional) + +--- + +## Testing Strategy + +### 1. Unit Tests + +**No changes required** - Unit tests use MockWebpackLoader in test.py, which is replaced by django-vite's manifest-based loading. + +**Verification:** + +```bash +cd backend +uv run pytest --cov +``` + +**Success Criteria:** + +- ✅ All tests pass +- ✅ Coverage remains ≥90% (or baseline) + +--- + +### 2. E2E Tests + +**Changes required** - Update `conftest.py` to use manifest.json instead of webpack-stats.json. + +**Verification:** + +```bash +cd backend +uv run pytest backend/tests_e2e/ -v +``` + +**Success Criteria:** + +- ✅ All tests pass +- ✅ Frontend assets rebuild when source files change +- ✅ No stale asset issues + +--- + +### 3. Manual Testing + +**Comprehensive page testing** - Every page that uses JavaScript bundles. + +**Test Matrix:** + +| Page | Bundle(s) | Test Cases | +|------|----------|------------| +| Homepage | main | Navigation, search autocomplete, login | +| Protein detail | main, simpleSpectraViewer, litemol | Spectra widget, 3D viewer, favorites | +| Spectra viewer | spectraViewer | Load spectra, change filters, calculate efficiency | +| BLAST | blast | Submit search, view results | +| Protein table | proteinTable | Sort, filter, export | +| Microscope form | microscopeForm | Add/remove items, submit form | +| Microscope embed | main (css), embedscope | **D3 v3 chart renders correctly** | +| Compare | simpleSpectraViewer | Compare proteins, view spectra | +| Spectra graph widget | simpleSpectraViewer | Embedded spectra render | +| Error page (500) | main (css) | Styles load correctly | + +**For each page:** + +- ✅ Page loads without errors +- ✅ JavaScript functionality works +- ✅ Styles are applied correctly +- ✅ No console errors (F12 → Console) +- ✅ Network tab shows assets load from `/static/assets/` (production) +- ✅ Network tab shows assets load from `localhost:5173` (dev) + +--- + +### 4. Performance Testing + +**Metrics to measure:** + +| Metric | Webpack (Baseline) | Vite (Target) | Change | +|--------|-------------------|---------------|--------| +| Production build time | 30-50s | 5-10s | -80% | +| Dev server startup | 10-30s | <1s | -90%+ | +| HMR update time | ~1000ms | ~100ms | -90% | +| Bundle size (main) | XXX KB | ±10% | Accept | +| Bundle size (embedscope) | XXX KB | ±10% | Accept | +| Bundle size (total) | XXX KB | ±10% | Accept | +| Lighthouse score | XX/100 | ≥XX/100 | Maintain | + +**Tools:** + +- Build time: `time pnpm run build` +- Bundle size: `du -sh dist/assets/*.js | sort -h` +- Lighthouse: Chrome DevTools → Lighthouse → Run audit + +**Acceptance Criteria:** + +- ✅ Build time improved by ≥50% +- ✅ HMR improved by ≥50% +- ✅ Bundle sizes within ±10% (or smaller) +- ✅ Lighthouse score unchanged or improved + +--- + +## Reference Information + +### Key Files Modified + +**Frontend:** + +- ✅ `frontend/vite.config.js` (new) +- ✅ `frontend/package.json` (scripts, dependencies) +- ✅ `frontend/src/index.js` (add modulepreload polyfill) +- ✅ `frontend/src/embedscope.js` (add modulepreload polyfill) +- ✅ `frontend/src/*.js` (all entry points: add modulepreload polyfill) +- ❌ `frontend/webpack.config.js` (deleted in Phase 5) + +**Backend:** + +- ✅ `backend/config/settings/base.py` (INSTALLED_APPS, DJANGO_VITE) +- ✅ `backend/config/settings/test.py` (DJANGO_VITE, remove MockWebpackLoader) +- ✅ `backend/tests_e2e/conftest.py` (_setup_frontend_assets,_frontend_assets_need_rebuild) + +**Templates (10 files):** + +- ✅ `backend/fpbase/templates/base.html` +- ✅ `backend/fpbase/templates/500.html` +- ✅ `backend/proteins/templates/spectra.html` +- ✅ `backend/proteins/templates/spectra_graph.html` +- ✅ `backend/proteins/templates/table.html` +- ✅ `backend/proteins/templates/compare.html` +- ✅ `backend/proteins/templates/proteins/microscope_form.html` +- ✅ `backend/proteins/templates/proteins/microscope_embed.html` +- ✅ `backend/proteins/templates/proteins/protein_detail.html` +- ✅ `backend/proteins/templates/proteins/blast.html` + +--- + +### Documentation Resources + +**Vite:** + +- Official Guide: +- Backend Integration: +- Migration from v6: + +**django-vite:** + +- GitHub: +- PyPI: +- README: + +**Sentry:** + +- Vite Plugin: + +**Related Guides:** + +- Webpack to Vite Migration: +- Vite vs Webpack 2025: + +--- + +### Commands Reference + +**Vite:** + +```bash +# Development +pnpm run dev # Start dev server (http://localhost:5173) +pnpm run build # Production build +pnpm run preview # Preview production build + +# Analysis +du -sh dist/assets/*.js # Bundle sizes +``` + +**Django:** + +```bash +# Development +uv run python manage.py runserver + +# Testing +uv run pytest # All tests +uv run pytest backend/tests_e2e/ -v # E2E tests only +``` + +**Combined Development:** + +```bash +# Vite dev server and Django dev server concurrently +pnpm dev + +# Visit: http://localhost:8000 +``` + +--- + +### Migration Checklist + +**Phase 1: Preparation** + +- [ ] Node.js version ≥20.19 or ≥22.12 +- [ ] Install Vite dependencies +- [ ] Create vite.config.js +- [ ] Add modulepreload polyfill to entry points +- [ ] Test standalone Vite build +- [ ] Verify manifest.json exists + +**Phase 2: Configuration** + +- [ ] Verify D3 v3/v7 isolation +- [ ] Profile bundle sizes (±10%) +- [ ] Test dev server + HMR +- [ ] Test Sentry integration + +**Phase 3: Django Integration** + +- [ ] Install django-vite +- [ ] Update settings (base.py, test.py) +- [ ] Migrate 10 templates +- [ ] Add HMR client to base.html +- [ ] Test each page manually + +**Phase 4: Testing** + +- [ ] Update E2E test setup (conftest.py) +- [ ] Run E2E test suite (all pass) +- [ ] Run unit test suite (all pass) +- [ ] Manual QA (all pages) + +**Phase 5: Production** + +- [ ] Remove webpack dependencies +- [ ] Update CI/CD scripts +- [ ] Test production build +- [ ] Verify Sentry source maps +- [ ] Update documentation +- [ ] Create PR and deploy From dd9035320950908db246f1a7483e32fbfabc9e24 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Wed, 29 Oct 2025 19:38:52 -0400 Subject: [PATCH 03/60] feat: Phase 1 - Vite 7 migration preparation complete MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Install Vite 7 and required plugins (@vitejs/plugin-react, vite-plugin-static-copy, @sentry/vite-plugin) - Create vite.config.js with: * All 8 entry points configured * Manual code splitting for vendor chunks (sentry, react, jquery, d3) * JSX support in .js files via esbuild * Sass/SCSS with autoprefixer and cssnano * Sentry source map upload configuration * Static file copying for microscope.js - Update package.json scripts (dev, build, preview) - Add modulepreload polyfill to all 8 entry points - Inject jQuery globally (replaces webpack ProvidePlugin) - Fix LiteMol CommonJS import compatibility - Verify build output: manifest.json, all bundles, vendor chunks, CSS extraction Build time: ~12s (vs webpack ~30-50s) All 8 entry points building successfully 🤖 Generated with Claude Code --- frontend/package.json | 11 +- frontend/src/blast-app.js | 3 + frontend/src/embedscope.js | 8 +- frontend/src/index.js | 8 + frontend/src/microscope-form.js | 5 + frontend/src/my-litemol.js | 8 +- frontend/src/protein-table.js | 3 + frontend/src/simple-spectra-viewer.js | 5 + frontend/src/spectra-viewer.js | 3 + frontend/vite.config.js | 160 +++++ pnpm-lock.yaml | 873 +++++++++++++++++++++++++- 11 files changed, 1076 insertions(+), 11 deletions(-) create mode 100644 frontend/vite.config.js diff --git a/frontend/package.json b/frontend/package.json index 097b6389..26cf48e3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -34,9 +34,11 @@ "@babel/preset-env": "^7.7.7", "@babel/preset-react": "^7.7.4", "@sentry/cli": "^2.57.0", + "@sentry/vite-plugin": "^4.6.0", "@sentry/webpack-plugin": "^4.5.0", "@types/react": "^19", "@types/react-dom": "^19", + "@vitejs/plugin-react": "^5.1.0", "autoprefixer": "^10.4.21", "babel-loader": "^10.0.0", "babel-plugin-transform-imports": "^2.0.0", @@ -51,6 +53,8 @@ "sass": "1.93.2", "sass-loader": "16.0.6", "terser-webpack-plugin": "^5.3.14", + "vite": "^7.1.12", + "vite-plugin-static-copy": "^3.1.4", "webpack": "^5.102.1", "webpack-bundle-analyzer": "^4.9.0", "webpack-bundle-tracker": "3.2.1", @@ -58,8 +62,11 @@ "webpack-dev-server": "^5.2.2" }, "scripts": { - "start": "HOT_RELOAD=1 webpack-dev-server --mode development", - "build": "NODE_ENV=production webpack --mode production", + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "start:old": "HOT_RELOAD=1 webpack-dev-server --mode development", + "build:old": "NODE_ENV=production webpack --mode production", "clean": "pnpm exec -- rm -rf dist" }, "browserslist": { diff --git a/frontend/src/blast-app.js b/frontend/src/blast-app.js index 82b649d5..46728879 100644 --- a/frontend/src/blast-app.js +++ b/frontend/src/blast-app.js @@ -1,3 +1,6 @@ +// Vite modulepreload polyfill (must be first) +import "vite/modulepreload-polyfill" + // Initialize Sentry first to catch errors during module loading import "./js/sentry-init.js" diff --git a/frontend/src/embedscope.js b/frontend/src/embedscope.js index f3beb01c..515d5520 100644 --- a/frontend/src/embedscope.js +++ b/frontend/src/embedscope.js @@ -1,7 +1,13 @@ +// Vite modulepreload polyfill (must be first) +import "vite/modulepreload-polyfill" + // Initialize Sentry first to catch errors during module loading import "./js/sentry-init.js" -import "jquery" +// Inject jQuery globally for Vite (replaces webpack ProvidePlugin) +import $ from "jquery" + +window.$ = window.jQuery = $ import "bootstrap" import "nouislider" // D3 v3 is loaded from CDN for microscope.js compatibility (see microscope_embed.html) diff --git a/frontend/src/index.js b/frontend/src/index.js index 7879c26e..d563b128 100644 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -1,8 +1,16 @@ +// Vite modulepreload polyfill (must be first) +import "vite/modulepreload-polyfill" + // Initialize Sentry first to catch errors during module loading import "./js/sentry-init.js" import "./js/jquery-ajax-sentry.js" // Track jQuery AJAX errors import "regenerator-runtime/runtime" + +// Inject jQuery globally for Vite (replaces webpack ProvidePlugin) +import $ from "jquery" + +window.$ = window.jQuery = $ import "select2/dist/css/select2.css" import "select2-theme-bootstrap4/dist/select2-bootstrap.css" import "nouislider/distribute/nouislider.min.css" diff --git a/frontend/src/microscope-form.js b/frontend/src/microscope-form.js index 3bbbb90a..47c285f5 100644 --- a/frontend/src/microscope-form.js +++ b/frontend/src/microscope-form.js @@ -1,8 +1,13 @@ +// Vite modulepreload polyfill (must be first) +import "vite/modulepreload-polyfill" + // Initialize Sentry first to catch errors during module loading import "./js/sentry-init.js" import "./js/jquery-ajax-sentry.js" // Track jQuery AJAX errors import $ from "jquery" + +window.$ = window.jQuery = $ import "select2/dist/css/select2.css" import "select2-theme-bootstrap4/dist/select2-bootstrap.css" import "select2/dist/js/select2.full" diff --git a/frontend/src/my-litemol.js b/frontend/src/my-litemol.js index 3ebbf6f4..727cced8 100644 --- a/frontend/src/my-litemol.js +++ b/frontend/src/my-litemol.js @@ -1,10 +1,16 @@ +// Vite modulepreload polyfill (must be first) +import "vite/modulepreload-polyfill" + // Initialize Sentry first to catch errors during module loading import "./js/sentry-init.js" import "./js/jquery-ajax-sentry.js" // Track jQuery AJAX errors import "./css/litemol/LiteMol-plugin-blue.css" import $ from "jquery" -import LiteMol from "./js/pdb/LiteMol-plugin" + +window.$ = window.jQuery = $ + +import * as LiteMol from "./js/pdb/LiteMol-plugin" // Mark this bundle for Sentry context window.FPBASE = window.FPBASE || {} diff --git a/frontend/src/protein-table.js b/frontend/src/protein-table.js index a119198a..5eee9379 100644 --- a/frontend/src/protein-table.js +++ b/frontend/src/protein-table.js @@ -1,3 +1,6 @@ +// Vite modulepreload polyfill (must be first) +import "vite/modulepreload-polyfill" + // Initialize Sentry first to catch errors during module loading import "./js/sentry-init.js" diff --git a/frontend/src/simple-spectra-viewer.js b/frontend/src/simple-spectra-viewer.js index eabe4983..89b55ebb 100644 --- a/frontend/src/simple-spectra-viewer.js +++ b/frontend/src/simple-spectra-viewer.js @@ -1,7 +1,12 @@ +// Vite modulepreload polyfill (must be first) +import "vite/modulepreload-polyfill" + // Initialize Sentry first to catch errors during module loading import "./js/sentry-init.js" import $ from "jquery" + +window.$ = window.jQuery = $ import "./js/detect-touch" import { SimpleSpectraViewer } from "@fpbase/spectra" import { createElement } from "react" diff --git a/frontend/src/spectra-viewer.js b/frontend/src/spectra-viewer.js index 8085e886..c403295b 100644 --- a/frontend/src/spectra-viewer.js +++ b/frontend/src/spectra-viewer.js @@ -1,3 +1,6 @@ +// Vite modulepreload polyfill (must be first) +import "vite/modulepreload-polyfill" + // Initialize Sentry first to catch errors during module loading import "./js/sentry-init.js" diff --git a/frontend/vite.config.js b/frontend/vite.config.js new file mode 100644 index 00000000..eb74c247 --- /dev/null +++ b/frontend/vite.config.js @@ -0,0 +1,160 @@ +import path from "node:path" +import { fileURLToPath } from "node:url" +import { sentryVitePlugin } from "@sentry/vite-plugin" +import react from "@vitejs/plugin-react" +import { defineConfig } from "vite" +import { viteStaticCopy } from "vite-plugin-static-copy" + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) + +export default defineConfig(({ mode }) => { + const isDev = mode === "development" + + return { + // Match Django STATIC_URL + base: "/static/", + + // Build configuration + build: { + // Output to frontend/dist/ + outDir: "dist", + emptyOutDir: true, + + // Generate manifest.json for django-vite + manifest: "manifest.json", + + // Source maps for Sentry + sourcemap: true, + + // Multi-page app configuration + rollupOptions: { + input: { + main: path.resolve(__dirname, "src/index.js"), + embedscope: path.resolve(__dirname, "src/embedscope.js"), + litemol: path.resolve(__dirname, "src/my-litemol.js"), + spectraViewer: path.resolve(__dirname, "src/spectra-viewer.js"), + simpleSpectraViewer: path.resolve(__dirname, "src/simple-spectra-viewer.js"), + microscopeForm: path.resolve(__dirname, "src/microscope-form.js"), + blast: path.resolve(__dirname, "src/blast-app.js"), + proteinTable: path.resolve(__dirname, "src/protein-table.js"), + }, + + // Manual code splitting (hybrid approach) + output: { + manualChunks(id) { + // Sentry (shared across all) + if (id.includes("node_modules/@sentry")) { + return "vendor-sentry" + } + + // React (shared, but NOT in embedscope) + if (id.includes("node_modules/react") || id.includes("node_modules/scheduler")) { + return "vendor-react" + } + + // jQuery (shared across multiple) + if (id.includes("node_modules/jquery")) { + return "vendor-jquery" + } + + // D3 v7 (EXCLUDE from embedscope - it uses CDN D3 v3) + if (id.includes("node_modules/d3")) { + return "vendor-d3" + } + }, + }, + }, + }, + + // Development server + server: { + port: 5173, + strictPort: true, + origin: "http://localhost:5173", + cors: true, + hmr: { + protocol: "ws", + host: "localhost", + }, + }, + + // Resolve aliases (match webpack) + resolve: { + alias: { + "@fpbase/spectra": path.resolve(__dirname, "../packages/spectra/src/index.jsx"), + "@fpbase/blast": path.resolve(__dirname, "../packages/blast/src/index.js"), + "@fpbase/protein-table": path.resolve(__dirname, "../packages/protein-table/src/index.jsx"), + jquery: path.resolve(__dirname, "node_modules/jquery/src/jquery"), + }, + }, + + // Plugins + plugins: [ + // React with Fast Refresh + react({ + // Include .js files for JSX processing (not just .jsx) + include: /\.(jsx|js|tsx|ts)$/, + babel: { + plugins: ["@babel/plugin-syntax-dynamic-import"], + }, + }), + + // Copy static files (microscope.js) + viteStaticCopy({ + targets: [ + { + src: "../backend/fpbase/static/js/microscope.js", + dest: "js", + }, + ], + }), + + // Sentry source map upload (production only) + !isDev && + sentryVitePlugin({ + org: "talley-lambert", + project: "fpbase", + authToken: process.env.SENTRY_AUTH_TOKEN, + release: process.env.HEROKU_SLUG_COMMIT, + }), + ].filter(Boolean), + + // CSS configuration + css: { + postcss: { + plugins: [require("autoprefixer"), require("cssnano")], + }, + preprocessorOptions: { + scss: { + // Silence deprecation warnings similar to webpack config + silenceDeprecations: ["import", "global-builtin", "color-functions", "abs-percent"], + }, + }, + }, + + // Define environment variables + define: { + "process.env.NODE_ENV": JSON.stringify(mode), + "process.env.SENTRY_DSN": JSON.stringify(process.env.SENTRY_DSN || ""), + "process.env.HEROKU_SLUG_COMMIT": JSON.stringify(process.env.HEROKU_SLUG_COMMIT || ""), + }, + + // Configure esbuild to handle JSX in .js and .jsx files + esbuild: { + loader: "jsx", + include: /src\/.*\.[jt]sx?$/, + exclude: [], + }, + + // Optimize dependencies + optimizeDeps: { + include: ["jquery", "process/browser"], + esbuildOptions: { + // Inject global jQuery (match webpack ProvidePlugin) + define: { + global: "globalThis", + }, + }, + }, + } +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3975700c..2aeb518e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -93,6 +93,9 @@ importers: '@sentry/cli': specifier: ^2.57.0 version: 2.57.0 + '@sentry/vite-plugin': + specifier: ^4.6.0 + version: 4.6.0 '@sentry/webpack-plugin': specifier: ^4.5.0 version: 4.5.0(webpack@5.102.1) @@ -102,9 +105,12 @@ importers: '@types/react-dom': specifier: ^19 version: 19.2.2(@types/react@19.2.2) + '@vitejs/plugin-react': + specifier: ^5.1.0 + version: 5.1.0(vite@7.1.12(@types/node@22.13.4)(jiti@2.6.1)(sass@1.93.2)(terser@5.39.0)) autoprefixer: specifier: ^10.4.21 - version: 10.4.21(postcss@8.5.2) + version: 10.4.21(postcss@8.5.6) babel-loader: specifier: ^10.0.0 version: 10.0.0(@babel/core@7.26.9)(webpack@5.102.1) @@ -128,13 +134,13 @@ importers: version: 7.0.2(webpack@5.102.1) cssnano: specifier: ^7.1.1 - version: 7.1.1(postcss@8.5.2) + version: 7.1.1(postcss@8.5.6) mini-css-extract-plugin: specifier: ^2.9.4 version: 2.9.4(webpack@5.102.1) postcss-loader: specifier: ^8.2.0 - version: 8.2.0(postcss@8.5.2)(webpack@5.102.1) + version: 8.2.0(postcss@8.5.6)(webpack@5.102.1) sass: specifier: 1.93.2 version: 1.93.2 @@ -144,6 +150,12 @@ importers: terser-webpack-plugin: specifier: ^5.3.14 version: 5.3.14(webpack@5.102.1) + vite: + specifier: ^7.1.12 + version: 7.1.12(@types/node@22.13.4)(jiti@2.6.1)(sass@1.93.2)(terser@5.39.0) + vite-plugin-static-copy: + specifier: ^3.1.4 + version: 3.1.4(vite@7.1.12(@types/node@22.13.4)(jiti@2.6.1)(sass@1.93.2)(terser@5.39.0)) webpack: specifier: ^5.102.1 version: 5.102.1(webpack-cli@6.0.1) @@ -395,18 +407,34 @@ packages: resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + '@babel/compat-data@7.26.8': resolution: {integrity: sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.28.5': + resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} + engines: {node: '>=6.9.0'} + '@babel/core@7.26.9': resolution: {integrity: sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==} engines: {node: '>=6.9.0'} + '@babel/core@7.28.5': + resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} + engines: {node: '>=6.9.0'} + '@babel/generator@7.26.9': resolution: {integrity: sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==} engines: {node: '>=6.9.0'} + '@babel/generator@7.28.5': + resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} + engines: {node: '>=6.9.0'} + '@babel/helper-annotate-as-pure@7.25.9': resolution: {integrity: sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==} engines: {node: '>=6.9.0'} @@ -415,6 +443,10 @@ packages: resolution: {integrity: sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==} engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + '@babel/helper-create-class-features-plugin@7.26.9': resolution: {integrity: sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg==} engines: {node: '>=6.9.0'} @@ -432,6 +464,10 @@ packages: peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + '@babel/helper-member-expression-to-functions@7.25.9': resolution: {integrity: sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==} engines: {node: '>=6.9.0'} @@ -440,12 +476,22 @@ packages: resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + '@babel/helper-module-transforms@7.26.0': resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-optimise-call-expression@7.25.9': resolution: {integrity: sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==} engines: {node: '>=6.9.0'} @@ -454,6 +500,10 @@ packages: resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==} engines: {node: '>=6.9.0'} + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} + '@babel/helper-remap-async-to-generator@7.25.9': resolution: {integrity: sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==} engines: {node: '>=6.9.0'} @@ -474,14 +524,26 @@ packages: resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.25.9': resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.25.9': resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + '@babel/helper-wrap-function@7.25.9': resolution: {integrity: sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==} engines: {node: '>=6.9.0'} @@ -490,11 +552,20 @@ packages: resolution: {integrity: sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==} engines: {node: '>=6.9.0'} + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} + engines: {node: '>=6.9.0'} + '@babel/parser@7.26.9': resolution: {integrity: sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==} engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9': resolution: {integrity: sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==} engines: {node: '>=6.9.0'} @@ -806,12 +877,24 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-react-jsx-source@7.25.9': resolution: {integrity: sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-react-jsx@7.25.9': resolution: {integrity: sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==} engines: {node: '>=6.9.0'} @@ -925,14 +1008,26 @@ packages: resolution: {integrity: sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==} engines: {node: '>=6.9.0'} + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + '@babel/traverse@7.26.9': resolution: {integrity: sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==} engines: {node: '>=6.9.0'} + '@babel/traverse@7.28.5': + resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} + engines: {node: '>=6.9.0'} + '@babel/types@7.26.9': resolution: {integrity: sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==} engines: {node: '>=6.9.0'} + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} @@ -1057,6 +1152,12 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.25.11': + resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.18.20': resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} engines: {node: '>=12'} @@ -1069,6 +1170,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.25.11': + resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.18.20': resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} engines: {node: '>=12'} @@ -1081,6 +1188,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-arm@0.25.11': + resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.18.20': resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} engines: {node: '>=12'} @@ -1093,6 +1206,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/android-x64@0.25.11': + resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.18.20': resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} engines: {node: '>=12'} @@ -1105,6 +1224,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.25.11': + resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.18.20': resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} engines: {node: '>=12'} @@ -1117,6 +1242,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.25.11': + resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.18.20': resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} engines: {node: '>=12'} @@ -1129,6 +1260,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.25.11': + resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.18.20': resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} engines: {node: '>=12'} @@ -1141,6 +1278,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.25.11': + resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.18.20': resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} engines: {node: '>=12'} @@ -1153,6 +1296,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.25.11': + resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.18.20': resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} engines: {node: '>=12'} @@ -1165,6 +1314,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.25.11': + resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.18.20': resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} engines: {node: '>=12'} @@ -1177,6 +1332,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.25.11': + resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.18.20': resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} engines: {node: '>=12'} @@ -1189,6 +1350,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.25.11': + resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.18.20': resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} engines: {node: '>=12'} @@ -1201,6 +1368,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.25.11': + resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.18.20': resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} engines: {node: '>=12'} @@ -1213,6 +1386,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.25.11': + resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.18.20': resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} engines: {node: '>=12'} @@ -1225,6 +1404,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.25.11': + resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.18.20': resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} engines: {node: '>=12'} @@ -1237,6 +1422,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.25.11': + resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.18.20': resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} engines: {node: '>=12'} @@ -1249,6 +1440,18 @@ packages: cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.25.11': + resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.11': + resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.18.20': resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} engines: {node: '>=12'} @@ -1261,6 +1464,18 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.25.11': + resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.11': + resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.18.20': resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} engines: {node: '>=12'} @@ -1273,6 +1488,18 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.25.11': + resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.11': + resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/sunos-x64@0.18.20': resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} engines: {node: '>=12'} @@ -1285,6 +1512,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.25.11': + resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.18.20': resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} engines: {node: '>=12'} @@ -1297,6 +1530,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.25.11': + resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.18.20': resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} engines: {node: '>=12'} @@ -1309,6 +1548,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.25.11': + resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.18.20': resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} engines: {node: '>=12'} @@ -1321,6 +1566,12 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.25.11': + resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@floating-ui/core@1.7.3': resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} @@ -1408,10 +1659,16 @@ packages: resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + '@jridgewell/gen-mapping@0.3.8': resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} engines: {node: '>=6.0.0'} + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -1429,6 +1686,9 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@jsonjoy.com/base64@1.1.2': resolution: {integrity: sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==} engines: {node: '>=10.0'} @@ -1775,6 +2035,9 @@ packages: '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + '@rolldown/pluginutils@1.0.0-beta.43': + resolution: {integrity: sha512-5Uxg7fQUCmfhax7FJke2+8B6cqgeUJUD9o2uXIKXhD+mG0mL6NObmVoi9wXEU1tY89mZKgAYA6fTbftx3q2ZPQ==} + '@rollup/rollup-android-arm-eabi@4.52.4': resolution: {integrity: sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==} cpu: [arm] @@ -1905,6 +2168,10 @@ packages: resolution: {integrity: sha512-9sn9tJFtNnhSitPXW8hTuteefGMBbnPFyDER8dz+2sgdvcdq7T99lEwprMf8gUv5JCiDKIvtLe20Sf/4KPAahA==} engines: {node: '>= 14'} + '@sentry/babel-plugin-component-annotate@4.6.0': + resolution: {integrity: sha512-3soTX50JPQQ51FSbb4qvNBf4z/yP7jTdn43vMTp9E4IxvJ9HKJR7OEuKkCMszrZmWsVABXl02msqO7QisePdiQ==} + engines: {node: '>= 14'} + '@sentry/browser@10.22.0': resolution: {integrity: sha512-wD2XqN+yeBpQFfdPo6+wlKDMyyuDctVGzZWE4qTPntICKQuwMdAfeq5Ma89ad0Dw+bzG9UijGeyuJQlswF87Mw==} engines: {node: '>=18'} @@ -1913,6 +2180,10 @@ packages: resolution: {integrity: sha512-LTgYe7qGgAP0BpsyCTpjk756l6wZUv3MtCE+G0qzlpsQ2AljYe2bN4qjDy0bQrsPo0QzNQm+S6d0zogcJj/tqw==} engines: {node: '>= 14'} + '@sentry/bundler-plugin-core@4.6.0': + resolution: {integrity: sha512-Fub2XQqrS258jjS8qAxLLU1k1h5UCNJ76i8m4qZJJdogWWaF8t00KnnTyp9TEDJzrVD64tRXS8+HHENxmeUo3g==} + engines: {node: '>= 14'} + '@sentry/cli-darwin@2.57.0': resolution: {integrity: sha512-v1wYQU3BcCO+Z3OVxxO+EnaW4oQhuOza6CXeYZ0z5ftza9r0QQBLz3bcZKTVta86xraNm0z8GDlREwinyddOxQ==} engines: {node: '>=10'} @@ -1969,6 +2240,10 @@ packages: resolution: {integrity: sha512-V1oeHbrOKzxadsCmgtPku3v3Emo/Bpb3VSuKmlLrQefiHX98MWtjJ3XDGfduzD5/dCdh0r/OOLwjcmrO/PZ2aw==} engines: {node: '>=18'} + '@sentry/vite-plugin@4.6.0': + resolution: {integrity: sha512-fMR2d+EHwbzBa0S1fp45SNUTProxmyFBp+DeBWWQOSP9IU6AH6ea2rqrpMAnp/skkcdW4z4LSRrOEpMZ5rWXLw==} + engines: {node: '>= 14'} + '@sentry/webpack-plugin@4.5.0': resolution: {integrity: sha512-LtAYr54YFdOiklVpMWzYRwj17PQxE0KNffGa2qrMdH/Ays7iQ8j3z1t50wke4UoTrmeqz5kaCSZTJhZXv/XGwA==} engines: {node: '>= 14'} @@ -2192,6 +2467,12 @@ packages: peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 + '@vitejs/plugin-react@5.1.0': + resolution: {integrity: sha512-4LuWrg7EKWgQaMJfnN+wcmbAW+VSsCmqGohftWjuct47bv8uE4n/nPpq4XjJPsxgq00GGG5J8dvBczp8uxScew==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + '@vitest/coverage-v8@1.6.1': resolution: {integrity: sha512-6YeRZwuO4oTGKxD3bijok756oktHSIm3eczVVzNe3scqzuhLwltIF3S9ZL/vwOVIpURmU6SnZhziXXAfw8/Qlw==} peerDependencies: @@ -3102,6 +3383,11 @@ packages: engines: {node: '>=12'} hasBin: true + esbuild@0.25.11: + resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -3910,6 +4196,11 @@ packages: resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} engines: {node: ^18.17.0 || >=20.5.0} + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + nanoid@3.3.8: resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -4031,6 +4322,10 @@ packages: resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} engines: {node: '>=6'} + p-map@7.0.3: + resolution: {integrity: sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==} + engines: {node: '>=18'} + p-retry@6.2.1: resolution: {integrity: sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==} engines: {node: '>=16.17'} @@ -4353,6 +4648,10 @@ packages: resolution: {integrity: sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + pretty-format@27.5.1: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -4442,6 +4741,10 @@ packages: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} + react-refresh@0.18.0: + resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} + engines: {node: '>=0.10.0'} + react-select@5.10.2: resolution: {integrity: sha512-Z33nHdEFWq9tfnfVXaiM12rbJmk+QjFEztWLtmXqQhz6Al4UZZ9xc0wiatmGtUOCCnHN0WizL3tCMYRENX4rVQ==} peerDependencies: @@ -5067,6 +5370,12 @@ packages: engines: {node: ^18.0.0 || >=20.0.0} hasBin: true + vite-plugin-static-copy@3.1.4: + resolution: {integrity: sha512-iCmr4GSw4eSnaB+G8zc2f4dxSuDjbkjwpuBLLGvQYR9IW7rnDzftnUjOH5p4RYR+d4GsiBqXRvzuFhs5bnzVyw==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 + vite@4.5.9: resolution: {integrity: sha512-qK9W4xjgD3gXbC0NmdNFFnVFLMWSNiR3swj957yutwzzN16xF/E7nmtAyp1rT9hviDroQANjE4HK3H4WqWdFtw==} engines: {node: ^14.18.0 || >=16.0.0} @@ -5126,6 +5435,46 @@ packages: terser: optional: true + vite@7.1.12: + resolution: {integrity: sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + vitest@1.6.1: resolution: {integrity: sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==} engines: {node: ^18.0.0 || >=20.0.0} @@ -5375,8 +5724,16 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + '@babel/compat-data@7.26.8': {} + '@babel/compat-data@7.28.5': {} + '@babel/core@7.26.9': dependencies: '@ampproject/remapping': 2.3.0 @@ -5397,6 +5754,26 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/core@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + '@babel/generator@7.26.9': dependencies: '@babel/parser': 7.26.9 @@ -5405,6 +5782,14 @@ snapshots: '@jridgewell/trace-mapping': 0.3.25 jsesc: 3.1.0 + '@babel/generator@7.28.5': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + '@babel/helper-annotate-as-pure@7.25.9': dependencies: '@babel/types': 7.26.9 @@ -5417,6 +5802,14 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.5 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.27.0 + lru-cache: 5.1.1 + semver: 6.3.1 + '@babel/helper-create-class-features-plugin@7.26.9(@babel/core@7.26.9)': dependencies: '@babel/core': 7.26.9 @@ -5448,6 +5841,8 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-globals@7.28.0': {} + '@babel/helper-member-expression-to-functions@7.25.9': dependencies: '@babel/traverse': 7.26.9 @@ -5462,6 +5857,13 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.9)': dependencies: '@babel/core': 7.26.9 @@ -5471,12 +5873,23 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + '@babel/helper-optimise-call-expression@7.25.9': dependencies: '@babel/types': 7.26.9 '@babel/helper-plugin-utils@7.26.5': {} + '@babel/helper-plugin-utils@7.27.1': {} + '@babel/helper-remap-async-to-generator@7.25.9(@babel/core@7.26.9)': dependencies: '@babel/core': 7.26.9 @@ -5504,10 +5917,16 @@ snapshots: '@babel/helper-string-parser@7.25.9': {} + '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-validator-identifier@7.25.9': {} + '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-option@7.25.9': {} + '@babel/helper-validator-option@7.27.1': {} + '@babel/helper-wrap-function@7.25.9': dependencies: '@babel/template': 7.26.9 @@ -5521,10 +5940,19 @@ snapshots: '@babel/template': 7.26.9 '@babel/types': 7.26.9 + '@babel/helpers@7.28.4': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + '@babel/parser@7.26.9': dependencies: '@babel/types': 7.26.9 + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9(@babel/core@7.26.9)': dependencies: '@babel/core': 7.26.9 @@ -5858,11 +6286,21 @@ snapshots: '@babel/core': 7.26.9 '@babel/helper-plugin-utils': 7.26.5 + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-react-jsx-source@7.25.9(@babel/core@7.26.9)': dependencies: '@babel/core': 7.26.9 '@babel/helper-plugin-utils': 7.26.5 + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.9)': dependencies: '@babel/core': 7.26.9 @@ -6054,6 +6492,12 @@ snapshots: '@babel/parser': 7.26.9 '@babel/types': 7.26.9 + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@babel/traverse@7.26.9': dependencies: '@babel/code-frame': 7.26.2 @@ -6066,11 +6510,28 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/traverse@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + '@babel/types@7.26.9': dependencies: '@babel/helper-string-parser': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 + '@babel/types@7.28.5': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@bcoe/v8-coverage@0.2.3': {} '@biomejs/biome@2.3.2': @@ -6198,138 +6659,216 @@ snapshots: '@esbuild/aix-ppc64@0.21.5': optional: true + '@esbuild/aix-ppc64@0.25.11': + optional: true + '@esbuild/android-arm64@0.18.20': optional: true '@esbuild/android-arm64@0.21.5': optional: true + '@esbuild/android-arm64@0.25.11': + optional: true + '@esbuild/android-arm@0.18.20': optional: true '@esbuild/android-arm@0.21.5': optional: true + '@esbuild/android-arm@0.25.11': + optional: true + '@esbuild/android-x64@0.18.20': optional: true '@esbuild/android-x64@0.21.5': optional: true + '@esbuild/android-x64@0.25.11': + optional: true + '@esbuild/darwin-arm64@0.18.20': optional: true '@esbuild/darwin-arm64@0.21.5': optional: true + '@esbuild/darwin-arm64@0.25.11': + optional: true + '@esbuild/darwin-x64@0.18.20': optional: true '@esbuild/darwin-x64@0.21.5': optional: true + '@esbuild/darwin-x64@0.25.11': + optional: true + '@esbuild/freebsd-arm64@0.18.20': optional: true '@esbuild/freebsd-arm64@0.21.5': optional: true + '@esbuild/freebsd-arm64@0.25.11': + optional: true + '@esbuild/freebsd-x64@0.18.20': optional: true '@esbuild/freebsd-x64@0.21.5': optional: true + '@esbuild/freebsd-x64@0.25.11': + optional: true + '@esbuild/linux-arm64@0.18.20': optional: true '@esbuild/linux-arm64@0.21.5': optional: true + '@esbuild/linux-arm64@0.25.11': + optional: true + '@esbuild/linux-arm@0.18.20': optional: true '@esbuild/linux-arm@0.21.5': optional: true + '@esbuild/linux-arm@0.25.11': + optional: true + '@esbuild/linux-ia32@0.18.20': optional: true '@esbuild/linux-ia32@0.21.5': optional: true + '@esbuild/linux-ia32@0.25.11': + optional: true + '@esbuild/linux-loong64@0.18.20': optional: true '@esbuild/linux-loong64@0.21.5': optional: true + '@esbuild/linux-loong64@0.25.11': + optional: true + '@esbuild/linux-mips64el@0.18.20': optional: true '@esbuild/linux-mips64el@0.21.5': optional: true + '@esbuild/linux-mips64el@0.25.11': + optional: true + '@esbuild/linux-ppc64@0.18.20': optional: true '@esbuild/linux-ppc64@0.21.5': optional: true + '@esbuild/linux-ppc64@0.25.11': + optional: true + '@esbuild/linux-riscv64@0.18.20': optional: true '@esbuild/linux-riscv64@0.21.5': optional: true + '@esbuild/linux-riscv64@0.25.11': + optional: true + '@esbuild/linux-s390x@0.18.20': optional: true '@esbuild/linux-s390x@0.21.5': optional: true + '@esbuild/linux-s390x@0.25.11': + optional: true + '@esbuild/linux-x64@0.18.20': optional: true '@esbuild/linux-x64@0.21.5': optional: true + '@esbuild/linux-x64@0.25.11': + optional: true + + '@esbuild/netbsd-arm64@0.25.11': + optional: true + '@esbuild/netbsd-x64@0.18.20': optional: true '@esbuild/netbsd-x64@0.21.5': optional: true + '@esbuild/netbsd-x64@0.25.11': + optional: true + + '@esbuild/openbsd-arm64@0.25.11': + optional: true + '@esbuild/openbsd-x64@0.18.20': optional: true '@esbuild/openbsd-x64@0.21.5': optional: true + '@esbuild/openbsd-x64@0.25.11': + optional: true + + '@esbuild/openharmony-arm64@0.25.11': + optional: true + '@esbuild/sunos-x64@0.18.20': optional: true '@esbuild/sunos-x64@0.21.5': optional: true + '@esbuild/sunos-x64@0.25.11': + optional: true + '@esbuild/win32-arm64@0.18.20': optional: true '@esbuild/win32-arm64@0.21.5': optional: true + '@esbuild/win32-arm64@0.25.11': + optional: true + '@esbuild/win32-ia32@0.18.20': optional: true '@esbuild/win32-ia32@0.21.5': optional: true + '@esbuild/win32-ia32@0.25.11': + optional: true + '@esbuild/win32-x64@0.18.20': optional: true '@esbuild/win32-x64@0.21.5': optional: true + '@esbuild/win32-x64@0.25.11': + optional: true + '@floating-ui/core@1.7.3': dependencies: '@floating-ui/utils': 0.2.10 @@ -6412,12 +6951,22 @@ snapshots: '@types/yargs': 17.0.33 chalk: 4.1.2 + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/gen-mapping@0.3.8': dependencies: '@jridgewell/set-array': 1.2.1 '@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/set-array@1.2.1': {} @@ -6434,6 +6983,11 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jsonjoy.com/base64@1.1.2(tslib@2.8.1)': dependencies: tslib: 2.8.1 @@ -6757,6 +7311,8 @@ snapshots: '@popperjs/core@2.11.8': {} + '@rolldown/pluginutils@1.0.0-beta.43': {} + '@rollup/rollup-android-arm-eabi@4.52.4': optional: true @@ -6843,6 +7399,8 @@ snapshots: '@sentry/babel-plugin-component-annotate@4.5.0': {} + '@sentry/babel-plugin-component-annotate@4.6.0': {} + '@sentry/browser@10.22.0': dependencies: '@sentry-internal/browser-utils': 10.22.0 @@ -6865,6 +7423,20 @@ snapshots: - encoding - supports-color + '@sentry/bundler-plugin-core@4.6.0': + dependencies: + '@babel/core': 7.26.9 + '@sentry/babel-plugin-component-annotate': 4.6.0 + '@sentry/cli': 2.57.0 + dotenv: 16.6.1 + find-up: 5.0.0 + glob: 9.3.5 + magic-string: 0.30.8 + unplugin: 1.0.1 + transitivePeerDependencies: + - encoding + - supports-color + '@sentry/cli-darwin@2.57.0': optional: true @@ -6911,6 +7483,14 @@ snapshots: '@sentry/core@10.22.0': {} + '@sentry/vite-plugin@4.6.0': + dependencies: + '@sentry/bundler-plugin-core': 4.6.0 + unplugin: 1.0.1 + transitivePeerDependencies: + - encoding + - supports-color + '@sentry/webpack-plugin@4.5.0(webpack@5.102.1)': dependencies: '@sentry/bundler-plugin-core': 4.5.0 @@ -7169,6 +7749,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@vitejs/plugin-react@5.1.0(vite@7.1.12(@types/node@22.13.4)(jiti@2.6.1)(sass@1.93.2)(terser@5.39.0))': + dependencies: + '@babel/core': 7.28.5 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.5) + '@rolldown/pluginutils': 1.0.0-beta.43 + '@types/babel__core': 7.20.5 + react-refresh: 0.18.0 + vite: 7.1.12(@types/node@22.13.4)(jiti@2.6.1)(sass@1.93.2)(terser@5.39.0) + transitivePeerDependencies: + - supports-color + '@vitest/coverage-v8@1.6.1(vitest@1.6.1)': dependencies: '@ampproject/remapping': 2.3.0 @@ -7443,14 +8035,14 @@ snapshots: dependencies: immediate: 3.3.0 - autoprefixer@10.4.21(postcss@8.5.2): + autoprefixer@10.4.21(postcss@8.5.6): dependencies: browserslist: 4.27.0 caniuse-lite: 1.0.30001751 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 - postcss: 8.5.2 + postcss: 8.5.6 postcss-value-parser: 4.2.0 babel-loader@10.0.0(@babel/core@7.26.9)(webpack@5.102.1): @@ -7757,6 +8349,10 @@ snapshots: dependencies: postcss: 8.5.2 + css-declaration-sorter@7.2.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + css-loader@7.1.2(webpack@5.102.1): dependencies: icss-utils: 5.1.0(postcss@8.5.2) @@ -7843,16 +8439,60 @@ snapshots: postcss-svgo: 7.1.0(postcss@8.5.2) postcss-unique-selectors: 7.0.4(postcss@8.5.2) + cssnano-preset-default@7.0.9(postcss@8.5.6): + dependencies: + browserslist: 4.27.0 + css-declaration-sorter: 7.2.0(postcss@8.5.6) + cssnano-utils: 5.0.1(postcss@8.5.6) + postcss: 8.5.6 + postcss-calc: 10.1.1(postcss@8.5.6) + postcss-colormin: 7.0.4(postcss@8.5.6) + postcss-convert-values: 7.0.7(postcss@8.5.6) + postcss-discard-comments: 7.0.4(postcss@8.5.6) + postcss-discard-duplicates: 7.0.2(postcss@8.5.6) + postcss-discard-empty: 7.0.1(postcss@8.5.6) + postcss-discard-overridden: 7.0.1(postcss@8.5.6) + postcss-merge-longhand: 7.0.5(postcss@8.5.6) + postcss-merge-rules: 7.0.6(postcss@8.5.6) + postcss-minify-font-values: 7.0.1(postcss@8.5.6) + postcss-minify-gradients: 7.0.1(postcss@8.5.6) + postcss-minify-params: 7.0.4(postcss@8.5.6) + postcss-minify-selectors: 7.0.5(postcss@8.5.6) + postcss-normalize-charset: 7.0.1(postcss@8.5.6) + postcss-normalize-display-values: 7.0.1(postcss@8.5.6) + postcss-normalize-positions: 7.0.1(postcss@8.5.6) + postcss-normalize-repeat-style: 7.0.1(postcss@8.5.6) + postcss-normalize-string: 7.0.1(postcss@8.5.6) + postcss-normalize-timing-functions: 7.0.1(postcss@8.5.6) + postcss-normalize-unicode: 7.0.4(postcss@8.5.6) + postcss-normalize-url: 7.0.1(postcss@8.5.6) + postcss-normalize-whitespace: 7.0.1(postcss@8.5.6) + postcss-ordered-values: 7.0.2(postcss@8.5.6) + postcss-reduce-initial: 7.0.4(postcss@8.5.6) + postcss-reduce-transforms: 7.0.1(postcss@8.5.6) + postcss-svgo: 7.1.0(postcss@8.5.6) + postcss-unique-selectors: 7.0.4(postcss@8.5.6) + cssnano-utils@5.0.1(postcss@8.5.2): dependencies: postcss: 8.5.2 + cssnano-utils@5.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + cssnano@7.1.1(postcss@8.5.2): dependencies: cssnano-preset-default: 7.0.9(postcss@8.5.2) lilconfig: 3.1.3 postcss: 8.5.2 + cssnano@7.1.1(postcss@8.5.6): + dependencies: + cssnano-preset-default: 7.0.9(postcss@8.5.6) + lilconfig: 3.1.3 + postcss: 8.5.6 + csso@5.0.5: dependencies: css-tree: 2.2.1 @@ -8209,6 +8849,35 @@ snapshots: '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 + esbuild@0.25.11: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.11 + '@esbuild/android-arm': 0.25.11 + '@esbuild/android-arm64': 0.25.11 + '@esbuild/android-x64': 0.25.11 + '@esbuild/darwin-arm64': 0.25.11 + '@esbuild/darwin-x64': 0.25.11 + '@esbuild/freebsd-arm64': 0.25.11 + '@esbuild/freebsd-x64': 0.25.11 + '@esbuild/linux-arm': 0.25.11 + '@esbuild/linux-arm64': 0.25.11 + '@esbuild/linux-ia32': 0.25.11 + '@esbuild/linux-loong64': 0.25.11 + '@esbuild/linux-mips64el': 0.25.11 + '@esbuild/linux-ppc64': 0.25.11 + '@esbuild/linux-riscv64': 0.25.11 + '@esbuild/linux-s390x': 0.25.11 + '@esbuild/linux-x64': 0.25.11 + '@esbuild/netbsd-arm64': 0.25.11 + '@esbuild/netbsd-x64': 0.25.11 + '@esbuild/openbsd-arm64': 0.25.11 + '@esbuild/openbsd-x64': 0.25.11 + '@esbuild/openharmony-arm64': 0.25.11 + '@esbuild/sunos-x64': 0.25.11 + '@esbuild/win32-arm64': 0.25.11 + '@esbuild/win32-ia32': 0.25.11 + '@esbuild/win32-x64': 0.25.11 + escalade@3.2.0: {} escape-html@1.0.3: {} @@ -8996,6 +9665,8 @@ snapshots: mute-stream@2.0.0: {} + nanoid@3.3.11: {} + nanoid@3.3.8: {} negotiator@0.6.3: {} @@ -9091,6 +9762,8 @@ snapshots: p-map@2.1.0: {} + p-map@7.0.3: {} + p-retry@6.2.1: dependencies: '@types/retry': 0.12.2 @@ -9183,6 +9856,12 @@ snapshots: postcss-selector-parser: 7.1.0 postcss-value-parser: 4.2.0 + postcss-calc@10.1.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + postcss-value-parser: 4.2.0 + postcss-colormin@7.0.4(postcss@8.5.2): dependencies: browserslist: 4.27.0 @@ -9191,34 +9870,65 @@ snapshots: postcss: 8.5.2 postcss-value-parser: 4.2.0 + postcss-colormin@7.0.4(postcss@8.5.6): + dependencies: + browserslist: 4.27.0 + caniuse-api: 3.0.0 + colord: 2.9.3 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + postcss-convert-values@7.0.7(postcss@8.5.2): dependencies: browserslist: 4.27.0 postcss: 8.5.2 postcss-value-parser: 4.2.0 + postcss-convert-values@7.0.7(postcss@8.5.6): + dependencies: + browserslist: 4.27.0 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + postcss-discard-comments@7.0.4(postcss@8.5.2): dependencies: postcss: 8.5.2 postcss-selector-parser: 7.1.0 + postcss-discard-comments@7.0.4(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + postcss-discard-duplicates@7.0.2(postcss@8.5.2): dependencies: postcss: 8.5.2 + postcss-discard-duplicates@7.0.2(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-discard-empty@7.0.1(postcss@8.5.2): dependencies: postcss: 8.5.2 + postcss-discard-empty@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-discard-overridden@7.0.1(postcss@8.5.2): dependencies: postcss: 8.5.2 - postcss-loader@8.2.0(postcss@8.5.2)(webpack@5.102.1): + postcss-discard-overridden@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-loader@8.2.0(postcss@8.5.6)(webpack@5.102.1): dependencies: cosmiconfig: 9.0.0 jiti: 2.6.1 - postcss: 8.5.2 + postcss: 8.5.6 semver: 7.7.1 optionalDependencies: webpack: 5.102.1(webpack-cli@6.0.1) @@ -9231,6 +9941,12 @@ snapshots: postcss-value-parser: 4.2.0 stylehacks: 7.0.6(postcss@8.5.2) + postcss-merge-longhand@7.0.5(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + stylehacks: 7.0.6(postcss@8.5.6) + postcss-merge-rules@7.0.6(postcss@8.5.2): dependencies: browserslist: 4.27.0 @@ -9239,11 +9955,24 @@ snapshots: postcss: 8.5.2 postcss-selector-parser: 7.1.0 + postcss-merge-rules@7.0.6(postcss@8.5.6): + dependencies: + browserslist: 4.27.0 + caniuse-api: 3.0.0 + cssnano-utils: 5.0.1(postcss@8.5.6) + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + postcss-minify-font-values@7.0.1(postcss@8.5.2): dependencies: postcss: 8.5.2 postcss-value-parser: 4.2.0 + postcss-minify-font-values@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + postcss-minify-gradients@7.0.1(postcss@8.5.2): dependencies: colord: 2.9.3 @@ -9251,6 +9980,13 @@ snapshots: postcss: 8.5.2 postcss-value-parser: 4.2.0 + postcss-minify-gradients@7.0.1(postcss@8.5.6): + dependencies: + colord: 2.9.3 + cssnano-utils: 5.0.1(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + postcss-minify-params@7.0.4(postcss@8.5.2): dependencies: browserslist: 4.27.0 @@ -9258,12 +9994,25 @@ snapshots: postcss: 8.5.2 postcss-value-parser: 4.2.0 + postcss-minify-params@7.0.4(postcss@8.5.6): + dependencies: + browserslist: 4.27.0 + cssnano-utils: 5.0.1(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + postcss-minify-selectors@7.0.5(postcss@8.5.2): dependencies: cssesc: 3.0.0 postcss: 8.5.2 postcss-selector-parser: 7.1.0 + postcss-minify-selectors@7.0.5(postcss@8.5.6): + dependencies: + cssesc: 3.0.0 + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + postcss-modules-extract-imports@3.1.0(postcss@8.5.2): dependencies: postcss: 8.5.2 @@ -9289,64 +10038,126 @@ snapshots: dependencies: postcss: 8.5.2 + postcss-normalize-charset@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-normalize-display-values@7.0.1(postcss@8.5.2): dependencies: postcss: 8.5.2 postcss-value-parser: 4.2.0 + postcss-normalize-display-values@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + postcss-normalize-positions@7.0.1(postcss@8.5.2): dependencies: postcss: 8.5.2 postcss-value-parser: 4.2.0 + postcss-normalize-positions@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + postcss-normalize-repeat-style@7.0.1(postcss@8.5.2): dependencies: postcss: 8.5.2 postcss-value-parser: 4.2.0 + postcss-normalize-repeat-style@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + postcss-normalize-string@7.0.1(postcss@8.5.2): dependencies: postcss: 8.5.2 postcss-value-parser: 4.2.0 + postcss-normalize-string@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + postcss-normalize-timing-functions@7.0.1(postcss@8.5.2): dependencies: postcss: 8.5.2 postcss-value-parser: 4.2.0 + postcss-normalize-timing-functions@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + postcss-normalize-unicode@7.0.4(postcss@8.5.2): dependencies: browserslist: 4.27.0 postcss: 8.5.2 postcss-value-parser: 4.2.0 + postcss-normalize-unicode@7.0.4(postcss@8.5.6): + dependencies: + browserslist: 4.27.0 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + postcss-normalize-url@7.0.1(postcss@8.5.2): dependencies: postcss: 8.5.2 postcss-value-parser: 4.2.0 + postcss-normalize-url@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + postcss-normalize-whitespace@7.0.1(postcss@8.5.2): dependencies: postcss: 8.5.2 postcss-value-parser: 4.2.0 + postcss-normalize-whitespace@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + postcss-ordered-values@7.0.2(postcss@8.5.2): dependencies: cssnano-utils: 5.0.1(postcss@8.5.2) postcss: 8.5.2 postcss-value-parser: 4.2.0 + postcss-ordered-values@7.0.2(postcss@8.5.6): + dependencies: + cssnano-utils: 5.0.1(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + postcss-reduce-initial@7.0.4(postcss@8.5.2): dependencies: browserslist: 4.27.0 caniuse-api: 3.0.0 postcss: 8.5.2 + postcss-reduce-initial@7.0.4(postcss@8.5.6): + dependencies: + browserslist: 4.27.0 + caniuse-api: 3.0.0 + postcss: 8.5.6 + postcss-reduce-transforms@7.0.1(postcss@8.5.2): dependencies: postcss: 8.5.2 postcss-value-parser: 4.2.0 + postcss-reduce-transforms@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + postcss-selector-parser@7.1.0: dependencies: cssesc: 3.0.0 @@ -9358,11 +10169,22 @@ snapshots: postcss-value-parser: 4.2.0 svgo: 4.0.0 + postcss-svgo@7.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + svgo: 4.0.0 + postcss-unique-selectors@7.0.4(postcss@8.5.2): dependencies: postcss: 8.5.2 postcss-selector-parser: 7.1.0 + postcss-unique-selectors@7.0.4(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + postcss-value-parser@4.2.0: {} postcss@8.5.2: @@ -9371,6 +10193,12 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + pretty-format@27.5.1: dependencies: ansi-regex: 5.0.1 @@ -9456,6 +10284,8 @@ snapshots: react-refresh@0.14.2: {} + react-refresh@0.18.0: {} + react-select@5.10.2(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: '@babel/runtime': 7.26.9 @@ -9888,6 +10718,12 @@ snapshots: postcss: 8.5.2 postcss-selector-parser: 7.1.0 + stylehacks@7.0.6(postcss@8.5.6): + dependencies: + browserslist: 4.27.0 + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + stylis@4.2.0: {} supports-color@7.2.0: @@ -10083,6 +10919,14 @@ snapshots: - supports-color - terser + vite-plugin-static-copy@3.1.4(vite@7.1.12(@types/node@22.13.4)(jiti@2.6.1)(sass@1.93.2)(terser@5.39.0)): + dependencies: + chokidar: 3.6.0 + p-map: 7.0.3 + picocolors: 1.1.1 + tinyglobby: 0.2.15 + vite: 7.1.12(@types/node@22.13.4)(jiti@2.6.1)(sass@1.93.2)(terser@5.39.0) + vite@4.5.9(@types/node@22.13.4)(sass@1.93.2)(terser@5.39.0): dependencies: esbuild: 0.18.20 @@ -10105,6 +10949,21 @@ snapshots: sass: 1.93.2 terser: 5.39.0 + vite@7.1.12(@types/node@22.13.4)(jiti@2.6.1)(sass@1.93.2)(terser@5.39.0): + dependencies: + esbuild: 0.25.11 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.52.4 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 22.13.4 + fsevents: 2.3.3 + jiti: 2.6.1 + sass: 1.93.2 + terser: 5.39.0 + vitest@1.6.1(@types/node@22.13.4)(@vitest/ui@1.6.1)(happy-dom@12.10.3)(sass@1.93.2)(terser@5.39.0): dependencies: '@vitest/expect': 1.6.1 From 8f794501a4e14df1787af7bf799a855dce37732d Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Wed, 29 Oct 2025 19:44:17 -0400 Subject: [PATCH 04/60] feat: Phase 3 - Django integration complete MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Install django-vite package - Update Django settings: * Replace webpack_loader with django_vite in INSTALLED_APPS * Replace WEBPACK_LOADER with DJANGO_VITE configuration * Update test.py to use manifest mode (no MockWebpackLoader needed) - Migrate all 11 templates: * base.html: Add vite_hmr_client + vite_asset for main bundle * 500.html: Remove webpack_static (Sentry in main bundle) * spectra.html: vite_asset for spectraViewer * spectra_graph.html: vite_asset for simpleSpectraViewer * table.html: vite_asset for proteinTable * compare.html: vite_asset for simpleSpectraViewer * microscope_form.html: vite_asset for microscopeForm * microscope_embed.html: vite_asset for main CSS + embedscope (D3 v3) * protein_detail.html: vite_asset for simpleSpectraViewer + litemol (defer) * blast.html: vite_asset for blast * test_autocomplete.html: Remove unused webpack_loader load All webpack_loader references removed from templates 🤖 Generated with Claude Code --- backend/config/settings/base.py | 20 +++++++++---------- backend/config/settings/test.py | 15 +++++++------- backend/fpbase/templates/500.html | 3 +-- backend/fpbase/templates/base.html | 6 +++--- .../templates/pages/test_autocomplete.html | 2 -- backend/proteins/templates/compare.html | 4 ++-- .../proteins/templates/proteins/blast.html | 4 ++-- .../templates/proteins/microscope_embed.html | 6 +++--- .../templates/proteins/microscope_form.html | 5 ++--- .../templates/proteins/protein_detail.html | 6 +++--- backend/proteins/templates/spectra.html | 4 ++-- backend/proteins/templates/spectra_graph.html | 4 ++-- backend/proteins/templates/table.html | 4 ++-- pyproject.toml | 3 ++- uv.lock | 14 +++++++++++++ 15 files changed, 55 insertions(+), 45 deletions(-) diff --git a/backend/config/settings/base.py b/backend/config/settings/base.py index 03e10070..4a74f406 100644 --- a/backend/config/settings/base.py +++ b/backend/config/settings/base.py @@ -223,16 +223,16 @@ "django.contrib.staticfiles.finders.AppDirectoriesFinder", ] -INSTALLED_APPS.append("webpack_loader") - -WEBPACK_LOADER = { - "DEFAULT": { - "CACHE": not DEBUG, - "BUNDLE_DIR_NAME": "/", - "STATS_FILE": str(ROOT_DIR.parent / "frontend" / "dist" / "webpack-stats.json"), - "POLL_INTERVAL": 0.1, - "TIMEOUT": None, - "IGNORE": [r".*\.hot-update.js", r".+\.map"], +INSTALLED_APPS.append("django_vite") + +DJANGO_VITE = { + "default": { + "dev_mode": DEBUG, + "dev_server_protocol": "http", + "dev_server_host": "localhost", + "dev_server_port": 5173, + "static_url_prefix": "", + "manifest_path": str(ROOT_DIR.parent / "frontend" / "dist" / "manifest.json"), } } diff --git a/backend/config/settings/test.py b/backend/config/settings/test.py index f18e72e1..6a9790c1 100644 --- a/backend/config/settings/test.py +++ b/backend/config/settings/test.py @@ -7,8 +7,6 @@ import getpass import os -from webpack_loader.loaders import FakeWebpackLoader - from .base import * # noqa # DEBUG @@ -97,9 +95,10 @@ ] -class MockWebpackLoader(FakeWebpackLoader): - def get_assets(self): - return {} - - -WEBPACK_LOADER["DEFAULT"]["LOADER_CLASS"] = "config.settings.test.MockWebpackLoader" +# django-vite in test mode uses manifest (never dev server) +DJANGO_VITE = { + "default": { + "dev_mode": False, + "manifest_path": str(ROOT_DIR.parent / "frontend" / "dist" / "manifest.json"), + } +} diff --git a/backend/fpbase/templates/500.html b/backend/fpbase/templates/500.html index c0995e27..80ed8f21 100644 --- a/backend/fpbase/templates/500.html +++ b/backend/fpbase/templates/500.html @@ -1,5 +1,4 @@ {% extends "base.html" %} -{% load webpack_static from webpack_loader %} {% block title %}Server Error{% endblock %} @@ -12,7 +11,7 @@

Looks like something went wrong :(

{% block javascript %} - +{% comment %}Sentry is now loaded as part of the main bundle{% endcomment %} {% if sentry_event_id %} diff --git a/backend/fpbase/templates/pages/test_autocomplete.html b/backend/fpbase/templates/pages/test_autocomplete.html index 03b0c89c..f895b688 100644 --- a/backend/fpbase/templates/pages/test_autocomplete.html +++ b/backend/fpbase/templates/pages/test_autocomplete.html @@ -1,6 +1,4 @@ {% extends "base.html" %} -{% load webpack_static from webpack_loader %} - {% block content %}
diff --git a/backend/proteins/templates/compare.html b/backend/proteins/templates/compare.html index 224e38d5..ec9aa309 100644 --- a/backend/proteins/templates/compare.html +++ b/backend/proteins/templates/compare.html @@ -1,6 +1,6 @@ {% extends "base.html" %} {% load humanize %} -{% load render_bundle from webpack_loader %} +{% load django_vite %} {% block title %}{% endblock %} {% block meta-description %}{% endblock %} @@ -89,6 +89,6 @@

Nothing to compare!

{% block javascript %} {% if spectra_ids %} - {% render_bundle 'simpleSpectraViewer' %} + {% vite_asset 'src/simple-spectra-viewer.js' %} {% endif %} {% endblock javascript %} diff --git a/backend/proteins/templates/proteins/blast.html b/backend/proteins/templates/proteins/blast.html index dded26b3..cf64fd9b 100644 --- a/backend/proteins/templates/proteins/blast.html +++ b/backend/proteins/templates/proteins/blast.html @@ -1,6 +1,6 @@ {% extends "base.html" %} {% load static %} -{% load render_bundle from webpack_loader %} +{% load django_vite %} {% block title %}FPbase Fluorescent Protein Sequence BLAST{% endblock %} {% block meta-description %}Search the database at FPbase for fluorescent protein sequences similar to a query amino acid or nucleotide sequence.{% endblock %} @@ -26,6 +26,6 @@

Fluorescent Protein BLAST

-{% render_bundle 'blast' %} +{% vite_asset 'src/blast-app.js' %} {% endblock content %} diff --git a/backend/proteins/templates/proteins/microscope_embed.html b/backend/proteins/templates/proteins/microscope_embed.html index 30cf320e..da9ee273 100644 --- a/backend/proteins/templates/proteins/microscope_embed.html +++ b/backend/proteins/templates/proteins/microscope_embed.html @@ -1,6 +1,6 @@ {% load static %} -{% load render_bundle from webpack_loader %} +{% load django_vite %} @@ -21,7 +21,7 @@ {# Load only CSS from main bundle (not JS which includes D3 v7) #} - {% render_bundle 'main' 'css' %} + {% vite_asset 'src/index.js' %} @@ -40,7 +40,7 @@

{{microscope}}

{% include 'proteins/_microscope_include.html' with embeddable=False %}
- {% render_bundle 'embedscope' 'js' %} + {% vite_asset 'src/embedscope.js' %} diff --git a/backend/proteins/templates/proteins/microscope_form.html b/backend/proteins/templates/proteins/microscope_form.html index ba2ed334..791b4f64 100644 --- a/backend/proteins/templates/proteins/microscope_form.html +++ b/backend/proteins/templates/proteins/microscope_form.html @@ -1,6 +1,5 @@ {% extends "base.html" %} -{% load webpack_static from webpack_loader %} -{% load render_bundle from webpack_loader %} +{% load django_vite %} {% block title %}{% if object %}{{object}} Microscope{% else %}Create a Microscope{% endif %} on FPbase{% endblock %} {% block meta-description %}Create and save your own microscope configuration (filters, light sources, detectors) and @@ -20,7 +19,7 @@ {% endif %} var formErrors = '{{ form.errors|safe }}'; -{% render_bundle 'microscopeForm' %} +{% vite_asset 'src/microscope-form.js' %} {% if object %}

Update microscope: {{object}}

diff --git a/backend/proteins/templates/proteins/protein_detail.html b/backend/proteins/templates/proteins/protein_detail.html index 240dc958..1881d3b9 100644 --- a/backend/proteins/templates/proteins/protein_detail.html +++ b/backend/proteins/templates/proteins/protein_detail.html @@ -1,7 +1,7 @@ {% extends "base.html" %} {% load i18n favit_tags %} {% load flag_object from protein_tags %} -{% load render_bundle from webpack_loader %} +{% load django_vite %} {% block title %}{{ protein.name|safe }} :: Fluorescent Protein Database {% endblock %} {% block meta-description %}{{ protein.description }}{% endblock %} @@ -487,11 +487,11 @@