mion's Vite plugin provides Ahead-of-Time (AOT) compilation that pre-compiles critical runtime functions at build time. Instead of writing files to disk, AOT caches are generated as Vite virtual modules that get embedded directly into your server bundle.
eval or new Functionaot: true is set, the heavy @mionjs/run-types package is NOT loaded at allmion uses Just-In-Time (JIT) compilation to generate validation and serialization functions at runtime. While this provides great flexibility, it has two main drawbacks:
AOT compilation solves both issues by pre-compiling these functions during your Vite build process.
The mion Vite plugin generates AOT caches through this pipeline (using the server configuration):
buildStart — The plugin spawns a vite-node child process that runs your server start script (or uses ssrLoadModule in middleware mode)MION_COMPILE env) and emits all cachesThe server.runMode determines how the server process is managed:
buildOnly: Spawn child, generate caches, kill child (best for production builds)childProcess: Spawn child, generate caches, keep server running (best for development/testing)middleware: Load in same Vite process (for frameworks like Nuxt)See Vite Configuration for details on each mode.
The plugin generates these virtual modules that you can import in your application:
| Module | Description |
|---|---|
virtual:mion-aot/jit-fns | Pre-compiled JIT validation/serialization functions |
virtual:mion-aot/pure-fns | Pre-compiled pure functions cache |
virtual:mion-aot/router-cache | Router methods metadata cache |
virtual:mion-aot/caches | Combined module — imports and registers all 3 caches |
virtual:mion-server-pure-fns | Server pure functions extracted from client code |
For TypeScript support, add the virtual module type definitions to your tsconfig.json:
{
"compilerOptions": {
"types": ["@mionjs/devtools/virtual-modules"]
}
}
The plugin caches AOT results on disk (in Vite's node_modules/.vite/ by default) so subsequent builds skip the expensive vite-node step when your server source hasn't changed. The cache is invalidated automatically when source files change, the devtools version updates, or AOT options change.
Set MION_AOT_FORCE=true to force regeneration regardless of the cache.
The AOT build process works by executing your start script and capturing all routes that get registered. This means:
import {Routes, route} from '@mionjs/router';
import {runType} from '@mionjs/run-types';
interface User {
id: string;
name: string;
}
interface AuditLog {
action: string;
timestamp: Date;
}
declare function findUser(id: string): User;
const routes = {
// ✅ User type WILL be compiled (exposed in return type)
getUser: route((ctx, id: string): User => {
return findUser(id);
}),
// ✅ string parameter WILL be compiled
saveUser: route((ctx, name: string): void => {
// ...
}),
} satisfies Routes;
// ❌ AuditLog type will NOT be compiled!
// It's not used in any route parameter or return type
async function logAction(log: AuditLog): Promise<void> {
const validate = await runType<AuditLog>().createJitFunction('isType');
// This will fail in secure environments (no JIT available)
}
// ❌ This route won't be compiled if it's in a file
// that is not imported by the start script
// routes-admin.ts (not imported)
const adminRoutes = {
deleteUser: route((ctx, id: string): void => {
/* ... */
}),
};
runType<T>() directly, make sure T is also used in at least one route parameter or return type.Import the combined caches module before initializing the router. The virtual:mion-aot/caches module automatically registers all caches:
// Import cache exports from your AOT package
import {routerCache, jitFnsCache, pureFnsCache} from 'my-api-aot';
// Import the cache loading function from @mionjs/core
import {addAOTCaches} from '@mionjs/core';
// Now initialize your router - it will use the pre-compiled functions
import {initMionRouter} from '@mionjs/router';
import {routes as myRoutes} from './aot-routes-example.ts';
// Load the pre-compiled caches BEFORE initializing the router
addAOTCaches(jitFnsCache, pureFnsCache);
export const myApi = await initMionRouter(myRoutes);
When you set aot: true in the router options, the router operates in strict AOT mode:
@mionjs/run-types package is NOT loaded at allAOTCacheError is thrown immediatelyawait initMionRouter(routes, {
aot: true, // Strict AOT mode - run-types will NOT be loaded
});
aot: true is set, ALL routes and middleFns MUST be present in the AOT cache. If any are missing, the router will throw an AOTCacheError instead of falling back to JIT compilation.// Load AOT caches first
import {routerCache, jitFnsCache, pureFnsCache} from 'my-api-aot';
import {addAOTCaches} from '@mionjs/core';
// Then initialize router and server
import {initMionRouter} from '@mionjs/router';
import {initHttp} from '@mionjs/platform-node';
import {routes} from './aot-routes-example.ts';
// Load the pre-compiled caches BEFORE initializing the router
addAOTCaches(jitFnsCache, pureFnsCache);
export const myApi = await initMionRouter(routes);
initHttp({port: 3000});
console.log('Server running on port 3000');
import {Routes, route} from '@mionjs/router';
export interface User {
id: string;
name: string;
email: string;
}
export type MyApi = typeof routes;
export const routes = {
users: {
getById: route((ctx, id: string): User => {
return {id, name: 'John', email: 'john@example.com'};
}),
create: route((ctx, user: Omit<User, 'id'>): User => {
return {id: 'USER-123', ...user};
}),
},
} satisfies Routes;
The router handles AOT cache loading automatically when you set aot: true in the router options. However, if you need to access AOT caches from your own packages or custom code, you can import them directly from @mionjs/core/aot-caches:
import {loadAOTCaches, getRawAOTCaches} from '@mionjs/core/aot-caches';
// Load and register all AOT caches into the global cache
loadAOTCaches();
// Or access the raw cache data directly
const {jitFnsCache, pureFnsCache, routerCache} = getRawAOTCaches();
The mion Vite plugin automatically intercepts this import and replaces the underlying empty caches with the real AOT-generated data. This works both when importing from source (.ts) and from pre-built output (.js), so any package in your monorepo can consume AOT caches without extra configuration — as long as the Vite plugin is active.
AOT compilation is essential for running mion in secure environments that restrict dynamic code execution. Environments like Cloudflare Workers, Deno Deploy, and some enterprise environments prohibit:
eval()new Function()mion's JIT compilation normally uses new Function() to generate validation and serialization functions. AOT compilation pre-generates these functions as static code, eliminating the need for runtime code generation.