/** * 跨平台验证: 模拟 uni-app 预处理, 把 pages.json 在每个平台上 * (H5 / MP-WEIXIN / APP-PLUS / MP-HARMONY / etc.) 展开后, * 用标准 JSON.parse 验证其合法性. * * uni-app 预处理规则: * - // #ifdef PLATFORM ... // #endif: 当 PLATFORM 匹配当前平台, 保留块; 否则删除 (含注释行) * - // #ifndef PLATFORM ... // #endif: 当 PLATFORM 不匹配当前平台, 保留块; 否则删除 * - 处理嵌套 * * 运行: node scripts/verify-pages-json-cross-platform.js */ const fs = require('fs'); const path = require('path'); const PAGES_JSON = path.join(__dirname, '..', 'pages.json'); const raw = fs.readFileSync(PAGES_JSON, 'utf-8'); const lines = raw.split(/\r?\n/); const PLATFORMS = ['H5', 'MP-WEIXIN', 'APP-PLUS', 'MP-HARMONY', 'MP-ALIPAY', 'MP-BAIDU', 'MP-TOUTIAO', 'MP-QQ', 'MP-KUAISHOU', 'MP-JD', 'MP-360', 'MP-XHS', 'APP', 'APP-NVUE']; /** * 模拟预处理: 给定平台, 返回剥除 // #ifdef / // #ifndef 后的行数组. */ function preprocess(platform) { const out = []; const stack = []; // 每项 { mode: 'ifdef' | 'ifndef', platform, keep } for (const line of lines) { // 检测 // #ifdef PLAT / // #ifndef PLAT const openMatch = line.match(/^\s*\/\/\s*#(ifdef|ifndef)\s+([\w-]+)\b/); if (openMatch) { const mode = openMatch[1]; const plat = openMatch[2]; const keep = mode === 'ifdef' ? plat === platform : plat !== platform; stack.push({ mode, platform: plat, keep }); if (!keep) { // 这行也不输出 (块被剥除) continue; } out.push(line); continue; } const closeMatch = line.match(/^\s*\/\/\s*#endif\b/); if (closeMatch) { const top = stack.pop(); if (!top) { // 不平衡的 #endif, 仍输出 (避免破坏后续) out.push(line); continue; } if (!top.keep) continue; // 整块被剥除, // #endif 也不输出 out.push(line); continue; } // 普通行: 若当前栈顶 keep=false, 不输出 if (stack.length > 0 && !stack[stack.length - 1].keep) continue; out.push(line); } if (stack.length > 0) { console.warn(`[warn] platform=${platform}: 残留未关闭 #ifdef/#ifndef ${stack.length} 个`); } return out; } /** * 剥除 // 行注释, 返回纯 JSON 字符串. * 注意: 必须先做预处理把 // #ifdef 块剥除, 否则 // 会被当成 JSON 注释. */ function stripLineComments(keptLines) { const out = []; for (const line of keptLines) { let lo = ''; let inS = false, e = false; for (let i = 0; i < line.length; i++) { const c = line[i]; if (inS) { if (e) e = false; else if (c === '\\') e = true; else if (c === '"') inS = false; } else { if (c === '/' && line[i + 1] === '/') break; if (c === '"') inS = true; } lo += c; } out.push(lo); } return out.join('\n'); } let allOk = true; for (const plat of PLATFORMS) { const kept = preprocess(plat); const cleanJson = stripLineComments(kept); try { JSON.parse(cleanJson); console.log(`✅ ${plat.padEnd(12)} OK`); } catch (err) { allOk = false; console.log(`❌ ${plat.padEnd(12)} FAIL: ${err.message}`); // 输出错误附近 6 行 const m = err.message.match(/position (\d+)/); if (m) { const pos = parseInt(m[1]); const allLines = cleanJson.split('\n'); let p = 0; for (let k = 0; k < allLines.length; k++) { if (p + allLines[k].length + 1 > pos) { console.log(' near preprocessed line', k + 1, '(in raw pages.json line', findRawLine(k, plat) + ')'); for (let j = Math.max(0, k - 3); j <= Math.min(allLines.length - 1, k + 3); j++) { console.log(' ', j === k ? '>>>' : ' ', allLines[j]); } break; } p += allLines[k].length + 1; } } } } function findRawLine(preprocIdx, platform) { // 找到 raw 中第 preprocIdx 行对应到哪一行. 用同样的预处理规则计算. const stack = []; let preIdx = -1; for (let k = 0; k < lines.length; k++) { const line = lines[k]; const openMatch = line.match(/^\s*\/\/\s*#(ifdef|ifndef)\s+([\w-]+)\b/); if (openMatch) { const mode = openMatch[1]; const plat = openMatch[2]; const keep = mode === 'ifdef' ? plat === platform : plat !== platform; stack.push({ keep }); if (!keep) continue; preIdx++; continue; } if (/^\s*\/\/\s*#endif\b/.test(line)) { const top = stack.pop(); if (top && !top.keep) continue; preIdx++; continue; } if (stack.length > 0 && !stack[stack.length - 1].keep) continue; preIdx++; if (preIdx === preprocIdx) return k + 1; } return -1; } process.exit(allOk ? 0 : 1);