@paultaku/node-mock-server
Version:
A TypeScript-based mock server with automatic Swagger-based mock file generation
355 lines (332 loc) • 12.1 kB
JavaScript
/******/ (() => { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({
/***/ 115:
/***/ ((module) => {
module.exports = require("yaml");
/***/ }),
/***/ 315:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.SwaggerDocSchema = void 0;
const zod_1 = __webpack_require__(569);
exports.SwaggerDocSchema = zod_1.z.object({
openapi: zod_1.z.string(),
info: zod_1.z.object({
title: zod_1.z.string(),
version: zod_1.z.string(),
}),
paths: zod_1.z.record(zod_1.z.string(), zod_1.z.any()),
components: zod_1.z.optional(zod_1.z.any()),
});
/***/ }),
/***/ 569:
/***/ ((module) => {
module.exports = require("zod");
/***/ }),
/***/ 652:
/***/ ((module) => {
module.exports = require("fs-extra");
/***/ }),
/***/ 755:
/***/ (function(module, exports, __webpack_require__) {
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.generateMockFromSwagger = generateMockFromSwagger;
const fs = __importStar(__webpack_require__(652));
const yaml = __importStar(__webpack_require__(115));
const path = __importStar(__webpack_require__(928));
const swagger_1 = __webpack_require__(315);
// Safe filename generation
function sanitizeFileName(name) {
return name
.toLowerCase()
.replace(/\s+/g, "-") // Convert spaces to -
.replace(/[^a-z0-9\-_]/g, "-") // Convert all illegal characters to -
.replace(/-+/g, "-") // Merge consecutive -
.replace(/^-|-$/g, "") // Remove leading and trailing -
.substring(0, 100); // Limit length
}
// 递归生成 mock 数据
function generateMockDataFromSchema(schema, components) {
if (!schema)
return null;
// 处理 $ref
if (schema.$ref) {
const refPath = schema.$ref.replace(/^#\//, "").split("/");
let ref = components;
for (const seg of refPath.slice(1)) {
if (ref && ref[seg]) {
ref = ref[seg];
}
else {
console.warn(`Warning: Could not resolve $ref: ${schema.$ref}`);
return null;
}
}
return generateMockDataFromSchema(ref, components);
}
// 优先 example
if (schema.example !== undefined)
return schema.example;
// type: object
if (schema.type === "object" || schema.properties) {
const obj = {};
const props = schema.properties || {};
for (const [key, propSchema] of Object.entries(props)) {
obj[key] = generateMockDataFromSchema(propSchema, components);
}
return obj;
}
// type: array
if (schema.type === "array" && schema.items) {
const item = generateMockDataFromSchema(schema.items, components);
return [item];
}
// type: string
if (schema.type === "string") {
if (schema.enum && schema.enum.length > 0) {
return schema.enum[0];
}
if (schema.format === "date-time")
return "2023-01-01T00:00:00Z";
if (schema.format === "date")
return "2023-01-01";
if (schema.format === "email")
return "user@example.com";
if (schema.format === "uri")
return "https://example.com";
if (schema.format === "uuid")
return "123e4567-e89b-12d3-a456-426614174000";
return "string";
}
// type: integer/number
if (schema.type === "integer" || schema.type === "number") {
if (schema.minimum !== undefined)
return schema.minimum;
if (schema.maximum !== undefined)
return Math.floor(schema.maximum / 2);
return 0;
}
// type: boolean
if (schema.type === "boolean") {
return true;
}
// fallback
return null;
}
// 生成默认的响应文件
function generateDefaultResponses() {
return {
"200": {
description: "Successful operation",
content: {
"application/json": {
schema: {
type: "object",
properties: {
success: { type: "boolean" },
message: { type: "string" },
},
},
},
},
},
"400": {
description: "Bad request",
content: {
"application/json": {
schema: {
type: "object",
properties: {
error: { type: "string" },
code: { type: "string" },
},
},
},
},
},
"404": {
description: "Not found",
content: {
"application/json": {
schema: {
type: "object",
properties: {
error: { type: "string" },
code: { type: "string" },
},
},
},
},
},
"500": {
description: "Internal server error",
content: {
"application/json": {
schema: {
type: "object",
properties: {
error: { type: "string" },
code: { type: "string" },
},
},
},
},
},
};
}
async function generateMockFromSwagger(swaggerPath, outputPath) {
try {
console.log(`Reading Swagger file: ${swaggerPath}`);
// 读取 swagger yaml
const raw = await fs.readFile(swaggerPath, "utf-8");
const doc = yaml.parse(raw);
// 类型校验
const swagger = swagger_1.SwaggerDocSchema.parse(doc);
const components = swagger.components || {};
console.log(`Processing ${Object.keys(swagger.paths).length} paths...`);
// 遍历 paths
for (const [apiPath, methods] of Object.entries(swagger.paths)) {
for (const [method, operation] of Object.entries(methods)) {
const methodUpper = method.toUpperCase();
// 跳过非HTTP方法
if (![
"GET",
"POST",
"PUT",
"DELETE",
"PATCH",
"HEAD",
"OPTIONS",
].includes(methodUpper)) {
continue;
}
// 构建目录路径
const pathParts = apiPath.replace(/^\//, "").split("/");
const safePathParts = pathParts.map((part) => {
// 保持路径参数格式 {paramName}
if (part.startsWith("{") && part.endsWith("}")) {
return part;
}
// 其他部分进行安全处理
return sanitizeFileName(part);
});
const endpointDir = path.join(outputPath, ...safePathParts, methodUpper);
await fs.ensureDir(endpointDir);
console.log(`Generated directory: ${endpointDir}`);
// 处理响应
const responses = operation.responses || generateDefaultResponses();
for (const [status, resp] of Object.entries(responses)) {
const desc = resp.description
? sanitizeFileName(resp.description)
: "response";
const fileName = `${desc}-${status}.json`;
const filePath = path.join(endpointDir, fileName);
// 生成 mock 数据
let mockBody = {};
// 只处理 application/json
const content = resp.content?.["application/json"];
if (content && content.schema) {
mockBody = generateMockDataFromSchema(content.schema, components);
}
else {
// 如果没有schema,生成默认响应
mockBody = {
success: status === "200",
message: resp.description || "Response",
status: parseInt(status),
};
}
const mock = {
header: [],
body: mockBody,
};
await fs.writeJson(filePath, mock, { spaces: 2 });
console.log(`Generated mock file: ${filePath}`);
}
}
}
console.log("Mock generation completed successfully!");
}
catch (error) {
console.error("Error generating mock files:", error);
throw error;
}
}
// 为了兼容性保留 CommonJS 导出
module.exports = { generateMockFromSwagger };
/***/ }),
/***/ 928:
/***/ ((module) => {
module.exports = require("path");
/***/ })
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
/******/
/******/ // startup
/******/ // Load entry module and return exports
/******/ // This entry module is referenced by other modules so it can't be inlined
/******/ var __webpack_exports__ = __webpack_require__(755);
/******/ module.exports = __webpack_exports__;
/******/
/******/ })()
;
//# sourceMappingURL=mock-generator.js.map