/** * 验证 pages.json 的所有 page.style 块都已经有 navigationBarTitleText 字段, * 且都在 // #ifdef / // #ifndef 块之外 (保证所有平台生效). * * 运行: node scripts/verify-nav-title.js */ const fs = require('fs'); const path = require('path'); const PAGES_JSON = path.join(__dirname, '..', 'pages.json'); const s = fs.readFileSync(PAGES_JSON, 'utf-8'); const lines = s.split(/\r?\n/); let i = 0; let totalStyle = 0, totalNavTitle = 0, insideIfdef = 0, doubleTitle = 0, missingTitle = []; // 跟踪 subPackage 上下文, 让 missing-title 报出全路径 (root/path), 不只是 path let currentSubRoot = null; while (i < lines.length) { // 跟踪 subPackage 的 root, 找路径时回退可见 const rootMatch = lines[i].match(/^\s*"root"\s*:\s*"([^"]+)"/); if (rootMatch) { currentSubRoot = rootMatch[1]; i++; continue; } const m = lines[i].match(/^(\s*)"style"\s*:\s*\{\s*,?\s*$/); if (m) { totalStyle++; const blockStart = i; i++; const bodyStart = i; let depth = 1; while (i < lines.length && depth > 0) { const c = lines[i]; let b = 0; let inS = false, e = false; for (let k = 0; k < c.length; k++) { const ch = c[k]; if (inS) { if (e) e = false; else if (ch === '\\') e = true; else if (ch === '"') inS = false; } else { if (ch === '/' && c[k + 1] === '/') break; if (ch === '"') inS = true; else if (ch === '{') b++; else if (ch === '}') b--; } } depth += b; i++; } const body = lines.slice(bodyStart, i - 1); const titleIdxs = body .map((l, idx) => /^\s*"navigationBarTitleText"\s*:/.test(l) ? idx : -1) .filter(x => x >= 0); totalNavTitle += titleIdxs.length; if (titleIdxs.length > 1) doubleTitle++; // 找最近的 path 行, 若处于 subPackage 则补上 root 前缀, 报全路径 let pathLine = ''; for (let k = blockStart - 1; k >= 0; k--) { const pm = lines[k].match(/^\s*"path"\s*:\s*"([^"]+)"/); if (pm) { pathLine = pm[1]; break; } // 跨过 subPackage 边界 (遇到另一个 root) 视为顶层 pages const rm = lines[k].match(/^\s*"root"\s*:\s*"([^"]+)"/); if (rm) { currentSubRoot = rm[1]; break; } } if (titleIdxs.length === 0) { // 只在缺标题时, 用 currentSubRoot 校正路径 // (找 path 时可能已被同包兄弟 root 行先 break 出, 这里用循环外的 currentSubRoot) const fullPath = pathLine && currentSubRoot ? `${currentSubRoot}/${pathLine}` : pathLine; missingTitle.push(fullPath); } else { for (const tIdx of titleIdxs) { let d = 0; for (let k = 0; k < tIdx; k++) { const t = body[k]; if (/^\s*\/\/\s*#(ifdef|ifndef)\b/.test(t)) d++; else if (/^\s*\/\/\s*#endif\b/.test(t)) d--; } if (d >= 1) insideIfdef++; } } } else { i++; } } console.log('style blocks:', totalStyle); console.log('navigationBarTitleText occurrences:', totalNavTitle); console.log('inside-ifdef:', insideIfdef); console.log('double-title:', doubleTitle); console.log('missing-title:', missingTitle.length, missingTitle); const ok = totalStyle === totalNavTitle && insideIfdef === 0 && doubleTitle === 0 && missingTitle.length === 0; console.log(ok ? '✅ PASS' : '❌ FAIL'); process.exit(ok ? 0 : 1);