UNPKG

mihawk

Version:

A tiny & simple mock server tool, support json,js,cjs,ts(typescript).

175 lines (174 loc) 10.8 kB
'use strict'; 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; } }); }