# vite-plugin-automock

Effortless API mock auto-generation for legacy projects. No manual files, just automock!

---

## Features

- Automatically captures API requests and generates local mock data
- Perfect for legacy projects, easily backfill missing mocks
- Supports manual mock file creation with a simple format
- Fine-grained control: enable/disable mock, proxy, and capture independently
- Visual mock inspector panel for managing mock files
- Production-ready with client-side interceptor (Axios / PureHttp)
- Fully compatible with the Vite plugin ecosystem
- Zero-config, plug-and-play

## Installation

```bash
pnpm add -D vite-plugin-automock
# or
npm install --save-dev vite-plugin-automock
# or
yarn add -D vite-plugin-automock
```

## Quick Start

```typescript
// vite.config.ts
import { automock } from 'vite-plugin-automock'

export default defineConfig({
  plugins: [
    automock({
      proxyBaseUrl: 'https://api.example.com', // Required for proxy/capture
    })
  ]
})
```

That's it! The plugin will:

1. Intercept requests matching `apiPrefix` (default `/api`)
2. Return mock data if a mock file exists
3. Proxy to `proxyBaseUrl` and auto-save the response as a mock file

## Configuration Options

### Plugin Options

| Option | Type | Required | Default | Description |
| ------ | ---- | -------- | ------- | ----------- |
| `proxyBaseUrl` | `string` | No | - | Base URL of your API server. Required only when `proxy` or `capture` is used |
| `mockDir` | `string` | No | `'mock'` | Directory to store mock files |
| `apiPrefix` | `string` | No | `'/api'` | API path prefix to intercept |
| `pathRewrite` | `(path: string) => string` | No | `(path) => path` | Function to rewrite request paths |
| `enabled` | `boolean` | No | `true` | Global switch. When `false`, all requests pass through without mock handling |
| `proxy` | `boolean` | No | `true` | Whether to proxy unmatched requests to `proxyBaseUrl` |
| `capture` | `boolean` | No | `true` | Whether to auto-capture proxy responses and save as mock files |
| `productionMock` | `boolean \| 'auto'` | No | `false` | Enable client-side mock bundle support |
| `inspector` | `boolean \| InspectorOptions` | No | `false` | Enable visual mock inspector UI |

### Bundle Options

These options are available when using the `automock()` function (which includes bundle support):

| Option | Type | Default | Description |
| ------ | ---- | ------- | ----------- |
| `bundleMockData` | `boolean` | `true` | Whether to bundle mock data for production |
| `bundleOutputPath` | `string` | `'public/mock-data.json'` | Output path for the mock bundle file |

For a complete explanation of option interactions, see [Configuration Guide](docs/CONFIGURATION_CN.md).

### Inspector Options

When `inspector` is an object:

| Option | Type | Default | Description |
| ------ | ---- | ------- | ----------- |
| `route` | `string` | `'/__mock/'` | Route path for the inspector UI |
| `enableToggle` | `boolean` | `true` | Allow toggling mock enable/disable in the UI |

### Full Configuration Example

```typescript
import { automock } from 'vite-plugin-automock'

export default defineConfig({
  plugins: [
    automock({
      // Basic
      proxyBaseUrl: 'https://api.example.com',
      mockDir: 'mock',
      apiPrefix: '/api',
      pathRewrite: (path) => path.replace(/^\/api/, ''),

      // Fine-grained control
      enabled: true,
      proxy: true,
      capture: true,

      // Production
      productionMock: 'auto',
      bundleMockData: true,
      bundleOutputPath: 'public/mock-data.json',

      // Inspector
      inspector: {
        route: '/__mock/',
        enableToggle: true,
      },
    })
  ]
})
```

## Behavior Modes

The `enabled`, `proxy`, and `capture` options control different parts of the development middleware. `enabled=false` is a global bypass: the plugin will not read mocks, proxy, or capture.

### Mock-only Mode

Only return mock data, never proxy to backend. Useful when you want strict offline development:

```typescript
automock({
  proxyBaseUrl: 'https://api.example.com',
  enabled: true,   // Intercept requests
  proxy: false,    // Don't proxy to backend
  capture: false,  // Don't save new mocks
})
```

### Pass-through Mode

Bypass all automock handling and let later Vite middleware, including `server.proxy`, handle the request:

```typescript
automock({
  enabled: false,
})
```

### Default Mode (Recommended)

All features enabled - mock first, proxy fallback, auto-capture:

```typescript
automock({
  proxyBaseUrl: 'https://api.example.com',
  // enabled: true (default)
  // proxy: true (default)
  // capture: true (default)
})
```

## Client API Reference

The client-side interceptor is used for **production mode** mock support. Import from `vite-plugin-automock/client`.

### For PureHttp Wrapper

```typescript
// src/main.ts
import {
  initMockInterceptorForPureHttp,
  setMockEnabled,
  registerHttpInstance
} from 'vite-plugin-automock/client'
import { http } from './http' // Your PureHttp instance

// Register your http instance
registerHttpInstance(http)

// Initialize mock interceptor
initMockInterceptorForPureHttp()
  .then(() => console.log('[MockInterceptor] Initialized'))
  .catch(error => console.error('[MockInterceptor] Failed:', error))

// Enable mock in development
if (import.meta.env.DEV) {
  setMockEnabled(true)
}
```

### For Plain Axios

```typescript
// src/main.ts
import { initMockInterceptor, setMockEnabled } from 'vite-plugin-automock/client'
import axios from 'axios'

initMockInterceptor(axios)
  .then(() => console.log('[MockInterceptor] Initialized'))
  .catch(error => console.error('[MockInterceptor] Failed:', error))

if (import.meta.env.DEV) {
  setMockEnabled(true)
}
```

### Client Exports

