"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = writeNewRoute; exports.getNewRouteCode = getNewRouteCode; exports.writeRouteNode = writeRouteNode; function _react() { const data = _interopRequireDefault(require("react")); _react = function _react() { return data; }; return data; } function _fs() { const data = require("fs"); _fs = function _fs() { return data; }; return data; } function _path() { const data = require("path"); _path = function _path() { return data; }; return data; } function parser() { const data = _interopRequireWildcard(require("@babel/parser")); parser = function parser() { return data; }; return data; } function _traverse() { const data = _interopRequireDefault(require("@babel/traverse")); _traverse = function _traverse() { return data; }; return data; } function _generator() { const data = _interopRequireDefault(require("@babel/generator")); _generator = function _generator() { return data; }; return data; } function t() { const data = _interopRequireWildcard(require("@babel/types")); t = function t() { return data; }; return data; } function _umiUtils() { const data = require("umi-utils"); _umiUtils = function _umiUtils() { return data; }; return data; } function _prettier() { const data = _interopRequireDefault(require("prettier")); _prettier = function _prettier() { return data; }; return data; } function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const debug = require('debug')('umi-build-dev:writeNewRoute'); /** * 将路由写入路由文件 * @param {*} newRoute 新的路由配置: { path, component, ... } * @param {*} configPath 配置路径 * @param {*} absSrcPath 代码路径 */ function writeNewRoute(newRoute, configPath, absSrcPath) { const _getNewRouteCode = getNewRouteCode(configPath, newRoute, absSrcPath), code = _getNewRouteCode.code, routesPath = _getNewRouteCode.routesPath; (0, _fs().writeFileSync)(routesPath, code, 'utf-8'); } /** * 获取目标 * @param {*} configPath * @param {*} newRoute */ function getNewRouteCode(configPath, newRoute, absSrcPath) { debug(`find routes in configPath: ${configPath}`); const ast = parser().parse((0, _fs().readFileSync)(configPath, 'utf-8'), { sourceType: 'module', plugins: ['typescript'] }); let routesNode = null; const importModules = []; // 查询当前配置文件是否导出 routes 属性 (0, _traverse().default)(ast, { Program({ node }) { // find import const body = node.body; body.forEach(item => { if (t().isImportDeclaration(item)) { const specifiers = item.specifiers; 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 }); } } }); }, AssignmentExpression({ node }) { // for exports.routes const left = node.left, operator = node.operator, right = node.right; if (operator === '=' && t().isMemberExpression(left) && t().isIdentifier(left.object) && left.object.name === 'exports' && t().isIdentifier(left.property) && left.property.name === 'routes') { routesNode = right; } }, ExportDefaultDeclaration({ node }) { // export default [] const declaration = node.declaration; if (t().isArrayExpression(declaration)) { routesNode = declaration; } }, ObjectExpression({ node, parent }) { // find routes on object, like { routes: [] } if (t().isArrayExpression(parent)) { // children routes return; } const properties = node.properties; properties.forEach(p => { const key = p.key, value = p.value; if (t().isObjectProperty(p) && t().isIdentifier(key) && key.name === 'routes') { routesNode = value; } }); } }); if (routesNode) { // routes 配置不在当前文件, 需要 load 对应的文件 export default { routes: pageRoutes } case 1 if (!t().isArrayExpression(routesNode)) { const source = importModules.find(m => { return m.identifierName === routesNode.name; }); if (source) { const newConfigPath = getModulePath(configPath, source.modulePath, absSrcPath); return getNewRouteCode(newConfigPath, newRoute, absSrcPath); } else { throw new Error(`can not find import of ${routesNode.name}`); } } else { // 配置在当前文件 // export default { routes: [] } case 2 writeRouteNode(routesNode, newRoute); } } else { throw new Error('route array config not found.'); } const code = generateCode(ast); debug(code, configPath); return { code, routesPath: configPath }; } function getNewRouteNode(newRoute) { return parser().parse(`(${JSON.stringify(newRoute)})`).program.body[0].expression; } /** * 写入节点 * @param {*} node 找到的节点 * @param {*} newRoute 新的路由配置 */ function writeRouteNode(targetNode, newRoute, currentPath = '/') { debug(`writeRouteNode currentPath newRoute.path: ${newRoute.path} currentPath: ${currentPath}`); const elements = targetNode.elements; const paths = elements.map(ele => { if (!t().isObjectExpression(ele)) { return false; } const properties = ele.properties; const redirect = properties.find(p => { return p.key.name === 'redirect'; }); if (redirect) { return false; } const pathProp = properties.find(p => { return p.key.name === 'path'; }); if (!pathProp) { return currentPath; } let fullPath = pathProp.value.value; if (fullPath.indexOf('/') !== 0) { fullPath = (0, _path().join)(currentPath, fullPath); } return fullPath; }); debug('paths', paths); const matchedIndex = paths.findIndex(p => { return p && newRoute.path.indexOf((0, _umiUtils().winPath)(p)) === 0; }); const newNode = getNewRouteNode(newRoute); if (matchedIndex === -1) { elements.push(newNode); // return container for test return targetNode; } else { // matched, insert to children routes const matchedEle = elements[matchedIndex]; const routesProp = matchedEle.properties.find(p => { return p.key.name === 'routes' || process.env.BIGFISH_COMPAT && p.key.name === 'childRoutes'; }); if (!routesProp) { // not find children routes, insert before the matched element elements.splice(matchedIndex, 0, newNode); return targetNode; } return writeRouteNode(routesProp.value, newRoute, paths[matchedIndex]); } } /** * 生成代码 * @param {*} ast */ function generateCode(ast) { const newCode = (0, _generator().default)(ast, {}).code; return _prettier().default.format(newCode, { // format same as ant-design-pro singleQuote: true, trailingComma: 'es5', printWidth: 100, parser: 'typescript' }); } /** * 获取路由配置的真实路径 * @param {*} modulePath */ function getModulePath(configPath, modulePath, absSrcPath) { // like @/route.config if (/^@\//.test(modulePath)) { modulePath = (0, _path().join)(absSrcPath, modulePath.replace(/^@\//, '')); } else { modulePath = (0, _path().join)((0, _path().dirname)(configPath), modulePath); } if (!/\.[j|t]s$/.test(modulePath)) { if ((0, _fs().existsSync)(`${modulePath}.js`)) { modulePath = `${modulePath}.js`; } else { modulePath = `${modulePath}.ts`; } } return modulePath; }