Browse Source
* 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.compull/4622/head
committed by
GitHub
15 changed files with 42 additions and 636 deletions
@ -0,0 +1 @@ |
|||||
|
test.preview.pro.ant.design |
||||
@ -1,130 +0,0 @@ |
|||||
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(); |
|
||||
}); |
|
||||
@ -1,161 +0,0 @@ |
|||||
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`); |
|
||||
}; |
|
||||
@ -1,77 +0,0 @@ |
|||||
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; |
|
||||
@ -1,236 +0,0 @@ |
|||||
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', |
|
||||
}, |
|
||||
], |
|
||||
}, |
|
||||
], |
|
||||
}, |
|
||||
]; |
|
||||
Loading…
Reference in new issue