vite-plugin-mock-data 6.0.3 → 8.0.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.
- package/dist/configureServer.d.ts +1 -1
- package/dist/index.js +274 -143
- package/dist/index.mjs +251 -138
- package/dist/logger.d.ts +3 -0
- package/package.json +11 -9
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { OutgoingHttpHeaders } from 'node:http';
|
|
2
2
|
import { Config as SirvConfig, HTTPVersion } from 'find-my-way';
|
|
3
|
-
import {
|
|
3
|
+
import { Options as SirvOptions } from 'sirv';
|
|
4
4
|
import { ViteDevServer } from 'vite';
|
|
5
5
|
import { RouteConfig } from './types';
|
|
6
6
|
export declare function sirvOptions(headers?: OutgoingHttpHeaders): SirvOptions;
|
package/dist/index.js
CHANGED
|
@@ -1,155 +1,286 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
1
|
+
//#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;
|
|
16
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));
|
|
22
|
+
//#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
|
+
//#region package.json
|
|
36
|
+
var name = "vite-plugin-mock-data";
|
|
37
|
+
var package_default = {
|
|
38
|
+
name,
|
|
39
|
+
version: "8.0.0",
|
|
40
|
+
description: "Provides a simple way to mock data.",
|
|
41
|
+
main: "./dist/index.js",
|
|
42
|
+
module: "./dist/index.mjs",
|
|
43
|
+
types: "./dist/index.d.ts",
|
|
44
|
+
exports: { ".": {
|
|
45
|
+
"types": "./dist/index.d.ts",
|
|
46
|
+
"import": "./dist/index.mjs",
|
|
47
|
+
"require": "./dist/index.js"
|
|
48
|
+
} },
|
|
49
|
+
engines: {
|
|
50
|
+
"node": "^20.19.0 || >=22.12.0",
|
|
51
|
+
"vite": ">=8.0.0"
|
|
52
|
+
},
|
|
53
|
+
scripts: {
|
|
54
|
+
"build": "vite build",
|
|
55
|
+
"watch": "vite build --watch",
|
|
56
|
+
"prepublishOnly": "npm run build",
|
|
57
|
+
"release": "pnpm publish --no-git-checks",
|
|
58
|
+
"test": "vitest run",
|
|
59
|
+
"test:watch": "vitest"
|
|
60
|
+
},
|
|
61
|
+
repository: {
|
|
62
|
+
"type": "git",
|
|
63
|
+
"url": "git+https://github.com/fengxinming/vite-plugins.git",
|
|
64
|
+
"directory": "plugins/vite-plugin-mock-data"
|
|
65
|
+
},
|
|
66
|
+
keywords: ["vite-plugin", "vite-plugin-mock-data"],
|
|
67
|
+
author: "Jesse Feng <fxm0016@126.com>",
|
|
68
|
+
license: "MIT",
|
|
69
|
+
bugs: { "url": "https://github.com/fengxinming/vite-plugins/issues" },
|
|
70
|
+
homepage: "https://fengxinming.github.io/vite-plugins/plugins/vite-plugin-mock-data/quick-start",
|
|
71
|
+
dependencies: {
|
|
72
|
+
"find-my-way": "^9.2.0",
|
|
73
|
+
"is-what-type": "^1.1.4",
|
|
74
|
+
"sirv": "^3.0.1",
|
|
75
|
+
"tinyglobby": "^0.2.12",
|
|
76
|
+
"vp-runtime-helper": "workspace:^"
|
|
77
|
+
},
|
|
78
|
+
devDependencies: {
|
|
79
|
+
"vite": "^8.0.0",
|
|
80
|
+
"vite-plugin-dts": "^5.0.3",
|
|
81
|
+
"vite-plugin-external": "workspace:^"
|
|
82
|
+
},
|
|
83
|
+
files: ["dist"]
|
|
84
|
+
};
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/configureServer.ts
|
|
87
|
+
/**
|
|
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`.
|
|
91
|
+
*/
|
|
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
|
+
}
|
|
17
121
|
function sirvOptions(headers) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
res.setHeader(key, val);
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
};
|
|
122
|
+
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
|
+
}
|
|
133
|
+
};
|
|
36
134
|
}
|
|
37
135
|
function configureServer(server, routerOpts, routes, cwd) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
+
};
|
|
195
|
+
}
|
|
196
|
+
if (handler) router.on(methods.split("/"), pathname, {}, handler, routeConfig.store);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
server.middlewares.use((req, res, next) => {
|
|
200
|
+
router.defaultRoute = () => next();
|
|
201
|
+
router.lookup(req, res);
|
|
202
|
+
});
|
|
81
203
|
}
|
|
82
|
-
|
|
83
|
-
|
|
204
|
+
//#endregion
|
|
205
|
+
//#region src/logger.ts
|
|
206
|
+
var PLUGIN_NAME = name;
|
|
207
|
+
var logger = vp_runtime_helper.logFactory.getLogger(PLUGIN_NAME);
|
|
208
|
+
//#endregion
|
|
209
|
+
//#region src/loadRoutes.ts
|
|
210
|
+
var _require = typeof require === "function" ? require : (0, node_module.createRequire)({}.url);
|
|
84
211
|
async function getRoute(filename) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
break;
|
|
108
|
-
case ".json":
|
|
109
|
-
config = JSON.parse(node_fs.readFileSync(filename, "utf-8"));
|
|
110
|
-
break;
|
|
111
|
-
}
|
|
112
|
-
return config;
|
|
212
|
+
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);
|
|
220
|
+
}
|
|
221
|
+
let config;
|
|
222
|
+
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"));
|
|
231
|
+
}
|
|
232
|
+
return config;
|
|
113
233
|
}
|
|
114
234
|
async function loadRoutes(dir, routes) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (config) {
|
|
119
|
-
routes.push(config);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
235
|
+
const paths = await (0, tinyglobby.glob)(`${dir}/**/*.{js,mjs,json,ts,mts}`, { absolute: true });
|
|
236
|
+
const configs = await Promise.all(paths.map(getRoute));
|
|
237
|
+
for (const config of configs) if (config) routes.push(config);
|
|
122
238
|
}
|
|
239
|
+
//#endregion
|
|
240
|
+
//#region src/index.ts
|
|
241
|
+
/**
|
|
242
|
+
* Provides a simple way to mock data.
|
|
243
|
+
*
|
|
244
|
+
* @example
|
|
245
|
+
* ```js
|
|
246
|
+
* import { defineConfig } from 'vite';
|
|
247
|
+
* import pluginMockDate from 'vite-plugin-mock-data';
|
|
248
|
+
*
|
|
249
|
+
* export default defineConfig({
|
|
250
|
+
* plugins: [
|
|
251
|
+
* pluginMockDate({
|
|
252
|
+
* routes: './mock'
|
|
253
|
+
* })
|
|
254
|
+
* ]
|
|
255
|
+
* });
|
|
256
|
+
* ```
|
|
257
|
+
*
|
|
258
|
+
* @param opts Options
|
|
259
|
+
* @returns a vite plugin
|
|
260
|
+
*/
|
|
123
261
|
function pluginMockDate(opts) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
} else if (isWhatType.isObject(routes)) {
|
|
148
|
-
logger.debug("Load routes from", routes);
|
|
149
|
-
allRoutes.push(routes);
|
|
150
|
-
}
|
|
151
|
-
return isAfter ? () => configureServer(server, routerOptions, allRoutes, cwd) : configureServer(server, routerOptions, allRoutes, cwd);
|
|
152
|
-
}
|
|
153
|
-
};
|
|
262
|
+
if (opts.enableBanner) (0, vp_runtime_helper.banner)(package_default.name);
|
|
263
|
+
const { isAfter, routerOptions, routes, logLevel, cwd = process.cwd() } = opts;
|
|
264
|
+
if (logLevel) logger.level = logLevel;
|
|
265
|
+
const allRoutes = [];
|
|
266
|
+
return {
|
|
267
|
+
name: PLUGIN_NAME,
|
|
268
|
+
async configureServer(server) {
|
|
269
|
+
if (typeof routes === "string") {
|
|
270
|
+
logger.debug("Load routes from", routes);
|
|
271
|
+
await loadRoutes((0, vp_runtime_helper.toAbsolutePath)(routes, cwd), allRoutes);
|
|
272
|
+
} else if (Array.isArray(routes)) for (const route of routes) {
|
|
273
|
+
logger.debug("Load routes from", route);
|
|
274
|
+
if (typeof route === "string") await loadRoutes((0, vp_runtime_helper.toAbsolutePath)(route, cwd), allRoutes);
|
|
275
|
+
else allRoutes.push(route);
|
|
276
|
+
}
|
|
277
|
+
else if ((0, is_what_type.isObject)(routes)) {
|
|
278
|
+
logger.debug("Load routes from", routes);
|
|
279
|
+
allRoutes.push(routes);
|
|
280
|
+
}
|
|
281
|
+
return isAfter ? () => configureServer(server, routerOptions, allRoutes, cwd) : configureServer(server, routerOptions, allRoutes, cwd);
|
|
282
|
+
}
|
|
283
|
+
};
|
|
154
284
|
}
|
|
285
|
+
//#endregion
|
|
155
286
|
module.exports = pluginMockDate;
|
package/dist/index.mjs
CHANGED
|
@@ -1,155 +1,268 @@
|
|
|
1
1
|
import { isObject } from "is-what-type";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { banner, logFactory, toAbsolutePath } from "vp-runtime-helper";
|
|
3
|
+
import { join, parse } from "node:path";
|
|
4
4
|
import getRouter from "find-my-way";
|
|
5
5
|
import sirv from "sirv";
|
|
6
|
-
import { send,
|
|
6
|
+
import { send, transformWithOxc } from "vite";
|
|
7
7
|
import { readFileSync } from "node:fs";
|
|
8
|
-
import {
|
|
8
|
+
import { unlink, writeFile } from "node:fs/promises";
|
|
9
9
|
import { createRequire } from "node:module";
|
|
10
10
|
import { glob } from "tinyglobby";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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"]
|
|
14
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
|
+
}
|
|
15
103
|
function sirvOptions(headers) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
res.setHeader(key, val);
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
};
|
|
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
|
+
};
|
|
34
116
|
}
|
|
35
117
|
function configureServer(server, routerOpts, routes, cwd) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
+
});
|
|
79
185
|
}
|
|
80
|
-
|
|
81
|
-
|
|
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);
|
|
82
193
|
async function getRoute(filename) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
break;
|
|
106
|
-
case ".json":
|
|
107
|
-
config = JSON.parse(readFileSync(filename, "utf-8"));
|
|
108
|
-
break;
|
|
109
|
-
}
|
|
110
|
-
return config;
|
|
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;
|
|
111
215
|
}
|
|
112
216
|
async function loadRoutes(dir, routes) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if (config) {
|
|
117
|
-
routes.push(config);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
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);
|
|
120
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
|
+
*/
|
|
121
243
|
function pluginMockDate(opts) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
} else if (isObject(routes)) {
|
|
146
|
-
logger.debug("Load routes from", routes);
|
|
147
|
-
allRoutes.push(routes);
|
|
148
|
-
}
|
|
149
|
-
return isAfter ? () => configureServer(server, routerOptions, allRoutes, cwd) : configureServer(server, routerOptions, allRoutes, cwd);
|
|
150
|
-
}
|
|
151
|
-
};
|
|
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
|
+
};
|
|
152
266
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
};
|
|
267
|
+
//#endregion
|
|
268
|
+
export { pluginMockDate as default };
|
package/dist/logger.d.ts
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-mock-data",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.0",
|
|
4
4
|
"description": "Provides a simple way to mock data.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -13,13 +13,13 @@
|
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"engines": {
|
|
16
|
-
"node": ">=
|
|
17
|
-
"vite": ">=
|
|
16
|
+
"node": "^20.19.0 || >=22.12.0",
|
|
17
|
+
"vite": ">=8.0.0"
|
|
18
18
|
},
|
|
19
19
|
"repository": {
|
|
20
20
|
"type": "git",
|
|
21
21
|
"url": "git+https://github.com/fengxinming/vite-plugins.git",
|
|
22
|
-
"directory": "
|
|
22
|
+
"directory": "plugins/vite-plugin-mock-data"
|
|
23
23
|
},
|
|
24
24
|
"keywords": [
|
|
25
25
|
"vite-plugin",
|
|
@@ -36,12 +36,12 @@
|
|
|
36
36
|
"is-what-type": "^1.1.4",
|
|
37
37
|
"sirv": "^3.0.1",
|
|
38
38
|
"tinyglobby": "^0.2.12",
|
|
39
|
-
"vp-runtime-helper": "^
|
|
39
|
+
"vp-runtime-helper": "^8.0.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"vite": "^
|
|
42
|
+
"vite": "^8.0.0",
|
|
43
|
+
"vite-plugin-dts": "^5.0.3",
|
|
44
|
+
"vite-plugin-external": "^8.0.0"
|
|
45
45
|
},
|
|
46
46
|
"files": [
|
|
47
47
|
"dist"
|
|
@@ -49,6 +49,8 @@
|
|
|
49
49
|
"scripts": {
|
|
50
50
|
"build": "vite build",
|
|
51
51
|
"watch": "vite build --watch",
|
|
52
|
-
"release": "pnpm publish --no-git-checks"
|
|
52
|
+
"release": "pnpm publish --no-git-checks",
|
|
53
|
+
"test": "vitest run",
|
|
54
|
+
"test:watch": "vitest"
|
|
53
55
|
}
|
|
54
56
|
}
|