import fs from 'node:fs' import path from 'node:path' import YAML from 'yaml' const workspace = process.cwd() const failures = [] const fail = (message) => failures.push(message) const readText = (filePath) => fs.readFileSync(path.join(workspace, filePath), 'utf8') const listFiles = (entryPaths, extensions) => { const files = [] const visit = (entryPath) => { const absolutePath = path.join(workspace, entryPath) if (!fs.existsSync(absolutePath)) return const stat = fs.statSync(absolutePath) if (stat.isDirectory()) { for (const name of fs.readdirSync(absolutePath)) { visit(path.join(entryPath, name)) } return } if (extensions.has(path.extname(entryPath))) files.push(entryPath) } entryPaths.forEach(visit) return files } const parseJson = (filePath) => { try { return JSON.parse(readText(filePath)) } catch (error) { fail(`${filePath} 不是有效 JSON:${error.message}`) return null } } const pagesConfig = parseJson('pages.json') parseJson('manifest.json') parseJson('package.json') parseJson('package-lock.json') const routeSource = readText('utils/navigation/routes.js') const routeEntries = [...routeSource.matchAll(/^\s{2}([A-Z]\d{2}): defineRoute\(\{[\s\S]*?^\s{2}\}\),/gm)] const routeKeys = routeEntries.map((match) => match[1]) const routePaths = routeEntries.map((match) => match[0].match(/path: "([^"]+)"/)?.[1]?.replace(/^\//, '')) const configuredPages = pagesConfig?.pages?.map((page) => page.path) || [] if (new Set(routeKeys).size !== routeKeys.length) fail('路由键存在重复') if (routePaths.some((routePath) => !routePath)) fail('存在没有静态 path 的路由') for (const pagePath of configuredPages) { if (!fs.existsSync(path.join(workspace, `${pagePath}.vue`))) fail(`页面文件缺失:${pagePath}.vue`) if (!routePaths.includes(pagePath)) fail(`页面没有路由语义:${pagePath}`) } for (const routePath of routePaths) { if (!configuredPages.includes(routePath)) fail(`路由没有在 pages.json 注册:${routePath}`) } const sourceFiles = listFiles( ['App.vue', 'main.js', 'pages', 'components', 'composables', 'services', 'utils'], new Set(['.js', '.vue']), ) const importPattern = /(?:from\s*|import\s*)["']([^"']+)["']/g for (const filePath of sourceFiles) { const source = readText(filePath) if (filePath.endsWith('.vue') && !/<(?:template|script)(?:\s|>)/.test(source)) { fail(`${filePath} 缺少