Skip to content

v1.8.0

Choose a tag to compare

@github-actions github-actions released this 30 Jul 09:51

What's Changed

Added

  • styleguide lint reports catalogue entries that nothing renders
    (no-fixture).
    A component with no styleguide.twig, no variant sibling
    and no styleguide: 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 the styleguide:
    key (a bare styleguide: 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.twig is
    excluded from the walk but is not a canonical variant, so that component
    really does render nothing.

    Notice, not warning, and kind: utility is 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 lint reads a component's <id>.yaml when it has one, like the
    runtime does.
    Cli\Linter called parseTwigComment() directly, while
    ComponentParser resolves metadata through readComponentMetadata(), where a
    sibling <id>.yaml wins 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>.yaml lands — 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 phantom metadata-yaml-invalid errors, 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>.twig for a value authored in <id>.yaml sends
    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>.yaml and 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 carrying name:. 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, and Renderer::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. A SyntaxError (exists, does not compile) and any throw from the
    render itself propagate to Renderer::render(), which turns them into HTTP
    500 with the real message. The render() call moved outside the try as
    well: a LoaderError raised 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 why render() moved out
    of the try, 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 paths error_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 own component_* /
    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.

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