250 lines
6.4 KiB
JavaScript
250 lines
6.4 KiB
JavaScript
import { readFileSync, writeFileSync } from 'fs';
|
||
import { dirname, resolve } from 'path';
|
||
import { fileURLToPath } from 'url';
|
||
|
||
const __filename = fileURLToPath(import.meta.url);
|
||
const __dirname = dirname(__filename);
|
||
|
||
/**
|
||
* 按特殊性排序路由(最具体的在前)
|
||
* 规则:
|
||
* 1. 路径段数多的优先级高
|
||
* 2. 静态段优先于参数段
|
||
* 3. 必需参数优先于可选参数
|
||
*/
|
||
function sortRoutesBySpecificity(routes) {
|
||
return routes.sort((a, b) => {
|
||
const aParts = a.split('/').filter(Boolean);
|
||
const bParts = b.split('/').filter(Boolean);
|
||
|
||
// 首先按路径段数排序(段数多的在前)
|
||
if (aParts.length !== bParts.length) {
|
||
return bParts.length - aParts.length;
|
||
}
|
||
|
||
// 如果段数相同,比较每个段
|
||
for (let i = 0; i < aParts.length; i++) {
|
||
const aPart = aParts[i];
|
||
const bPart = bParts[i];
|
||
|
||
// 静态段优先于参数段
|
||
const aIsStatic = !aPart.startsWith(':');
|
||
const bIsStatic = !bPart.startsWith(':');
|
||
|
||
if (aIsStatic && !bIsStatic) return -1;
|
||
if (!aIsStatic && bIsStatic) return 1;
|
||
|
||
// 如果都是参数,必需参数优先于可选参数
|
||
if (!aIsStatic && !bIsStatic) {
|
||
const aIsOptional = aPart.endsWith('?');
|
||
const bIsOptional = bPart.endsWith('?');
|
||
if (!aIsOptional && bIsOptional) return -1;
|
||
if (aIsOptional && !bIsOptional) return 1;
|
||
}
|
||
|
||
// 如果都是静态段,按字母顺序(保持稳定性)
|
||
if (aIsStatic && bIsStatic) {
|
||
if (aPart !== bPart) {
|
||
return aPart.localeCompare(bPart);
|
||
}
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 构建完整路径
|
||
*/
|
||
function buildFullPath(path, parentPath) {
|
||
if (path === '/') {
|
||
return parentPath || '';
|
||
} else if (path.startsWith('/')) {
|
||
return path;
|
||
} else {
|
||
if (!parentPath || parentPath === '/') {
|
||
return `/${path}`;
|
||
} else {
|
||
return `${parentPath}/${path}`;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 规范化路径
|
||
*/
|
||
function normalizePath(path) {
|
||
if (!path || path === '/') return path;
|
||
return path.endsWith('/') ? path.slice(0, -1) : path;
|
||
}
|
||
|
||
/**
|
||
* 解析路由对象,返回路径和子路由内容
|
||
*/
|
||
function parseRouteObject(objContent, parentPath = '') {
|
||
const routes = [];
|
||
|
||
// 提取 path
|
||
const pathMatch = objContent.match(/path:\s*['"`]([^'"`]+)['"`]/);
|
||
if (!pathMatch) return routes;
|
||
|
||
const path = pathMatch[1];
|
||
const fullPath = normalizePath(buildFullPath(path, parentPath));
|
||
|
||
// 如果路径不为空且不是根路径,添加到列表
|
||
if (fullPath && fullPath !== '/') {
|
||
routes.push(fullPath);
|
||
}
|
||
|
||
// 检查是否有 children,使用括号计数来正确匹配嵌套数组
|
||
const childrenIndex = objContent.indexOf('children:');
|
||
if (childrenIndex !== -1) {
|
||
// 找到 children: 后面的 [
|
||
let bracketStart = childrenIndex;
|
||
while (
|
||
bracketStart < objContent.length &&
|
||
objContent[bracketStart] !== '['
|
||
) {
|
||
bracketStart++;
|
||
}
|
||
|
||
if (bracketStart < objContent.length) {
|
||
// 使用括号计数找到匹配的 ]
|
||
let bracketCount = 1;
|
||
let bracketEnd = bracketStart + 1;
|
||
|
||
for (let i = bracketStart + 1; i < objContent.length; i++) {
|
||
if (objContent[i] === '[') bracketCount++;
|
||
if (objContent[i] === ']') {
|
||
bracketCount--;
|
||
if (bracketCount === 0) {
|
||
bracketEnd = i;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 提取 children 数组内容(不包括外层的 [])
|
||
const childrenContent = objContent.substring(
|
||
bracketStart + 1,
|
||
bracketEnd,
|
||
);
|
||
|
||
// 分割 children 数组中的各个对象
|
||
const childObjects = [];
|
||
let depth = 0;
|
||
let start = 0;
|
||
|
||
for (let i = 0; i < childrenContent.length; i++) {
|
||
if (childrenContent[i] === '{') {
|
||
if (depth === 0) start = i;
|
||
depth++;
|
||
} else if (childrenContent[i] === '}') {
|
||
depth--;
|
||
if (depth === 0) {
|
||
const childObj = childrenContent.substring(start, i + 1);
|
||
childObjects.push(childObj);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 递归处理每个子路由对象
|
||
for (const childObj of childObjects) {
|
||
const childRoutes = parseRouteObject(childObj, fullPath);
|
||
routes.push(...childRoutes);
|
||
}
|
||
}
|
||
}
|
||
|
||
return routes;
|
||
}
|
||
|
||
/**
|
||
* 解析路由配置
|
||
*/
|
||
function parseRoutes(content) {
|
||
const routes = [];
|
||
|
||
// 分割顶层路由数组中的各个对象
|
||
const topLevelObjects = [];
|
||
let depth = 0;
|
||
let start = 0;
|
||
|
||
for (let i = 0; i < content.length; i++) {
|
||
if (content[i] === '{') {
|
||
if (depth === 0) start = i;
|
||
depth++;
|
||
} else if (content[i] === '}') {
|
||
depth--;
|
||
if (depth === 0) {
|
||
const obj = content.substring(start, i + 1);
|
||
topLevelObjects.push(obj);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 处理每个顶层路由对象
|
||
for (const obj of topLevelObjects) {
|
||
const objRoutes = parseRouteObject(obj);
|
||
routes.push(...objRoutes);
|
||
}
|
||
|
||
return routes;
|
||
}
|
||
|
||
/**
|
||
* 更新 index.html 中的路由列表
|
||
*/
|
||
function updateIndexHtml() {
|
||
const routerPath = resolve(__dirname, '../src/router.tsx');
|
||
const indexPath = resolve(__dirname, '../index.html');
|
||
|
||
// 读取 router.tsx 文件
|
||
const routerContent = readFileSync(routerPath, 'utf-8');
|
||
|
||
// 提取 router 数组的内容
|
||
const routerMatch = routerContent.match(
|
||
/const\s+router\s*=\s*\[([\s\S]*?)\];/,
|
||
);
|
||
if (!routerMatch) {
|
||
throw new Error('无法在 router.tsx 中找到 router 配置');
|
||
}
|
||
|
||
// 解析路由配置
|
||
const extractedRoutes = parseRoutes(routerMatch[1]);
|
||
|
||
// 去重并排序
|
||
const uniqueRoutes = [...new Set(extractedRoutes)];
|
||
const sortedRoutes = sortRoutesBySpecificity(uniqueRoutes);
|
||
|
||
// 读取 index.html
|
||
const htmlContent = readFileSync(indexPath, 'utf-8');
|
||
|
||
// 生成新的路由数组字符串
|
||
const routesString = sortedRoutes
|
||
.map(route => ` '${route}'`)
|
||
.join(',\n');
|
||
|
||
// 替换路由数组(匹配 var routes = [...] 部分)
|
||
const updatedContent = htmlContent.replace(
|
||
/var routes = \[[\s\S]*?\];/,
|
||
`var routes = [\n${routesString},\n ];`,
|
||
);
|
||
|
||
// 写回文件
|
||
writeFileSync(indexPath, updatedContent, 'utf-8');
|
||
|
||
console.log('✅ 路由列表已更新:');
|
||
sortedRoutes.forEach(route => console.log(` ${route}`));
|
||
}
|
||
|
||
// 执行更新
|
||
try {
|
||
updateIndexHtml();
|
||
} catch (error) {
|
||
console.error('❌ 更新路由列表失败:', error.message);
|
||
console.error(error.stack);
|
||
process.exit(1);
|
||
}
|