(html: string)
| 545 | |
| 546 | // fallow-ignore-next-line complexity |
| 547 | const walk = (html: string): void => { |
| 548 | const compositionSrcRe = /<[^>]*\bdata-composition-src\s*=\s*["']([^"']+)["'][^>]*>/gi; |
| 549 | const scannable = maskNonScannableRanges(html); |
| 550 | let match: RegExpExecArray | null; |
| 551 | while ((match = compositionSrcRe.exec(scannable)) !== null) { |
| 552 | const srcPath = (match[1] ?? "").trim(); |
| 553 | if (!srcPath) continue; |
| 554 | if (/^__[A-Z_]+__$/.test(srcPath)) continue; // template placeholder |
| 555 | |
| 556 | // data-composition-src is always written root-relative (even from a |
| 557 | // nested sub-composition) — matches the resolution the renderer uses |
| 558 | // in packages/producer/src/services/htmlCompiler.ts (parseSubCompositions |
| 559 | // / assertSubCompositionsUsable). |
| 560 | const filePath = resolve(projectDir, srcPath); |
| 561 | |
| 562 | // Circular reference guard — same as assertSubCompositionsUsable. |
| 563 | // Already-visited files were already checked (or are mid-walk); skip |
| 564 | // re-checking/re-recursing but still let a later distinct reference to |
| 565 | // the same broken file surface (checked is keyed by srcPath, not filePath). |
| 566 | if (visited.has(filePath)) continue; |
| 567 | visited.add(filePath); |
| 568 | |
| 569 | if (!existsSync(filePath)) { |
| 570 | if (!checked.has(srcPath)) { |
| 571 | checked.set(srcPath, { srcPath, problem: "the file does not exist" }); |
| 572 | } |
| 573 | continue; |
| 574 | } |
| 575 | |
| 576 | const fileHtml = readFileSync(filePath, "utf-8"); |
| 577 | const validity = checkSubCompositionUsability(fileHtml, parseSubCompHtml); |
| 578 | if (!validity.ok) { |
| 579 | if (!checked.has(srcPath)) { |
| 580 | checked.set(srcPath, { |
| 581 | srcPath, |
| 582 | problem: validity.detail ?? "the file is empty or could not be parsed", |
| 583 | }); |
| 584 | } |
| 585 | continue; |
| 586 | } |
| 587 | |
| 588 | // Usable — recurse into it so nested references are validated too, |
| 589 | // but only because this file is itself reachable from the root. |
| 590 | walk(fileHtml); |
| 591 | } |
| 592 | }; |
| 593 | |
| 594 | walk(rootHtml); |
| 595 |
no test coverage detected