| Export | Type | Description |
| ------ | ---- | ----------- |
| `initMockInterceptor` | `(axios: AxiosInstance) => Promise<void>` | Initialize interceptor with an Axios instance |
| `initMockInterceptorForPureHttp` | `() => Promise<void>` | Initialize interceptor for PureHttp (call `registerHttpInstance` first) |
| `registerHttpInstance` | `(http: { constructor: { axiosInstance: AxiosInstance } }) => void` | Register a PureHttp instance |
| `setMockEnabled` | `(enabled: boolean) => void` | Enable/disable mock at runtime |
| `isMockEnabled` | `() => boolean` | Check if mock is currently enabled |
| `loadMockData` | `() => Promise<Record<string, MockBundleData>>` | Load mock data from the bundled JSON file |
| `createMockInterceptor` | `(options: MockInterceptorOptions) => MockInterceptor` | Create a custom interceptor instance |

### MockInterceptorOptions

```typescript
interface MockInterceptorOptions {
  mockData: Record<string, MockBundleData>
  enabled?: boolean | (() => boolean)
  onMockHit?: (url: string, method: string, mock: MockBundleData) => void
  onBypass?: (url: string, method: string, reason: string) => void
}

interface MockBundleData {
  enable: boolean
  data: unknown
  delay?: number
  status?: number
  isBinary?: boolean
}
```

## Production Mode

To use mock data in production builds:

1. Enable `productionMock` and `bundleMockData`:

```typescript
automock({
  proxyBaseUrl: 'https://api.example.com',
  productionMock: true,    // or 'auto'
  bundleMockData: true,    // default
  bundleOutputPath: 'public/mock-data.json',
})
```

2. The plugin will generate `public/mock-data.json` at build time containing all mock data.

3. Initialize the client interceptor in your app (see [Client API Reference](#client-api-reference)).

4. The `productionMock: 'auto'` option enables mock outside `production` mode and disables it in `production` mode.

## Mock Inspector

The visual inspector provides a web UI to manage mock files:

```typescript
automock({
  proxyBaseUrl: 'https://api.example.com',
  inspector: true,  // or { route: '/__mock/', enableToggle: true }
})
```

Then visit `http://localhost:5173/__mock/` to:

- **Browse** all mock files organized by URL
- **Toggle** mock enable/disable status
- **Adjust** response delay and status codes
- **Edit** JSON response data directly
- **Create** new mock files
- **Delete** existing mock files
- **Batch update** multiple mocks at once

## Mock File Structure

Mock files are organized by URL path and HTTP method:

```
mock/
├── api/
│   ├── users/
│   │   ├── get.js     # GET /api/users
│   │   └── post.js    # POST /api/users
│   └── items/
│       ├── get.js     # GET /api/items
│       └── post.js    # POST /api/items
```

Each mock file exports an object:

```javascript
/**
 * Mock data for /api/users (GET)
 * Generated at 2025-01-11T10:30:00.000Z
 */
export default {
  enable: false,   // Whether to use this mock
  data: {
    code: 0,
    message: "success",
    data: [
      { id: 1, name: "User 1" },
      { id: 2, name: "User 2" },
    ],
  },
  delay: 0,    // Artificial delay in milliseconds
  status: 200, // HTTP status code
}
```

### Dynamic Mock Data

```javascript
export default {
  enable: true,
  data: () => ({
    timestamp: new Date().toISOString(),
    randomId: Math.floor(Math.random() * 1000),
  }),
}
```

## Common Pitfalls

### Dual Proxy Configuration

**Do NOT** configure both Vite's `server.proxy` and automock's `proxyBaseUrl` for the same API prefix:

```typescript
// ❌ Wrong - will cause request conflicts
export default defineConfig({
  plugins: [
    automock({ proxyBaseUrl: 'http://localhost:8888', apiPrefix: '/api' })
  ],
  server: {
    proxy: {
      '/api': { target: 'http://localhost:8888', changeOrigin: true } // CONFLICTS
    }
  }
})

// ✅ Correct - use automock for /api, remove server.proxy
export default defineConfig({
  plugins: [
    automock({ proxyBaseUrl: 'http://localhost:8888', apiPrefix: '/api' })
  ]
})
```

The plugin will automatically warn you if a conflicting proxy is detected.

## How It Works

1. **Request Interception**: The plugin intercepts all requests matching `apiPrefix`
2. **Mock Check**: Checks if a corresponding mock file exists and is enabled
3. **Conditional Response**:
   - Mock exists and enabled -> Returns mock data
   - Mock exists but disabled -> Skips to proxy or next middleware
   - Mock doesn't exist -> Proxies to real API (if `proxy: true`)
4. **Auto-Capture**: Real API responses are automatically saved as mock files (if `capture: true`)
5. **Hot Reloading**: Changes to mock files are automatically detected and reloaded
6. **Bundle**: Mock data is bundled into JSON for production use

## Comparison with Traditional Mock Plugins

| Feature | Traditional Mock Plugins | vite-plugin-automock |
| ------- | ----------------------- | -------------------- |
| Auto backfill | - | Yes |
| Legacy project support | - | Yes |
| Manual mock files | Yes | Yes |
| Config complexity | High | Very low |
| Zero setup | - | Yes |
| Real API integration | - | Yes |
| Fine-grained control | - | Yes |
| Visual inspector | - | Yes |
| Production mock | - | Yes |

## When to Use

- **Legacy Projects**: Quickly add mock capability to existing projects
- **API Development**: Test frontend while backend is being developed
- **Offline Development**: Work without internet or when APIs are unavailable
- **Testing**: Consistent test data without external dependencies
- **Demo/Presentation**: Reliable demo data that doesn't change

## Contributing

We welcome contributions! See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.

## License

ISC
