Releases: oekazuma/svelte-vitals-action
Release list
v0.11.0
Minor Changes
-
cbb99cf: Update the bundled analyzer to
svelte-vitals0.53.0 /@svelte-vitals/core0.49.0. The action's inputs and outputs are unchanged, and the step still fails onfailOnseverity rather than on any score. What changes is how much the scan now finds:- Thirteen new rules, all on by default: six
warning, seveninfo, nonecritical. Under the defaultfailOn: criticalthey add annotations and move scores without being able to fail the step; a project onfailOn: warningcan turn red on the six. The warnings area11y/no-accesskeyanda11y/no-autofocus(focus-hijacking global attributes),a11y/positive-tabindex(a literaltabindexabove 0 reorders keyboard navigation for the whole page),a11y/disallowed-aria-props(anaria-*attribute the element's role prohibits — most oftenaria-labelon a bare<div>, which the Svelte compiler does not warn about),a11y/permitted-contents(a literal child that is not permitted content of its literal parent; broken structure iswarning, category mismatches areinfo), andcorrectness/autoplay-muted(<video autoplay>withoutmuted, which Chrome and Safari block silently — it works in development and never starts for visitors). The info rules area11y/no-duplicate-dt,a11y/abbr-title,a11y/pattern-title,a11y/deprecated-aria,a11y/deprecated-element,a11y/deprecated-attr, andperformance/iframe-loading. - Existing a11y rules reach further.
a11y/accessible-namenow checks<iframe>fortitle/aria-label/aria-labelledby;a11y/no-missing-id-reffollows every ARIA id-reference property plus HTML'slist,headers,form,popovertargetandcommandfor;a11y/id-duplicationcatches a route id colliding with one insrc/app.html; anda11y/no-autofocusreportsautofocus="", which used to read as unknowable. Because these widen existing rules rather than add new ones, a suppressions entry already recorded for the same rule, route and file keeps matching — the new findings arrive pre-suppressed where one exists. metaComponentsis now a fallback rather than an override. A declared component is credited as a broad meta source only when the analyzer cannot resolve it. Declaring a resolvable local wrapper used to discard its transitively resolved tags, so the option could make results strictly worse; those routes' findings now reappear. Declare only components the analyzer cannot follow.- A run can no longer look clean because it checked nothing. An
overridesentry whoserouteorfilesglob matches nothing is now reported, as is an inlinesvelte-vitals-disable-next-linenaming a rule id no rule declares — previously that directive silently suppressed nothing. These surface as workflow annotations. svelte-vitals-disable-next-linenow works on route-level findings — a duplicate landmark, a second<h1>, an image missing dimensions. The directive was previously read only by file-scoped rules, so a comment above one of these did nothing. A suppressed finding becomes a pass for that rule and route, so the route stays in the category average.- Fewer false positives.
a11y/use-listnow needs two or more bullet items before reporting, so a lone- noteline no longer counts as a list;a11y/required-aria-propsstops asking a native combobox foraria-expanded/aria-controlsthe host already supplies; anda11y/unknown-aria-attributeanda11y/invalid-aria-valueanchor at the element's start tag, so one directive above a multi-line element reaches them. - New opt-in rule
a11y/unverified-id-refreports id references that cannot be verified on routesa11y/no-missing-id-refhas to skip. Off until you enable it, so scores are unchanged for every project that does not.a11y/disallowed-elementanda11y/required-elementare likewise inert until theirelementsoption names tag names.
- Thirteen new rules, all on by default: six
v0.10.0
Minor Changes
-
0153223: Update the bundled analyzer to
svelte-vitals0.48.0 /@svelte-vitals/core0.44.0. The action's inputs and outputs are unchanged, but one change here stops a workflow that currently passes until a file is renamed, and a new rule category moves every Health score.Config files must be ESM, and
svelte-vitals.config.mjsis no longer read. The loader searchessvelte-vitals.config.{js,ts}only. A leftover.mjsthrows with a rename hint, and a.jsconfig that parses as CommonJS throws with a "config files are ESM" error — both propagate out of the analysis and fail the step outright, rather than quietly falling back to defaults. Rename a.mjsconfig to.js(the project must be"type": "module", which is SvelteKit's default) or to.ts. CommonJS projects are no longer supported.A new Accessibility category adds 15 rules, all on by default, and shifts every Health score. ARIA role, attribute and value validity, required ARIA props, interactive-element nesting, accessible-name computability, label/control association, list-like text,
<select>placeholder options, machine-readable<time>, anapp.htmldoctype check, plus landmark duplication/nesting and project-wide id/idref integrity resolved across component boundaries. Existing projects will see new findings. Twelve of the fifteen arewarningand three areinfo; none iscritical, so under the defaultfailOn: criticalthe new category adds annotations and moves scores without being able to fail the step. A project configured withfailOn: warningis a different matter — twelve warning-level rules landing at once can turn it red. Separately from any finding, a sixth category now enters the weighted average, so the Health number in the job summary and the sticky comment moves on upgrade with no change on your side — recalibrate anything reading it.The analyzer also raises its minimum Node to 24.16.0. The action runs on
node24, so this is only a concern if your runner's Node 24 predates that patch.Beyond that, the scan reaches code it previously could not:
- Projects styling components in a CSS dialect were not analyzable at all. Svelte parses a
<style>body as CSS whatever itslangsays, so one<style lang="scss">block made a component unparseable, and a single unparseable route failed the entire run — which for this action meant the step failed with no report. SCSS, Less and Stylus projects now analyze normally, and will see their first real report. - Large projects were losing files to an exhausted descriptor limit. Every
.sveltefile was read in parallel with no bound, so a big tree ran out of descriptors and eachEMFILEwas misattributed as a parse failure and dropped — the file went unanalyzed and the score never reflected the gap. The action did report the count as a skipped-file warning, so this was visible if you were reading annotations, just misdescribed. Reads are now bounded, so those files are analyzed and can carry findings. - Source mode no longer collapses
<link>and<script src>tags that share arelorsrc. The composed<svelte:head>kept only the last one per key across the layout chain, so a page with tworel="preload"entries, both Google Fontspreconnectorigins, or severalhreflangalternates was judged on one of them — producing a false "un-preconnected origin" on a correctly configured site — and a page'sdefercopy of a script masked the layout's render-blocking one.rel="canonical"is the deliberate exception and still collapses, so a page canonical continues to override the layout's. Findings move in both directions here, and stored baselines or suppressions may need re-recording. <link>relandaskeywords are now matched case-insensitively as the HTML spec requires, sorel="Canonical"andrel="Preload"are recognised.- The inline
svelte-vitals-disable-next-linedirective now honoursa11y/*rule ids, which it silently ignored.
- Projects styling components in a CSS dialect were not analyzable at all. Svelte parses a
Patch Changes
-
74130f7: Update the bundled analyzer to
svelte-vitals0.48.1 /@svelte-vitals/core0.45.0, and take the report and gating functions from@svelte-vitals/core's stable entry now that they are exported there.Nothing the action reports changes: no rule, severity, score, annotation, job summary or sticky-comment output moves. The promotion upstream is a pure re-export, and the four functions this action calls —
formatGithubReport,formatMarkdownReport,summarize,hasFailureAtOrAbove— keep the same signatures and behaviour.What changes is the promise behind them. They previously came from
@svelte-vitals/core/internal, which upstream excludes from semver and may reshape in any release including a patch, so a dependency bump could break this action's committed bundle with only its own CI typecheck standing in the way. They now come from an entry covered by semver.
v0.9.0
Minor Changes
-
9f513e6: Update the bundled analyzer to
svelte-vitals0.45.1 /@svelte-vitals/core0.41.1, a wide range covering several upstream releases. The action's inputs and outputs are unchanged, and the step still fails onfailOnseverity rather than on any score — but that severity table itself moved, so read the first two entries before upgrading a workflow you rely on:- The default gate loosens:
seo/description-presencedrops fromcriticaltowarning. Under the defaultfailOn: critical, a project whose only failure was a missing<meta name="description">now passes the step where it used to fail it. If you were relying on that block, setfailOn: warningor override the rule's severity in your config. Three more severities moved, and they only bite under a non-defaultfailOn: warning:seo/og-urlinfo→warning(the one tightening — a previously green run can turn red),seo/og-descriptionwarning→info, andseo/single-h1splitting per finding so that two or more<h1>is nowinfowhile a missing one stayswarning. Theseo::routescoring pair's total weight drops from 110 to 100 as a result, so SEO and Health can shift a point or two with no finding change at all. - A previously green run can turn red from files that were never analyzed. A parse crash on argument-less
$state()—let el = $state();, the idiomaticbind:thisdeclaration — used to make the whole component invisible to every rule, silently. Those files are analyzed now, and what surfaces in them can includecriticalfindings that fail the default gate. That is the fix working. - A rule that throws no longer fails the whole step. The run completes without that rule and its weight is removed from the Health denominator, so the score is not silently inflated. Previously the exception propagated and the action failed the job outright. Note the tradeoff: the action does not yet surface the analyzer's non-fatal warnings, so the skipped rule's id is not reported anywhere — a rule that fails now goes unmentioned instead of loud.
- The job summary and the sticky PR comment are hardened against the analyzed project's own content. Strings quoted from the repo under analysis — file paths, route ids, and rule messages embedding page content such as
<title>text or JSON-LD values — can no longer forge report structure: an embedded newline, code fence, heading,[text](url)link or bare<tag>renders as inert quoted text. Visible on well-behaved projects in one place: a message containing a literal tag (Missing <title>) now renders as inline code, which also fixes table cells silently dropping such tags. - More of the project is reachable, so findings move in both directions. Head and heading resolution now follows a component imported through a
kit.alias/kit.files.libalias ($components,$ui, …) instead of only$lib/…and relative paths; everyapplication/ld+jsonscript on a route is analyzed instead of only the last one; andseo/single-h1counts headings rendered by imported local components. False "Missing" findings on routes whose content lives in such components disappear and Health can rise, while defects inside them — an empty<title>, invalid JSON-LD, a second<h1>— become visible for the first time. seo/json-ld-validitynow checks@typeagainst the schema.org vocabulary. A bare type name that is not an exact, case-sensitive schema.org type produces awarning, with a did-you-mean hint for a casing slip or a typo within edit distance 2. IRI and prefixed forms are never flagged, and a document whose@contextnames a non-schema.org vocabulary is exempt.- Several false positives removed.
seo/json-ld-required-propswas stale against Google's current requirements — theArticle/BlogPosting/NewsArticle,OrganizationandPersonrows are gone,Productnow accepts any one ofreview/aggregateRating/offers,Recipeneeds onlyname+image,VideoObjectdropsdescription.security/handler-state-writeandsecurity/shared-state-importno longer fire on a universal+page.ts/+layout.tsthat exportsssr = false; since the former iscritical, that can turn a red run green.performance/render-blocking-scriptno longer flags non-executing script types (text/partytown,importmap,speculationrules).correctness/effect-as-onmountno longer flags an$effectreading reactive state through a member expression on an imported binding or anew …()local.
- The default gate loosens:
Patch Changes
-
e40f45c: Fix the
baselineinput reporting every finding as new on projects whosesvelte-vitals.config.*importssvelte-vitals— the shape theinstallwizard scaffolds.The baseline ref is analyzed inside a temporary git worktree, and that worktree has no
node_modulesin its ancestry, so re-loading the config file from within it threw on the import. The comparison caught the error and fell back to reporting everything, which is the opposite of what the input is for: a gate meant to show only new findings showed all of them. The action now hands its own config-file load to the baseline analysis instead of letting it look for one.Both sides of the comparison therefore run under the same config, so editing
svelte-vitals.config.*between the baseline ref and the current commit no longer makes findings look new on its own. -
f2f9314: Update the bundled analyzer to
svelte-vitals0.46.0 /@svelte-vitals/core0.42.0. Nothing the action reports changes: no rule severity, score, finding, annotation, job summary or sticky-comment output moves. Upstream's visible work in this range is CLI-only (shell completion, spinner cursor restore, theci installworkflow scaffold, and the dispatch layer's exit code), and the rest is internal refactoring plus two new library exports the action does not use yet. -
18d8dea: Surface the analyzer's non-fatal warnings as workflow annotations.
analyzeProjectreports config-file problems, version-floor notices, unparseable files it skipped, and rules that crashed and were dropped from the run — the action collected all of it and printed none of it.The crashed-rule case is why this matters now. A rule that throws no longer aborts the analysis; the run completes without it and its weight leaves the Health denominator, so nothing about the report looks wrong. Before, the exception propagated and failed the job outright. Without this, an incomplete scan passed the gate with no trace of which rule was missing.
The gate is unchanged — these are annotations, not failures.
v0.8.0
Minor Changes
-
376842d: Update the bundled analyzer to
svelte-vitals0.44.0 /@svelte-vitals/core0.38.0. The action's inputs and outputs are unchanged, and the step still fails onfailOnseverity rather than on any score. What changes is the numbers the report prints and what the scan finds:- Category scores rise wherever a category checks few things. Within one
(category, scope)pair awarningnow costs five times aninfoand acriticalfifteen times, so a more severe finding always costs more there. Across pairs it does not: a key is never scored against less than 25 points of checks, so in a one-rule pair the three severities give 96, 80 and 40, where a lonewarningused to score 0. Anything reading the Health number out of the job summary should be recalibrated — this moves in the opposite direction from the previous release. - New findings in TypeScript-heavy projects. Rune declarations behind a TS cast (
let count = $state(0) as number) now feed the same facts as the uncast form, and imports inside.svelte.ts/.svelte.jsrunes modules are now collected — soperformance/heavy-import,performance/namespace-import,architecture/private-scope-importandarchitecture/route-component-importsee code they used to skip. These were silent false negatives, not new checks. - The
diffinput no longer drops findings in non-ASCII paths. Git octal-escapes such paths under its defaultcore.quotePath, which never matched the raw UTF-8 location, so findings under e.g. a Japanese route directory vanished from a diff-scoped run. Changed-file detection now reads NUL-separated output. - New opt-in rule
architecture/reserved-name-placementsays which positions a reserved directory name may appear in, the inverse ofarchitecture/reserved-directory-names. Off until its placement maps are configured, so it adds nothing to a scan until then.
- Category scores rise wherever a category checks few things. Within one
-
613dbf8: Update the bundled analyzer to
svelte-vitals0.44.1 /@svelte-vitals/core0.39.0. The action's inputs and outputs are unchanged, and the step still fails onfailOnseverity rather than on any score. What changes is what a scoped run reports and the scores beside it:- A
diff-scoped run's Health drops, and the old number was wrong. Every rule's passing results now carry the samelocationa penalized result would, so changed-file filtering had to stop keeping a result merely because itslocationwas in the changed set. Before this, a single incidental passing SEO check on a changed file could promote its whole category from absent to a fabricated 100 and pull Health upward. On the reference shape — one criticalcorrectnessfinding plus one such SEO pass, both on changed files — Health moves from 89 to 79, and 79 is the correct number. If you read the Health value out of the job summary ondiff-scoped runs, expect it lower and recalibrate against it. One pass of the same shape is deliberately kept:architecture/unit-entry-file's route-less seed still survivesdiffscoping, soarchitecturekeeps its own upward pull — that tradeoff predates this release and is unchanged by it. - The
baselineinput no longer masks a genuine regression. Forseo/title-presenceand the tenheadTagRule-backed ids (canonical-url,og-title,og-image,charset,viewport,twitter-card,description-presence,og-description,json-ld,og-url), a route that passed at the baseline ref and then regressed — a deleted<title>, say — produced identical comparison keys on both sides and was dropped as "not new". Comparison is now penalized-findings-only, so those regressions are reported. Passing results no longer appear in baseline-scoped output at all. - A
files:-scopedseverity: 'off'override now removes a rule's passing seed, not just its penalized findings, which it always claimed to do. Scores move where such an override is configured — the upstream reproduction goes 98 → 96 once the stale seed is gone. - A scoped run no longer warns that suppressions are stale just because the scope hid their findings. With
svelte-vitals-suppressions.jsonpresent, using thediffandbaselineinputs together printed a misleading "N stale entries — re-run--update-suppressionsto prune" annotation on every run. Staleness is now judged against the project-wide result set, so that annotation stops. architecture/prop-count, on by default, now counts named props destructured alongside a rest element (let { a, b, ...rest } = $props()) instead of staying silent on the whole destructure. It can only surface findings on components that were previously invisible to it; themaxdefault stays 6.architecture/reserved-directory-namesgainsanyCaseUnitScopes, governing units whose name does not begin A–Z — the lowercase and.ts-entry unitsunitScopescould never reach. Defaults to{}, so a project that does not declare it sees no new findings.
- A
v0.7.0
Minor Changes
-
e3216c2: Update the bundled analyzer to
svelte-vitals0.39.0 /@svelte-vitals/core0.34.0. The action's inputs and outputs are unchanged, and so is what makes the step fail — the gate still keys onfailOnseverity, not on any score. What changes is the numbers the report prints and what the scan reports:- Category scores now measure how much is wrong, not merely that something is. A key used to start at 100 and lose fixed points per failing rule, which capped what a category could express and flattened one finding against several hundred. A key now scores the share of what it was measured against that is intact, weighted by severity, so every category can reach 0. Any category carrying a finding moves in the summary and the sticky PR comment, most of them downward and by more than a point;
architecture,securityandperformancemove furthest,seoandcorrectnessstay within a point. A clean 100 still means no finding among the checks that ran, and acriticalstill caps a category at 79. Anything reading the Health number out of the job summary should be recalibrated against the new scale. security/handler-state-write, on by default, now reports a hand-rolled in-memory store under$lib/server. The.set()/.update()exemption for that directory was path-based, so a sharednew Map()overwritten by every request was exempt alongside the database clients it was meant to cover. The export's initializer is now read: aMap/Set/WeakMap/WeakSetor an object or array literal is reported, anything else stays exempt. Existing projects may see new findings here.- New opt-in rule
architecture/doc-link-targetreports a documentation link in a component comment whose target no longer exists. Inert until you declareurlRoots, so it adds nothing to a scan until configured.
- Category scores now measure how much is wrong, not merely that something is. A key used to start at 100 and lose fixed points per failing rule, which capped what a category could express and flattened one finding against several hundred. A key now scores the share of what it was measured against that is intact, weighted by severity, so every category can reach 0. Any category carrying a finding moves in the summary and the sticky PR comment, most of them downward and by more than a point;
v0.6.0
Minor Changes
-
ca45599: Update the bundled analyzer to
svelte-vitals0.37.0 /@svelte-vitals/core0.31.1. The action's inputs and outputs are unchanged — what changes is what the scan reports and the numbers it prints:- Scores are now floored instead of rounded, so a reported 100 means the deduction was exactly zero. Every category score and Health can move down by one point, and Health is computed from unrounded category scores (the old double rounding could move it two). A workflow gating on the summary's Health number should expect it a point lower.
- One new rule on by default:
architecture/route-component-importreports a component importing a SvelteKit route entry (+page.svelte,+layout.svelte,+error.svelte, and their@breakout forms). Existing projects may see newinfofindings; stories, tests and specs are exempt. - Three new opt-in Architecture rules, inert until configured:
architecture/unit-entry-file,architecture/directory-naming,architecture/reserved-directory-names. - Import specifiers now resolve through the aliases a project declares in
svelte.config.{js,ts}(kit.alias, andkit.files.libwhen$libhas been moved). Projects importing through their own aliases will see findings that were previously invisible —security/shared-state-importin particular was inert for them. - Fewer false positives:
performance/heavy-importno longer reports type-only imports, which are erased at build.
v0.5.0
Minor Changes
-
7ccb121: Update the bundled analyzer to
svelte-vitals0.34.0 /@svelte-vitals/core0.30.0. The action's inputs and outputs are unchanged — what changes is what the scan reports:- Two new
correctnessrules.correctness/base-path-navigationflags hardcoded root-relative navigation (<a href="/about">,goto('/about'),redirect(303, '/login')) in projects that setkit.paths.base, where it 404s in production;correctness/checkable-bind-valueflagsbind:valueon<input type="checkbox">/<input type="radio">, where the bound state silently never updates. - Recalibrated Architecture thresholds:
architecture/prop-countnow flags more than 6 props (was 10) andarchitecture/component-sizemore than 200 lines (was 400). Expect newinfofindings on existing projects. Nothing new fails by default (failOndefaults tocritical), but a repo runningfailOn: 'info'can start failing on components that passed before. - New opt-in rule
architecture/private-scope-import, inert untilscopesis configured. - Rule settings accept an object form,
{ severity, options }, so a project can move a configurable rule's thresholds or extend its built-in lists fromsvelte-vitals.config.*— including the two recalibrated Architecture thresholds. Values inrulesare now validated: an invalid severity that was previously ignored is a fatal config error, and the action's step fails on it. - Fewer false positives across the component-analysis rules: writes to
{@const ...}/{let ...}/{const ...}template locals are no longer misattributed to a same-named top-level$state.
- Two new
v0.4.1
Patch Changes
- 18bbb18: Added
branding(icon/color) toaction.yml— required by GitHub before the action can be published to the GitHub Marketplace.
v0.4.0
Minor Changes
-
Migrated out of the svelte-vitals monorepo into this dedicated repository, following the same pattern as
changesets/action,pnpm/action-setup, andrenovatebot/github-action. This repo'ssvelte-vitals/@svelte-vitals/coredependencies are now regular npm-registry semver ranges (not workspace-linked), and its git tags are plainvX.Y.Z— Renovate's built-ingithub-actionsmanager now works out of the box for consumers, with no custom configuration needed. Continues the version series from the old@svelte-vitals/action@0.3.8.Breaking: the action reference changes from
oekazuma/svelte-vitals/packages/action@<sha>tooekazuma/svelte-vitals-action@<sha>. Re-runnpx svelte-vitals@latest ci install --force(orci upgrade) to update an existing workflow.