v1.8.0
What's Changed
Added
-
styleguide lintreports catalogue entries that nothing renders
(no-fixture). A component with nostyleguide.twig, no variant sibling
and nostyleguide:key parses perfectly — so every existing metadata rule
passes it — and then shows an empty frame in the sidebar. No preview, no
visual baseline and no behavioural test can reach it, and nothing anywhere
said so. Found nine such entries in one downstream project, one of them a
page.Fixture detection follows the runtime exactly:
isset()on thestyleguide:
key (a barestyleguide:with no value is not a fixture there, and
array_key_exists()would have silently exempted the component), and the
strict canonical variant-filename pattern rather than the looser one the
catalogue walk uses to exclude fixtures —styleguide.WIDE.twigis
excluded from the walk but is not a canonical variant, so that component
really does render nothing.Notice, not warning, and
kind: utilityis exempt outright: a utility
renders whatever it is handed, so it has no stable appearance a demo page
could pin (its fixture-less state is correct, not a gap). Structural partials
a project keeps as catalogue entries are a judgement call rather than a
defect, so the rule informs and never fails a build — a rule that broke CI
over either would be the first one consumers switch off.
Fixed
-
styleguide lintreads a component's<id>.yamlwhen it has one, like the
runtime does.Cli\LintercalledparseTwigComment()directly, while
ComponentParserresolves metadata throughreadComponentMetadata(), where a
sibling<id>.yamlwins over the twig front-comment.That is not a subtle inconsistency once a project starts migrating. ADR 0007
retires the front-comment per component as its<id>.yamllands — so from
the first migrated component onward, the two sources disagree by design, and
the twig file's leading{# … #}is just an ordinary code comment. The linter
parsed it as YAML anyway. Downstream, completing the migration for 29
components produced 29 phantommetadata-yaml-invaliderrors, each one a
correct code comment being read as a broken metadata block, on components the
catalogue was serving perfectly.The linter now goes through
readComponentMetadata()(made@internal-public
for this), so every rule sees the same document the catalogue does, and
findings are attributed to the file the metadata actually came from —
pointing an author at<id>.twigfor a value authored in<id>.yamlsends
them to edit a document the catalogue ignores.Two new rules cover the blind spots that adopting the runtime's precedence
would otherwise have created:sidecar-yaml-invalid(error).readComponentMetadata()swallows a
malformed<id>.yamland falls back to the twig comment. That is right for
the renderer — one bad file must not blank a component — but it means the
canonical document can be broken while the component renders perfectly
and nothing says so. Reported on the.yaml.redundant-twig-metadata(warning). Both documents present and the
sidecar winning means the twig front-comment is dead: editing it changes
nothing, silently. ADR 0007 says the template keeps only its render code
once the sidecar lands. Downstream this state had already produced three
components whose corrected descriptions lived in the dead block and had
never been visible. Reported on the.twig— that is the file to edit.
An ordinary leading code comment does not trigger the second rule: the check
requires the comment to parse as a YAML mapping carryingname:. Reporting
prose about the markup would be the mirror of the bug being fixed.
||||||| parent of 71af4b1 (feat(lint): report catalogue entries that nothing renders (no-fixture)) -
A broken component template no longer reports itself as a missing one.
component_*()/page_*()wrapped both the template load AND the render in
catch (\Throwable), so every failure produced the same output: the alert
component saying "template not found", returned normally — HTTP 200.A template with a fatal Twig syntax error was therefore indistinguishable
from a file that is not there. The real parser message went only to
error_log, andRenderer::render()'s 500 path — added in 1.6.0 precisely
so that "a health check or CI smoke test polling/render/component/<id>"
cannot see success for a broken component — could never fire, because the
throw was swallowed one layer below it.Downstream (portadesign/tailwind-base, 2026-07) eleven templates shipped that
way for days. HTTP 200, no console error, no failed request, and the
components stayed in the catalogue (discovery reads only the leading{# #}
block and never tokenizes the body) — so their behavioural tests ran against
the alert fallback and passed. Every automated check was green while the
pages rendered none of the components they named.Now only a
LoaderError— the file genuinely is not there — falls back to the
alert. ASyntaxError(exists, does not compile) and any throw from the
render itself propagate toRenderer::render(), which turns them into HTTP
500 with the real message. Therender()call moved outside thetryas
well: aLoaderErrorraised by a nested{% include %}is a failure of the
template being rendered, not evidence that that template is missing, and must
not be relabelled as one.A missing
{% include %}target or{% extends %}parent is resolved at
render time, so it used to be caught here too and reported as the including
template being missing — pointing the author at a file that is right there
while the genuinely absent one went unnamed. That is whyrender()moved out
of thetry, not merely why the catch narrowed.Behaviour change — read before upgrading. This is a bug fix, but it is
not a silent one, and the blast radius is wider than the styleguide:- A page fixture containing a broken component now returns 500 for the
whole render, instead of 200 with one alert box among healthy siblings. component_*()/page_*()are project Twig helpers. Wherever a consumer
calls them outside the styleguide sandbox — a real page template in
production — a component that was quietly rendering an alert box will now
raise instead.
The upgrade therefore turns any already broken, not yet noticed component
into a visible failure. Since not noticing is precisely the bug being fixed,
assume such components exist rather than assuming they do not: run
vendor/bin/styleguide lint --templates=<path>and one render pass over the
catalogue before rolling this out, not after.The message shown is now the actual Twig error rather than "not found", and
both new failure pathserror_log()before rethrowing — a consumer whose CMS
or proxy swallows the 500 body would otherwise have no server-side trace at
all, which would be strictly less diagnosable than the behaviour this
replaces.If a consumer genuinely wants the old resilient behaviour, the escape
hatch already exists and is unchanged: register your owncomponent_*/
page_*function on the Twig environment before handing it to the package.
registerBundledHelpers()never overwrites a name the project already
registered, so your implementation wins. No new configuration flag is
introduced for this — a switch that restores "broken renders as 200" would
reintroduce the failure mode by name. - A page fixture containing a broken component now returns 500 for the
Pull Requests
- #104 — fix(render): a broken component template no longer reports itself as missing
- #106 — feat(lint): report catalogue entries that nothing renders (no-fixture)
- #107 — fix(lint): read a component's .yaml when it has one, like the runtime does
Full Changelog: v1.7.2...v1.8.0