124 lines
3.8 KiB
JavaScript
124 lines
3.8 KiB
JavaScript
import fs from 'node:fs'
|
||
import path from 'node:path'
|
||
|
||
const workspaceRoot = process.cwd()
|
||
const referenceRoot = path.resolve(workspaceRoot, '..', 'Jiapu-App')
|
||
const referencePagesPath = path.join(referenceRoot, 'pages.json')
|
||
const parityDocumentPath = path.join(
|
||
workspaceRoot,
|
||
'docs',
|
||
'frontend-reference-parity-2026-08-17.md'
|
||
)
|
||
|
||
const stripJsonLineComments = (source) =>
|
||
source
|
||
.split(/\r?\n/u)
|
||
.map((line) => {
|
||
let inString = false
|
||
let escaped = false
|
||
for (let index = 0; index < line.length - 1; index += 1) {
|
||
const character = line[index]
|
||
if (escaped) {
|
||
escaped = false
|
||
continue
|
||
}
|
||
if (character === '\\' && inString) {
|
||
escaped = true
|
||
continue
|
||
}
|
||
if (character === '"') {
|
||
inString = !inString
|
||
continue
|
||
}
|
||
if (!inString && character === '/' && line[index + 1] === '/') {
|
||
return line.slice(0, index)
|
||
}
|
||
}
|
||
return line
|
||
})
|
||
.join('\n')
|
||
|
||
const fail = (message) => {
|
||
console.error(`FRONTEND PARITY CHECK FAIL: ${message}`)
|
||
process.exit(1)
|
||
}
|
||
|
||
if (!fs.existsSync(referencePagesPath)) {
|
||
fail(`参考项目不存在:${referencePagesPath}`)
|
||
}
|
||
if (!fs.existsSync(parityDocumentPath)) {
|
||
fail(`对比总表不存在:${parityDocumentPath}`)
|
||
}
|
||
|
||
const referenceConfig = JSON.parse(
|
||
stripJsonLineComments(fs.readFileSync(referencePagesPath, 'utf8'))
|
||
)
|
||
const referenceRoutes = referenceConfig.pages.map(({ path: routePath }) => routePath)
|
||
if (referenceRoutes.length !== 78) {
|
||
fail(`参考项目活动路由应为 78 条,实际为 ${referenceRoutes.length} 条`)
|
||
}
|
||
if (new Set(referenceRoutes).size !== referenceRoutes.length) {
|
||
fail('参考项目 pages.json 存在重复活动路由')
|
||
}
|
||
|
||
for (const routePath of referenceRoutes) {
|
||
const vuePath = path.join(referenceRoot, `${routePath}.vue`)
|
||
const nvuePath = path.join(referenceRoot, `${routePath}.nvue`)
|
||
if (!fs.existsSync(vuePath) && !fs.existsSync(nvuePath)) {
|
||
fail(`参考路由没有 .vue 或 .nvue 页面文件:${routePath}`)
|
||
}
|
||
}
|
||
|
||
const parityDocument = fs.readFileSync(parityDocumentPath, 'utf8')
|
||
const documentedRouteRows = parityDocument
|
||
.split(/\r?\n/u)
|
||
.filter((line) => /^\| `[^`]+` \|/u.test(line))
|
||
.map((line) => line.split('|').slice(1, -1).map((column) => column.trim()))
|
||
const documentedRoutes = documentedRouteRows.map(([routeCell]) => routeCell.slice(1, -1))
|
||
const allowedStatuses = new Set([
|
||
'覆盖',
|
||
'合并覆盖',
|
||
'升级替代',
|
||
'非产品页',
|
||
'本轮补齐',
|
||
'部分覆盖',
|
||
'部分合并',
|
||
'部分升级',
|
||
'前端待后端'
|
||
])
|
||
|
||
for (const columns of documentedRouteRows) {
|
||
if (columns.length !== 5 || columns.some((column) => !column)) {
|
||
fail(`对比总表路由行字段不完整:${columns[0] || '未知路由'}`)
|
||
}
|
||
if (!allowedStatuses.has(columns[3])) {
|
||
fail(`对比总表路由状态无效:${columns[0]} -> ${columns[3]}`)
|
||
}
|
||
}
|
||
|
||
const documentedRouteCounts = new Map()
|
||
for (const routePath of documentedRoutes) {
|
||
documentedRouteCounts.set(
|
||
routePath,
|
||
(documentedRouteCounts.get(routePath) || 0) + 1
|
||
)
|
||
}
|
||
|
||
const missingRoutes = referenceRoutes.filter(
|
||
(routePath) => !documentedRouteCounts.has(routePath)
|
||
)
|
||
const duplicateRoutes = [...documentedRouteCounts.entries()]
|
||
.filter(([, count]) => count !== 1)
|
||
.map(([routePath]) => routePath)
|
||
const unknownRoutes = documentedRoutes.filter(
|
||
(routePath) => !referenceRoutes.includes(routePath)
|
||
)
|
||
|
||
if (missingRoutes.length) fail(`对比总表遗漏路由:${missingRoutes.join(', ')}`)
|
||
if (duplicateRoutes.length) fail(`对比总表重复路由:${duplicateRoutes.join(', ')}`)
|
||
if (unknownRoutes.length) fail(`对比总表包含未知路由:${unknownRoutes.join(', ')}`)
|
||
|
||
console.log(
|
||
`FRONTEND PARITY CHECK PASS referenceRoutes=${referenceRoutes.length} documentedRoutes=${documentedRoutes.length}`
|
||
)
|