🎉 1.0.0-RC2

This commit is contained in:
smallchill 2019-01-18 22:39:21 +08:00
parent ff3eb7eddd
commit 53f8528b9e
12 changed files with 209 additions and 181 deletions

6
.gitpod.yml Normal file
View File

@ -0,0 +1,6 @@
ports:
- port: 8000
onOpen: open-preview
tasks:
- init: npm install
command: npm start

View File

@ -20,6 +20,7 @@ const plugins = [
},
dynamicImport: {
loadingComponent: './components/PageLoading/index',
webpackChunkName: true,
},
pwa: {
workboxPluginMode: 'InjectManifest',
@ -33,7 +34,7 @@ const plugins = [
include: ['dva', 'dva/router', 'dva/saga', 'dva/fetch'],
exclude: ['@babel/runtime'],
},
hardSource: true,
hardSource: false,
}
: {}),
},
@ -58,6 +59,7 @@ export default {
define: {
APP_TYPE: process.env.APP_TYPE || '',
},
treeShaking: true,
targets: {
ie: 11,
},

View File

@ -1,9 +1,10 @@
// [START functionsimport]
const functions = require('firebase-functions');
const express = require('express');
const matchMock = require('./matchMock');
const app = express();
app.use(matchMock);
exports.api = functions.https.onRequest(app);

View File

@ -1,10 +1,11 @@
const mockFile = require('./mock/index');
const pathToRegexp = require('path-to-regexp');
const debug = console.log;
const bodyParser = require('body-parser');
const mockFile = require('./mock/index');
const BODY_PARSED_METHODS = ['post', 'put', 'patch'];
const debug = console.log;
function parseKey(key) {
let method = 'get';
let path = key;
@ -20,7 +21,14 @@ function parseKey(key) {
}
function createHandler(method, path, handler) {
return function(req, res, next) {
return (req, res, next) => {
function sendData() {
if (typeof handler === 'function') {
handler(req, res, next);
} else {
res.json(handler);
}
}
if (BODY_PARSED_METHODS.includes(method)) {
bodyParser.json({ limit: '5mb', strict: false })(req, res, () => {
bodyParser.urlencoded({ limit: '5mb', extended: true })(req, res, () => {
@ -30,14 +38,6 @@ function createHandler(method, path, handler) {
} else {
sendData();
}
function sendData() {
if (typeof handler === 'function') {
handler(req, res, next);
} else {
res.json(handler);
}
}
};
}
@ -63,6 +63,24 @@ const mockData = normalizeConfig(mockFile);
function matchMock(req) {
const { path: exceptPath } = req;
const exceptMethod = req.method.toLowerCase();
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.statusCode = 400;
err.status = 400;
}
throw err;
}
}
// eslint-disable-next-line no-restricted-syntax
for (const mock of mockData) {
const { method, re, keys } = mock;
if (method === exceptMethod) {
@ -70,7 +88,7 @@ function matchMock(req) {
if (match) {
const params = {};
for (let i = 1; i < match.length; i = i + 1) {
for (let i = 1; i < match.length; i += 1) {
const key = keys[i - 1];
const prop = key.name;
const val = decodeParam(match[i]);
@ -85,33 +103,13 @@ function matchMock(req) {
}
}
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 }) => {
return method === exceptMethod && re.test(exceptPath);
})[0];
return mockData.filter(({ method, re }) => method === exceptMethod && re.test(exceptPath))[0];
}
module.exports = (req, res, next) => {
const match = matchMock(req);
if (match) {
debug(`mock matched: [${match.method}] ${match.path}`);
return match.handler(req, res, next);
} else {
return next();
}
return next();
};

View File

@ -2,19 +2,19 @@
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"serve": "npm run mock && firebase serve --only functions",
"serve": "firebase serve --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"deploy": "npm run mock && firebase deploy --only functions",
"logs": "firebase functions:log",
"mock": "node ../scripts/generateMock.js"
},
"dependencies": {
"@babel/runtime": "^7.0.0",
"body-parser": "^1.18.3",
"express": "^4.16.3",
"firebase-admin": "^5.12.1",
"firebase-functions": "^2.0.5",
"express": "^4.16.4",
"firebase-admin": "^6.4.0",
"firebase-functions": "^2.1.0",
"mockjs": "^1.0.1-beta3",
"moment": "^2.22.2",
"path-to-regexp": "^2.2.1"

View File

@ -9,7 +9,6 @@ module.exports = {
'--no-first-run',
'--no-sandbox',
'--no-zygote',
'--single-process',
],
},
};

View File

@ -1,104 +1,113 @@
const fakeNotices = [
{
id: '000000001',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
title: '你收到了 14 份新周报',
datetime: '2017-08-09',
type: 'notification',
},
{
id: '000000002',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png',
title: '你推荐的 曲妮妮 已通过第三轮面试',
datetime: '2017-08-08',
type: 'notification',
},
{
id: '000000003',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png',
title: '这种模板可以区分多种通知类型',
datetime: '2017-08-07',
read: true,
type: 'notification',
},
{
id: '000000004',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
title: '左侧图标用于区分不同的类型',
datetime: '2017-08-07',
type: 'notification',
},
{
id: '000000005',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
title: '内容不要超过两行字,超出时自动截断',
datetime: '2017-08-07',
type: 'notification',
},
{
id: '000000006',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '曲丽丽 评论了你',
description: '描述信息描述信息描述信息',
datetime: '2017-08-07',
type: 'message',
clickClose: true,
},
{
id: '000000007',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '朱偏右 回复了你',
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
datetime: '2017-08-07',
type: 'message',
clickClose: true,
},
{
id: '000000008',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '标题',
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
datetime: '2017-08-07',
type: 'message',
clickClose: true,
},
{
id: '000000009',
title: '任务名称',
description: '任务需要在 2017-01-12 20:00 前启动',
extra: '未开始',
status: 'todo',
type: 'event',
},
{
id: '000000010',
title: '第三方紧急代码变更',
description: '冠霖提交于 2017-01-06需在 2017-01-07 前完成代码变更任务',
extra: '马上到期',
status: 'urgent',
type: 'event',
},
{
id: '000000011',
title: '信息安全考试',
description: '指派竹尔于 2017-01-09 前完成更新并发布',
extra: '已耗时 8 天',
status: 'doing',
type: 'event',
},
{
id: '000000012',
title: 'ABCD 版本发布',
description: '冠霖提交于 2017-01-06需在 2017-01-07 前完成代码变更任务',
extra: '进行中',
status: 'processing',
type: 'event',
},
];
const getNotices = (req, res) => {
const json = { code: 200, success: true, msg: '操作成功' };
const data = [
{
id: '000000001',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
title: '你收到了 14 份新周报',
datetime: '2017-08-09',
type: 'notification',
},
{
id: '000000002',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png',
title: '你推荐的 曲妮妮 已通过第三轮面试',
datetime: '2017-08-08',
type: 'notification',
},
{
id: '000000003',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png',
title: '这种模板可以区分多种通知类型',
datetime: '2017-08-07',
read: true,
type: 'notification',
},
{
id: '000000004',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
title: '左侧图标用于区分不同的类型',
datetime: '2017-08-07',
type: 'notification',
},
{
id: '000000005',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
title: '内容不要超过两行字,超出时自动截断',
datetime: '2017-08-07',
type: 'notification',
},
{
id: '000000006',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '曲丽丽 评论了你',
description: '描述信息描述信息描述信息',
datetime: '2017-08-07',
type: 'message',
clickClose: true,
},
{
id: '000000007',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '朱偏右 回复了你',
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
datetime: '2017-08-07',
type: 'message',
clickClose: true,
},
{
id: '000000008',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '标题',
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
datetime: '2017-08-07',
type: 'message',
clickClose: true,
},
{
id: '000000009',
title: '任务名称',
description: '任务需要在 2017-01-12 20:00 前启动',
extra: '未开始',
status: 'todo',
type: 'event',
},
{
id: '000000010',
title: '第三方紧急代码变更',
description: '冠霖提交于 2017-01-06需在 2017-01-07 前完成代码变更任务',
extra: '马上到期',
status: 'urgent',
type: 'event',
},
{
id: '000000011',
title: '信息安全考试',
description: '指派竹尔于 2017-01-09 前完成更新并发布',
extra: '已耗时 8 天',
status: 'doing',
type: 'event',
},
{
id: '000000012',
title: 'ABCD 版本发布',
description: '冠霖提交于 2017-01-06需在 2017-01-07 前完成代码变更任务',
extra: '进行中',
status: 'processing',
type: 'event',
},
];
json.data = data;
return res.json(json);
if (req.query && req.query.type) {
const startFrom = parseInt(req.query.lastItemId, 10) + 1;
const result = fakeNotices
.filter(({ type }) => type === req.query.type)
.map((notice, index) => ({
...notice,
id: `0000000${startFrom + index}`,
}));
return res.json(startFrom > 24 ? result.concat(null) : result);
}
return res.json(fakeNotices);
};
export default {

5
mock/route.js Normal file
View File

@ -0,0 +1,5 @@
export default {
'/api/auth_routes': {
'/form/advanced-form': { authority: ['admin', 'user'] },
},
};

View File

@ -4,7 +4,7 @@
"description": "An out-of-box UI solution for enterprise applications",
"private": true,
"scripts": {
"presite": "node ./scripts/generateMock.js && cd functions && npm install",
"presite": "cd functions && npm install",
"start": "cross-env APP_TYPE=site PORT=8888 umi dev",
"start:no-mock": "cross-env MOCK=none PORT=8888 umi dev",
"build": "umi build",
@ -27,8 +27,8 @@
"docker-prod:dev": "docker-compose -f ./docker/docker-compose.yml up",
"docker-prod:build": "docker-compose -f ./docker/docker-compose.yml build",
"docker-hub:build": "docker build -f Dockerfile.hub -t ant-design-pro ./",
"docker:tag": "docker tag ant-design-pro chenshuai2144/ant-design-pro",
"docker:push": "npm run docker-hub:build && npm run docker:tag && docker push chenshuai2144/ant-design-pro"
"docker:tag": "docker tag ant-design-pro antdesign/ant-design-pro",
"docker:push": "npm run docker-hub:build && npm run docker:tag && docker push antdesign/ant-design-pro"
},
"dependencies": {
"@antv/data-set": "^0.10.0",
@ -36,6 +36,7 @@
"antd": "^3.11.6",
"bizcharts": "^3.4.2",
"bizcharts-plugin-slider": "^2.1.1-beta.1",
"chalk": "^2.4.2",
"classnames": "^2.2.6",
"dva": "^2.4.0",
"enquire-js": "^0.2.1",
@ -83,6 +84,7 @@
"gh-pages": "^2.0.1",
"husky": "^1.2.0",
"jest-puppeteer": "^3.5.1",
"less": "^3.9.0",
"lint-staged": "^8.1.0",
"merge-umi-mock-data": "^0.0.3",
"mockjs": "^1.0.1-beta3",
@ -95,9 +97,9 @@
"tslint": "^5.10.0",
"tslint-config-prettier": "^1.10.0",
"tslint-react": "^3.6.0",
"umi": "^2.3.1",
"umi": "^2.4.2",
"umi-plugin-ga": "^1.1.3",
"umi-plugin-react": "^1.2.0"
"umi-plugin-react": "^1.3.4"
},
"optionalDependencies": {
"puppeteer": "^1.10.0"

View File

@ -6,43 +6,45 @@
*----------*****--------------
*/
const glob = require('glob');
const prettier = require('prettier');
const fs = require('fs');
const chalk = require('chalk');
const prettierConfigPath = require.resolve('../.prettierrc');
const files = process.argv.slice(2);
let didError = false;
let didWarn = false;
files.forEach(file => {
const options = prettier.resolveConfig.sync(file, {
config: prettierConfigPath,
});
try {
const fileInfo = prettier.getFileInfo.sync(file);
if (fileInfo.ignored) {
return;
}
const input = fs.readFileSync(file, 'utf8');
const withParserOptions = {
...options,
parser: fileInfo.inferredParser,
};
const isPrettier = prettier.check(input, withParserOptions);
if (!isPrettier) {
console.log(
`\x1b[31m ${file} is no prettier, please use npm run prettier and git add !\x1b[0m`
);
didWarn = true;
}
} catch (e) {
didError = true;
}
Promise.all([
prettier.resolveConfig(file, {
config: prettierConfigPath,
}),
prettier.getFileInfo(file),
])
.then(resolves => {
const [options, fileInfo] = resolves;
if (fileInfo.ignored) {
return;
}
const input = fs.readFileSync(file, 'utf8');
const withParserOptions = {
...options,
parser: fileInfo.inferredParser,
};
const output = prettier.format(input, withParserOptions);
if (output !== input) {
fs.writeFileSync(file, output, 'utf8');
console.log(chalk.green(`${file} is prettier`));
}
})
.catch(e => {
didError = true;
})
.finally(() => {
if (didError) {
process.exit(1);
}
console.log(chalk.hex('#1890FF')('prettier success!'));
});
});
if (didWarn || didError) {
process.exit(1);
}
console.log('\x1b[32m lint prettier success!\x1b[0m');

View File

@ -6,11 +6,11 @@
*----------*****--------------
*/
const glob = require('glob');
const prettier = require('prettier');
const fs = require('fs');
const getPrettierFiles = require('./getPrettierFiles');
const prettierConfigPath = require.resolve('../.prettierrc');
const chalk = require('chalk');
let didError = false;
@ -33,7 +33,7 @@ files.forEach(file => {
const output = prettier.format(input, withParserOptions);
if (output !== input) {
fs.writeFileSync(file, output, 'utf8');
console.log(`\x1b[34m ${file} is prettier`);
console.log(chalk.green(`${file} is prettier`));
}
} catch (e) {
didError = true;
@ -43,4 +43,4 @@ files.forEach(file => {
if (didError) {
process.exit(1);
}
console.log('\x1b[32m prettier success!');
console.log(chalk.hex('#1890FF')('prettier success!'));

View File

@ -24,7 +24,11 @@ startServer.on('exit', () => {
console.log('Starting development server for e2e tests...');
startServer.stdout.on('data', data => {
console.log(data.toString());
if (!once && data.toString().indexOf('Compiled successfully') >= 0) {
// hack code , wait umi
if (
(!once && data.toString().indexOf('Compiled successfully') >= 0) ||
data.toString().indexOf('Theme generated successfully') >= 0
) {
// eslint-disable-next-line
once = true;
console.log('Development server is started, ready to run tests.');