mihawk
Version:
A tiny & simple mock server tool, support json,js,cjs,ts(typescript).
227 lines (226 loc) • 9.03 kB
JavaScript
;
import { join } from 'path';
import Colors from 'color-cc';
import { existsSync, ensureDirSync, ensureFileSync } from 'fs-extra';
import { writeJSONSafeSync, writeFileSafeSync } from '../utils/file';
import { Printer } from '../utils/print';
import { absifyPath, getLogicFileExt, getRoutesFileExt } from '../utils/path';
import { CWD, PKG_NAME, MOCK_DATA_DIR_NAME, MOCK_DIR_NAME } from '../consts';
const dataDemo = {
code: 200,
data: {
str: 'test',
},
message: 'success',
};
const routesDemo = {
'GET /test': './GET/test',
'GET /test-*': './GET/test',
};
export async function initMockDataDir(mockDirName = MOCK_DIR_NAME) {
const mockDataDir = join(mockDirName || MOCK_DIR_NAME, MOCK_DATA_DIR_NAME);
const mockDataDirPath = join(CWD, mockDataDir);
if (!existsSync(mockDataDirPath)) {
ensureDirSync(mockDataDirPath);
Printer.log(Colors.success(`Create mock data dir ${Colors.green(mockDataDir)} success!`));
}
for (const subDir of ['PUT', 'DELETE', 'POST', 'GET']) {
const subDirPath = join(mockDataDirPath, subDir);
if (!existsSync(subDirPath)) {
ensureFileSync(join(subDirPath, '.gitkeep'));
const subDirPathRel = join(mockDataDir, subDir);
Printer.log(Colors.success(`Create mock data dir ${Colors.green(subDirPathRel)} success!`));
}
}
const demoFilePathRel = join(mockDataDir, './GET/test.json');
const demoFilePath = join(CWD, demoFilePathRel);
if (!existsSync(demoFilePath)) {
writeJSONSafeSync(demoFilePath, dataDemo);
Printer.log(Colors.success(`Create mock data file ${Colors.green(demoFilePathRel)} success!`));
}
}
export async function initMockRoutesFile(fileType = 'none', mockDirName = 'mocks') {
const fileExt = getRoutesFileExt(fileType) || 'json';
const routesFileName = `routes.${fileExt}`;
const routesFilePathRel = join(mockDirName, routesFileName);
const routesFilePathAbs = join(CWD, mockDirName, routesFileName);
if (existsSync(routesFilePathAbs)) {
Printer.log(`Mock routes file ${Colors.yellow(routesFilePathRel)} is already existed, skip init!`);
return;
}
else {
const demoRouteMap = JSON.stringify(routesDemo, null, 2);
let initContent = demoRouteMap;
const comPrefixs = [
'/**',
` * ${PKG_NAME}'s routes file:`,
' */',
];
switch (fileType) {
case 'js':
case 'cjs':
case 'javascript': {
initContent = [
...comPrefixs,
`module.exports = ${demoRouteMap};`,
'',
].join('\n');
break;
}
case 'ts':
case 'typescript':
initContent = [
...comPrefixs,
`const routes: Record<string, string> = ${demoRouteMap}`,
'//',
'export default routes;',
'',
].join('\n');
break;
case 'none':
default: {
break;
}
}
ensureDirSync(join(CWD, mockDirName));
writeFileSafeSync(routesFilePathAbs, initContent);
Printer.log(Colors.success(`Init mock routes file ${Colors.green(routesFilePathRel)} success!`));
}
}
export async function initMockMiddlewareFile(fileType = 'none', mockDirName = 'mocks') {
const fileExt = getLogicFileExt(fileType);
const middlewareFileName = `middleware.${fileExt || 'cjs'}`;
const middlewareFilePathRel = join(mockDirName, middlewareFileName);
const middlewareFilePathAbs = join(CWD, mockDirName, middlewareFileName);
if (existsSync(middlewareFilePathAbs)) {
Printer.log(`Mock middleware file ${Colors.yellow(middlewareFilePathRel)} is already existed, skip init!`);
return;
}
else {
const comPrefixs = [
'/**',
` * ${PKG_NAME}'s custom koa2-middleware file:`,
' * - just a Koa Middleware',
' */',
];
const methodCommentCode = [
'/**',
' * Middleware functions, to implement some special data deal logic,',
' * - This function exec before the default-mock-logic. Simply return or don`t call "await next()" could skip default-mock-logic',
' * - This function is a standard Koa2 middleware that follows the KOA-onion-ring-model',
' * - see more:https://koajs.com/#middleware',
' * @param {KoaContext} ctx',
' * @param {KoaNext} next',
' * @returns {Promise<void>}',
' */',
];
const methodBodyCode = [
' // do something here',
' console.log(ctx.url);',
' if (ctx.peth === "/diy") {',
' ctx.body = "it is my diy logic";',
' } else {',
' await next(); // default logic (such like mock json logic)',
' }',
];
let initContent = '';
switch (fileType) {
case 'ts':
case 'typescript':
initContent = [
...comPrefixs,
`// import typs { Context: KoaContext, Next: KoaNext } from 'koa'; // need koa@v2.0.0+ (eg: koa@^2.15.3)`,
`import type { KoaContext, KoaNext } from '${PKG_NAME}/com-types';`,
'',
...methodCommentCode,
'export default async function middleware(ctx: KoaContext, next: KoaNext) {',
...methodBodyCode,
'}',
'',
].join('\n');
break;
case 'none':
case 'js':
case 'cjs':
case 'javascript':
default:
initContent = [
...comPrefixs,
'',
...methodCommentCode,
'module.exports = async function middleware(ctx, next) {',
...methodBodyCode,
'}',
'',
].join('\n');
break;
}
ensureDirSync(join(CWD, mockDirName));
writeFileSafeSync(middlewareFilePathAbs, initContent);
Printer.log(Colors.success(`Init mock middleware file ${Colors.green(middlewareFilePathRel)} success!`));
}
}
export function initMockLogicFile(mockLogicFilePath, options) {
mockLogicFilePath = absifyPath(mockLogicFilePath);
const { logicFileExt, routePath, jsonPath4log, overwrite } = options;
if (!logicFileExt) {
Printer.warn('Empty logic file ext, skip init logic file.');
return;
}
if (!overwrite && existsSync(mockLogicFilePath)) {
Printer.warn('File is already exists, skip init logic file.', Colors.gray(mockLogicFilePath));
return;
}
let initContent = '';
const commentCode = [
"'use strict;'",
'/**',
` * ${routePath}`,
' * This file isn‘t mandatory. If it is not needed (such as when there is no need to modify response data), it can be deleted directly',
' */',
];
const methodCommentCode = [
'/**',
' * Mock data resolve function, the original data source is the JSON file with the same name as this file',
` * @param {object} originData (${jsonPath4log})`,
' * @param {MhkCvtrExtra} extra { url,method,path,query,body }',
' * @returns {object} newData',
' */',
];
switch (logicFileExt) {
case 'ts': {
const useTypeDefine = true;
initContent = [
...commentCode,
useTypeDefine ? `import { MhkCvtrExtra } from '${PKG_NAME}/com-types';\n` : '',
...methodCommentCode,
useTypeDefine
? 'export default async function convertData(originData: Record<string, any>, extra: MhkCvtrExtra) {'
: 'export default async function convertData(originData: Record<string, any>, extra: Record<string, any>) {',
' // 👇🏻 write your logic here...',
' return originData;',
'}',
].join('\n');
break;
}
case 'js':
case 'cjs':
initContent = [
...commentCode,
'',
...methodCommentCode,
'module.exports = async function convertData(originData, extra) {',
' // 👇🏻 write your logic here...',
' return originData;',
'};',
].join('\n');
break;
default:
break;
}
if (!initContent) {
Printer.warn('No logic file ext was matched, skip init logic file.', logicFileExt);
return;
}
writeFileSafeSync(mockLogicFilePath, initContent);
}