import type { Plugin } from 'vite' import { mockRules, type MockContext } from './index' const MOCK_PREFIX = '/api/v1' function readBody(req: NodeJS.ReadableStream): Promise { return new Promise((resolve) => { let raw = '' req.on('data', (chunk) => (raw += chunk)) req.on('end', () => resolve(raw)) }) } /** * 本地 mock 中间件:始终挂载于 dev,但仅命中「全局 mock 开启」或标记 alwaysMock 的规则。 * 这样新增页面的接口可被单独 mock,而项目其余接口仍走真实后端代理。 * 不引入三方 mock 依赖,避免污染生产依赖树。 */ export function mockPlugin(globalMock = false): Plugin { return { name: 'portal-mock', apply: 'serve', configureServer(server) { server.middlewares.use((req, res, next) => { if (!req.url?.startsWith(MOCK_PREFIX)) return next() const requestUrl = new URL(req.url, 'http://localhost') void (async () => { const raw = req.method === 'GET' ? '' : await readBody(req) const isJsonBody = (req.headers['content-type'] ?? '').includes('application/json') const rule = mockRules.find( (item) => item.method === (req.method ?? 'GET').toUpperCase() && item.pattern.test(requestUrl.pathname) && (globalMock || item.alwaysMock === true), ) if (!rule) return next() const matched = requestUrl.pathname.match(rule.pattern) const ctx: MockContext = { url: requestUrl.pathname, params: matched ? matched.slice(1) : [], query: Object.fromEntries(requestUrl.searchParams.entries()), body: raw && isJsonBody ? JSON.parse(raw) : {}, } res.setHeader('Content-Type', 'application/json;charset=utf-8') res.end( JSON.stringify({ code: '00000', data: rule.handler(ctx) ?? null, msg: 'success' }), ) })() }) }, } }