unreferencedPlates follows the artifact's linked stylesheets by name

Greptile's third P1 on the same seam: a stylesheet linked from the artifact but outside the bounded walk's root, depth, or file limit was still invisible. The hrefs the artifact itself declares are resolved against its directory and joined to the corpus, which closes every variant.

AI-assisted (Claude Code).
This commit is contained in:
Paul Bakaus
2026-08-28 15:26:28 -07:00
parent 18e8c287b5
commit 64001fe213
2 changed files with 25 additions and 3 deletions
+18 -3
View File
@@ -342,9 +342,24 @@ export function unreferencedPlates(spec, artifact = null) {
const plates = (spec?.regions || []).filter((r) => r.medium === 'raster' && r.plate);
if (!plates.length) return [];
// The artifact narrows nothing: a plate may be referenced only from a
// stylesheet the artifact links (assets/hero.css), so the source walk runs
// either way and the artifact is simply guaranteed a seat in the corpus.
const files = [...new Set([...(artifact && fs.existsSync(artifact) ? [artifact] : []), ...sourceFiles()])];
// stylesheet the artifact links, so the source walk runs either way, the
// artifact keeps a seat in the corpus, and the stylesheets it links are
// added by name (resolved against the artifact's own directory), which
// covers a stylesheet outside the walk's root, depth, or file limit.
const linked = [];
if (artifact && fs.existsSync(artifact)) {
linked.push(artifact);
try {
const html = fs.readFileSync(artifact, 'utf8');
for (const m of html.matchAll(/<link\b[^>]*href=["']([^"']+)["'][^>]*>/gi)) {
const href = m[1];
if (/^(https?:|data:|\/\/)/i.test(href)) continue;
if (!/rel=["']?stylesheet/i.test(m[0]) && !/\.css(\?|$)/i.test(href)) continue;
linked.push(path.resolve(path.dirname(artifact), href.split('?')[0]));
}
} catch { /* the artifact still counts */ }
}
const files = [...new Set([...linked, ...sourceFiles()])];
let corpus = '';
for (const f of files) { try { corpus += fs.readFileSync(f, 'utf8') + '\n'; } catch { /* skip */ } }
const missing = [];
+7
View File
@@ -478,7 +478,14 @@ describe('unreferencedPlates', () => {
// an explicit artifact does not bypass the stylesheet walk
fs.writeFileSync(path.join(d, 'index.html'), '<link rel="stylesheet" href="assets/hero.css"><main class="hero"></main>');
assert.deepEqual(unreferencedPlates(spec, path.join(d, 'index.html')), [], 'the linked stylesheet still counts with --artifact set');
// a linked stylesheet beyond the walk's reach (deep path) still counts
const deep = path.join(d, 'a', 'b', 'c', 'e', 'f', 'g', 'h', 'styles');
fs.mkdirSync(deep, { recursive: true });
fs.writeFileSync(path.join(deep, 'deep.css'), ".hero { background-image: url('hero-plate.png'); }");
fs.writeFileSync(path.join(d, 'index.html'), `<link rel="stylesheet" href="a/b/c/e/f/g/h/styles/deep.css"><main class="hero"></main>`);
fs.writeFileSync(path.join(d, 'assets', 'hero.css'), '.hero { background: red; }');
assert.deepEqual(unreferencedPlates(spec, path.join(d, 'index.html')), [], 'a stylesheet linked by the artifact counts wherever it lives');
fs.writeFileSync(path.join(deep, 'deep.css'), '.hero { background: red; }');
assert.equal(unreferencedPlates(spec, null).length, 1, 'an unreferenced plate is still named');
assert.equal(unreferencedPlates(spec, path.join(d, 'index.html')).length, 1, 'and with the artifact set too');
} finally { process.chdir(prev); fs.rmSync(d, { recursive: true, force: true }); }