Repository navigation
check_links validates img src, closing the figure-variant gap - #285
Conversation
The theme hook rewrites every <img> pointing at a themed SVG to variants it generates separately, and nothing verified that the two halves agree. check_links.py collected hrefs only where tag == "a", so a figure could 404 in production with all five CI checks green. - collect <img src> alongside <a href>; report it as "no such image" - check fragments only on HTML targets, since the variants carry #only-light / #only-dark markers that name CSS hooks, not anchors - svg_theme: glob -> rglob, so a figure in a subdirectory gets the variants its <img> is already rewritten to expect - svg_theme: key _themed by path under images/ rather than bare stem, so same-named figures in different subdirectories cannot collide - svg_theme: strip query and fragment before the .svg test, so a src like foo.svg#view rewrites instead of silently passing through - svg_theme: accept single-quoted src, which previously failed open - drop the build-time-split note from pipeline.svg; the module docstring covers it, and the note would otherwise want copying into 17 more figures
MilagrosMarin
left a comment
There was a problem hiding this comment.
This closes the gap from #278, so I mutation-tested the new check rather than trusting a green run — a passing CI proves no false positives, not that the check has teeth.
- Missing variant: caught, exit 1. The #283 failure mode now fails the build.
- All references valid, including
#only-lighton an SVG: clean, no false positives. - Bad anchor on an HTML target: still caught.
That last one was my main worry: gating fragments on target.suffix == ".html" could have silently disabled anchor checking for ordinary page links. It doesn't, because resolve() already maps a directory URL to its index.html, so page targets keep the suffix. Worth knowing the two are coupled — if resolve() ever stops appending index.html, anchor checking goes quiet rather than failing.
Verified all four svg_theme.py fixes across eleven cases: relative, root-absolute and bare images/ paths, subdirectories, single quotes, fragments, queries, unthemed figures, non-SVG sources, and stem collision in both directions — a/dup.svg themed rewrites while b/dup.svg unthemed stays untouched, which bare-stem keying got wrong. pipeline.svg is an exact revert to its pre-#278 blob.
One small thing, not worth changing: _image_key searches for (?:^|/)images/ anywhere in the path, slightly looser than on_files's startswith. There is a second images directory at src/.overrides/assets/images, currently one PNG, so nothing collides today — and if it ever did, this PR is exactly what would make it loud instead of silent.
Closes #283.
The gap
#278 introduced an invariant — every
<img>the hook rewrites points at a fileon_post_buildactually emits — and nothing checked it.scripts/check_links.pycollected hrefs only wheretag == "a", so if the generator and the rewriter disagreed, all five CI checks stayed green and the figure 404'd in production.mkdocs build --strictcannot catch this either: the variants are written inon_post_build, after MkDocs' own reference validation, and the rewrite happens inon_post_pageon raw HTML that MkDocs never re-inspects. Thebuilt-sitejob is the only place that can close it.Changes
scripts/check_links.py— collect<img src>alongside<a href>, reported asno such image. Fragments are now validated only on HTML targets: the rewriter emits#only-light/#only-dark, which name CSS hooks rather than anchors, so checking them against a parsed SVG would have produced 36 false failures on the current site.scripts/svg_theme.py— four fixes, all of which the new check would otherwise surface as breakage:glob→rglobinon_post_build.on_filesaccepts any depth, so a themed SVG undersrc/images/<subdir>/had its pages rewritten to variants that were never generated._themedis keyed by path underimages/rather than by bare stem, so same-named figures in different subdirectories cannot collide..svgtest and the slice. Previously line 93 stripped them and line 94 tested the unstripped path, sofoo.svg#viewnever rewrote.IMG_SRCaccepts single-quotedsrc, which previously fell through silently.src/images/pipeline.svg— drop the build-time-split note. The module docstring atsvg_theme.py:12-23covers it more completely,extra.csscarries a third copy, and keeping it would mean copying the text into the other 17 themed figures, which are regenerated by script.Verification
Reproduced the CI path locally (
pip install -r pip_requirements.txt,mkdocs build --strict,check_links.py site):<img>pairs.src/images/sub/fixture.svgand revertedrglobtoglob. The build stayed green, and the extended checker reported both missing variants — the exact failure check_links.py does not validate img src, so a rewritten figure can 404 with CI green #283 describes. Withrglobrestored, the fixture builds and passes. Fixture removed before this PR.images/paths, subdirectories, single quotes, fragments, queries, unthemed figures, non-SVG sources, and stem collisions in both directions.Left alone deliberately
Both theme variants are still fetched, since
display:nonedoes not suppress the request. That is inherent to the approach and small for SVG — recorded in #283, not a defect this PR fixes.