Bridge @awacloud/fw with a Vite build : exposes virtual modules that bundle a preset or a side-bundle, and optionally injects the sanity layer.

Install

The plugin lives inside @awacloud/fw itself — no separate install needed once the framework is published.

npm i @awacloud/fw

Usage

// vite.config.js
import { defineConfig } from 'vite';
import fw from '@awacloud/fw/vite';

export default defineConfig({
    plugins: [
        fw({
            preset: 'site-interactive',
            sideBundles: ['crypto-basic', 'realtime'],
            sanity: 'base'              // or 'community', 'lockdown', or false
        })
    ]
});
// app/main.js
import { runtime } from 'virtual:@awacloud/fw/preset/site-interactive';

const hex = runtime.resolve('hex');
console.log(hex.fromBytes(new Uint8Array([72])));

// Side-bundle : dynamically imported, Vite code-splits it automatically.
async function login() {
    const cryptoBasic = await import('virtual:@awacloud/fw/side-bundle/crypto-basic');
    cryptoBasic.install(runtime);
    const hmac = runtime.resolve('hmac');
    // ...
}

Virtual modules

ID Exports
virtual:@awacloud/fw/preset/<name> runtime (pre-registered), moduleNames
virtual:@awacloud/fw/side-bundle/<name> install(runtime), modules, moduleNames

Available preset names match the keys of presets in fw.config.json (minimal, core, site, site-interactive, spa, pwa) plus the synthetic full (everything in src/).

Available side-bundle names match the keys of sideBundles in fw.config.json (realtime, crypto-basic, crypto-identity, crypto-advanced, compress-heavy, sensors, text-advanced, structures-advanced).

How tree-shaking works

The plugin emits code with direct subpath imports :

// generated by the plugin
import { ModuleRuntime } from '@awacloud/fw/core/runtime';
import { sanitize } from '@awacloud/fw/dom/rendering/sanitize.js';
import { hex }      from '@awacloud/fw/io/codec/hex.js';

export const runtime = new ModuleRuntime();
runtime.registerAllDeep([sanitize, hex]);

Each module file imports its own dependencies statically (the deps field is the runtime mirror of those imports). Rollup follows the import graph and pulls only what sanitize and hex actually need — parser.js, render.js, secPolicy.js, etc. — never the full core/modules catalogue.

Tree-shaking is preset-level, not within a preset. When the plugin emits registerAllDeep([a, b, c]) for a preset, all three bindings are statically referenced ; Rollup cannot drop c even if runtime.resolve('c') is never called downstream. Picking a smaller preset (or composing by hand) is the only way to shrink the bundle further.

You can write the same code by hand if you don't want a virtual import :

import { ModuleRuntime } from '@awacloud/fw/core/runtime';
import { sanitize } from '@awacloud/fw/dom/rendering/sanitize.js';

const runtime = new ModuleRuntime();
runtime.registerDeep(sanitize);

TypeScript — transparent narrowing

Add this once in any .ts of your project (e.g. src/vite-env.d.ts) :

/// <reference types="@awacloud/fw/vite-env" />

The runtime exported by virtual:@awacloud/fw/preset/<name> is then typed as a TypedModuleRuntime, so resolve narrows by module name with no extra step :

import { runtime } from 'virtual:@awacloud/fw/preset/site-interactive';
const hex = runtime.resolve('hex');   // typed — no asTyped, no <T>

This costs nothing at runtime and nothing in the bundle (the name→type map is type-only and erased). See docs/guide/typescript.md.

Notes

  • Source of truth : both the autonomous build (tools/fw-bundler) and this Vite plugin consume the same fw.config.json file, so they cannot drift.
  • Catalog scan : the plugin scans src/ once at instantiation to map module names → file paths. Cost is paid once per Vite session.
  • Dev-session config/catalog reload — no restart needed : in vite dev, editing fw.config.json (default path or an options.configPath override) or regenerating the committed catalog (bun run integrations:catalog) is picked up automatically — the plugin watches both files via Vite's own configureServer dev-server hook, re-validates and swaps its cached state, invalidates the affected virtual modules, and triggers a full browser reload. No manual "restart the dev server" step is required for either input. This is dev-only : vite build never calls configureServer, so production/build output is byte-identical to before this behaviour was added — the cache is still built exactly once, at plugin construction, and never re-read outside a running dev session. A malformed edit (invalid JSON, an unknown preset/side-bundle) logs an error to the Vite console and leaves the previously-working cache in place, rather than crashing the dev session.
  • Sanity injection : when sanity: 'base' or sanity: 'community' is set, the plugin injects import { applyBase } from '@awacloud/fw/sanity/base'; applyBase(); (resp. applyCommunity) as the first <head> script of every HTML page (via transformIndexHtml, head-prepend). When sanity: 'lockdown' is set, the snippet is import { lockdown } from '@awacloud/fw/sanity/lockdown'; lockdown(); — because lockdown.js is an explicit-call ES module, not a side-effect IIFE. Module scripts run in document order, so the import+invocation executes before any app entry. This is client-only and works identically in vite dev and vite build.
  • Webpack / Rollup / esbuild : equivalent plugins are planned. The current Vite plugin is the reference implementation and reads the same config.json — porting amounts to wiring resolveId/load hooks of the target bundler.