vite-plugin-mock-dev-server
Advanced tools
Comparing version 1.1.5 to 1.1.6
@@ -6,2 +6,3 @@ import { Connect, Plugin, ResolvedConfig } from 'vite'; | ||
import formidable from 'formidable'; | ||
import { WebSocketServer } from 'ws'; | ||
import EventEmitter from 'node:events'; | ||
@@ -12,7 +13,7 @@ import chokidar from 'chokidar'; | ||
/** | ||
* To configure the path matching rules for mock services, | ||
* To configure the path matching rules for http mock services, | ||
* any request path starting with prefix will be intercepted and proxied. | ||
* If the prefix starts with `^`, it will be recognized as a `RegExp`. | ||
* | ||
* 为 mock 服务配置 路径匹配规则,任何请求路径以 prefix 开头的都将被拦截代理。 | ||
* 为 http mock 服务配置 路径匹配规则,任何请求路径以 prefix 开头的都将被拦截代理。 | ||
* 如果 prefix 以 `^` 开头,将被识别为 `RegExp`。 | ||
@@ -24,2 +25,15 @@ * @default [] | ||
/** | ||
* Configure path matching rules for WebSocket mock service. | ||
* Any ws/wss requests with a request path starting with wsPrefix | ||
* will be intercepted by the proxy. | ||
* If wsPrefix starts with `^`, it will be recognized as a `RegExp`. | ||
* | ||
* 为 websocket mock 服务配置 路径匹配规则, 任何请求路径以 wsPrefix 开头的 ws/wss请求, | ||
* 都将被代理拦截。 | ||
* 如果 wsPrefix 以 `^` 开头,将被识别为 `RegExp`。 | ||
* @default [] | ||
* @example ['/socket.io'] | ||
*/ | ||
wsPrefix?: string | string[]; | ||
/** | ||
* glob string matching mock includes files | ||
@@ -134,3 +148,3 @@ * | ||
type ResponseCookiesFn = (request: MockRequest) => ResponseCookies | Promise<ResponseCookies>; | ||
interface MockOptionsItem { | ||
interface MockBaseItem { | ||
/** | ||
@@ -151,8 +165,9 @@ * The interface address that needs to be mocked, | ||
/** | ||
* The interface allows request methods, and by default allows both GET and POST. | ||
* Enable WebSocket interface simulation | ||
* | ||
* 该接口允许的 请求方法,默认同时支持 GET 和 POST | ||
* @default ['POST','GET'] | ||
* 开启 websocket 接口模拟 | ||
* | ||
* @default false | ||
*/ | ||
method?: Method | Method[]; | ||
ws?: boolean; | ||
/** | ||
@@ -164,3 +179,3 @@ * Whether to enable mock for this interface. | ||
* | ||
* 是否启动对该接口的mock,在多数场景下,我们进需要对部分接口进行 mock, | ||
* 是否启动对该接口的mock,在多数场景下,我们仅需要对部分接口进行 mock, | ||
* 而不是对所有配置了mock的请求进行全量mock,所以是否能够配置是否启用很重要 | ||
@@ -170,3 +185,12 @@ * @default true | ||
enabled?: boolean; | ||
} | ||
interface MockHttpItem extends MockBaseItem { | ||
/** | ||
* The interface allows request methods, and by default allows both GET and POST. | ||
* | ||
* 该接口允许的 请求方法,默认同时支持 GET 和 POST | ||
* @default ['POST','GET'] | ||
*/ | ||
method?: Method | Method[]; | ||
/** | ||
* Configure the response body headers | ||
@@ -340,7 +364,31 @@ * | ||
validator?: Partial<Omit<ExtraRequest, 'getCookie'>> | ((request: ExtraRequest) => boolean); | ||
ws?: false; | ||
} | ||
type MockOptions = MockOptionsItem[]; | ||
type MockWebsocketServerDestroy = (() => void) | void; | ||
interface MockWebsocketItem extends MockBaseItem { | ||
ws: true; | ||
/** | ||
* Configure Websocket Server | ||
* | ||
* 配置 Websocket Server | ||
* @example | ||
* ```ts | ||
* export default { | ||
* ws: true | ||
* setup: (wss) => { | ||
* wss.on('connection', (ws,req) => { | ||
* ws.on('message', (raw) => console.log(raw)) | ||
* ws.send(JSON.stringify({ type: 'connected' })) | ||
* }) | ||
* wss.on('error', (error) => console.error(error)) | ||
* } | ||
* } | ||
* ``` | ||
*/ | ||
setup: (wss: WebSocketServer) => MockWebsocketServerDestroy; | ||
} | ||
type MockOptions = (MockHttpItem | MockWebsocketItem)[]; | ||
type FormidableFile = formidable.File | formidable.File[]; | ||
declare function mockDevServerPlugin({ prefix, include, exclude, reload, formidableOptions, build, cookiesOptions, }?: MockServerPluginOptions): Plugin[]; | ||
declare function mockDevServerPlugin({ prefix, wsPrefix, include, exclude, reload, formidableOptions, build, cookiesOptions, }?: MockServerPluginOptions): Plugin[]; | ||
@@ -362,4 +410,5 @@ /** | ||
*/ | ||
declare function defineMock(config: MockOptionsItem): MockOptionsItem; | ||
declare function defineMock(config: MockHttpItem): MockHttpItem; | ||
declare function defineMock(config: MockOptions): MockOptions; | ||
declare function defineMock(config: MockWebsocketItem): MockWebsocketItem; | ||
/** | ||
@@ -372,3 +421,3 @@ * 返回一个自定义的 defineMock 函数,用于支持对 mock config 的预处理。 | ||
*/ | ||
declare function createDefineMock(transformer: (mock: MockOptionsItem) => MockOptionsItem | void): typeof defineMock; | ||
declare function createDefineMock(transformer: (mock: MockHttpItem | MockWebsocketItem) => MockHttpItem | MockWebsocketItem | void): typeof defineMock; | ||
@@ -387,4 +436,3 @@ interface MockLoaderOptions { | ||
options: MockLoaderOptions; | ||
static EXT_JSON: RegExp; | ||
moduleCache: Map<string, MockOptions | MockOptionsItem>; | ||
moduleCache: Map<string, MockOptions | MockHttpItem | MockWebsocketItem>; | ||
moduleDeps: Map<string, Set<string>>; | ||
@@ -409,4 +457,2 @@ cwd: string; | ||
private loadMock; | ||
private loadJson; | ||
private loadModule; | ||
private loadFromCode; | ||
@@ -423,4 +469,6 @@ private transformWithEsbuild; | ||
declare function transformMockData(mockList: Map<string, MockOptionsItem | MockOptions> | (MockOptionsItem | MockOptions)[]): Record<string, MockOptions>; | ||
declare function mockWebSocket(loader: MockLoader, httpServer: http.Server | null, proxies: string[], cookiesOptions: MockServerPluginOptions['cookiesOptions']): void; | ||
export { BaseMiddlewareOptions, FormidableFile, MockOptions, MockOptionsItem, MockServerPluginOptions, baseMiddleware, createDefineMock, mockDevServerPlugin as default, defineMock, mockDevServerPlugin, transformMockData }; | ||
declare function transformMockData(mockList: Map<string, MockHttpItem | MockWebsocketItem | MockOptions> | (MockHttpItem | MockWebsocketItem | MockOptions)[]): Record<string, MockOptions>; | ||
export { BaseMiddlewareOptions, FormidableFile, MockHttpItem, MockOptions, MockRequest, MockServerPluginOptions, MockWebsocketItem, baseMiddleware, createDefineMock, mockDevServerPlugin as default, defineMock, mockDevServerPlugin, mockWebSocket, transformMockData }; |
@@ -1,22 +0,31 @@ | ||
import Fe from"fs";import A from"fs/promises";import R from"path";import{build as Te}from"esbuild";import $e from"fast-glob";import Le from"is-core-module";import{createFilter as Ie,normalizePath as We}from"vite";var Z="vite-plugin-mock-dev-server",ee="1.1.5";import te from"fs/promises";import Oe from"path";import je from"json5";var N={name:"externalize-deps",setup(t){t.onResolve({filter:/.*/},({path:r})=>{if(r[0]!=="."&&!Oe.isAbsolute(r))return{external:!0}})}},oe={name:"json5-loader",setup(t){t.onLoad({filter:/\.json5$/},async({path:r})=>{let e=await te.readFile(r,"utf-8");return{contents:`export default ${JSON.stringify(je.parse(e))}`,loader:"js"}})}},re={name:"json-loader",setup(t){t.onLoad({filter:/\.json$/},async({path:r})=>({contents:`export default ${await te.readFile(r,"utf-8")}`,loader:"js"}))}},B=t=>({name:"alias-plugin",setup(r){r.onResolve({filter:/.*/},async({path:e})=>{let o=t.find(({find:a})=>Se(a,e));if(!o)return null;let{find:n,replacement:s}=o;return{path:(await r.resolve(e.replace(n,s),{kind:"import-statement",resolveDir:s,namespace:"file"})).path,external:!1}})}});function Se(t,r){return t instanceof RegExp?t.test(r):r.length<t.length?!1:r===t?!0:r.startsWith(`${t}/`)}import _ from"fs";import z from"path";import{fileURLToPath as Re}from"url";import Pe from"debug";import{match as De}from"path-to-regexp";import ne from"picocolors";var v=t=>Array.isArray(t),b=t=>typeof t=="function",Ce=t=>Object.prototype.toString.call(t)==="[object Object]",se=t=>Ce(t)&&Object.keys(t).length===0,Ee=t=>t!==null&&typeof t=="object"&&typeof t.pipe=="function",ie=t=>Ee(t)&&t.readable!==!1&&typeof t._read=="function"&&typeof t._readableState=="object";function ae(t){return new Promise(r=>setTimeout(r,t))}function ce(t){return z.dirname(Re(t))}var j=Pe("vite:plugin-mock-dev-server"),M=t=>v(t)?t:t==null?[]:[t],S={info(...t){console.info(ne.cyan("mock-dev-server: "),...t)},error(...t){console.error(` | ||
`,ne.cyan("mock-dev-server: "),...t,` | ||
`)}};function D(t,r,e){for(let n of r){let s=z.join(t,n);if(_.existsSync(s)&&_.statSync(s).isFile()){let i=e!=null&&e.pathOnly?s:_.readFileSync(s,"utf-8");if(!(e!=null&&e.predicate)||e.predicate(i))return i}}let o=z.dirname(t);if(o!==t&&(!(e!=null&&e.rootDir)||o.startsWith(e==null?void 0:e.rootDir)))return D(o,r,e)}var J=(t={})=>Object.keys(t).map(e=>{var n,s;let o=t[e];return typeof o=="string"?e:o.ws||(n=o.target)!=null&&n.toString().startsWith("ws:")||(s=o.target)!=null&&s.toString().startsWith("wss:")?"":e}).filter(Boolean);function U(t,r){return(De(t,{decode:decodeURIComponent})(r)||{params:{}}).params||{}}async function pe(t,r,e){let o=M(e.include),n=M(e.exclude),s={};if(r.define)for(let m in r.define){let d=r.define[m];s[m]=typeof d=="string"?d:JSON.stringify(d)}let i=J(r.server.proxy||{}),a=M(e.prefix),c={};try{let m=D(r.root,["package.json"]);m&&(c=JSON.parse(m))}catch{}let p=e.build.dist,u=await Ae(process.cwd(),o,n),l=R.join(r.root,`mock-data-${Date.now()}.js`);await A.writeFile(l,u,"utf-8");let{code:L,deps:I}=await qe(l,s,r.resolve.alias),O=Ne(I);await A.unlink(l);let P=[{filename:R.join(p,"mock-data.js"),source:L},{filename:R.join(p,"index.js"),source:Je([...a,...i],e.cookiesOptions,e.build.serverPort)},{filename:R.join(p,"package.json"),source:Be(c,O)}];try{if(R.isAbsolute(p)){await A.rm(p,{recursive:!0}),Fe.mkdirSync(p,{recursive:!0});for(let{filename:m,source:d}of P)await A.writeFile(m,d,"utf-8")}else for(let{filename:m,source:d}of P)t.emitFile({type:"asset",fileName:m,source:d})}catch{}}function Ne(t){let r=new Set,e=[Z,"connect","cors"];return Object.keys(t).forEach(o=>{t[o].imports.filter(s=>s.external).map(s=>s.path).forEach(s=>{!e.includes(s)&&!Le(s)&&r.add(s)})}),Array.from(r)}function Be(t,r){let{dependencies:e={},devDependencies:o={}}=t,n={...e,...o},s={name:"mock-server",type:"module",scripts:{start:"node index.js"},dependencies:{connect:"^3.7.0","vite-plugin-mock-dev-server":`^${ee}`,cors:"^2.8.5"},pnpm:{peerDependencyRules:{ignoreMissing:["vite"]}}};return r.forEach(i=>{s.dependencies[i]=n[i]||"latest"}),JSON.stringify(s,null,2)}function Je(t,r={},e=8080){return`import connect from 'connect'; | ||
import Fe from"fs";import G from"fs/promises";import I from"path";import{build as Le}from"esbuild";import $e from"fast-glob";import He from"is-core-module";import{createFilter as _e,normalizePath as Ue}from"vite";var re="vite-plugin-mock-dev-server",se="1.1.6";import ne from"fs/promises";import Se from"path";import Re from"json5";var N={name:"externalize-deps",setup(t){t.onResolve({filter:/.*/},({path:o})=>{if(o[0]!=="."&&!Se.isAbsolute(o))return{external:!0}})}},q={name:"json5-loader",setup(t){t.onLoad({filter:/\.json5$/},async({path:o})=>{let e=await ne.readFile(o,"utf-8");return{contents:`export default ${JSON.stringify(Re.parse(e))}`,loader:"js"}})}},A={name:"json-loader",setup(t){t.onLoad({filter:/\.json$/},async({path:o})=>({contents:`export default ${await ne.readFile(o,"utf-8")}`,loader:"js"}))}},B=t=>({name:"alias-plugin",setup(o){o.onResolve({filter:/.*/},async({path:e})=>{let r=t.find(({find:c})=>De(c,e));if(!r)return null;let{find:n,replacement:s}=r;return{path:(await o.resolve(e.replace(n,s),{kind:"import-statement",resolveDir:s,namespace:"file"})).path,external:!1}})}});function De(t,o){return t instanceof RegExp?t.test(o):o.length<t.length?!1:o===t?!0:o.startsWith(`${t}/`)}import K from"fs";import V from"path";import{fileURLToPath as Ce}from"url";import Ee from"debug";import{match as We}from"path-to-regexp";import ie from"picocolors";var P=t=>Array.isArray(t),R=t=>typeof t=="function",Ie=t=>Object.prototype.toString.call(t)==="[object Object]",ce=t=>Ie(t)&&Object.keys(t).length===0,Te=t=>t!==null&&typeof t=="object"&&typeof t.pipe=="function",ae=t=>Te(t)&&t.readable!==!1&&typeof t._read=="function"&&typeof t._readableState=="object";function pe(t){return new Promise(o=>setTimeout(o,t))}function le(t){return V.dirname(Ce(t))}var D=Ee("vite:plugin-mock-dev-server"),j=t=>P(t)?t:t==null?[]:[t],W={info(...t){console.info(ie.cyan("mock-dev-server: "),...t)},error(...t){console.error(` | ||
`,ie.cyan("mock-dev-server: "),...t,` | ||
`)}};function F(t,o,e){for(let n of o){let s=V.join(t,n);if(K.existsSync(s)&&K.statSync(s).isFile()){let i=e!=null&&e.pathOnly?s:K.readFileSync(s,"utf-8");if(!(e!=null&&e.predicate)||e.predicate(i))return i}}let r=V.dirname(t);if(r!==t&&(!(e!=null&&e.rootDir)||r.startsWith(e==null?void 0:e.rootDir)))return F(r,o,e)}var J=(t={})=>{let o=[],e=[];return Object.keys(t).forEach(r=>{var s,i;let n=t[r];typeof n=="string"||!n.ws&&!((s=n.target)!=null&&s.toString().startsWith("ws:"))&&!((i=n.target)!=null&&i.toString().startsWith("wss:"))?o.push(r):e.push(r)}),{httpProxies:o,wsProxies:e}};function z(t,o){return t[0]==="^"&&new RegExp(t).test(o)||o.startsWith(t)}function L(t,o){return(We(t,{decode:decodeURIComponent})(o)||{params:{}}).params||{}}async function ue(t,o,e){let r=j(e.include),n=j(e.exclude),s={};if(o.define)for(let m in o.define){let d=o.define[m];s[m]=typeof d=="string"?d:JSON.stringify(d)}let{httpProxies:i}=J(o.server.proxy||{});i.push(...j(e.prefix));let c=j(e.wsPrefix),a={};try{let m=F(o.root,["package.json"]);m&&(a=JSON.parse(m))}catch{}let p=e.build.dist,u=await Be(process.cwd(),r,n),l=I.join(o.root,`mock-data-${Date.now()}.js`);await G.writeFile(l,u,"utf-8");let{code:v,deps:M}=await Je(l,s,o.resolve.alias),y=Ne(M);await G.unlink(l);let k=[{filename:I.join(p,"mock-data.js"),source:v},{filename:I.join(p,"index.js"),source:Ae(i,c,e.cookiesOptions,e.build.serverPort)},{filename:I.join(p,"package.json"),source:qe(a,y)}];try{if(I.isAbsolute(p)){await G.rm(p,{recursive:!0}),Fe.mkdirSync(p,{recursive:!0});for(let{filename:m,source:d}of k)await G.writeFile(m,d,"utf-8")}else for(let{filename:m,source:d}of k)t.emitFile({type:"asset",fileName:m,source:d})}catch{}}function Ne(t){let o=new Set,e=[re,"connect","cors"];return Object.keys(t).forEach(r=>{t[r].imports.filter(s=>s.external).map(s=>s.path).forEach(s=>{!e.includes(s)&&!He(s)&&o.add(s)})}),Array.from(o)}function qe(t,o){let{dependencies:e={},devDependencies:r={}}=t,n={...e,...r},s={name:"mock-server",type:"module",scripts:{start:"node index.js"},dependencies:{connect:"^3.7.0","vite-plugin-mock-dev-server":`^${se}`,cors:"^2.8.5"},pnpm:{peerDependencyRules:{ignoreMissing:["vite"]}}};return o.forEach(i=>{s.dependencies[i]=n[i]||"latest"}),JSON.stringify(s,null,2)}function Ae(t,o,e={},r=8080){return`import { createServer } from 'node:http'; | ||
import connect from 'connect'; | ||
import corsMiddleware from 'cors'; | ||
import { baseMiddleware } from 'vite-plugin-mock-dev-server'; | ||
import { baseMiddleware, mockWebSocket } from 'vite-plugin-mock-dev-server'; | ||
import mockData from './mock-data.js'; | ||
const app = connect(); | ||
const server = createServer(app); | ||
const httpProxies = ${JSON.stringify(t)}; | ||
const wxProxies = ${JSON.stringify(o)} | ||
const cookiesOptions = ${JSON.stringify(e)}; | ||
mockWebSocket({ mockData }, server, wxProxies, cookiesOptions) | ||
app.use(corsMiddleware()); | ||
app.use(baseMiddleware({ mockData }, { | ||
formidableOptions: { multiples: true }, | ||
proxies: ${JSON.stringify(t)} | ||
cookiesOptions: ${JSON.stringify(r)} | ||
proxies: httpProxies, | ||
cookiesOptions, | ||
})); | ||
app.listen(${e}); | ||
console.log('listen: http://localhost:${e}'); | ||
`}async function Ae(t,r,e){let o=await $e(r,{cwd:t}),n=Ie(r,e,{resolve:!1}),s=o.filter(n),i="",a="";return s.forEach((c,p)=>{let u=We(R.join(t,c));i+=`import * as m${p} from '${u}'; | ||
`,a+=`m${p}, `}),`import { transformMockData } from 'vite-plugin-mock-dev-server'; | ||
server.listen(${r}); | ||
console.log('listen: http://localhost:${r}'); | ||
`}async function Be(t,o,e){let r=await $e(o,{cwd:t}),n=_e(o,e,{resolve:!1}),s=r.filter(n),i="",c="";return s.forEach((a,p)=>{let u=Ue(I.join(t,a));i+=`import * as m${p} from '${u}'; | ||
`,c+=`m${p}, `}),`import { transformMockData } from 'vite-plugin-mock-dev-server'; | ||
${i} | ||
const exporters = [${a}]; | ||
const exporters = [${c}]; | ||
const mockList = exporters.map((raw) => raw && raw.default | ||
@@ -27,7 +36,7 @@ ? raw.default | ||
export default transformMockData(mockList); | ||
`}async function qe(t,r,e){var o;try{let n=await Te({entryPoints:[t],outfile:"out.js",write:!1,target:["node14.18","node16"],platform:"node",bundle:!0,metafile:!0,format:"esm",define:r,plugins:[B(e),N,oe,re]});return{code:n.outputFiles[0].text,deps:((o=n.metafile)==null?void 0:o.inputs)||{}}}catch(n){console.error(n)}return{code:"",deps:{}}}import{Buffer as de}from"buffer";import{parse as me}from"url";import Ue from"cookies";import He from"http-status";import*as C from"mime-types";import{pathToRegexp as ge}from"path-to-regexp";import E from"picocolors";import H from"co-body";import _e from"formidable";async function le(t,r){var n;let e=t.method.toUpperCase();if(["GET","DELETE","HEAD"].includes(e))return;let o=((n=t.headers["content-type"])==null?void 0:n.toLocaleLowerCase())||"";try{if(o.startsWith("application/json"))return await H.json(t);if(o.startsWith("application/x-www-form-urlencoded"))return await H.form(t);if(o.startsWith("text/plain"))return await H.text(t);if(o.startsWith("multipart/form-data"))return await ze(t,r)}catch(s){console.error(s)}}async function ze(t,r){let e=_e(r);return new Promise((o,n)=>{e.parse(t,(s,i,a)=>{if(s){n(s);return}o({...i,...a})})})}function ue(t,r){return x(t.headers,r.headers)&&x(t.body,r.body)&&x(t.params,r.params)&&x(t.query,r.query)&&x(t.refererQuery,r.refererQuery)}function x(t,r){if(!r)return!0;for(let e in r)if(r[e]!==t[e])return!1;return!0}function ye(t,{formidableOptions:r={},proxies:e,cookiesOptions:o}){return async function(n,s,i){let a=Date.now(),{query:c,pathname:p}=me(n.url,!0);if(!p||e.length===0||!e.some(k=>Ve(k,n.url)))return i();let u=t.mockData,l=Object.keys(u).find(k=>ge(k).test(p));if(!l)return i();let{query:L}=me(n.headers.referer||"",!0),I=await le(n,r),O=new Ue(n,s,o),P=O.get.bind(O),m=n.method.toUpperCase(),d=Ge(u[l],{pathname:p,method:m,request:{query:c,refererQuery:L,body:I,headers:n.headers,getCookie:P}});if(!d)return i();j("middleware: ",m,n.url);let y=n,h=s;y.body=I,y.query=c,y.refererQuery=L,y.params=U(d.url,p),y.getCookie=P,h.setCookie=O.set.bind(O);let{body:W,delay:K,type:Me="json",response:V,status:xe=200,statusText:we}=d;if(G(h,xe,we),await Qe(y,h,d),await Xe(y,h,d),W){try{let k=b(W)?await W(y):W;await fe(a,K),Ke(h,k,Me)}catch(k){S.error(`${E.red("[body error]")} ${n.url} | ||
`,k),G(h,500),s.end("")}return}if(V){try{await fe(a,K),await V(y,h,i)}catch(k){S.error(`${E.red("[response error]")} ${n.url} | ||
`,k),G(h,500),s.end("")}return}s.end("")}}function Ge(t,{pathname:r,method:e,request:o}){return t.find(n=>{if(!r||!n||!n.url||!(n.method?v(n.method)?n.method:[n.method]:["GET","POST"]).includes(e))return!1;let i=ge(n.url).test(r);if(i&&n.validator){let a=U(n.url,r);if(b(n.validator))return n.validator({params:a,...o});try{return ue({params:a,...o},n.validator)}catch(c){return S.error(`${E.red("[validator error]")} ${r} | ||
`,c),!1}}return i})}function G(t,r=200,e){t.statusCode=r,t.statusMessage=e||Ye(r)}async function Qe(t,r,{headers:e,type:o="json"}){let n=C.contentType(o)||C.contentType(C.lookup(o)||"");if(n&&r.setHeader("Content-Type",n),r.setHeader("Cache-Control","no-cache,max-age=0"),r.setHeader("X-Mock","generate by vite:plugin-mock-dev-server"),!!e)try{let s=b(e)?await e(t):e;Object.keys(s).forEach(i=>{r.setHeader(i,s[i])})}catch(s){S.error(`${E.red("[headers error]")} ${t.url} | ||
`,s)}}async function Xe(t,r,{cookies:e}){if(e)try{let o=b(e)?await e(t):e;Object.keys(o).forEach(n=>{let s=o[n];if(v(s)){let[i,a]=s;r.setCookie(n,i,a)}else r.setCookie(n,s)})}catch(o){S.error(`${E.red("[cookies error]")} ${t.url} | ||
`,o)}}function Ke(t,r,e){if(ie(r))r.pipe(t);else if(de.isBuffer(r))t.end(e==="text"||e==="json"?r.toString("utf-8"):r);else{let o=typeof r=="string"?r:JSON.stringify(r);t.end(e==="buffer"?de.from(o):o)}}async function fe(t,r){if(!r||r<=0)return;let e=Date.now()-t,o=r-e;o>0&&await ae(o)}function Ve(t,r){return t[0]==="^"&&new RegExp(t).test(r)||r.startsWith(t)}function Ye(t){return He[t]||"Unknown"}import tt from"events";import q from"fs";import{createRequire as ot}from"module";import he from"path";import{pathToFileURL as rt}from"url";import ve from"chokidar";import{build as nt}from"esbuild";import st from"fast-glob";import it from"json5";import{createFilter as at,normalizePath as T}from"vite";import{parse as Ze}from"url";import et from"lodash.sortby";function ke(t){let r=[];for(let[,o]of t.entries())o&&(v(o)?r.push(...o):r.push(o));let e={};return r.filter(o=>(o.enabled||typeof o.enabled>"u")&&o.url).forEach(o=>{let{pathname:n,query:s}=Ze(o.url,!0),i=e[n]??(e[n]=[]),a={...o,url:n},c=a.validator;se(s)||(b(c)?a.validator=function(p){return x(p.query,s)&&c(p)}:c?(a.validator={...c},a.validator.query=a.validator.query?{...s,...a.validator.query}:s):a.validator={query:s}),i.push(a)}),Object.keys(e).forEach(o=>{e[o]=et(e[o],({validator:n})=>{if(!n)return 2;if(b(n))return 0;let{query:s,params:i,headers:a,body:c,refererQuery:p}=n;return 1/(F(s)+F(i)+F(a)+F(c)+F(p))})}),e}function F(t){return t?Object.keys(t).length:0}var ct=ce(import.meta.url),w=ot(ct),Q=class extends tt{constructor(e){super();this.options=e;this.moduleCache=new Map;this.moduleDeps=new Map;this.moduleType="cjs";this._mockData={};this.cwd=e.cwd||process.cwd();try{let o=D(this.cwd,["package.json"]);this.moduleType=o&&JSON.parse(o).type==="module"?"esm":"cjs"}catch{}}get mockData(){return this._mockData}async load(){let{include:e,exclude:o}=this.options,n=await st(e,{cwd:this.cwd}),s=at(e,o,{resolve:!1});this.watchMockEntry(),this.watchDeps();for(let i of n.filter(s))await this.loadMock(i);this.updateMockList(),this.on("mock:update",async i=>{s(i)&&(await this.loadMock(i),this.updateMockList(),this.emit("mock:update-end"))}),this.on("mock:unlink",async i=>{s(i)&&(this.moduleCache.delete(i),this.updateMockList(),this.emit("mock:update-end"))})}watchMockEntry(){let{include:e}=this.options,[o,...n]=e,s=ve.watch(o,{ignoreInitial:!0,cwd:this.cwd});n.length>0&&n.forEach(i=>s.add(i)),s.on("add",async i=>{i=T(i),this.emit("mock:update",i),j("watcher:add",i)}),s.on("change",async i=>{i=T(i),this.emit("mock:update",i),j("watcher:change",i)}),s.on("unlink",async i=>{i=T(i),this.emit("mock:unlink",i),j("watcher:unlink",i)}),this.mockWatcher=s}watchDeps(){let e=[];this.depsWatcher=ve.watch([],{ignoreInitial:!0,cwd:this.cwd}),this.depsWatcher.on("change",o=>{o=T(o);let n=this.moduleDeps.get(o);n&&n.forEach(s=>{this.emit("mock:update",s)})}),this.depsWatcher.on("unlink",o=>{o=T(o),this.moduleDeps.delete(o)}),this.on("update:deps",()=>{let o=[];for(let[s]of this.moduleDeps.entries())o.push(s);let n=o.filter(s=>!e.includes(s));n.length>0&&this.depsWatcher.add(n)})}close(){var e,o;(e=this.mockWatcher)==null||e.close(),(o=this.depsWatcher)==null||o.close()}updateMockList(){this._mockData=ke(this.moduleCache)}updateModuleDeps(e,o){Object.keys(o).forEach(n=>{o[n].imports.map(i=>i.path).forEach(i=>{this.moduleDeps.has(i)||this.moduleDeps.set(i,new Set),this.moduleDeps.get(i).add(e)})}),this.emit("update:deps")}async loadMock(e){e&&(Q.EXT_JSON.test(e)?await this.loadJson(e):await this.loadModule(e))}async loadJson(e){let o=await q.promises.readFile(e,"utf-8");try{let n=it.parse(o);this.moduleCache.set(e,n)}catch{}}async loadModule(e){if(!e)return;let o=!1;/\.m[jt]s$/.test(e)?o=!0:/\.c[jt]s$/.test(e)?o=!1:o=this.moduleType==="esm";let{code:n,deps:s}=await this.transformWithEsbuild(e,o);try{let i=await this.loadFromCode(e,n,o),a=i&&i.default?i.default:Object.keys(i||{}).map(c=>i[c]);this.moduleCache.set(e,a),this.updateModuleDeps(e,s)}catch(i){console.error(i)}}async loadFromCode(e,o,n){if(n){let s=`${e}.timestamp-${Date.now()}`,i=`${s}.mjs`,a=`${rt(s)}.mjs`;await q.promises.writeFile(i,o,"utf8");try{return await import(a)}finally{try{q.unlinkSync(i)}catch{}}}else{e=he.resolve(this.cwd,e);let s=he.extname(e),i=q.realpathSync(e),a=s in w.extensions?s:".js",c=w.extensions[a];w.extensions[a]=(u,l)=>{l===i?u._compile(o,l):c(u,l)},delete w.cache[w.resolve(e)];let p=w(e);return w.extensions[a]=c,p.__esModule?p:{default:p}}}async transformWithEsbuild(e,o){var n;try{let s=await nt({entryPoints:[e],outfile:"out.js",write:!1,target:["node14.18","node16"],platform:"node",bundle:!0,metafile:!0,format:o?"esm":"cjs",define:this.options.define,plugins:[B(this.options.alias),N]});return{code:s.outputFiles[0].text,deps:((n=s.metafile)==null?void 0:n.inputs)||{}}}catch{}return{code:"",deps:{}}}},$=Q;$.EXT_JSON=/\.json5?$/;async function X(t,r,e,o){let n=M(r.include),s=M(r.exclude),i={};if(t.define)for(let u in t.define){let l=t.define[u];i[u]=typeof l=="string"?l:JSON.stringify(l)}let a=new $({include:n,exclude:s,define:i,alias:t.resolve.alias});await a.load(),e==null||e.on("close",()=>a.close()),a.on("mock:update-end",()=>{r.reload&&(o==null||o.send({type:"full-reload"}))});let c=J(t.server.proxy||{}),p=M(r.prefix);return ye(a,{formidableOptions:r.formidableOptions,proxies:[...p,...c],cookiesOptions:r.cookiesOptions})}function be({prefix:t=[],include:r=["mock/**/*.mock.{js,ts,cjs,mjs,json,json5}"],exclude:e=["**/node_modules/**","**/.vscode/**","**/.git/**"],reload:o=!1,formidableOptions:n={},build:s=!1,cookiesOptions:i={}}={}){let a={prefix:t,include:r,exclude:e,reload:o,cookiesOptions:i,formidableOptions:{multiples:!0,...n},build:s?Object.assign({serverPort:8080,dist:"mockServer"},typeof s=="object"?s:{}):!1},c=[lt(a)];return a.build&&c.push(pt(a)),c}function pt(t){let r={};return{name:"vite-plugin-mock-dev-server-generator",enforce:"post",apply:"build",configResolved(e){r=e,e.logger.warn("")},async buildEnd(e){e||r.command==="build"&&await pe(this,r,t)}}}function lt(t){let r={};return{name:"vite-plugin-mock-dev-server",enforce:"pre",apply:"serve",configResolved(e){r=e,e.logger.warn("")},async configureServer({middlewares:e,config:o,httpServer:n,ws:s}){let i=await X(o,t,n,s);e.use(i)},async configurePreviewServer({middlewares:e,httpServer:o}){let n=await X(r,t,o);e.use(n)}}}function Fo(t){return t}function To(t){return e=>(v(e)?e=e.map(o=>t(o)||o):e=t(e)||e,e)}var Wo=be;export{ye as baseMiddleware,To as createDefineMock,Wo as default,Fo as defineMock,be as mockDevServerPlugin,ke as transformMockData}; | ||
`}async function Je(t,o,e){var r;try{let n=await Le({entryPoints:[t],outfile:"out.js",write:!1,target:["node14.18","node16"],platform:"node",bundle:!0,metafile:!0,format:"esm",define:o,plugins:[B(e),N,q,A]});return{code:n.outputFiles[0].text,deps:((r=n.metafile)==null?void 0:r.inputs)||{}}}catch(n){console.error(n)}return{code:"",deps:{}}}import{Buffer as fe}from"buffer";import{parse as ke}from"url";import Qe from"cookies";import Xe from"http-status";import*as $ from"mime-types";import{pathToRegexp as ye}from"path-to-regexp";import H from"picocolors";import Y from"co-body";import ze from"formidable";async function de(t,o){var n;let e=t.method.toUpperCase();if(["GET","DELETE","HEAD"].includes(e))return;let r=((n=t.headers["content-type"])==null?void 0:n.toLocaleLowerCase())||"";try{if(r.startsWith("application/json"))return await Y.json(t);if(r.startsWith("application/x-www-form-urlencoded"))return await Y.form(t);if(r.startsWith("text/plain"))return await Y.text(t);if(r.startsWith("multipart/form-data"))return await Ge(t,o)}catch(s){console.error(s)}}async function Ge(t,o){let e=ze(o);return new Promise((r,n)=>{e.parse(t,(s,i,c)=>{if(s){n(s);return}r({...i,...c})})})}function me(t,o){return C(t.headers,o.headers)&&C(t.body,o.body)&&C(t.params,o.params)&&C(t.query,o.query)&&C(t.refererQuery,o.refererQuery)}function C(t,o){if(!o)return!0;for(let e in o)if(o[e]!==t[e])return!1;return!0}function he(t,{formidableOptions:o={},proxies:e,cookiesOptions:r}){return async function(n,s,i){let c=Date.now(),{query:a,pathname:p}=ke(n.url,!0);if(!p||e.length===0||!e.some(S=>z(S,n.url)))return i();let u=t.mockData,l=Object.keys(u).find(S=>ye(S).test(p));if(!l)return i();let{query:v}=ke(n.headers.referer||"",!0),M=await de(n,o),y=new Qe(n,s,r),k=y.get.bind(y),m=n.method.toUpperCase(),d=Ke(u[l],{pathname:p,method:m,request:{query:a,refererQuery:v,body:M,headers:n.headers,getCookie:k}});if(!d)return i();D("middleware: ",m,n.url);let h=n,g=s;h.body=M,h.query=a,h.refererQuery=v,h.params=L(d.url,p),h.getCookie=k,g.setCookie=y.set.bind(y);let{body:w,delay:f,type:O="json",response:T,status:X=200,statusText:je}=d;if(Z(g,X,je),await Ve(h,g,d),await Ye(h,g,d),w){try{let S=R(w)?await w(h):w;await ge(c,f),Ze(g,S,O)}catch(S){W.error(`${H.red("[body error]")} ${n.url} | ||
`,S),Z(g,500),s.end("")}return}if(T){try{await ge(c,f),await T(h,g,i)}catch(S){W.error(`${H.red("[response error]")} ${n.url} | ||
`,S),Z(g,500),s.end("")}return}s.end("")}}function Ke(t,{pathname:o,method:e,request:r}){return t.find(n=>{if(!o||!n||!n.url||n.ws===!0||!(n.method?P(n.method)?n.method:[n.method]:["GET","POST"]).includes(e))return!1;let i=ye(n.url).test(o);if(i&&n.validator){let c=L(n.url,o);if(R(n.validator))return n.validator({params:c,...r});try{return me({params:c,...r},n.validator)}catch(a){return W.error(`${H.red("[validator error]")} ${o} | ||
`,a),!1}}return i})}function Z(t,o=200,e){t.statusCode=o,t.statusMessage=e||et(o)}async function Ve(t,o,{headers:e,type:r="json"}){let n=$.contentType(r)||$.contentType($.lookup(r)||"");if(n&&o.setHeader("Content-Type",n),o.setHeader("Cache-Control","no-cache,max-age=0"),o.setHeader("X-Mock","generate by vite:plugin-mock-dev-server"),!!e)try{let s=R(e)?await e(t):e;Object.keys(s).forEach(i=>{o.setHeader(i,s[i])})}catch(s){W.error(`${H.red("[headers error]")} ${t.url} | ||
`,s)}}async function Ye(t,o,{cookies:e}){if(e)try{let r=R(e)?await e(t):e;Object.keys(r).forEach(n=>{let s=r[n];if(P(s)){let[i,c]=s;o.setCookie(n,i,c)}else o.setCookie(n,s)})}catch(r){W.error(`${H.red("[cookies error]")} ${t.url} | ||
`,r)}}function Ze(t,o,e){if(ae(o))o.pipe(t);else if(fe.isBuffer(o))t.end(e==="text"||e==="json"?o.toString("utf-8"):o);else{let r=typeof o=="string"?o:JSON.stringify(o);t.end(e==="buffer"?fe.from(r):r)}}async function ge(t,o){if(!o||o<=0)return;let e=Date.now()-t,r=o-e;r>0&&await pe(r)}function et(t){return Xe[t]||"Unknown"}import rt from"events";import ee from"fs";import{createRequire as st}from"module";import Me from"path";import{pathToFileURL as nt}from"url";import be from"chokidar";import{build as it}from"esbuild";import ct from"fast-glob";import{createFilter as at,normalizePath as U}from"vite";import{parse as tt}from"url";import ot from"lodash.sortby";function ve(t){let o=[];for(let[,r]of t.entries())r&&(P(r)?o.push(...r):o.push(r));let e={};return o.filter(r=>(r.enabled||typeof r.enabled>"u")&&r.url).forEach(r=>{let{pathname:n,query:s}=tt(r.url,!0),i=e[n]??(e[n]=[]),c={...r,url:n};if(c.ws!==!0){let a=c.validator;ce(s)||(R(a)?c.validator=function(p){return C(p.query,s)&&a(p)}:a?(c.validator={...a},c.validator.query=c.validator.query?{...s,...c.validator.query}:s):c.validator={query:s})}i.push(c)}),Object.keys(e).forEach(r=>{e[r]=ot(e[r],n=>{if(n.ws===!0)return 0;let{validator:s}=n;if(!s)return 1;if(R(s))return 0;let{query:i,params:c,headers:a,body:p,refererQuery:u}=s;return 1/(_(i)+_(c)+_(a)+_(p)+_(u))})}),e}function _(t){return t?Object.keys(t).length:0}var pt=le(import.meta.url),E=st(pt),Q=class extends rt{constructor(e){super();this.options=e;this.moduleCache=new Map;this.moduleDeps=new Map;this.moduleType="cjs";this._mockData={};this.cwd=e.cwd||process.cwd();try{let r=F(this.cwd,["package.json"]);this.moduleType=r&&JSON.parse(r).type==="module"?"esm":"cjs"}catch{}}get mockData(){return this._mockData}async load(){let{include:e,exclude:r}=this.options,n=await ct(e,{cwd:this.cwd}),s=at(e,r,{resolve:!1});this.watchMockEntry(),this.watchDeps();for(let c of n.filter(s))await this.loadMock(c);this.updateMockList();let i=null;this.on("mock:update",async c=>{s(c)&&(await this.loadMock(c),i&&clearTimeout(i),i=setTimeout(()=>{this.updateMockList(),this.emit("mock:update-end",c),i=null},0))}),this.on("mock:unlink",async c=>{s(c)&&(this.moduleCache.delete(c),this.updateMockList(),this.emit("mock:update-end",c))})}watchMockEntry(){let{include:e}=this.options,[r,...n]=e,s=be.watch(r,{ignoreInitial:!0,cwd:this.cwd});n.length>0&&n.forEach(i=>s.add(i)),s.on("add",async i=>{i=U(i),this.emit("mock:update",i),D("watcher:add",i)}),s.on("change",async i=>{i=U(i),this.emit("mock:update",i),D("watcher:change",i)}),s.on("unlink",async i=>{i=U(i),this.emit("mock:unlink",i),D("watcher:unlink",i)}),this.mockWatcher=s}watchDeps(){let e=[];this.depsWatcher=be.watch([],{ignoreInitial:!0,cwd:this.cwd}),this.depsWatcher.on("change",r=>{r=U(r);let n=this.moduleDeps.get(r);n&&n.forEach(s=>{this.emit("mock:update",s)})}),this.depsWatcher.on("unlink",r=>{r=U(r),this.moduleDeps.delete(r)}),this.on("update:deps",()=>{let r=[];for(let[s]of this.moduleDeps.entries())r.push(s);let n=r.filter(s=>!e.includes(s));n.length>0&&this.depsWatcher.add(n)})}close(){var e,r;(e=this.mockWatcher)==null||e.close(),(r=this.depsWatcher)==null||r.close()}updateMockList(){this._mockData=ve(this.moduleCache)}updateModuleDeps(e,r){Object.keys(r).forEach(n=>{r[n].imports.map(i=>i.path).forEach(i=>{this.moduleDeps.has(i)||this.moduleDeps.set(i,new Set),this.moduleDeps.get(i).add(e)})}),this.emit("update:deps")}async loadMock(e){if(!e)return;let r=!1;/\.m[jt]s$/.test(e)?r=!0:/\.c[jt]s$/.test(e)?r=!1:r=this.moduleType==="esm";let{code:n,deps:s}=await this.transformWithEsbuild(e,r);try{let i=await this.loadFromCode(e,n,r),c=i&&i.default?i.default:Object.keys(i||{}).map(a=>i[a]);P(c)?c.forEach(a=>a.__filepath__=e):c.__filepath__=e,this.moduleCache.set(e,c),this.updateModuleDeps(e,s)}catch(i){console.error(i)}}async loadFromCode(e,r,n){if(n){let s=`${e}.timestamp-${Date.now()}`,i=`${s}.mjs`,c=`${nt(s)}.mjs`;await ee.promises.writeFile(i,r,"utf8");try{return await import(c)}finally{try{ee.unlinkSync(i)}catch{}}}else{e=Me.resolve(this.cwd,e);let s=Me.extname(e),i=ee.realpathSync(e),c=s in E.extensions?s:".js",a=E.extensions[c];E.extensions[c]=(u,l)=>{l===i?u._compile(r,l):a(u,l)},delete E.cache[E.resolve(e)];let p=E(e);return E.extensions[c]=a,p.__esModule?p:{default:p}}}async transformWithEsbuild(e,r){var n;try{let s=await it({entryPoints:[e],outfile:"out.js",write:!1,target:["node14.18","node16"],platform:"node",bundle:!0,metafile:!0,format:r?"esm":"cjs",define:this.options.define,plugins:[B(this.options.alias),N,A,q]});return{code:s.outputFiles[0].text,deps:((n=s.metafile)==null?void 0:n.inputs)||{}}}catch(s){console.error(s)}return{code:"",deps:{}}}};import{parse as xe}from"url";import lt from"cookies";import{pathToRegexp as we}from"path-to-regexp";import{WebSocketServer as ut}from"ws";function Oe(t,o,e,r){var c;let n=new Set,s=new Map,i=new Map;(c=t.on)==null||c.call(t,"mock:update-end",a=>{if(!n.has(a))return;let p={};for(let[u,l]of i.entries())t.mockData[u].forEach(v=>{v.__filepath__===a&&v.ws&&l.forEach(({pathname:M,req:y,ws:k})=>{p[M]??(p[M]={mock:v,list:[],mockUrl:u}),p[M].list.push({req:y,ws:k}),k.removeAllListeners()})});Object.keys(p).forEach(u=>{var k,m;let l=s.get(u),{mock:v,list:M,mockUrl:y}=p[u];l.wss.removeAllListeners(),(k=l.cancel)==null||k.call(l),l.cancel=(m=v.setup)==null?void 0:m.call(v,l.wss),l.wss.on("close",()=>{s.delete(u)}),M.forEach(({req:d,ws:h})=>{l.wss.emit("connection",h,d),h.on("close",()=>{let g=i.get(y),w=(g==null?void 0:g.findIndex(f=>f.ws===h))||-1;w>=0&&(g==null||g.splice(w,1))})})})}),o==null||o.on("upgrade",(a,p,u)=>{var w;let{pathname:l,query:v}=xe(a.url,!0);if(!l||e.length===0||!e.some(f=>z(f,a.url)))return;let M=t.mockData,y=Object.keys(M).find(f=>we(f).test(l));if(!y)return;let k=M[y].find(f=>f.url&&f.ws&&we(f.url).test(l));if(!k)return;n.add(k.__filepath__);let m=s.get(l);if(!m){let f=new ut({noServer:!0}),O=(w=k.setup)==null?void 0:w.call(k,f);f.on("close",()=>{s.delete(l)}),m={wss:f,cancel:O},s.set(l,m)}let d=a,h=new lt(a,a,r),{query:g}=xe(a.headers.referer||"",!0);d.query=v,d.refererQuery=g,d.params=L(y,l),d.getCookie=h.get.bind(h),m.wss.handleUpgrade(d,p,u,f=>{D(`websocket-mock: ${a.url} connected`),m.wss.emit("connection",f,d);let O=i.get(y);O||(O=[],i.set(y,O)),O.push({req:d,ws:f,pathname:l}),f.on("close",()=>{let T=O.findIndex(X=>X.ws===f);T>=0&&O.splice(T,1)})})}),o==null||o.on("close",()=>{s.forEach(({wss:a,cancel:p})=>{p==null||p(),a.close()}),s.clear(),n.clear(),i.clear()})}async function te(t,o,e,r){let n=j(o.include),s=j(o.exclude),i={};if(t.define)for(let u in t.define){let l=t.define[u];i[u]=typeof l=="string"?l:JSON.stringify(l)}let c=new Q({include:n,exclude:s,define:i,alias:t.resolve.alias});await c.load(),c.on("mock:update-end",()=>{o.reload&&(r==null||r.send({type:"full-reload"}))}),e==null||e.on("close",()=>c.close());let{httpProxies:a}=J(t.server.proxy||{}),p=j(o.prefix);return Oe(c,e,j(o.wsPrefix),o.cookiesOptions),he(c,{formidableOptions:o.formidableOptions,proxies:[...p,...a],cookiesOptions:o.cookiesOptions})}function Pe({prefix:t=[],wsPrefix:o=[],include:e=["mock/**/*.mock.{js,ts,cjs,mjs,json,json5}"],exclude:r=["**/node_modules/**","**/.vscode/**","**/.git/**"],reload:n=!1,formidableOptions:s={},build:i=!1,cookiesOptions:c={}}={}){let a={prefix:t,wsPrefix:o,include:e,exclude:r,reload:n,cookiesOptions:c,formidableOptions:{multiples:!0,...s},build:i?Object.assign({serverPort:8080,dist:"mockServer"},typeof i=="object"?i:{}):!1},p=[mt(a)];return a.build&&p.push(dt(a)),p}function dt(t){let o={};return{name:"vite-plugin-mock-dev-server-generator",enforce:"post",apply:"build",configResolved(e){o=e,e.logger.warn("")},async buildEnd(e){e||o.command==="build"&&await ue(this,o,t)}}}function mt(t){let o={};return{name:"vite-plugin-mock-dev-server",enforce:"pre",apply:"serve",configResolved(e){o=e,e.logger.warn("")},async configureServer({middlewares:e,config:r,httpServer:n,ws:s}){let i=await te(r,t,n,s);e.use(i)},async configurePreviewServer({middlewares:e,httpServer:r}){let n=await te(o,t,r);e.use(n)}}}function qo(t){return t}function Ao(t){return e=>(P(e)?e=e.map(r=>t(r)||r):e=t(e)||e,e)}var Go=Pe;export{he as baseMiddleware,Ao as createDefineMock,Go as default,qo as defineMock,Pe as mockDevServerPlugin,Oe as mockWebSocket,ve as transformMockData}; |
{ | ||
"name": "vite-plugin-mock-dev-server", | ||
"version": "1.1.5", | ||
"version": "1.1.6", | ||
"keywords": [ | ||
@@ -46,3 +46,4 @@ "vite", | ||
"path-to-regexp": "^6.2.1", | ||
"picocolors": "^1.0.0" | ||
"picocolors": "^1.0.0", | ||
"ws": "^8.13.0" | ||
}, | ||
@@ -60,2 +61,3 @@ "devDependencies": { | ||
"@types/node": "^18.16.1", | ||
"@types/ws": "^8.5.4", | ||
"bumpp": "^9.1.0", | ||
@@ -62,0 +64,0 @@ "conventional-changelog-cli": "^2.2.2", |
124
README.md
@@ -14,3 +14,3 @@ # vite-plugin-mock-dev-server | ||
<img alt="npm peer dependency version" src="https://img.shields.io/npm/dependency-version/vite-plugin-mock-dev-server/peer/vite?style=flat-square"> | ||
<img alt="npm" src="https://img.shields.io/npm/dm/vite-plugin-mock-dev-server?style=flat-square"> | ||
<img alt="npm" src="https://img.shields.io/npm/dt/vite-plugin-mock-dev-server?style=flat-square"> | ||
<br> | ||
@@ -47,2 +47,3 @@ <img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/pengzhanbo/vite-plugin-mock-dev-server/lint.yml?style=flat-square"> | ||
- 📥 Support mock download file. | ||
- ⚜️ Support `WebSocket Mock` | ||
- 🗂 Support building small independent deployable mock services. | ||
@@ -153,2 +154,14 @@ | ||
- `options.wsPrefix` | ||
**类型:** `string | string[]` | ||
Configure the matching rules for WebSocket service. Any request path starting with the value of `wsPrefix` and using the `ws/wss` protocol will be proxied to the corresponding target. | ||
If the value of `wsPrefix` starts with `^`, it will be recognized as a RegExp. | ||
> Different from using `viteConfig.server.proxy` by default for http mock, `websocket mock` does not use the ws-related configuration in `viteConfig.server.proxy`. Also, rules configured in `wsPrefix` cannot be configured simultaneously in `viteConfig.server.proxy`, as it will cause conflicts when starting the vite server because multiple instances of WebSocketServer cannot be implemented for the same request. | ||
> This conflict is neither a problem with Vite nor with the plugin; it belongs to a reasonable error type. When switching between WebSocket Mock and WebSocket Proxy, please pay attention to avoid duplicate configurations that may cause conflicts. | ||
- `option.include` | ||
@@ -168,10 +181,3 @@ | ||
**Default:** | ||
```ts | ||
[ | ||
'**/node_modules/**', | ||
'**/.vscode/**', | ||
'**/.git/**', | ||
] | ||
``` | ||
**Default:** `['**/node_modules/**','**/.vscode/**','**/.git/**']` | ||
@@ -265,2 +271,3 @@ - `options.reload` | ||
```ts | ||
// Configure the http mock | ||
export default defineMock({ | ||
@@ -388,4 +395,29 @@ /** | ||
}) | ||
``` | ||
```ts | ||
// Configure the WebSocket mock | ||
export default defineMock({ | ||
/** | ||
* Request address, supports the `/api/user/:id` format. | ||
* The plugin matches the path through `path-to-regexp`. | ||
* @see https://github.com/pillarjs/path-to-regexp | ||
*/ | ||
url: '/api/test', | ||
/** | ||
* Value must be explicitly specified as `true`. | ||
* The plugin needs to make a judgment based on this field. | ||
*/ | ||
ws: true, | ||
/** | ||
* Configure the WebSocketServer | ||
* @see https://github.com/websockets/ws/blob/master/doc/ws.md#class-websocketserver | ||
*/ | ||
setup(wss) { | ||
wss.on('connection', (ws, request) => { | ||
ws.on('message', (rawData) => {}) | ||
ws.send('data') | ||
}) | ||
} | ||
}) | ||
``` | ||
@@ -659,2 +691,74 @@ ### Request/Response Enhance | ||
**exp:** Graphql | ||
```ts | ||
import { buildSchema, graphql } from 'graphql' | ||
const schema = buildSchema(` | ||
type Query { | ||
hello: String | ||
} | ||
`) | ||
const rootValue = { hello: () => 'Hello world!' } | ||
export default defineMock({ | ||
url: '/api/graphql', | ||
method: 'POST', | ||
body: async (request) => { | ||
const source = request.body.source | ||
const { data } = await graphql({ schema, rootValue, source }) | ||
return data | ||
}, | ||
}) | ||
``` | ||
```ts | ||
fetch('/api/graphql', { | ||
method: 'POST', | ||
body: JSON.stringify({ source: '{ hello }' }) | ||
}) | ||
``` | ||
**exp:** WebSocket Mock | ||
```ts | ||
// ws.mock.ts | ||
export default defineMock({ | ||
url: '/socket.io', | ||
ws: true, | ||
setup(wss) { | ||
const wsMap = new Map() | ||
wss.on('connection', (ws, req) => { | ||
const token = req.getCookie('token') | ||
wsMap.set(token, ws) | ||
ws.on('message', (raw) => { | ||
const data = JSON.parse(String(raw)) | ||
if (data.type === 'ping') return | ||
// Broadcast | ||
for (const [_token, _ws] of wsMap.entires()) { | ||
if (_token !== token) | ||
_ws.send(raw) | ||
} | ||
}) | ||
}) | ||
wss.on('error', (err) => { | ||
console.error(err) | ||
}) | ||
return () => { | ||
wsMap.clear() | ||
} | ||
} | ||
}) | ||
``` | ||
```ts | ||
// app.ts | ||
const ws = new WebSocket('ws://localhost:5173/socket.io') | ||
ws.addEventListener('open', () => { | ||
setInterval(() => { | ||
// heartbeat | ||
ws.send({ type: 'ping' }) | ||
}, 1000) | ||
}, { once: true }) | ||
ws.addEventListener('message', (raw) => { | ||
console.log(raw) | ||
}) | ||
``` | ||
## Mock Services | ||
@@ -661,0 +765,0 @@ |
@@ -16,3 +16,3 @@ # vite-plugin-mock-dev-server | ||
<img alt="npm peer dependency version" src="https://img.shields.io/npm/dependency-version/vite-plugin-mock-dev-server/peer/vite?style=flat-square"> | ||
<img alt="npm" src="https://img.shields.io/npm/dm/vite-plugin-mock-dev-server?style=flat-square"> | ||
<img alt="npm" src="https://img.shields.io/npm/dt/vite-plugin-mock-dev-server?style=flat-square"> | ||
<br> | ||
@@ -41,3 +41,3 @@ <img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/pengzhanbo/vite-plugin-mock-dev-server/lint.yml?style=flat-square"> | ||
- ⚙️ 随意开启或关闭对某个接口的 mock配置 | ||
- - 📀 支持多种响应体数据类型,包括 `text/json/buffer/stream`. | ||
- 📀 支持多种响应体数据类型,包括 `text/json/buffer/stream`. | ||
- ⚖️ 使用 `server.proxy` 配置 | ||
@@ -49,2 +49,3 @@ - 🍕 支持在 mock文件中使用 `viteConfig.define`配置字段 | ||
- 📥 支持模拟文件下载 | ||
- ⚜️ 支持模拟 `WebSocket` | ||
- 🗂 支持构建可独立部署的小型mock服务 | ||
@@ -146,3 +147,3 @@ | ||
为mock服务器配置自定义匹配规则。任何请求路径以 `prefix` 值开头的请求将被代理到对应的目标。如果 `prefix` 值以 ^ 开头,将被识别为 RegExp。 | ||
为mock服务器配置自定义匹配规则。任何请求路径以 `prefix` 值开头的请求将被代理到对应的目标。如果 `prefix` 值以 `^` 开头,将被识别为 RegExp。 | ||
@@ -153,2 +154,12 @@ > 一般情况下, `server.proxy` 已经足够满足需求,添加此项是为了与某些场景兼容。 | ||
- `options.wsPrefix` | ||
**类型:** `string | string[]` | ||
配置 webSocket 服务 匹配规则。任何请求路径以 `wsPrefix` 值开头的 `ws/wss` 协议请求,将被代理到对应的目标。 | ||
如果`wsPrefix`值以 `^` 开头,将被识别为 RegExp。 | ||
> 与 http mock 默认使用 `viteConfig.server.proxy` 不同的是,`websocket mock` 不会使用 `viteConfig.server.proxy` 中的 ws 相关的配置,且配置在 `wsPrefix` 中的规则,不能同时配置在 `viteConfig.server.proxy`中,因为会导致在 vite 在启动服务时产生冲突,因为不能对同一个请求实现多个的 `WebSocketServer`实例。 | ||
> 该冲突既不是 `vite` 的问题,也不是插件的问题,这属于合理的错误类型。在进行 `WebSocket Mock`和 `WebSocket Proxy` 切换时,请注意配置不要出现重复导致冲突。 | ||
- `option.include` | ||
@@ -168,10 +179,3 @@ | ||
**默认值:** | ||
```ts | ||
[ | ||
'**/node_modules/**', | ||
'**/.vscode/**', | ||
'**/.git/**', | ||
] | ||
``` | ||
**默认值:** `['**/node_modules/**', '**/.vscode/**', '**/.git/**']` | ||
@@ -263,2 +267,3 @@ - `options.reload` | ||
```ts | ||
// 配置 http mock | ||
export default defineMock({ | ||
@@ -386,4 +391,29 @@ /** | ||
}) | ||
``` | ||
```ts | ||
// 配置 WebSocket mock | ||
export default defineMock({ | ||
/** | ||
* 请求地址,支持 `/api/user/:id` 格式 | ||
* 插件通过 `path-to-regexp` 匹配路径 | ||
* @see https://github.com/pillarjs/path-to-regexp | ||
*/ | ||
url: '/api/test', | ||
/** | ||
* 必须显式的指定值为 `true` | ||
* 插件内部需要根据此值进行判断 | ||
*/ | ||
ws: true, | ||
/** | ||
* 配置 WebSocketServer | ||
* @see https://github.com/websockets/ws/blob/master/doc/ws.md#class-websocketserver | ||
*/ | ||
setup(wss) { | ||
wss.on('connection', (ws, request) => { | ||
ws.on('message', (rawData) => {}) | ||
ws.send('data') | ||
}) | ||
} | ||
}) | ||
``` | ||
@@ -656,2 +686,74 @@ ### Request/Response 增强 | ||
**exp:** Graphql | ||
```ts | ||
import { buildSchema, graphql } from 'graphql' | ||
const schema = buildSchema(` | ||
type Query { | ||
hello: String | ||
} | ||
`) | ||
const rootValue = { hello: () => 'Hello world!' } | ||
export default defineMock({ | ||
url: '/api/graphql', | ||
method: 'POST', | ||
body: async (request) => { | ||
const source = request.body.source | ||
const { data } = await graphql({ schema, rootValue, source }) | ||
return data | ||
}, | ||
}) | ||
``` | ||
```ts | ||
fetch('/api/graphql', { | ||
method: 'POST', | ||
body: JSON.stringify({ source: '{ hello }' }) | ||
}) | ||
``` | ||
**exp:** WebSocket Mock | ||
```ts | ||
// ws.mock.ts | ||
export default defineMock({ | ||
url: '/socket.io', | ||
ws: true, | ||
setup(wss) { | ||
const wsMap = new Map() | ||
wss.on('connection', (ws, req) => { | ||
const token = req.getCookie('token') | ||
wsMap.set(token, ws) | ||
ws.on('message', (raw) => { | ||
const data = JSON.parse(String(raw)) | ||
if (data.type === 'ping') return | ||
// Broadcast | ||
for (const [_token, _ws] of wsMap.entires()) { | ||
if (_token !== token) | ||
_ws.send(raw) | ||
} | ||
}) | ||
}) | ||
wss.on('error', (err) => { | ||
console.error(err) | ||
}) | ||
return () => { | ||
wsMap.clear() | ||
} | ||
} | ||
}) | ||
``` | ||
```ts | ||
// app.ts | ||
const ws = new WebSocket('ws://localhost:5173/socket.io') | ||
ws.addEventListener('open', () => { | ||
setInterval(() => { | ||
// heartbeat | ||
ws.send({ type: 'ping' }) | ||
}, 1000) | ||
}, { once: true }) | ||
ws.addEventListener('message', (raw) => { | ||
console.log(raw) | ||
}) | ||
``` | ||
## 独立部署的小型mock服务 | ||
@@ -658,0 +760,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
98347
644
787
2
16
20
+ Addedws@^8.13.0
+ Addedws@8.18.0(transitive)