()
| 198 | } |
| 199 | |
| 200 | function checkMetaCoverage() { |
| 201 | const errors = []; |
| 202 | |
| 203 | (function walk(dir) { |
| 204 | const entries = fs.readdirSync(dir, { withFileTypes: true }); |
| 205 | |
| 206 | for (const entry of entries) { |
| 207 | if (!entry.isFile() || !isMetaFile(entry.name)) continue; |
| 208 | const metaPath = path.join(dir, entry.name); |
| 209 | let meta; |
| 210 | try { |
| 211 | meta = JSON.parse(fs.readFileSync(metaPath, 'utf-8')); |
| 212 | } catch (e) { |
| 213 | errors.push({ file: metaPath, missing: [], parseError: e.message }); |
| 214 | continue; |
| 215 | } |
| 216 | const pages = Array.isArray(meta.pages) ? meta.pages : []; |
| 217 | |
| 218 | const declared = new Set(); |
| 219 | const wildcards = new Set(); |
| 220 | for (const p of pages) { |
| 221 | if (typeof p !== 'string') continue; |
| 222 | if (/^---.*---$/.test(p)) continue; |
| 223 | if (p.startsWith('...')) { |
| 224 | wildcards.add(p.slice(3)); |
| 225 | continue; |
| 226 | } |
| 227 | declared.add(p); |
| 228 | } |
| 229 | |
| 230 | const expected = collectExpectedSlugs(dir); |
| 231 | const missing = []; |
| 232 | for (const slug of expected) { |
| 233 | const topLevel = slug.split('/')[0]; |
| 234 | if (wildcards.has(topLevel)) continue; |
| 235 | if (declared.has(slug)) continue; |
| 236 | missing.push(slug); |
| 237 | } |
| 238 | if (missing.length > 0) errors.push({ file: metaPath, missing }); |
| 239 | } |
| 240 | |
| 241 | for (const entry of entries) { |
| 242 | if (entry.isDirectory() && !entry.name.startsWith('.')) { |
| 243 | walk(path.join(dir, entry.name)); |
| 244 | } |
| 245 | } |
| 246 | })(CONTENT_DIR); |
| 247 | |
| 248 | return errors; |
| 249 | } |
| 250 | |
| 251 | function main() { |
| 252 | const args = process.argv.slice(2).filter(Boolean); |
no test coverage detected