未验证 提交 b9b88595 编写于 作者: 陈帅 提交者: GitHub

support Access-Control-Allow-Headers (#4576)

* support Access-Control-Allow-Headers

* add magic code

* add magic code

* add magic!!!! code

* try auto publish

* new plugin

* update script

* use fetch-blocks package

* add env

* add fetch-blocks package

* fix ci error

* fix lint error

* delete extra comments

* use https://ant-design-pro.netlify.com
上级 49e1da77
......@@ -36,3 +36,5 @@ functions/*
screenshot
.firebase
.eslintcache
build
test.preview.pro.ant.design
\ No newline at end of file
......@@ -67,6 +67,12 @@ if (isAntDesignProPreview) {
code: 'UA-72788897-6',
},
]);
plugins.push([
'umi-plugin-pro',
{
serverUrl: 'https://ant-design-pro.netlify.com',
},
]);
}
export default {
......
......@@ -25,21 +25,11 @@ function getModulePackageName(module: { context: string }) {
}
export default (config: any) => {
// preview.pro.ant.design only do not use in your production ; preview.pro.ant.design 专用环境变量,请不要在你的项目中使用它。
// preview.pro.ant.design only do not use in your production;
if (
process.env.ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION === 'site' ||
process.env.NODE_ENV !== 'production'
) {
// 将所有 less 合并为一个供 themePlugin使用
// const outFile = path.join(__dirname, '../.temp/ant-design-pro.less');
// const stylesDir = path.join(__dirname, '../src/');
// config.plugin('merge-less').use(MergeLessPlugin, [
// {
// stylesDir,
// outFile,
// },
// ]);
config.plugin('webpack-theme-color-replacer').use(ThemeColorReplacer, [
{
fileName: 'css/theme-colors.css',
......@@ -59,21 +49,12 @@ export default (config: any) => {
},
},
]);
// config.plugin('ant-design-theme').use(AntDesignThemePlugin, [
// {
// antDir: path.join(__dirname, '../node_modules/antd'),
// stylesDir,
// varFile: path.join(__dirname, '../node_modules/antd/es/style/themes/default.less'),
// mainLessFile: outFile, // themeVariables: ['@primary-color'],
// indexFileName: 'index.html',
// generateOne: true,
// lessUrl: 'https://gw.alipayobjects.com/os/lib/less.js/3.8.1/less.min.js',
// },
// ]);
}
// optimize chunks
config.optimization
.runtimeChunk(false) // share the same chunks across different modules
// share the same chunks across different modules
.runtimeChunk(false)
.splitChunks({
chunks: 'async',
name: 'vendors',
......
......@@ -6,6 +6,21 @@ const matchMock = require('./mock/matchMock');
const app = express();
app.all('*', (req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header(
'Access-Control-Allow-Headers',
'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild',
);
res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
if (req.method == 'OPTIONS') {
res.send(200);
} else {
next();
}
});
app.use(matchMock);
exports.handler = serverLess(app);
......@@ -6,6 +6,7 @@
"scripts": {
"analyze": "cross-env ANALYZE=1 umi build",
"build": "umi build",
"deploy": "cross-env ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION=site npm run site && npm run gh-pages",
"docker-hub:build": "docker build -f Dockerfile.hub -t ant-design-pro ./",
"docker-prod:build": "docker-compose -f ./docker/docker-compose.yml build",
"docker-prod:dev": "docker-compose -f ./docker/docker-compose.yml up",
......@@ -13,10 +14,11 @@
"docker:dev": "docker-compose -f ./docker/docker-compose.dev.yml up",
"docker:push": "npm run docker-hub:build && npm run docker:tag && docker push antdesign/ant-design-pro",
"docker:tag": "docker tag ant-design-pro antdesign/ant-design-pro",
"fetch:blocks": "node ./scripts/fetch-blocks.js",
"fetch:blocks": "fetch-blocks",
"format-imports": "import-sort --write '**/*.{js,jsx,ts,tsx}'",
"functions:build": "netlify-lambda build ./lambda",
"functions:run": "cross-env NODE_ENV=dev netlify-lambda serve ./lambda",
"gh-pages": "cp CNAME ./dist/ && gh-pages -d dist",
"lint": "npm run lint:js && npm run lint:style && npm run lint:prettier",
"lint-staged": "lint-staged",
"lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ",
......@@ -25,7 +27,7 @@
"lint:prettier": "check-prettier lint",
"lint:style": "stylelint --fix \"src/**/*.less\" --syntax less",
"prettier": "prettier -c --write **/*",
"site": "npm run fetch:blocks && npm run functions:build && umi build",
"site": "npm run fetch:blocks && npm run build && npm run functions:build",
"start": "umi dev",
"start:no-mock": "cross-env MOCK=none umi dev",
"test": "umi test",
......@@ -75,7 +77,6 @@
"react-media-hook2": "^1.0.5",
"redux": "^4.0.1",
"umi": "^2.7.2",
"umi-plugin-ga": "^1.1.3",
"umi-plugin-pro-block": "^1.3.2",
"umi-plugin-react": "^1.8.2",
"umi-request": "^1.0.7"
......@@ -99,6 +100,7 @@
"enzyme": "^3.9.0",
"eslint": "^5.16.0",
"express": "^4.17.1",
"fetch-blocks": "^1.0.0",
"gh-pages": "^2.0.1",
"husky": "^2.3.0",
"import-sort-cli": "^6.0.0",
......@@ -115,6 +117,8 @@
"serverless-http": "^2.0.2",
"slash2": "^2.0.0",
"stylelint": "^10.1.0",
"umi-plugin-ga": "^1.1.3",
"umi-plugin-pro": "^1.0.2",
"umi-types": "^0.3.8",
"webpack-theme-color-replacer": "^1.1.5"
},
......
const path = require('path');
const fs = require('fs');
const fetch = require('node-fetch');
const exec = require('child_process').exec;
const getNewRouteCode = require('./repalceRouter');
const router = require('./router.config');
const chalk = require('chalk');
const insertCode = require('./insertCode');
const fetchGithubFiles = async () => {
const ignoreFile = ['_scripts'];
const data = await fetch(`https://api.github.com/repos/ant-design/pro-blocks/git/trees/master`);
if (data.status !== 200) {
return;
}
const { tree } = await data.json();
const files = tree.filter(file => file.type === 'tree' && !ignoreFile.includes(file.path));
return Promise.resolve(files);
};
const relativePath = path.join(__dirname, '../config/config.ts');
const findAllInstallRouter = router => {
let routers = [];
router.forEach(item => {
if (item.component && item.path) {
if (item.path !== '/user' || item.path !== '/') {
routers.push({
...item,
routes: !!item.routes,
});
}
}
if (item.routes) {
routers = routers.concat(findAllInstallRouter(item.routes));
}
});
return routers;
};
const filterParentRouter = (router, layout) => {
return [...router]
.map(item => {
if (!item.path && item.component === '404') {
return item;
}
if (item.routes && (!router.component || layout)) {
return { ...item, routes: filterParentRouter(item.routes, false) };
}
if (item.redirect) {
return item;
}
return null;
})
.filter(item => item);
};
const firstUpperCase = pathString => {
return pathString
.replace('.', '')
.split(/\/|\-/)
.map(s => s.toLowerCase().replace(/( |^)[a-z]/g, L => L.toUpperCase()))
.filter(s => s)
.join('');
};
const execCmd = shell => {
return new Promise((resolve, reject) => {
exec(shell, { encoding: 'utf8' }, (error, statusbar) => {
if (error) {
console.log(error);
return reject(error);
}
console.log(statusbar);
resolve();
});
});
};
// replace router config
const parentRouter = filterParentRouter(router, true);
const { routesPath, code } = getNewRouteCode(relativePath, parentRouter);
// write ParentRouter
fs.writeFileSync(routesPath, code);
const installBlock = async () => {
let gitFiles = await fetchGithubFiles();
const installRouters = findAllInstallRouter(router);
const installBlockIteration = async i => {
const item = installRouters[i];
if (!item || !item.path) {
return Promise.resolve();
}
const gitPath = firstUpperCase(item.path);
// 如果这个区块在 git 上存在
if (gitFiles.find(file => file.path === gitPath)) {
console.log('install ' + chalk.green(item.name) + ' to: ' + chalk.yellow(item.path));
gitFiles = gitFiles.filter(file => file.path !== gitPath);
const skipModifyRouter = item.routes ? '--skip-modify-routes' : '';
const cmd = `umi block add https://github.com/ant-design/pro-blocks/tree/master/${gitPath} --path=${item.path} ${skipModifyRouter}`;
try {
await execCmd(cmd);
console.log(`install ${chalk.hex('#1890ff')(item.name)} success`);
} catch (error) {
console.error(error);
}
}
return installBlockIteration(i + 1);
};
// 安装路由中设置的区块
await installBlockIteration(0);
const installGitFile = async i => {
const item = gitFiles[i];
if (!item || !item.path) {
return Promise.resolve();
}
console.log('install ' + chalk.green(item.path));
const cmd = `umi block add https://github.com/ant-design/pro-blocks/tree/master/${item.path}`;
await execCmd(cmd);
return installBlockIteration(1);
};
// 安装 router 中没有的剩余区块.
installGitFile(0);
};
installBlock().then(() => {
// 插入 pro 需要的演示代码
insertCode();
});
const parser = require('@babel/parser');
const traverse = require('@babel/traverse');
const generate = require('@babel/generator');
const t = require('@babel/types');
const fs = require('fs');
const path = require('path');
const prettier = require('prettier');
const chalk = require('chalk');
const parseCode = code => {
return parser.parse(code, {
sourceType: 'module',
plugins: ['typescript', 'jsx'],
}).program.body[0];
};
/**
* 生成代码
* @param {*} ast
*/
function generateCode(ast) {
const newCode = generate.default(ast, {}).code;
return prettier.format(newCode, {
// format same as ant-design-pro
singleQuote: true,
trailingComma: 'es5',
printWidth: 100,
parser: 'typescript',
});
}
const SettingCodeString = `
<SettingDrawer
settings={settings}
onSettingChange={config =>
dispatch({
type: 'settings/changeSetting',
payload: config,
})
}
/>
`;
const mapAst = (configPath, callBack) => {
const ast = parser.parse(fs.readFileSync(configPath, 'utf-8'), {
sourceType: 'module',
plugins: ['typescript', 'jsx'],
});
// 查询当前配置文件是否导出 routes 属性
traverse.default(ast, {
Program({ node }) {
const { body } = node;
callBack(body);
},
});
return generateCode(ast);
};
const insertBasicLayout = configPath => {
return mapAst(configPath, body => {
const index = body.findIndex(item => {
return item.type !== 'ImportDeclaration';
});
body.forEach(item => {
// 从包中导出 SettingDrawer
if (item.type === 'ImportDeclaration') {
if (item.source.value === '@ant-design/pro-layout') {
item.specifiers.push(parseCode(`SettingDrawer`).expression);
}
}
if (item.type === 'VariableDeclaration') {
const {
id,
init: { body },
} = item.declarations[0];
// 给 BasicLayout 中插入 button 和 设置抽屉
if (id.name === `BasicLayout`) {
body.body.forEach(node => {
if (node.type === 'ReturnStatement') {
const JSXFragment = parseCode(`<></>`).expression;
JSXFragment.children.push({ ...node.argument });
JSXFragment.children.push(parseCode(SettingCodeString).expression);
node.argument = JSXFragment;
}
});
}
}
});
});
};
const insertBlankLayout = configPath => {
return mapAst(configPath, body => {
const index = body.findIndex(item => {
return item.type !== 'ImportDeclaration';
});
// 从组件中导入 CopyBlock
body.splice(
index,
0,
parseCode(`import CopyBlock from '@/components/CopyBlock';
`),
);
body.forEach(item => {
if (item.type === 'VariableDeclaration') {
const { id, init } = item.declarations[0];
// 给 BasicLayout 中插入 button 和 设置抽屉
if (id.name === `Layout`) {
const JSXFragment = parseCode(`<></>`).expression;
JSXFragment.children.push({ ...init.body });
JSXFragment.children.push(parseCode(` <CopyBlock id={Date.now()}/>`).expression);
init.body = JSXFragment;
}
}
});
});
};
const insertRightContent = configPath => {
return mapAst(configPath, body => {
const index = body.findIndex(item => {
return item.type !== 'ImportDeclaration';
});
// 从组件中导入 CopyBlock
body.splice(index, 0, parseCode(`import NoticeIconView from './NoticeIconView';`));
body.forEach(item => {
if (item.type === 'VariableDeclaration') {
const classBody = item.declarations[0].init.body;
classBody.body.forEach(node => {
if (node.type === 'ReturnStatement') {
const index = node.argument.children.findIndex(item => {
if (item.type === 'JSXElement') {
if (item.openingElement.name.name === 'Avatar') {
return true;
}
}
});
node.argument.children.splice(index, 1, parseCode(`<Avatar menu />`).expression);
node.argument.children.splice(index, 0, parseCode(`<NoticeIconView />`).expression);
}
});
}
});
});
};
module.exports = () => {
const basicLayoutPath = path.join(__dirname, '../src/layouts/BasicLayout.tsx');
fs.writeFileSync(basicLayoutPath, insertBasicLayout(basicLayoutPath));
console.log(`insert ${chalk.hex('#1890ff')('BasicLayout')} success`);
const rightContentPath = path.join(__dirname, '../src/components/GlobalHeader/RightContent.tsx');
fs.writeFileSync(rightContentPath, insertRightContent(rightContentPath));
console.log(`insert ${chalk.hex('#1890ff')('RightContent')} success`);
const blankLayoutPath = path.join(__dirname, '../src/layouts/BlankLayout.tsx');
fs.writeFileSync(blankLayoutPath, insertBlankLayout(blankLayoutPath));
console.log(`insert ${chalk.hex('#1890ff')('blankLayoutPath')} success`);
};
const parser = require('@babel/parser');
const traverse = require('@babel/traverse');
const generate = require('@babel/generator');
const t = require('@babel/types');
const fs = require('fs');
const prettier = require('prettier');
const getNewRouteCode = (configPath, newRoute) => {
const ast = parser.parse(fs.readFileSync(configPath, 'utf-8'), {
sourceType: 'module',
plugins: ['typescript'],
});
let routesNode = null;
const importModules = [];
// 查询当前配置文件是否导出 routes 属性
traverse.default(ast, {
Program({ node }) {
// find import
const { body } = node;
body.forEach(item => {
if (t.isImportDeclaration(item)) {
const { specifiers } = item;
const defaultEpecifier = specifiers.find(s => {
return t.isImportDefaultSpecifier(s) && t.isIdentifier(s.local);
});
if (defaultEpecifier && t.isStringLiteral(item.source)) {
importModules.push({
identifierName: defaultEpecifier.local.name,
modulePath: item.source.value,
});
}
}
});
},
ObjectExpression({ node, parent }) {
// find routes on object, like { routes: [] }
if (t.isArrayExpression(parent)) {
// children routes
return;
}
const { properties } = node;
properties.forEach(p => {
const { key, value } = p;
if (t.isObjectProperty(p) && t.isIdentifier(key) && key.name === 'routes') {
if (value) {
// find json file program expression
(p.value = parser.parse(JSON.stringify(newRoute)).program.body[0].expression),
(routesNode = value);
}
}
});
},
});
if (routesNode) {
const code = generateCode(ast);
return { code, routesPath: configPath };
} else {
throw new Error('route array config not found.');
}
};
/**
* 生成代码
* @param {*} ast
*/
function generateCode(ast) {
const newCode = generate.default(ast, {}).code;
return prettier.format(newCode, {
// format same as ant-design-pro
singleQuote: true,
trailingComma: 'es5',
printWidth: 100,
parser: 'typescript',
});
}
module.exports = getNewRouteCode;
module.exports = [
{
path: '/',
component: '../layouts/BlankLayout',
routes: [
// user
{
path: '/user',
component: '../layouts/UserLayout',
routes: [
{ path: '/user/login', name: 'login', component: './User/Login' },
{ path: '/user/register', name: 'register', component: './User/Register' },
{
path: '/user/register-result',
name: 'register.result',
component: './User/RegisterResult',
},
{ path: '/user', redirect: '/user/login' },
{
component: '404',
},
],
},
// app
{
path: '/',
component: '../layouts/BasicLayout',
Routes: ['src/pages/Authorized'],
authority: ['admin', 'user'],
routes: [
// dashboard
{
path: '/dashboard',
name: 'dashboard',
icon: 'dashboard',
routes: [
{
path: '/dashboard/analysis',
name: 'analysis',
component: './Dashboard/Analysis',
},
{
path: '/dashboard/monitor',
name: 'monitor',
component: './Dashboard/Monitor',
},
{
path: '/dashboard/workplace',
name: 'workplace',
component: './Dashboard/Workplace',
},
],
},
// forms
{
path: '/form',
icon: 'form',
name: 'form',
routes: [
{
path: '/form/basic-form',
name: 'basicform',
component: './Form/BasicForm',
},
{
path: '/form/step-form',
name: 'stepform',
component: './Form/StepForm',
},
{
path: '/form/advanced-form',
name: 'advancedform',
authority: ['admin'],
component: './Form/AdvancedForm',
},
],
},
// list
{
path: '/list',
icon: 'table',
name: 'list',
routes: [
{
path: '/list/table-list',
name: 'searchtable',
component: './list/Tablelist',
},
{
path: '/list/basic-list',
name: 'basiclist',
component: './list/Basiclist',
},
{
path: '/list/card-list',
name: 'cardlist',
component: './list/Cardlist',
},
{
path: '/list/search',
name: 'search-list',
component: './list/search',
routes: [
{
path: '/list/search/articles',
name: 'articles',
component: './list/Articles',
},
{
path: '/list/search/projects',
name: 'projects',
component: './list/Projects',
},
{
path: '/list/search/applications',
name: 'applications',
component: './list/Applications',
},
{
path: '/list/search',
redirect: '/list/search/articles',
},
],
},
],
},
{
path: '/profile',
name: 'profile',
icon: 'profile',
routes: [
// profile
{
path: '/profile/basic',
name: 'basic',
component: './Profile/BasicProfile',
},
{
path: '/profile/basic/:id',
hideInMenu: true,
component: './Profile/BasicProfile',
},
{
path: '/profile/advanced',
name: 'advanced',
authority: ['admin'],
component: './Profile/AdvancedProfile',
},
],
},
{
name: 'result',
icon: 'check-circle-o',
path: '/result',
routes: [
// result
{
path: '/result/success',
name: 'success',
component: './Result/Success',
},
{ path: '/result/fail', name: 'fail', component: './Result/Error' },
],
},
{
name: 'exception',
icon: 'warning',
path: '/exception',
routes: [
// exception
{
path: '/exception/403',
name: 'not-permission',
component: './Exception/403',
},
{
path: '/exception/404',
name: 'not-find',
component: './Exception/404',
},
{
path: '/exception/500',
name: 'server-error',
component: './Exception/500',
},
],
},
{
name: 'account',
icon: 'user',
path: '/account',
routes: [
{
path: '/account/center',
name: 'center',
component: './Account/Center/Center',
},
{
path: '/account/settings',
name: 'settings',
component: './Account/Settings/Info',
},
],
},
// editor
{
name: 'editor',
icon: 'highlight',
path: '/editor',
routes: [
{
path: '/editor/flow',
name: 'flow',
component: './Editor/GGEditor/Flow',
},
{
path: '/editor/mind',
name: 'mind',
component: './Editor/GGEditor/Mind',
},
{
path: '/editor/koni',
name: 'koni',
component: './Editor/GGEditor/Koni',
},
],
},
{ path: '/', redirect: '/dashboard/analysis', authority: ['admin', 'user'] },
{
component: '404',
},
],
},
],
},
];
......@@ -12,7 +12,7 @@ export default {
const colorPalettes = generate(color);
return lightens.concat(colorPalettes);
},
changeColor(newColor: string) {
changeColor(newColor?: string) {
const lastColor = this.lastColor || this.primaryColor;
const options = {
// hash模式下用相对路径
......
......@@ -20,7 +20,7 @@ import { formatMessage } from 'umi-plugin-react/locale';
import { isAntDesignPro } from '@/utils/utils';
import logo from '../assets/logo.svg';
export interface BasicLayoutProps extends ProLayoutProps, ConnectProps {
export interface BasicLayoutProps extends ProLayoutProps, Omit<ConnectProps, 'location'> {
breadcrumbNameMap: {
[path: string]: MenuDataItem;
};
......
......@@ -26,7 +26,7 @@ const UserLayout: React.SFC<UserLayoutProps> = props => {
pathname: '',
},
} = props;
const { breadcrumb } = getMenuData(routes, props);
const { breadcrumb } = getMenuData(routes);
return (
<DocumentTitle
......
......@@ -49,7 +49,6 @@ export interface Route extends MenuDataItem {
/**
* @type T: Params matched in dynamic routing
*/
export interface ConnectProps<T extends { [key: string]: any } = {}>
extends Partial<RouterTypes<Route, T>> {
export interface ConnectProps<T = {}> extends Partial<RouterTypes<Route, T>> {
dispatch?: Dispatch;
}
......@@ -4,7 +4,9 @@ import { parse, stringify } from 'qs';
import { EffectsCommandMap } from 'dva';
import { routerRedux } from 'dva/router';
export function getPageQuery(): string {
export function getPageQuery(): {
[key: string]: string;
} {
return parse(window.location.href.split('?')[1]);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册