======================= JavaScript Architecture ======================= Overview ======== Wilco's JavaScript layer enables Python backends to serve React components without requiring a full frontend build pipeline. The architecture consists of three main parts: 1. **Standalone Loader** (``loader.js``) - Self-contained runtime that renders components on any HTML page 2. **Live Loader** (``live-loader.js``) - Extension for Django admin live preview 3. **Component Bundles** - ESM bundles created by esbuild at runtime All JavaScript assets are pre-built and included in the Python wheel, so users don't need Node.js installed to use wilco. However, **esbuild is required** for runtime component bundling. File structure ============== .. code-block:: text src/ ├── wilco/bridges/django/static/wilco/ │ ├── loader.js # Pre-built standalone loader (~195KB) │ └── live-loader.js # Live preview extension (~9KB) └── wilcojs/react/ └── src/loader/ └── standalone.ts # TypeScript source for loader.js The ``loader.js`` file is compiled from ``standalone.ts`` using esbuild. This happens during package release (via ``make wheel``), not at install time. Standalone loader ================= The standalone loader (``loader.js``) is a self-contained IIFE that bundles: - **React 19** and **ReactDOM** - Full React runtime - **goober** - Lightweight CSS-in-JS library (~1KB) - **Error Boundary** - Catches and displays component errors - **Source Map Support** - Maps errors to original TypeScript Size: ~195KB minified (includes React) How it works ------------ 1. Waits for ``DOMContentLoaded`` 2. Finds elements with ``data-wilco-component`` attribute 3. Fetches component bundle from ``/api/bundles/{name}.js`` 4. Transforms ESM imports to use the module registry 5. Executes the bundle and renders the component Example usage: .. code-block:: html
Loading...
Module registry --------------- Components are bundled with external dependencies (React, goober) that are provided at runtime via ``window.__MODULES__``: .. code-block:: javascript window.__MODULES__ = { "react": React, "react/jsx-runtime": ReactJsxRuntime, "@wilcojs/react": { useComponent }, "goober": goober, }; This allows components to use standard imports: .. code-block:: typescript import React, { useState } from "react"; import { styled } from "goober"; useComponent hook ----------------- The loader provides a ``useComponent`` hook for dynamic component loading with React Suspense: .. code-block:: tsx import { useComponent } from "@wilcojs/react"; function ProductList({ products }) { const ProductCard = useComponent("store:product"); return products.map(p => ); } The hook integrates with React Suspense, so components automatically show a loading fallback while child components load. Static mode ----------- When pre-built bundles are available, the loader can serve bundles from static file URLs instead of the API. This is controlled via two global variables set on the ``window`` object: .. code-block:: javascript window.staticManifest = { "store:product": { "file": "bundles/store--product.a1b2c3d4.js", "hash": "a1b2c3d4" } }; window.staticManifestBaseUrl = "/static/wilco/"; When ``staticManifest`` is set, the loader: 1. Looks up the component in the manifest 2. Loads the bundle from ``{staticManifestBaseUrl}{file}`` instead of the API 3. Falls back to API loading if the component is not in the manifest The Django template tag ``{% wilco_loader_script %}`` automatically sets these variables when ``WILCO_BUILD_DIR`` is configured and contains a valid manifest. These variables are stored on ``window`` (not as module-level variables) to survive duplicate `` Image preview ------------- When users select an image file, the live loader creates a blob URL for instant preview without uploading: .. code-block:: javascript // Field "image" gets mapped to prop "imageUrl" const blobUrl = URL.createObjectURL(file); props.imageUrl = blobUrl; This works transparently with components that accept ``imageUrl`` props. Building the loader =================== The loader is pre-built in the wheel, but for development: .. code-block:: bash # Build loader only make build-loader # Build wheel (includes loader build) make wheel Or directly: .. code-block:: bash cd src/wilcojs/react pnpm build:loader The build command uses esbuild: .. code-block:: bash esbuild src/loader/standalone.ts \ --bundle \ --minify \ --format=iife \ --outfile=../../wilco/bridges/django/static/wilco/loader.js Runtime requirements ==================== To **use** wilco components (render on pages): - No Node.js required - JavaScript files included in wheel - Modern browser with ES2020 support To **create** components (bundle TypeScript at runtime): - esbuild must be available in PATH - Or: Node.js with ``npx`` (downloads esbuild on demand) Install esbuild globally: .. code-block:: bash npm install -g esbuild Or let wilco use npx to download it automatically. Component bundles ================= When a component is requested, wilco's Python bundler: 1. Locates the component's ``index.tsx`` file 2. Runs esbuild with external dependencies 3. Returns ESM bundle with inline source maps Bundler configuration: .. code-block:: python bundle_component( ts_path=Path("components/product/index.tsx"), component_name="store:product", external_deps=["react", "react-dom", "react/jsx-runtime", "@wilcojs/react", "goober"] ) External dependencies are resolved via the module registry at runtime. See also ======== - :doc:`/explanation/standalone-loader` - Detailed loader internals - :doc:`/reference/cli` - CLI reference for ``wilco build`` - :doc:`/how-to/django` - Django integration guide - :doc:`/how-to/fastapi` - FastAPI integration guide