Skip to content

Commit f944bc1

Browse files
committed
feat(server): add mocking
1 parent 93b877a commit f944bc1

File tree

6 files changed

+564
-95
lines changed

6 files changed

+564
-95
lines changed

lib/commands/server.js

Lines changed: 143 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ var _defineProperty2 = require('babel-runtime/helpers/defineProperty');
88

99
var _defineProperty3 = _interopRequireDefault(_defineProperty2);
1010

11-
var _keys = require('babel-runtime/core-js/object/keys');
12-
13-
var _keys2 = _interopRequireDefault(_keys);
14-
1511
var _stringify = require('babel-runtime/core-js/json/stringify');
1612

1713
var _stringify2 = _interopRequireDefault(_stringify);
1814

15+
var _keys = require('babel-runtime/core-js/object/keys');
16+
17+
var _keys2 = _interopRequireDefault(_keys);
18+
1919
var _typeof2 = require('babel-runtime/helpers/typeof');
2020

2121
var _typeof3 = _interopRequireDefault(_typeof2);
@@ -36,6 +36,7 @@ var connect = require('connect'),
3636
logSymbols = require('log-symbols'),
3737
favicon = require('serve-favicon'),
3838
webpackDevMiddleware = require('webpack-dev-middleware'),
39+
Mock = require('mockjs'),
3940
httpProxy = require('http-proxy-middleware');
4041

4142
var Manager = require('../modules/manager.js');
@@ -56,8 +57,10 @@ exports.setOptions = function (optimist) {
5657
optimist.describe('hot', '开启 hot-reload');
5758
optimist.alias('v', 'verbose');
5859
optimist.describe('v', '显示详细编译信息');
59-
optimist.alias('m', 'middlewares');
60-
optimist.describe('m', '加载项目中间件');
60+
optimist.alias('mw', 'middlewares');
61+
optimist.describe('mw', '加载项目中间件');
62+
optimist.alias('m', 'mock');
63+
optimist.describe('m', '启用 mock 服务');
6164
};
6265

6366
exports.run = function (options) {
@@ -66,12 +69,15 @@ exports.run = function (options) {
6669
verbose = options.v || options.verbose,
6770
proxy = options.x || options.proxy,
6871
hot = options.hot,
69-
middlewares = options.m || options.middlewares,
72+
middlewares = options.mw || options.middlewares,
73+
mock = options.m || options.mock,
7074
isHttps = options.s || options.https,
7175
port = options.p || options.port || 80;
7276

7377
var middlewareCache = {},
7478
promiseCache = {},
79+
mockCache = {},
80+
mockRules = {},
7581
allAssetsEntry = {},
7682
watchCacheNames = {};
7783

@@ -92,39 +98,6 @@ exports.run = function (options) {
9298
});
9399
}
94100

95-
// proxy
96-
var currentProxy = [];
97-
app.use(function (req, res, next) {
98-
try {
99-
var projectInfo = getProjectInfo(req);
100-
var project = Manager.getProject(projectInfo.projectCwd, { cache: false });
101-
102-
if (project.proxy && project.proxy.length !== currentProxy.length && project.proxy[0] !== currentProxy[0]) {
103-
currentProxy = project.proxy;
104-
currentProxy.map(function (proxyItem) {
105-
// hacky way to add middleware
106-
if (typeof proxyItem === 'string') {
107-
app.stack.unshift({
108-
route: '', handle: httpProxy(proxyItem)
109-
});
110-
} else {
111-
if ((typeof proxyItem === 'undefined' ? 'undefined' : (0, _typeof3.default)(proxyItem)) !== 'object' || !proxyItem.route || !proxyItem.options) {
112-
logWarn('Not valid proxy: ' + (0, _stringify2.default)(proxyItem));
113-
} else {
114-
app.stack.unshift({
115-
route: proxyItem.route, handle: httpProxy(proxyItem.options)
116-
});
117-
}
118-
}
119-
});
120-
}
121-
} catch (e) {
122-
logError(e);
123-
}
124-
125-
next();
126-
});
127-
128101
// a simple middleware
129102
app.use(favicon(sysPath.join(__dirname, '../../static/imgs/favicon.ico')));
130103

