vite-plugin-mock-data 8.0.0 → 8.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,17 @@
1
- import { OutgoingHttpHeaders } from 'node:http';
2
- import { Config as SirvConfig, HTTPVersion } from 'find-my-way';
3
- import { Options as SirvOptions } from 'sirv';
1
+ import { FastifyServerOptions } from 'fastify';
4
2
  import { ViteDevServer } from 'vite';
5
3
  import { RouteConfig } from './types';
6
- export declare function sirvOptions(headers?: OutgoingHttpHeaders): SirvOptions;
7
- export declare function configureServer(server: ViteDevServer, routerOpts: SirvConfig<HTTPVersion.V1> | SirvConfig<HTTPVersion.V2> | undefined, routes: RouteConfig[], cwd: string): void;
4
+ /**
5
+ * Mounts mock routes on the Vite dev server via a fastify instance.
6
+ *
7
+ * fastify is embedded as a connect middleware using its internal
8
+ * `routing(req, res)` API, so the plugin keeps the exact middleware
9
+ * semantics of the previous find-my-way based implementation:
10
+ * - matched mock routes are answered by fastify;
11
+ * - unmatched requests fall through to the next middleware via
12
+ * `setNotFoundHandler` + `reply.hijack()` + `next()`.
13
+ *
14
+ * Route config contract (unchanged from before):
15
+ * key = "METHOD /path", supports `METHOD1/METHOD2 /path` and `:param`.
16
+ */
17
+ export declare function configureServer(server: ViteDevServer, fastifyOptions: FastifyServerOptions | undefined, routes: RouteConfig[], cwd: string): Promise<void>;
package/dist/index.js CHANGED
@@ -1,50 +1,32 @@
1
+ import { isObject } from "is-what-type";
2
+ import { banner, logFactory, toAbsolutePath } from "vp-runtime-helper";
3
+ import fastifyFormbody from "@fastify/formbody";
4
+ import fastifyStatic from "@fastify/static";
5
+ import fastify from "fastify";
6
+ import { createHash } from "node:crypto";
7
+ import { readFileSync } from "node:fs";
8
+ import { mkdir, unlink, writeFile } from "node:fs/promises";
9
+ import { createRequire } from "node:module";
10
+ import { join, parse } from "node:path";
11
+ import { glob } from "tinyglobby";
12
+ import { transformWithOxc } from "vite";
1
13
  //#region \0rolldown/runtime.js
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __copyProps = (to, from, except, desc) => {
9
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
- key = keys[i];
11
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
- get: ((k) => from[k]).bind(null, key),
13
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
- });
15
- }
16
- return to;
17
- };
18
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
19
- value: mod,
20
- enumerable: true
21
- }) : target, mod));
14
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) {
15
+ if (typeof require !== "undefined") return require.apply(this, arguments);
16
+ throw Error("Calling `require` for \"" + x + "\" in an environment that doesn't expose the `require` function. See https://rolldown.rs/in-depth/bundling-cjs#require-external-modules for more details.");
17
+ });
22
18
  //#endregion
23
- let is_what_type = require("is-what-type");
24
- let vp_runtime_helper = require("vp-runtime-helper");
25
- let node_path = require("node:path");
26
- let find_my_way = require("find-my-way");
27
- find_my_way = __toESM(find_my_way);
28
- let sirv = require("sirv");
29
- sirv = __toESM(sirv);
30
- let vite = require("vite");
31
- let node_fs = require("node:fs");
32
- let node_fs_promises = require("node:fs/promises");
33
- let node_module = require("node:module");
34
- let tinyglobby = require("tinyglobby");
35
19
  //#region package.json
36
20
  var name = "vite-plugin-mock-data";
