mihawk
Version:
A tiny & simple mock server tool, support json,js,cjs,ts(typescript).
175 lines (174 loc) • 10.8 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createDataResolver = void 0;
const path_1 = require("path");
const color_cc_1 = __importDefault(require("color-cc"));
const fs_extra_1 = require("fs-extra");
const deepmerge_1 = __importDefault(require("deepmerge"));
const file_1 = require("../utils/file");
const print_1 = require("../utils/print");
const path_2 = require("../utils/path");
const loader_1 = require("../composites/loader");
const is_1 = require("../utils/is");
const consts_1 = require("../consts");
const request_1 = require("../utils/request");
const init_file_1 = require("./init-file");
const RESOLVER_NAME = '[resolver]';
const LOGFLAG_RESOLVER = `${color_cc_1.default.cyan(RESOLVER_NAME)}${color_cc_1.default.gray(':')}`;
function createDataResolver(options) {
const { mockDir, cache, useLogicFile, isTypesctiptMode, mockDataDirPath: MOCK_DATA_DIR_PATH, dataFileExt: JSON_EXT, logicFileExt: LOGIC_EXT, autoCreateMockLogicFile = false, setJsonByRemote, useRemoteData, } = options || {};
const loadConvertLogicFile = isTypesctiptMode ? loader_1.loadTS : loader_1.loadJS;
const DATA_BASE_PATH = (0, path_2.formatPath)((0, path_1.join)(mockDir, consts_1.MOCK_DATA_DIR_NAME));
return function getMockData(ctx) {
return __awaiter(this, void 0, void 0, function* () {
const { disableLogPrint, mockRelPath, routePath } = ctx || {};
const mockRelPathNoExt = (0, path_2.formatMockPath)(mockRelPath);
!disableLogPrint && print_1.Printer.log(LOGFLAG_RESOLVER, `${color_cc_1.default.cyan(routePath)} ${consts_1.LOG_ARROW} ${color_cc_1.default.green(`./${mockRelPathNoExt}`)}`);
const jsonPath = `${mockRelPathNoExt}.${JSON_EXT}`;
const jsonPath4log = `${DATA_BASE_PATH}/${jsonPath}`;
const mockJsonAbsPath = (0, path_2.absifyPath)((0, path_1.join)(MOCK_DATA_DIR_PATH, jsonPath));
const initData = { code: 200, data: 'Empty data', msg: `Auto init file: ${jsonPath4log}` };
let mockJson = initData;
if ((0, fs_extra_1.existsSync)(mockJsonAbsPath)) {
let jsonData = null;
if (useRemoteData && (setJsonByRemote === null || setJsonByRemote === void 0 ? void 0 : setJsonByRemote.coverExistedJson)) {
const { method, headers, request } = ctx || {};
const body = request === null || request === void 0 ? void 0 : request.body;
const remoteData = yield fetchRemoteData(mockRelPath, { method, headers, body }, options);
if ((0, is_1.isObjStrict)(remoteData)) {
(0, file_1.writeJSONSafeSync)(mockJsonAbsPath, remoteData);
jsonData = remoteData;
}
else {
print_1.Printer.warn(LOGFLAG_RESOLVER, color_cc_1.default.yellow(`RemoteData isn't a normal json response!`), color_cc_1.default.yellow('Unexception value='), remoteData);
jsonData = null;
}
}
if (!jsonData) {
jsonData = yield (0, loader_1.loadJson)(mockJsonAbsPath, { noCache: !cache });
}
if ((0, is_1.isObjStrict)(jsonData)) {
jsonData = cache ? jsonData : (0, deepmerge_1.default)({}, jsonData);
}
else {
print_1.Printer.warn(LOGFLAG_RESOLVER, color_cc_1.default.yellow(`MockDataFile isn't a normal json file!`), color_cc_1.default.gray(jsonPath4log), color_cc_1.default.yellow('Unexception value='), jsonData);
}
mockJson = jsonData || initData;
}
else {
let finalInitData = initData;
let finalInitType = 'default';
let remoteData = null;
if (useRemoteData) {
const { method, headers, request } = ctx || {};
const body = request === null || request === void 0 ? void 0 : request.body;
remoteData = yield fetchRemoteData(mockRelPath, { method, headers, body }, options);
}
if (remoteData) {
finalInitData = remoteData;
finalInitType = 'fallbackRemoteData';
}
print_1.Debugger.log(RESOLVER_NAME, `MockDataFile isn't exists, will auto create it with ${finalInitType}...`, jsonPath4log);
(0, file_1.writeJSONSafeSync)(mockJsonAbsPath, finalInitData);
}
ctx.set('X-Mock-Use-Default', mockJson === initData ? '1' : '0');
ctx.set('X-Mock-Use-Logic', 'none');
if (useLogicFile) {
const logicPath = `${mockRelPathNoExt}.${LOGIC_EXT}`;
const logicPath4log = `${DATA_BASE_PATH}/${logicPath}`;
const mockLogicAbsPath = (0, path_1.join)(MOCK_DATA_DIR_PATH, logicPath);
if ((0, fs_extra_1.existsSync)(mockLogicAbsPath)) {
const dataConvertor = yield loadConvertLogicFile(mockLogicAbsPath, { noCache: !cache });
if (typeof dataConvertor === 'function') {
const { request } = ctx || {};
const extra = request;
try {
mockJson = yield dataConvertor(mockJson, extra);
ctx.set('X-Mock-Use-Logic', LOGIC_EXT);
}
catch (error) {
print_1.Printer.error(LOGFLAG_RESOLVER, color_cc_1.default.error(`Convert-function of MockLogicFile exec failed!`), color_cc_1.default.yellow(logicPath4log), '\n', error);
print_1.Printer.log(color_cc_1.default.yellow(`Will return json (${jsonPath4log}) instead.`));
}
if (!(0, is_1.isObjStrict)(mockJson)) {
print_1.Printer.warn(LOGFLAG_RESOLVER, color_cc_1.default.yellow("Convert-function of MockLogicFile, isn't return an json-object!"), color_cc_1.default.gray(logicPath4log));
}
}
else {
const exportInfo = isTypesctiptMode ? 'export default' : 'module.exports';
print_1.Printer.warn(LOGFLAG_RESOLVER, color_cc_1.default.yellow(`MockLogicFile isn't ${exportInfo} a convert-function!`), color_cc_1.default.gray(logicPath4log));
}
}
else {
if (autoCreateMockLogicFile) {
print_1.Printer.warn(LOGFLAG_RESOLVER, "MockLogicFile isn't exists, will auto ctreate it...", color_cc_1.default.gray(logicPath4log));
(0, init_file_1.initMockLogicFile)(mockLogicAbsPath, { routePath, jsonPath4log, logicPath4log, logicFileExt: LOGIC_EXT, overwrite: false });
}
else {
print_1.Printer.warn(LOGFLAG_RESOLVER, color_cc_1.default.yellow("MockLogicFile isn't exists!"), color_cc_1.default.gray(logicPath4log));
}
}
}
return mockJson;
});
};
}
exports.createDataResolver = createDataResolver;
function fetchRemoteData(reqPath, reqOptions, mhkOptions) {
return __awaiter(this, void 0, void 0, function* () {
const { setJsonByRemote } = mhkOptions;
if (typeof setJsonByRemote !== 'object') {
print_1.Debugger.log(LOGFLAG_RESOLVER, 'FetchRemoteData: setJsonByRemote isnot a object, will skip remote data fetch!');
return null;
}
if (!(setJsonByRemote === null || setJsonByRemote === void 0 ? void 0 : setJsonByRemote.enable)) {
print_1.Debugger.log(LOGFLAG_RESOLVER, 'FetchRemoteData: setJsonByRemote.enable != true, will skip remote data fetch!');
return null;
}
if (!(setJsonByRemote === null || setJsonByRemote === void 0 ? void 0 : setJsonByRemote.target)) {
print_1.Printer.warn(LOGFLAG_RESOLVER, 'FetchRemoteData: setJsonByRemote.target is not defined, will skip remote data fetch!');
return null;
}
try {
const { target, timeout = 10000, changeOrigin, rewrite } = setJsonByRemote;
if (typeof rewrite === 'function') {
reqPath = rewrite(reqPath);
}
const requestPath = `${target}/${reqPath}`;
const { method = 'GET', headers: originalHeaders = {}, body } = reqOptions || {};
const headers = Object.assign(Object.assign({}, originalHeaders), { 'Cache-Control': 'no-cache', Accept: 'application/json' });
const targetUrl = new URL(target);
const targetHost = targetUrl.host;
if (changeOrigin && targetHost) {
headers.Host = targetHost;
print_1.Debugger.log(LOGFLAG_RESOLVER, `Apply changeOrigin: ${color_cc_1.default.cyan(headers.Host)}`);
}
print_1.Printer.log(LOGFLAG_RESOLVER, `FetchRemoteData: Fetching remote data from ${color_cc_1.default.cyan(requestPath)}`);
const data = yield (0, request_1.jsonRequest)(requestPath, { method, timeout, headers, body });
if ((0, is_1.isObjStrict)(data)) {
data.mihawkMessage = `Auto init json data from remote: ${requestPath}`;
return data;
}
else {
print_1.Printer.error(LOGFLAG_RESOLVER, 'FetchRemoteData: Invalid response data format', data);
return null;
}
}
catch (error) {
print_1.Printer === null || print_1.Printer === void 0 ? void 0 : print_1.Printer.error(LOGFLAG_RESOLVER, 'FetchRemoteData: Remote data fetch failed:', error);
return null;
}
});
}