vue-ant-patterns
Version:
vue-ant-patterns
189 lines (168 loc) • 5.02 kB
JavaScript
const { existsSync } = require('fs');
const { join, basename } = require('path');
const bodyParser = require('body-parser');
const glob = require('glob');
const assert = require('assert');
const chokidar = require('chokidar');
const pathToRegexp = require('path-to-regexp');
const signale = require('signale');
const multer = require('multer');
const VALID_METHODS = ['get', 'post', 'put', 'patch', 'delete'];
const BODY_PARSED_METHODS = ['post', 'put', 'patch', 'delete'];
const cwd = process.cwd();
const mockCwd = join(cwd, 'mock');
const absMockPath = join(cwd, 'mock/api');
const absMockPathData = join(cwd, 'mock/data');
const cfg = require('../site/public-config.js');
global.globalJSConfig = cfg;
function MOCK(app) {
let mockData = getConfig();
watch();
function watch() {
const watcher = chokidar.watch([mockCwd], {
ignoreInitial: true,
});
watcher.on('all', (event, file) => {
mockData = getConfig();
// console.log(event);
signale.success(`Mock file parse success `);
signale.success(`event: ${file}`);
});
}
function getConfig() {
cleanRequireCache();
let ret = {};
if (existsSync(absMockPath)) {
const mockFiles = glob
.sync('**/*.js', {
cwd: absMockPath,
})
.map(p => join(absMockPath, p));
try {
ret = mockFiles.reduce((memo, mockFile) => {
const m = require(mockFile); // eslint-disable-line
memo = {
...memo,
...(m.default || m),
};
return memo;
}, {});
} catch (e) {
signale.error('Mock file parse failed');
console.error(e);
}
}
const rs = normalizeConfig(ret);
return rs;
}
function cleanRequireCache() {
Object.keys(require.cache).forEach(file => {
if (file.indexOf(absMockPath) > -1 || file.indexOf(absMockPathData) > -1 || basename(file) === '_mock.js') {
delete require.cache[file];
}
});
}
function parseKey(key) {
let method = 'get';
let path = key;
if (key.indexOf(' ') > -1) {
const splited = key.split(' ');
method = splited[0].toLowerCase();
path = splited[1]; // eslint-disable-line
}
assert(VALID_METHODS.includes(method), `Invalid method ${method} for path ${path}, please check your mock files.`);
return {
method,
path,
};
}
function createHandler(method, path, handler) {
return function(req, res, next) {
if (BODY_PARSED_METHODS.includes(method)) {
bodyParser.json({ limit: '5mb', strict: false })(req, res, () => {
bodyParser.urlencoded({ limit: '5mb', extended: true })(req, res, () => {
sendData();
});
});
} else {
sendData();
}
function sendData() {
if (typeof handler === 'function') {
multer().any()(req, res, () => {
handler(req, res, next);
});
} else {
res.json(handler);
}
}
};
}
function normalizeConfig(config) {
return Object.keys(config).reduce((memo, key) => {
const handler = config[key];
const type = typeof handler;
assert(
type === 'function' || type === 'object',
`mock value of ${key} should be function or object, but got ${type}`,
);
const { method, path } = parseKey(key);
const keys = [];
const re = pathToRegexp(path, keys);
memo.push({
method,
path,
re,
keys,
handler: createHandler(method, path, handler),
});
return memo;
}, []);
}
function matchMock(req) {
const { path: exceptPath } = req;
const exceptMethod = req.method.toLowerCase();
for (const mock of mockData) {
const { method, re, keys } = mock;
if (method === exceptMethod) {
const match = re.exec(req.path);
if (match) {
const params = {};
for (let i = 1; i < match.length; i += 1) {
const key = keys[i - 1];
const prop = key.name;
const val = decodeParam(match[i]);
if (val !== undefined || !hasOwnProperty.call(params, prop)) {
params[prop] = val;
}
}
req.params = params;
return mock;
}
}
}
function decodeParam(val) {
if (typeof val !== 'string' || val.length === 0) {
return val;
}
try {
return decodeURIComponent(val);
} catch (err) {
if (err instanceof URIError) {
err.message = `Failed to decode param ' ${val} '`;
err.status = err.statusCode = 400;
}
throw err;
}
}
return mockData.filter(({ method, re }) => method === exceptMethod && re.test(exceptPath))[0];
}
return function MOCK(req, res, next) {
const match = matchMock(req);
if (match) {
return match.handler(req, res, next);
}
return next();
};
}
module.exports = MOCK;