37
21
  var package_default = {
38
22
  name,
39
- version: "8.0.0",
23
+ version: "8.1.0",
40
24
  description: "Provides a simple way to mock data.",
41
- main: "./dist/index.js",
42
- module: "./dist/index.mjs",
25
+ type: "module",
43
26
  types: "./dist/index.d.ts",
44
27
  exports: { ".": {
45
- "types": "./dist/index.d.ts",
46
- "import": "./dist/index.mjs",
47
- "require": "./dist/index.js"
28
+ "import": "./dist/index.js",
29
+ "types": "./dist/index.d.ts"
48
30
  } },
49
31
  engines: {
50
32
  "node": "^20.19.0 || >=22.12.0",
@@ -69,9 +51,10 @@ var package_default = {
69
51
  bugs: { "url": "https://github.com/fengxinming/vite-plugins/issues" },
70
52
  homepage: "https://fengxinming.github.io/vite-plugins/plugins/vite-plugin-mock-data/quick-start",
71
53
  dependencies: {
72
- "find-my-way": "^9.2.0",
54
+ "@fastify/formbody": "^9.0.0",
55
+ "@fastify/static": "^10.1.3",
56
+ "fastify": "^5.12.1",
73
57
  "is-what-type": "^1.1.4",
74
- "sirv": "^3.0.1",
75
58
  "tinyglobby": "^0.2.12",
76
59
  "vp-runtime-helper": "workspace:^"
77
60
  },
@@ -85,155 +68,134 @@ var package_default = {
85
68
  //#endregion
86
69
  //#region src/configureServer.ts
87
70
  /**
88
- * Simple request body parser for JSON / urlencoded payloads.
89
- * Populates `req.body` so that route handlers can read it directly,
90
- * matching the documented handler signature `(req) => req.body`.
71
+ * 解析路由 key:`"METHOD /path"`,支持 `METHOD1/METHOD2 /path` `:param`。
72
+ * 缺省 METHOD 时默认 GET。
91
73
  */
92
- function readJsonBody(req) {
93
- return new Promise((resolve, reject) => {
94
- const chunks = [];
95
- req.on("data", (c) => chunks.push(Buffer.isBuffer(c) ? c : Buffer.from(c)));
96
- req.on("end", () => {
97
- const raw = Buffer.concat(chunks).toString("utf8");
98
- if (!raw) {
99
- resolve(void 0);
100
- return;
101
- }
102
- const type = (req.headers["content-type"] || "").toLowerCase();
103
- try {
104
- if (type.includes("application/json")) resolve(JSON.parse(raw));
105
- else if (type.includes("application/x-www-form-urlencoded")) {
106
- const out = {};
107
- for (const [k, v] of new URLSearchParams(raw)) out[k] = v;
108
- resolve(out);
109
- } else resolve(raw);
110
- } catch (e) {
111
- reject(e);
112
- }
113
- });
114
- req.on("error", reject);
115
- });
116
- }
117
- function sendResult(req, res, ret, defaultHeaders) {
118
- if (res.headersSent || ret === void 0) return;
119
- (0, vite.send)(req, res, typeof ret !== "string" ? JSON.stringify(ret) : ret, (0, is_what_type.isObject)(ret) ? "json" : "html", { headers: defaultHeaders });
120
- }
121
- function sirvOptions(headers) {
74
+ function parseRouteKey(xpath) {
75
+ const [first, second] = xpath.split(" ");
76
+ const pathname = second ?? first;
122
77
  return {
123
- dev: true,
124
- etag: true,
125
- extensions: [],
126
- setHeaders(res, pathname) {
127
- res.setHeader("Access-Control-Allow-Origin", "*");
128
- if (/\.[tj]sx?$/.test(pathname)) res.setHeader("Content-Type", "application/javascript");
129
- if (headers) Object.entries(headers).forEach(([key, val]) => {
130
- if (val) res.setHeader(key, val);
131
- });
132
- }
78
+ methods: (second ? first : "GET").toUpperCase().split("/"),
79
+ pathname
133
80
  };
134
81
  }
135
- function configureServer(server, routerOpts, routes, cwd) {
136
- const router = (0, find_my_way.default)(routerOpts);
137
- if (Array.isArray(routes)) routes.forEach((route) => {
138
- Object.keys(route).forEach((xpath) => {
139
- let [methods, pathname] = xpath.split(" ");
140
- if (!pathname) {
141
- pathname = methods;
142
- methods = "GET";
143
- }
144
- methods = methods.toUpperCase();
145
- let routeConfig = route[xpath];
146
- if (!(0, is_what_type.isObject)(routeConfig)) routeConfig = { handler: routeConfig };
147
- let handler;
148
- if (typeof routeConfig.file === "string") handler = (req, res) => {
149
- const parsedPath = (0, node_path.parse)((0, vp_runtime_helper.toAbsolutePath)(routeConfig.file, cwd));
150
- const serve = (0, sirv.default)(parsedPath.dir, sirvOptions(server.config.server.headers));
151
- req.url = `/${parsedPath.base}`;
152
- serve(req, res);
153
- };
154
- else if (typeof routeConfig.handler !== "function") {
155
- const ret = routeConfig.handler;
156
- const retType = typeof ret;
157
- handler = (req, res) => {
158
- (0, vite.send)(req, res, retType !== "string" ? JSON.stringify(ret) : ret, (0, is_what_type.isObject)(ret) ? "json" : "html", { headers: server.config.server.headers });
159
- };
160
- } else {
161
- const userHandler = routeConfig.handler;
162
- const needsBody = methods.toUpperCase().split("/").some((m) => [
163
- "POST",
164
- "PUT",
165
- "PATCH",
166
- "DELETE"
167
- ].includes(m));
168
- handler = async (req, res, params) => {
169
- req.params = params ?? {};
170
- try {
171
- if (needsBody) req.body = await readJsonBody(req);
172
- } catch (e) {
173
- res.statusCode = 400;
174
- res.setHeader("Content-Type", "application/json");
175
- res.end(JSON.stringify({
176
- error: "Invalid request body",
177
- detail: String(e)
178
- }));
179
- return;
180
- }
181
- try {
182
- sendResult(req, res, await userHandler(req, res), server.config.server.headers);
183
- } catch (e) {
184
- server.config.logger.error(`[mock-data] handler error on ${methods} ${pathname}: ${e}`);
185
- if (!res.headersSent) {
186
- res.statusCode = 500;
187
- res.setHeader("Content-Type", "application/json");
188
- res.end(JSON.stringify({
189
- error: "Mock handler error",
190
- detail: String(e)
191
- }));
192
- }
193
- }
194
- };
82
+ /**
83
+ * 静态数据发送 handler:
84
+ * - 对象 / 数组(isObject 为真,数组也是 object)→ `reply.send(data)`,fastify 序列化为 JSON;
85
+ * - 其他(string / number / boolean / null)→ `String()` 转成字符串按文本发送
86
+ * (fastify string 默认 Content-Type: text/plain)。
87
+ */
88
+ function createStaticHandler(data) {
89
+ return (_request, reply) => {
90
+ reply.send(isObject(data) ? data : String(data));
91
+ };
92
+ }
93
+ /**
94
+ * 在 fastify 实例上注册单条 mock 路由。
95
+ *
96
+ * 路由值只分两种(与 types.ts RouteValue 一一对应):
97
+ * - 函数 原样透传,它就是 fastify 的 handler
98
+ * (fastify 原生处理两种响应方式:async 返回值自动发送 / 内部 reply.send());
99
+ * - 其他一切(对象 / 数组 / string / number / boolean / null)→ 静态数据,
100
+ * 由 createStaticHandler 生成 handler 发送。
101
+ */
102
+ function registerMockRoute(app, xpath, raw) {
103
+ const { methods, pathname } = parseRouteKey(xpath);
104
+ app.route({
105
+ method: methods,
106
+ url: pathname,
107
+ handler: typeof raw === "function" ? raw : createStaticHandler(raw)
108
+ });
109
+ }
110
+ /**
111
+ * Mounts mock routes on the Vite dev server via a fastify instance.
112
+ *
113
+ * fastify is embedded as a connect middleware using its internal
114
+ * `routing(req, res)` API, so the plugin keeps the exact middleware
115
+ * semantics of the previous find-my-way based implementation:
116
+ * - matched mock routes are answered by fastify;
117
+ * - unmatched requests fall through to the next middleware via
118
+ * `setNotFoundHandler` + `reply.hijack()` + `next()`.
119
+ *
120
+ * Route config contract (unchanged from before):
121
+ * key = "METHOD /path", supports `METHOD1/METHOD2 /path` and `:param`.
122
+ */
123
+ async function configureServer(server, fastifyOptions, routes, cwd) {
124
+ const app = fastify({
125
+ logger: false,
126
+ ...fastifyOptions
127
+ });
128
+ app.register(fastifyFormbody);
129
+ app.register(fastifyStatic, {
130
+ root: cwd,
131
+ setHeaders(reply, pathname) {
132
+ reply.header("Access-Control-Allow-Origin", "*");
133
+ if (/\.[tj]sx?$/.test(pathname)) reply.header("Content-Type", "application/javascript");
134
+ const headers = server.config.server.headers;
135
+ if (headers) {
136
+ for (const [key, val] of Object.entries(headers)) if (val) reply.header(key, val);
195
137
  }
196
- if (handler) router.on(methods.split("/"), pathname, {}, handler, routeConfig.store);
197
- });
138
+ }
198
139
  });
140
+ app.setNotFoundHandler((request, reply) => {
141
+ reply.hijack();
142
+ const raw = request.raw;
143
+ const next = raw.__mockNext;
144
+ delete raw.__mockNext;
145
+ next?.();
146
+ });
147
+ routes.forEach((route) => {
148
+ for (const [key, val] of Object.entries(route)) registerMockRoute(app, key, val);
149
+ });
150
+ await app.ready();
199
151
  server.middlewares.use((req, res, next) => {
200
- router.defaultRoute = () => next();
201
- router.lookup(req, res);
152
+ req.__mockNext = next;
153
+ app.routing(req, res);
202
154
  });
203
155
  }
204
156
  //#endregion
205
157
  //#region src/logger.ts
206
158
  var PLUGIN_NAME = name;
207
- var logger = vp_runtime_helper.logFactory.getLogger(PLUGIN_NAME);
159
+ var logger = logFactory.getLogger(PLUGIN_NAME);
208
160
  //#endregion
209
161
  //#region src/loadRoutes.ts
210
- var _require = typeof require === "function" ? require : (0, node_module.createRequire)({}.url);
211
- async function getRoute(filename) {
162
+ var _require = typeof __require === "function" ? __require : createRequire(import.meta.url);
163
+ /**
164
+ * Temp directory for transpiled mock files, shared by every mock dir of this project.
165
+ * Kept under node_modules so `rm -rf node_modules` cleans it up along with everything
166
+ * else, and git ignores it by default.
167
+ */
168
+ function getTempDir(cwd) {
169
+ return join(cwd, "node_modules", ".vite-plugin-mock-data");
170
+ }
171
+ async function getRoute(filename, tmpDir) {
212
172
  logger.debug("Load mock file:", filename);
213
- let { ext, dir, name } = (0, node_path.parse)(filename);
214
- const isTs = ext === ".ts" || ext === ".mts";
215
- if (isTs) {
216
- const { code } = await (0, vite.transformWithOxc)((0, node_fs.readFileSync)(filename, "utf-8"), filename);
217
- filename = (0, node_path.join)(dir, `${name}-${PLUGIN_NAME}.mjs`);
218
- ext = ".mjs";
219
- await (0, node_fs_promises.writeFile)(filename, code);
173
+ const { ext, name } = parse(filename);
174
+ if (ext === ".ts" || ext === ".mts") {
175
+ const { code } = await transformWithOxc(readFileSync(filename, "utf-8"), filename);
176
+ const hash = createHash("sha256").update(filename).digest("hex").slice(0, 12);
177
+ const tmpFile = join(tmpDir, `${hash}-${name}-${PLUGIN_NAME}.mjs`);
178
+ await mkdir(tmpDir, { recursive: true });
179
+ await writeFile(tmpFile, code);
180
+ try {
181
+ return (await import(tmpFile)).default;
182
+ } finally {
183
+ await unlink(tmpFile).catch(() => {});
184
+ }
220
185
  }
221
- let config;
222
186
  switch (ext) {
223
- case ".js":
224
- config = _require(filename);
225
- break;
226
- case ".mjs":
227
- config = (await import(filename)).default;
228
- if (isTs) await (0, node_fs_promises.unlink)(filename);
229
- break;
230
- case ".json": config = JSON.parse((0, node_fs.readFileSync)(filename, "utf-8"));
187
+ case ".js": return _require(filename);
188
+ case ".mjs": return (await import(filename)).default;
189
+ case ".json": return JSON.parse(readFileSync(filename, "utf-8"));
231
190
  }
232
- return config;
233
191
  }
234
- async function loadRoutes(dir, routes) {
235
- const paths = await (0, tinyglobby.glob)(`${dir}/**/*.{js,mjs,json,ts,mts}`, { absolute: true });
236
- const configs = await Promise.all(paths.map(getRoute));
192
+ async function loadRoutes(dir, routes, cwd = process.cwd()) {
193
+ const tmpDir = getTempDir(cwd);
194
+ const paths = await glob(`${dir}/**/*.{js,mjs,json,ts,mts}`, {
195
+ absolute: true,
196
+ ignore: [`**/*-${PLUGIN_NAME}.mjs`]
197
+ });
198
+ const configs = await Promise.all(paths.map((p) => getRoute(p, tmpDir)));
237
199
  for (const config of configs) if (config) routes.push(config);
238
200
  }
239
201
  //#endregion
@@ -259,28 +221,29 @@ async function loadRoutes(dir, routes) {
259
221
  * @returns a vite plugin
260
222
  */
261
223
  function pluginMockDate(opts) {
262
- if (opts.enableBanner) (0, vp_runtime_helper.banner)(package_default.name);
263
- const { isAfter, routerOptions, routes, logLevel, cwd = process.cwd() } = opts;
224
+ if (opts.enableBanner) banner(package_default.name);
225
+ const { isAfter, fastifyOptions, routes, logLevel, cwd = process.cwd() } = opts;
264
226
  if (logLevel) logger.level = logLevel;
265
227
  const allRoutes = [];
266
228
  return {
267
229
  name: PLUGIN_NAME,
268
230
  async configureServer(server) {
231
+ allRoutes.length = 0;
269
232
  if (typeof routes === "string") {
270
233
  logger.debug("Load routes from", routes);
271
- await loadRoutes((0, vp_runtime_helper.toAbsolutePath)(routes, cwd), allRoutes);
234
+ await loadRoutes(toAbsolutePath(routes, cwd), allRoutes, cwd);
272
235
  } else if (Array.isArray(routes)) for (const route of routes) {
273
236
  logger.debug("Load routes from", route);
274
- if (typeof route === "string") await loadRoutes((0, vp_runtime_helper.toAbsolutePath)(route, cwd), allRoutes);
237
+ if (typeof route === "string") await loadRoutes(toAbsolutePath(route, cwd), allRoutes, cwd);
275
238
  else allRoutes.push(route);
276
239
  }
277
- else if ((0, is_what_type.isObject)(routes)) {
240
+ else if (isObject(routes)) {
278
241
  logger.debug("Load routes from", routes);
279
242
  allRoutes.push(routes);
280
243
  }
281
- return isAfter ? () => configureServer(server, routerOptions, allRoutes, cwd) : configureServer(server, routerOptions, allRoutes, cwd);
244
+ return isAfter ? () => configureServer(server, fastifyOptions, allRoutes, cwd) : configureServer(server, fastifyOptions, allRoutes, cwd);
282
245
  }
283
246
  };
284
247
  }
285
248
  //#endregion
286
- module.exports = pluginMockDate;
249
+ export { pluginMockDate as default };
@@ -1,2 +1,2 @@
1
1
  import { RouteConfig } from './types';
2
- export default function loadRoutes(dir: string, routes: RouteConfig[]): Promise<void>;
2
+ export default function loadRoutes(dir: string, routes: RouteConfig[], cwd?: string): Promise<void>;
package/dist/types.d.ts CHANGED
@@ -1,13 +1,52 @@
1
- import { Config as SirvConfig, Handler, HTTPVersion, RouteOptions } from 'find-my-way';
1
+ import { FastifyServerOptions, RouteHandlerMethod } from 'fastify';
2
2
  import { LogLevel } from 'vp-runtime-helper';
3
- export interface HandleRoute {
4
- file?: string;
5
- handler?: any | Handler<HTTPVersion.V1>;
6
- options?: RouteOptions;
7
- store?: any;
8
- }
3
+ /**
4
+ * 函数形式的 handler:fastify 原生风格 `(request, reply)`。
5
+ * 原样透传给 fastify,两种响应方式都由 fastify 处理:
6
+ * - async handler 的返回值自动发送(对象序列化为 JSON);
7
+ * - handler 内部自行调用 `reply.send()`。
8
+ * Function handler in fastify style. Passed through to fastify as-is.
9
+ */
10
+ export type MockHandler = RouteHandlerMethod;
11
+ /**
12
+ * 任意 JSON 可序列化的静态数据。
13
+ *
14
+ * 可直接写在路由 key 后面(顶层),对象 / 数组 / 原始值一律按数据发送:
15
+ * - string → String() 后按文本发送(fastify 默认 text/plain);
16
+ * - 其他值(object / array / number / boolean / null)→ 对象按 JSON 发送,
17
+ * 非对象经 String() 转成字符串按文本发送(见 configureServer.ts 的 createStaticHandler)。
18
+ *
19
+ * 不再需要排除任何字段——运行时分流只判断「是不是函数」:
20
+ * 函数 → handler 透传;非函数 → 本类型(静态数据)。
21
+ *
22
+ * Any JSON-serializable static data, usable directly at the route top level.
23
+ * Runtime dispatch only checks `typeof value === 'function'` — functions are
24
+ * passed through as handlers, everything else is static data.
25
+ */
26
+ export type MockData = string | number | boolean | null | MockData[] | {
27
+ [key: string]: MockData;
28
+ };
29
+ /**
30
+ * 路由配置值:函数 handler 或静态数据两种形态。
31
+ * 函数原样透传给 fastify;非函数按静态数据发送。
32
+ *
33
+ * Route config value: a function handler (passed through to fastify) or
34
+ * static data (sent by a plugin-generated handler).
35
+ */
36
+ export type RouteValue = MockHandler | MockData;
9
37
  export interface RouteConfig {
10
- [route: string]: string | Handler<HTTPVersion.V1> | HandleRoute;
38
+ [route: string]: RouteValue;
39
+ }
40
+ /**
41
+ * connect 中间件的 next 回调在 fastify 404 handler 之间的传递载体。
42
+ *
43
+ * fastify 以 `app.routing(req, res)` 嵌入 connect 中间件时,未匹配的请求会
44
+ * 走到 fastify 的 404 handler,但那里拿不到 connect 的 `next`。因此
45
+ * configureServer 在 middleware 里先把 `next` 挂到 `request.raw.__mockNext`,
46
+ * 404 handler 取出并调用,让请求落回 Vite 的中间件链(配合 reply.hijack())。
47
+ */
48
+ export interface MockRequest {
49
+ __mockNext?: () => void;
11
50
  }
12
51
  export interface Options {
13
52
  /**
@@ -35,9 +74,9 @@ export interface Options {
35
74
  */
36
75
  isAfter?: boolean;
37
76
  /**
38
- * Initial options of `find-my-way`. see more at https://github.com/delvedor/find-my-way#findmywayoptions
77
+ * Initial options of `fastify`. see more at https://fastify.dev/docs/latest/Reference/Server/
39
78
  */
40
- routerOptions?: SirvConfig<HTTPVersion.V1> | SirvConfig<HTTPVersion.V2>;
79
+ fastifyOptions?: FastifyServerOptions;
41
80
  /**
42
81
  * Initial list of mock routes that should be added to the dev server
43
82
  * or specify the directory to define mock routes that should be added to the dev server.
package/package.json CHANGED
@@ -1,15 +1,13 @@
1
1
  {
2
2
  "name": "vite-plugin-mock-data",
3
- "version": "8.0.0",
3
+ "version": "8.1.0",
4
4
  "description": "Provides a simple way to mock data.",
5
- "main": "./dist/index.js",
6
- "module": "./dist/index.mjs",
5
+ "type": "module",
7
6
  "types": "./dist/index.d.ts",
8
7
  "exports": {
9
8
  ".": {
10
- "types": "./dist/index.d.ts",
11
- "import": "./dist/index.mjs",
12
- "require": "./dist/index.js"
9
+ "import": "./dist/index.js",
10
+ "types": "./dist/index.d.ts"
13
11
  }
14
12
  },
15
13
  "engines": {
@@ -32,16 +30,17 @@
32
30
  },
33
31
  "homepage": "https://fengxinming.github.io/vite-plugins/plugins/vite-plugin-mock-data/quick-start",
34
32
  "dependencies": {
35
- "find-my-way": "^9.2.0",
33
+ "@fastify/formbody": "^9.0.0",
34
+ "@fastify/static": "^10.1.3",
35
+ "fastify": "^5.12.1",
36
36
  "is-what-type": "^1.1.4",
37
- "sirv": "^3.0.1",
38
37
  "tinyglobby": "^0.2.12",
39
38
  "vp-runtime-helper": "^8.0.0"
40
39
  },
41
40
  "devDependencies": {
42
41
  "vite": "^8.0.0",
43
42
  "vite-plugin-dts": "^5.0.3",
44
- "vite-plugin-external": "^8.0.0"
43
+ "vite-plugin-external": "^8.0.3"
45
44
  },
46
45
  "files": [
47
46
  "dist"
package/dist/index.mjs DELETED
@@ -1,268 +0,0 @@
1
- import { isObject } from "is-what-type";
2
- import { banner, logFactory, toAbsolutePath } from "vp-runtime-helper";
3
- import { join, parse } from "node:path";
4
- import getRouter from "find-my-way";
5
- import sirv from "sirv";
6
- import { send, transformWithOxc } from "vite";
7
- import { readFileSync } from "node:fs";
8
- import { unlink, writeFile } from "node:fs/promises";
9
- import { createRequire } from "node:module";
10
- import { glob } from "tinyglobby";
11
- //#region \0rolldown/runtime.js
12
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) {
13
- if (typeof require !== "undefined") return require.apply(this, arguments);
14
- throw Error("Calling `require` for \"" + x + "\" in an environment that doesn't expose the `require` function. See https://rolldown.rs/in-depth/bundling-cjs#require-external-modules for more details.");
15
- });
16
- //#endregion
17
- //#region package.json
18
- var name = "vite-plugin-mock-data";
19
- var package_default = {
20
- name,
21
- version: "8.0.0",
22
- description: "Provides a simple way to mock data.",
23
- main: "./dist/index.js",
24
- module: "./dist/index.mjs",
25
- types: "./dist/index.d.ts",
26
- exports: { ".": {
27
- "types": "./dist/index.d.ts",
28
- "import": "./dist/index.mjs",
29
- "require": "./dist/index.js"
30
- } },
31
- engines: {
32
- "node": "^20.19.0 || >=22.12.0",
33
- "vite": ">=8.0.0"
34
- },
35
- scripts: {
36
- "build": "vite build",
37
- "watch": "vite build --watch",
38
- "prepublishOnly": "npm run build",
39
- "release": "pnpm publish --no-git-checks",
40
- "test": "vitest run",
41
- "test:watch": "vitest"
42
- },
43
- repository: {
44
- "type": "git",
45
- "url": "git+https://github.com/fengxinming/vite-plugins.git",
46
- "directory": "plugins/vite-plugin-mock-data"
47
- },
48
- keywords: ["vite-plugin", "vite-plugin-mock-data"],
49
- author: "Jesse Feng <fxm0016@126.com>",
50
- license: "MIT",
51
- bugs: { "url": "https://github.com/fengxinming/vite-plugins/issues" },
52
- homepage: "https://fengxinming.github.io/vite-plugins/plugins/vite-plugin-mock-data/quick-start",
53
- dependencies: {
54
- "find-my-way": "^9.2.0",
55
- "is-what-type": "^1.1.4",
56
- "sirv": "^3.0.1",
57
- "tinyglobby": "^0.2.12",
58
- "vp-runtime-helper": "workspace:^"
59
- },
60
- devDependencies: {
61
- "vite": "^8.0.0",
62
- "vite-plugin-dts": "^5.0.3",
63
- "vite-plugin-external": "workspace:^"
64
- },
65
- files: ["dist"]
66
- };
67
- //#endregion
68
- //#region src/configureServer.ts
69
- /**
70
- * Simple request body parser for JSON / urlencoded payloads.
71
- * Populates `req.body` so that route handlers can read it directly,
72
- * matching the documented handler signature `(req) => req.body`.
73
- */
74
- function readJsonBody(req) {
75
- return new Promise((resolve, reject) => {
76
- const chunks = [];
77
- req.on("data", (c) => chunks.push(Buffer.isBuffer(c) ? c : Buffer.from(c)));
78
- req.on("end", () => {
79
- const raw = Buffer.concat(chunks).toString("utf8");
80
- if (!raw) {
81
- resolve(void 0);
82
- return;
83
- }
84
- const type = (req.headers["content-type"] || "").toLowerCase();
85
- try {
86
- if (type.includes("application/json")) resolve(JSON.parse(raw));
87
- else if (type.includes("application/x-www-form-urlencoded")) {
88
- const out = {};
89
- for (const [k, v] of new URLSearchParams(raw)) out[k] = v;
90
- resolve(out);
91
- } else resolve(raw);
92
- } catch (e) {
93
- reject(e);
94
- }
95
- });
96
- req.on("error", reject);
97
- });
98
- }
99
- function sendResult(req, res, ret, defaultHeaders) {
100
- if (res.headersSent || ret === void 0) return;
101
- send(req, res, typeof ret !== "string" ? JSON.stringify(ret) : ret, isObject(ret) ? "json" : "html", { headers: defaultHeaders });
102
- }
103
- function sirvOptions(headers) {
104
- return {
105
- dev: true,
106
- etag: true,
107
- extensions: [],
108
- setHeaders(res, pathname) {
109
- res.setHeader("Access-Control-Allow-Origin", "*");
110
- if (/\.[tj]sx?$/.test(pathname)) res.setHeader("Content-Type", "application/javascript");
111
- if (headers) Object.entries(headers).forEach(([key, val]) => {
112
- if (val) res.setHeader(key, val);
113
- });
114
- }
115
- };
116
- }
117
- function configureServer(server, routerOpts, routes, cwd) {
118
- const router = getRouter(routerOpts);
119
- if (Array.isArray(routes)) routes.forEach((route) => {
120
- Object.keys(route).forEach((xpath) => {
121
- let [methods, pathname] = xpath.split(" ");
122
- if (!pathname) {
123
- pathname = methods;
124
- methods = "GET";
125
- }
126
- methods = methods.toUpperCase();
127
- let routeConfig = route[xpath];
128
- if (!isObject(routeConfig)) routeConfig = { handler: routeConfig };
129
- let handler;
130
- if (typeof routeConfig.file === "string") handler = (req, res) => {
131
- const parsedPath = parse(toAbsolutePath(routeConfig.file, cwd));
132
- const serve = sirv(parsedPath.dir, sirvOptions(server.config.server.headers));
133
- req.url = `/${parsedPath.base}`;
134
- serve(req, res);
135
- };
136
- else if (typeof routeConfig.handler !== "function") {
137
- const ret = routeConfig.handler;
138
- const retType = typeof ret;
139
- handler = (req, res) => {
140
- send(req, res, retType !== "string" ? JSON.stringify(ret) : ret, isObject(ret) ? "json" : "html", { headers: server.config.server.headers });
141
- };
142
- } else {
143
- const userHandler = routeConfig.handler;
144
- const needsBody = methods.toUpperCase().split("/").some((m) => [
145
- "POST",
146
- "PUT",
147
- "PATCH",
148
- "DELETE"
149
- ].includes(m));
150
- handler = async (req, res, params) => {
151
- req.params = params ?? {};
152
- try {
153
- if (needsBody) req.body = await readJsonBody(req);
154
- } catch (e) {
155
- res.statusCode = 400;
156
- res.setHeader("Content-Type", "application/json");
157
- res.end(JSON.stringify({
158
- error: "Invalid request body",
159
- detail: String(e)
160
- }));
161
- return;
162
- }
163
- try {
164
- sendResult(req, res, await userHandler(req, res), server.config.server.headers);
165
- } catch (e) {
166
- server.config.logger.error(`[mock-data] handler error on ${methods} ${pathname}: ${e}`);
167
- if (!res.headersSent) {
168
- res.statusCode = 500;
169
- res.setHeader("Content-Type", "application/json");
170
- res.end(JSON.stringify({
171
- error: "Mock handler error",
172
- detail: String(e)
173
- }));
174
- }
175
- }
176
- };
177
- }
178
- if (handler) router.on(methods.split("/"), pathname, {}, handler, routeConfig.store);
179
- });
180
- });
181
- server.middlewares.use((req, res, next) => {
182
- router.defaultRoute = () => next();
183
- router.lookup(req, res);
184
- });
185
- }
186
- //#endregion
187
- //#region src/logger.ts
188
- var PLUGIN_NAME = name;
189
- var logger = logFactory.getLogger(PLUGIN_NAME);
190
- //#endregion
191
- //#region src/loadRoutes.ts
192
- var _require = typeof __require === "function" ? __require : createRequire(import.meta.url);
193
- async function getRoute(filename) {
194
- logger.debug("Load mock file:", filename);
195
- let { ext, dir, name } = parse(filename);
196
- const isTs = ext === ".ts" || ext === ".mts";
197
- if (isTs) {
198
- const { code } = await transformWithOxc(readFileSync(filename, "utf-8"), filename);
199
- filename = join(dir, `${name}-${PLUGIN_NAME}.mjs`);
200
- ext = ".mjs";
201
- await writeFile(filename, code);
202
- }
203
- let config;
204
- switch (ext) {
205
- case ".js":
206
- config = _require(filename);
207
- break;
208
- case ".mjs":
209
- config = (await import(filename)).default;
210
- if (isTs) await unlink(filename);
211
- break;
212
- case ".json": config = JSON.parse(readFileSync(filename, "utf-8"));
213
- }
214
- return config;
215
- }
216
- async function loadRoutes(dir, routes) {
217
- const paths = await glob(`${dir}/**/*.{js,mjs,json,ts,mts}`, { absolute: true });
218
- const configs = await Promise.all(paths.map(getRoute));
219
- for (const config of configs) if (config) routes.push(config);
220
- }
221
- //#endregion
222
- //#region src/index.ts
223
- /**
224
- * Provides a simple way to mock data.
225
- *
226
- * @example
227
- * ```js
228
- * import { defineConfig } from 'vite';
229
- * import pluginMockDate from 'vite-plugin-mock-data';
230
- *
231
- * export default defineConfig({
232
- * plugins: [
233
- * pluginMockDate({
234
- * routes: './mock'
235
- * })
236
- * ]
237
- * });
238
- * ```
239
- *
240
- * @param opts Options
241
- * @returns a vite plugin
242
- */
243
- function pluginMockDate(opts) {
244
- if (opts.enableBanner) banner(package_default.name);
245
- const { isAfter, routerOptions, routes, logLevel, cwd = process.cwd() } = opts;
246
- if (logLevel) logger.level = logLevel;
247
- const allRoutes = [];
248
- return {
249
- name: PLUGIN_NAME,
250
- async configureServer(server) {
251
- if (typeof routes === "string") {
252
- logger.debug("Load routes from", routes);
253
- await loadRoutes(toAbsolutePath(routes, cwd), allRoutes);
254
- } else if (Array.isArray(routes)) for (const route of routes) {
255
- logger.debug("Load routes from", route);
256
- if (typeof route === "string") await loadRoutes(toAbsolutePath(route, cwd), allRoutes);
257
- else allRoutes.push(route);
258
- }
259
- else if (isObject(routes)) {
260
- logger.debug("Load routes from", routes);
261
- allRoutes.push(routes);
262
- }
263
- return isAfter ? () => configureServer(server, routerOptions, allRoutes, cwd) : configureServer(server, routerOptions, allRoutes, cwd);
264
- }
265
- };
266
- }
267
- //#endregion
268
- export { pluginMockDate as default };