Devtools

Vite Configuration

How to configure the mion Vite plugin for server and client packages.

The mionPlugin from @mionjs/devtools/vite-plugin is the single Vite plugin that handles all mion build-time features: runtime type reflection, pure function extraction, and AOT cache generation.

Plugin Options

import {mionPlugin} from '@mionjs/devtools/vite-plugin';

mionPlugin({
    // Enables the type compiler, preserves TypeScript type metadata at runtime.
    // Required for mion's validation and serialization.
    runTypes?: {
        tsConfig?: string;              // Path to tsconfig.json (auto-discovered by default)
        include?: string | string[];    // Glob patterns (default: ['**/*.tsx', '**/*.ts'])
        exclude?: string | string[];    // Glob patterns (default: 'node_modules/**')
        reflection?: 'default' | 'explicit' | 'never';
        compilerOptions?: CompilerOptions;
    },

    // Scans client source for pureServerFn(), registerPureFnFactory(), and mapFrom() calls.
    // Extracts them into a virtual module the server imports automatically.
    serverPureFunctions?: {
        clientSrcPath: string;          // Path to client package source directory (required)
        include?: string[];             // Glob patterns (default: ['**/*.ts', '**/*.tsx'])
        exclude?: string[];             // Glob patterns (default: ['**/node_modules/**', '**/.dist/**', '**/dist/**'])
        noViteClient?: boolean;         // Require explicit names for pureServerFn/mapFrom (default: false)
    },

    // Generates AOT caches for validation, serialization, and pure functions.
    // Without this, caches are fetched at runtime via fetchRemoteMethodsMetadata().
    aotCaches?: {
        excludedFns?: string[];         // JIT function IDs to exclude
        excludedPureFns?: string[];     // Pure function names to exclude
        cache?: boolean | string;       // Disk cache: true (default), false, or custom path
        excludeReflection?: boolean;    // Stub out reflection modules in bundle (reduces size)
        customVirtualModuleId?: string; // Custom virtual module prefix
    },

    // Runs the server to generate AOT caches and optionally keeps it alive.
    // If omitted, only mion's internal routes are cached (when aotCaches is set).
    server?: {
        startScript: string;      // Server start script (required)
        viteConfig?: string;      // Server's vite.config.ts for vite-node (auto-discovered)
        runMode: 'buildOnly' | 'childProcess' | 'middleware'; // Server run mode (required)
        waitTimeout?: number;           // Max wait time in ms for setPlatformConfig() IPC (default: 30000)
        env?: Record<string, string>;   // Extra env vars for the child process
        args?: string[];                // Extra args appended after the start script
    },
});
The aotCaches option only applies to vite build — it has no effect during vite dev.

Configuration Examples

Server Only

For setups where server and client use separate build tools (e.g., Next.js, Turbopack), or when you only have a server package.

import {defineConfig} from 'vite';
import {resolve} from 'path';
import {mionVitePlugin} from '@mionjs/devtools/vite-plugin';

export default defineConfig({
    plugins: [
        mionVitePlugin({
            // runTypes metadata
            runTypes: {tsConfig: resolve(__dirname, 'tsconfig.json')},
            // Scan client source for pureServerFn() and mapFrom() calls
            serverPureFunctions: {
                clientSrcPath: resolve(__dirname, '../client/src'),
            },
        }),
    ],
    build: {
        lib: {
            entry: resolve(__dirname, 'src/init.ts'),
            formats: ['es'],
        },
        rollupOptions: {
            external: [/^[^./]/],
        },
    },
});

Client + Server

When both client and server are managed by Vite. In childProcess mode the server stays running for dev; in middleware mode it runs inside the same Vite process (e.g., Nuxt).

import {defineConfig} from 'vite';
import {resolve} from 'path';
import {mionVitePlugin} from '@mionjs/devtools/vite-plugin';

// IPC mode: spawns the server, generates AOT caches, and keeps the server running.
// Useful during development when the client needs a live server for API calls.
export default defineConfig({
    plugins: [
        mionVitePlugin({
            runTypes: {tsConfig: resolve(__dirname, 'tsconfig.json')},
            // runs the server in a child process separate from vite dev server
            server: {
                startScript: resolve(__dirname, '../server/src/init.ts'),
                viteConfig: resolve(__dirname, '../server/vite.config.ts'),
                runMode: 'childProcess',
                waitTimeout: 30000,
                env: {PORT: '3000'},
            },
        }),
    ],
});

Client Only

The client fetches validation and serialization metadata from the server at runtime using optimistic updates, so no AOT caches or build-time server spawning is needed.

Client (no Vite plugin)
// Client without mion Vite plugin — no AOT caches needed.
// Validation and serialization metadata is fetched at runtime
// from the server via fetchRemoteMethodsMetadata().
import {initClient} from '@mionjs/client';
import type {MyApi} from './aot-routes-example.ts';

const {routes, middleFns} = initClient<MyApi>({baseURL: 'http://localhost:3000'});

ESLint Rules

mion provides an ESLint plugin with rules to catch common mistakes with type imports, route typing, and pure functions. See the ESLint Rules page for full documentation.

.eslintrc.json
{
  "extends": ["plugin:@mionjs/recommended"]
}