@@ -162,16 +135,16 @@ exports.run = function (options) {
162135

163136
function parse(req, res, format) {
164137
/* eslint-disable */
165-
var status = function () {
138+
var statusColor = function () {
166139
switch (true) {
167140
case 500 <= res.statusCode:
168-
return '\x1b[31m';
141+
return 'red';
169142
case 400 <= res.statusCode:
170-
return '\x1b[33m';
143+
return 'yellow';
171144
case 300 <= res.statusCode:
172-
return '\x1b[36m';
145+
return 'cyan';
173146
case 200 <= res.statusCode:
174-
return '\x1b[32m';
147+
return 'green';
175148
}
176149
}();
177150
/* eslint-enable */
@@ -182,11 +155,11 @@ exports.run = function (options) {
182155
contentLength = '( ' + contentLength + ' )';
183156
}
184157

185-
format = format.replace(/%date/g, '\x1b[90m' + '[' + moment().format(dateFormat) + ']' + '\x1b[0m');
186-
format = format.replace(/%method/g, '\x1b[35m' + req.method.toUpperCase() + '\x1b[0m');
158+
format = format.replace(/%date/g, ('[' + moment().format(dateFormat) + ']').grey);
159+
format = format.replace(/%method/g, '' + req.method.toUpperCase().magenta + (req.mock ? '(mock)'.cyan : ''));
187160
format = format.replace(/%url/g, decodeURI(req.originalUrl));
188-
format = format.replace(/%status/g, '' + status + res.statusCode + '\x1b[0m');
189-
format = format.replace(/%contentLength/g, '\x1b[90m' + contentLength + '\x1b[31m' + '\x1b[0m');
161+
format = format.replace(/%status/g, String(res.statusCode)[statusColor]);
162+
format = format.replace(/%contentLength/g, contentLength.grey);
190163

191164
return {
192165
message: format,
@@ -197,6 +170,127 @@ exports.run = function (options) {
197170
return next();
198171
});
199172

173+
// mock
174+
app.use(function (req, res, next) {
175+
var projectInfo = getProjectInfo(req);
176+
var project = Manager.getProject(projectInfo.projectCwd, { cache: false });
177+
var cwd = projectInfo.projectCwd;
178+
var shouldMock = mock || project.server && project.server.mock;
179+
180+
if (!shouldMock) {
181+
next();
182+
}
183+
184+
if (req.headers['x-requested-with'] !== 'XMLHttpRequest') {
185+
if (!mockCache[projectInfo.projectName]) {
186+
mockCache[projectInfo.projectName] = true;
187+
188+
// get mock file
189+
var mockFile = '';
190+
if (typeof mock === 'string') {
191+
if (UtilFs.fileExists(mock)) {
192+
mockFile = mock;
193+
} else {
194+
logError('Mock file ' + mock.bold + ' not found in ' + cwd.bold);
195+
}
196+
} else {
197+
var localConfigFiles = getMockFiles(['mock.js', 'mock.json']);
198+
if (localConfigFiles.length > 0) {
199+
mockFile = localConfigFiles[0];
200+
}
201+
}
202+
203+
// get mock rules
204+
if (mockFile) {
205+
var ext = sysPath.extname(mockFile);
206+
if (ext === '.js') {
207+
var appMockRules = require(sysPath.join(cwd, mockFile));
208+
var formattedRules = [];
209+
if ((typeof appMockRules === 'undefined' ? 'undefined' : (0, _typeof3.default)(appMockRules)) === 'object') {
210+
log(('Start using ' + mockFile + ' for simulation.').cyan);
211+
(0, _keys2.default)(appMockRules).map(function (itemKey) {
212+
if (itemKey === 'rules') {
213+
formattedRules = formattedRules.concat(appMockRules[itemKey]);
214+
} else {
215+
formattedRules.push({
216+
pattern: itemKey,
217+
respondwith: appMockRules[itemKey]
218+
});
219+
}
220+
});
221+
mockRules[projectInfo.projectName] = formattedRules.map(function (r) {
222+
return extend(r, { cwd: cwd });
223+
});
224+
} else {
225+
logError('Invalid mock rules, please check your mock config.');
226+
}
227+
}
228+
}
229+
}
230+
231+
next();
232+
} else {
233+
var mockResult = void 0;
234+
var mockAction = function mockAction(rule, req, res) {
235+
var rw = rule.respondwith;
236+
var cwd = rule.cwd; // different from current req cwd
237+
238+
var resObj = {};
239+
240+
// TODO handle object
241+
242+
// handle file
243+
var mockPath = sysPath.join(cwd, rw);
244+
if (UtilFs.fileExists(mockPath)) {
245+
if (sysPath.extname(rw) === '.js' || sysPath.extname(rw) === '.json') {
246+
resObj = Mock.mock(require(mockPath));
247+
} else if (sysPath.extname(rw)) {
248+
try {
249+
resObj = Mock.mock(JSON.parse(fs.readFileSync(mockPath, 'utf-8')));
250+
} catch (e) {
251+
logError('Parse error in ' + mockPath.bold + ' \n' + e);
252+
}
253+
}
254+
} else {
255+
logError('Not found ' + mockPath.bold);
256+
}
257+
258+
req.mock = true;
259+
res.writeHead(200, { 'Content-Type': 'application/json' });
260+
res.end((0, _stringify2.default)(resObj));
261+
};
262+
263+
(0, _keys2.default)(mockRules).map(function (mockApp) {
264+
mockRules[mockApp].map(function (rule) {
265+
var isReg = Object.prototype.toString.call(rule.pattern).indexOf('RegExp') > -1;
266+
var result = void 0;
267+
268+
if (isReg) {
269+
result = req.url.match(rule.pattern);
270+
} else {
271+
result = req.url.indexOf(rule.pattern) === 0;
272+
}
273+
274+
if (result) {
275+
mockResult = mockAction(rule, req, res);
276+
}
277+
});
278+
});
279+
280+
if (!mockResult) {
281+
next();
282+
}
283+
}
284+
285+
function getMockFiles() {
286+
var names = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
287+
var searchPath = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
288+
289+
console.log();
290+
return globby.sync(['mock.js', 'mock.json'], { cwd: sysPath.join(cwd, searchPath) });
291+
}
292+
});
293+
200294
app.use(function (req, res, next) {
201295
try {
202296
var projectInfo = getProjectInfo(req);

0 commit comments

Comments
 (0)