svelte-vitals 0.51.0 → 0.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { c as findUnknownRuleIds, f as isReporterName, i as run, l as knownRuleIds, m as readPackageVersion, p as readCoreVersion } from "./src-BSphfbnn.js";
1
+ import { c as findUnknownRuleIds, h as readPackageVersion, i as run, l as knownRuleIds, m as readCoreVersion, p as isReporterName } from "./src-hgEeAWHb.js";
2
2
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
3
  import { dirname, resolve } from "node:path";
4
4
  import { spawnSync } from "node:child_process";
@@ -636,7 +636,7 @@ const BOOLEAN_FLAGS = [
636
636
  const ROOT_ARGS = {
637
637
  "meta-components": {
638
638
  type: "string",
639
- description: "Comma-separated component names that emit head metadata"
639
+ description: "Comma-separated names of head-metadata components the analyzer cannot resolve"
640
640
  },
641
641
  "treat-dynamic-as": {
642
642
  type: "string",
@@ -775,7 +775,7 @@ function neutralizeBareDiffAndBaseline(argv) {
775
775
  */
776
776
  async function buildHelpText(rootCommand, locale) {
777
777
  if (locale === "ja") {
778
- const { JA_ARG_DESCRIPTIONS, rootHelpJa } = await import("./ja-CULhsFnk.js").then((n) => n.n);
778
+ const { JA_ARG_DESCRIPTIONS, rootHelpJa } = await import("./ja-BDLP3KLG.js").then((n) => n.n);
779
779
  return rootHelpJa(await localizedOptionsSection(rootCommand, "svelte-vitals", locale, JA_ARG_DESCRIPTIONS.root));
780
780
  }
781
781
  return `svelte-vitals — a deterministic SvelteKit code-health scanner (SEO · performance · correctness · security · architecture · accessibility)
package/dist/bin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { g as realIO, i as resolveLocale, m as consoleIO, n as runAnalyzeCliGunshi } from "./analyze-BaQfhf-Q.js";
2
+ import { g as realIO, i as resolveLocale, m as consoleIO, n as runAnalyzeCliGunshi } from "./analyze-njXPaqmd.js";
3
3
  //#region src/cli.ts
4
4
  /**
5
5
  * CLI dispatch: routes `docs`/`explain`/`install`/`ci` subcommands, otherwise hands off to the
@@ -16,35 +16,35 @@ async function runCli(argv, io = consoleIO, env = process.env) {
16
16
  try {
17
17
  const locale = resolveLocale(env);
18
18
  if (argv[0] === "complete") {
19
- const { runCompleteCliGunshi } = await import("./complete-1RY2W-3I.js");
19
+ const { runCompleteCliGunshi } = await import("./complete-Cy8SdF0S.js");
20
20
  return {
21
21
  code: await runCompleteCliGunshi(argv, io),
22
22
  exit: "natural"
23
23
  };
24
24
  }
25
25
  if (argv[0] === "docs") {
26
- const { runDocsCliGunshi } = await import("./docs-53bz9lhZ.js");
26
+ const { runDocsCliGunshi } = await import("./docs-YEEkUHTO.js");
27
27
  return {
28
28
  code: await runDocsCliGunshi(argv.slice(1), io, locale),
29
29
  exit: "natural"
30
30
  };
31
31
  }
32
32
  if (argv[0] === "explain") {
33
- const { runExplainCliGunshi } = await import("./explain-DdgC6CdN.js");
33
+ const { runExplainCliGunshi } = await import("./explain-B0DWOujg.js");
34
34
  return {
35
35
  code: await runExplainCliGunshi(argv.slice(1), io, locale),
36
36
  exit: "natural"
37
37
  };
38
38
  }
39
39
  if (argv[0] === "install") {
40
- const { runInstallCliGunshi } = await import("./install-ckW1HTAL.js").then((n) => n.n);
40
+ const { runInstallCliGunshi } = await import("./install-CzogylBI.js").then((n) => n.n);
41
41
  return {
42
42
  code: await runInstallCliGunshi(argv.slice(1), io, locale),
43
43
  exit: "immediate"
44
44
  };
45
45
  }
46
46
  if (argv[0] === "ci") {
47
- const { runCiCliGunshi } = await import("./ci-CG1mwBD4.js");
47
+ const { runCiCliGunshi } = await import("./ci-BWM2gYNJ.js");
48
48
  return {
49
49
  code: await runCiCliGunshi(argv.slice(1), {
50
50
  ...realIO(),
@@ -1,4 +1,4 @@
1
- import { a as guardArgs, c as stripUnknownFlags, g as realIO, l as suggestClosest, o as splitAtTerminator, r as localizedOptionsSection, s as stripAutoVersionLine } from "./analyze-BaQfhf-Q.js";
1
+ import { a as guardArgs, c as stripUnknownFlags, g as realIO, l as suggestClosest, o as splitAtTerminator, r as localizedOptionsSection, s as stripAutoVersionLine } from "./analyze-njXPaqmd.js";
2
2
  import { a as planWorkflowWrite, i as buildWorkflowYaml, n as ACTION_VERSION, r as WORKFLOW_PATH, t as ACTION_SHA } from "./action-pin.generated-C2XJJ3NW.js";
3
3
  import { join } from "node:path";
4
4
  import { cli } from "gunshi/bone";
@@ -115,7 +115,7 @@ const KNOWN_SHORT_FLAGS = /* @__PURE__ */ new Set(["h"]);
115
115
  * (`docs/superpowers/specs/2026-08-11-cli-ja-help-design.md`).
116
116
  */
117
117
  async function buildCiHelpText(ciArgsCommand, locale) {
118
- const ja = locale === "ja" ? await import("./ja-CULhsFnk.js").then((n) => n.n) : void 0;
118
+ const ja = locale === "ja" ? await import("./ja-BDLP3KLG.js").then((n) => n.n) : void 0;
119
119
  const optionsSection = stripAutoVersionLine(await localizedOptionsSection(ciArgsCommand, "svelte-vitals ci", locale, ja?.JA_ARG_DESCRIPTIONS.ci ?? {}));
120
120
  if (locale === "ja") return ja.ciHelpJa(optionsSection, WORKFLOW_PATH);
121
121
  return `svelte-vitals ci — scaffold CI integration
@@ -1,9 +1,9 @@
1
- import { d as REPORTER_NAMES } from "./src-BSphfbnn.js";
2
- import { d as FAIL_ON_VALUES, f as TREAT_DYNAMIC_AS_VALUES, m as consoleIO, t as ROOT_ARGS, u as CATEGORIES } from "./analyze-BaQfhf-Q.js";
3
- import { t as INSTALL_ARGS } from "./install-ckW1HTAL.js";
4
- import { CI_ARGS, CI_UPGRADE_ARGS } from "./ci-CG1mwBD4.js";
5
- import { DOCS_LIST_ARGS, DOCS_ROOT_ARGS, DOCS_SHOW_ARGS } from "./docs-53bz9lhZ.js";
6
- import { EXPLAIN_ARGS } from "./explain-DdgC6CdN.js";
1
+ import { f as REPORTER_NAMES } from "./src-hgEeAWHb.js";
2
+ import { d as FAIL_ON_VALUES, f as TREAT_DYNAMIC_AS_VALUES, m as consoleIO, t as ROOT_ARGS, u as CATEGORIES } from "./analyze-njXPaqmd.js";
3
+ import { t as INSTALL_ARGS } from "./install-CzogylBI.js";
4
+ import { CI_ARGS, CI_UPGRADE_ARGS } from "./ci-BWM2gYNJ.js";
5
+ import { DOCS_LIST_ARGS, DOCS_ROOT_ARGS, DOCS_SHOW_ARGS } from "./docs-YEEkUHTO.js";
6
+ import { EXPLAIN_ARGS } from "./explain-B0DWOujg.js";
7
7
  import { cli } from "gunshi/bone";
8
8
  import { define } from "gunshi/definition";
9
9
  import { kebabnize } from "gunshi/utils";
@@ -1,5 +1,5 @@
1
- import { l as knownRuleIds } from "./src-BSphfbnn.js";
2
- import { a as guardArgs, c as stripUnknownFlags, l as suggestClosest, m as consoleIO, o as splitAtTerminator, r as localizedOptionsSection, s as stripAutoVersionLine } from "./analyze-BaQfhf-Q.js";
1
+ import { l as knownRuleIds } from "./src-hgEeAWHb.js";
2
+ import { a as guardArgs, c as stripUnknownFlags, l as suggestClosest, m as consoleIO, o as splitAtTerminator, r as localizedOptionsSection, s as stripAutoVersionLine } from "./analyze-njXPaqmd.js";
3
3
  import { docsUrlFor } from "@svelte-vitals/core/internal";
4
4
  import { cli } from "gunshi/bone";
5
5
  import { define } from "gunshi/definition";
@@ -16,7 +16,7 @@ const EMBEDDED_DOCS = [
16
16
  name: "config",
17
17
  title: "The config file",
18
18
  description: "Where svelte-vitals.config lives, every top-level option, how to disable or re-grade a rule, and how to scope rules to routes or files.",
19
- body: "# The config file\n\n## Where it lives\n\nIn the **analyzed directory only** — no upward search. First match wins:\n\n1. `svelte-vitals.config.js`\n2. `svelte-vitals.config.ts`\n\nNo file means built-in defaults. `svelte-vitals install --client config-file` scaffolds one with\nevery option commented out.\n\n`--config <path>` analyzes under the config file at that path instead of the one in the analyzed\ndirectory — no discovery, no merge. A relative path resolves against the directory you run the\ncommand from, never the analyzed directory: from a repo root,\n`svelte-vitals apps/web --config shared/sv.config.js` reads `./shared/sv.config.js`. It accepts\n`.js` and `.ts` only, and a missing or unreadable file exits `2`. Useful for trying a config out\nbefore committing it, and for sharing one config across the apps in a monorepo. CLI only: the Vite\nplugin resolves its own config from the `cwd` passed to `svelteVitals({ ... })` (else the Vite\nconfig root) — share with it by importing the shared file in `vite.config.ts` and spreading it\ninto the plugin's options.\n\n```js\n// svelte-vitals.config.js\nexport default {\n treatDynamicAs: 'warn',\n metaComponents: ['Seo'],\n rules: { 'seo/json-ld': 'off' },\n failOn: 'warning',\n weights: { seo: 2 }\n};\n```\n\nA `.ts` config can `import { defineConfig } from 'svelte-vitals'` for type-checking, but that is a\n**runtime** import: it needs svelte-vitals as a declared dependency. A plain `export default {}`\nin `.js` behaves identically and needs no dependency. Both are ESM — the project must be\n`\"type\": \"module\"` (SvelteKit's default).\n\n## Options\n\n| Option | Type | Default |\n| ---------------- | -------------------------------------------------------------- | ------------------ |\n| `treatDynamicAs` | `'pass' \\| 'warn' \\| 'fail'` | `'pass'` |\n| `metaComponents` | `string[]` | `[]` |\n| `rules` | `Record<ruleId, 'off' \\| Severity \\| { severity?, options? }>` | `{}` |\n| `failOn` | `'critical' \\| 'warning' \\| 'info'` | `'critical'` |\n| `weights` | `Partial<Record<Category, number>>` | every category `1` |\n| `overrides` | `RuleOverride[]` | (none) |\n\n`Severity` is `'critical' | 'warning' | 'info'`. `Category` is `'seo' | 'performance' |\n'correctness' | 'security' | 'architecture' | 'a11y'`. A weight of `0` drops a category from the Health\naverage; setting every category to `0` is an error (exit `2`).\n\n## Turning a rule off or down\n\n```js\nexport default {\n rules: {\n 'seo/json-ld': 'off', // remove its findings entirely\n 'architecture/prop-count': 'info' // keep it, stop it failing the build\n }\n};\n```\n\nMany rules take options, so check whether the finding is a **threshold disagreement** rather than\na defect first. `svelte-vitals explain <rule-id>` prints each option's name, default, bounds, and\nmerge semantics (`integer` replaces, `string-list` appends, `string-map` is spread over).\n\n```js\nexport default {\n rules: {\n 'architecture/prop-count': { options: { max: 12 } }\n }\n};\n```\n\n## Scoping to routes or files (`overrides`)\n\n`rules` applies everywhere; `overrides` applies only where it matches — typically routes that\nare deliberately not public.\n\n```js\nexport default {\n overrides: [\n { files: 'src/routes/(app)/**', rules: { seo: 'off' } },\n { route: '/admin/**', rules: { 'seo/title-presence': 'info' } }\n ]\n};\n```\n\nEach entry needs `rules` (keys are rule ids **or** category names) plus at least one of:\n\n- **`route`** — glob(s) against the route id as reported (`/blog/[slug]`). SvelteKit `(group)`\n segments are **not** in the route id, so use `files` to target a group.\n- **`files`** — glob(s) against the source path.\n\nGlobs are deliberately small: `*` within a segment, `**` across segments, a trailing `/**` also\nmatches the bare prefix. Everything else — including `(`, `)`, `[`, `]` — is literal. Later entries win.\n\n## Precedence\n\nPer field: **CLI flag > config file > built-in default**. One exception — `--rules` and `--ignore`\nare selection, not configuration: `--rules` narrows the run to the ids it names and overrides a\nconfig-file `off` for them, but keeps their declared severity and options; `--ignore` adds `off`\nentries for the ids it names, layered on top of whatever `rules` resolved to, and beats `--rules`\nwhen both name the same rule.\n\n`overrides` has no CLI flag; route policy belongs in a committed file.\n\n## Validation\n\nAn unknown rule id or category, a negative weight, a malformed `overrides` entry, or an invalid\nrule setting is a **hard error (exit `2`)** — a typo must not silently un-gate CI. An unrecognized\n`treatDynamicAs`/`failOn` value, or an unknown top-level key, only warns.\n\n## Related\n\n- `svelte-vitals explain --list` — every rule id\n- `svelte-vitals docs show scoping` — accepting an existing backlog instead of disabling rules"
19
+ body: "# The config file\n\n## Where it lives\n\nIn the **analyzed directory only** — no upward search. First match wins:\n\n1. `svelte-vitals.config.js`\n2. `svelte-vitals.config.ts`\n\nNo file means built-in defaults. `svelte-vitals install --client config-file` scaffolds one with\nevery option commented out.\n\n`--config <path>` analyzes under the config file at that path instead of the one in the analyzed\ndirectory — no discovery, no merge. A relative path resolves against the directory you run the\ncommand from, never the analyzed directory: from a repo root,\n`svelte-vitals apps/web --config shared/sv.config.js` reads `./shared/sv.config.js`. It accepts\n`.js` and `.ts` only, and a missing or unreadable file exits `2`. Useful for trying a config out\nbefore committing it, and for sharing one config across the apps in a monorepo. CLI only: the Vite\nplugin resolves its own config from the `cwd` passed to `svelteVitals({ ... })` (else the Vite\nconfig root) — share with it by importing the shared file in `vite.config.ts` and spreading it\ninto the plugin's options.\n\n```js\n// svelte-vitals.config.js\nexport default {\n treatDynamicAs: 'warn',\n metaComponents: ['Seo'],\n rules: { 'seo/json-ld': 'off' },\n failOn: 'warning',\n weights: { seo: 2 }\n};\n```\n\nA `.ts` config can `import { defineConfig } from 'svelte-vitals'` for type-checking, but that is a\n**runtime** import: it needs svelte-vitals as a declared dependency. A plain `export default {}`\nin `.js` behaves identically and needs no dependency. Both are ESM — the project must be\n`\"type\": \"module\"` (SvelteKit's default).\n\n## Options\n\n| Option | Type | Default |\n| ---------------- | -------------------------------------------------------------- | ------------------ |\n| `treatDynamicAs` | `'pass' \\| 'warn' \\| 'fail'` | `'pass'` |\n| `metaComponents` | `string[]` | `[]` |\n| `rules` | `Record<ruleId, 'off' \\| Severity \\| { severity?, options? }>` | `{}` |\n| `failOn` | `'critical' \\| 'warning' \\| 'info'` | `'critical'` |\n| `weights` | `Partial<Record<Category, number>>` | every category `1` |\n| `overrides` | `RuleOverride[]` | (none) |\n\n`Severity` is `'critical' | 'warning' | 'info'`. `Category` is `'seo' | 'performance' |\n'correctness' | 'security' | 'architecture' | 'a11y'`. A weight of `0` drops a category from the Health\naverage; setting every category to `0` is an error (exit `2`).\n\n`metaComponents` names head-metadata components the analyzer cannot resolve (e.g. from an npm\npackage without an adapter). Components the analyzer can resolve in your own repo are followed\nautomatically — declaring one of those is a no-op; the declaration only kicks in when\nresolution fails.\n\n## Turning a rule off or down\n\n```js\nexport default {\n rules: {\n 'seo/json-ld': 'off', // remove its findings entirely\n 'architecture/prop-count': 'info' // keep it, stop it failing the build\n }\n};\n```\n\nMany rules take options, so check whether the finding is a **threshold disagreement** rather than\na defect first. `svelte-vitals explain <rule-id>` prints each option's name, default, bounds, and\nmerge semantics (`integer` replaces, `string-list` appends, `string-map` is spread over).\n\n```js\nexport default {\n rules: {\n 'architecture/prop-count': { options: { max: 12 } }\n }\n};\n```\n\n## Scoping to routes or files (`overrides`)\n\n`rules` applies everywhere; `overrides` applies only where it matches — typically routes that\nare deliberately not public.\n\n```js\nexport default {\n overrides: [\n { files: 'src/routes/(app)/**', rules: { seo: 'off' } },\n { route: '/admin/**', rules: { 'seo/title-presence': 'info' } }\n ]\n};\n```\n\nEach entry needs `rules` (keys are rule ids **or** category names) plus at least one of:\n\n- **`route`** — glob(s) against the route id as reported (`/blog/[slug]`). SvelteKit `(group)`\n segments are **not** in the route id, so use `files` to target a group.\n- **`files`** — glob(s) against the source path.\n\nGlobs are deliberately small: `*` within a segment, `**` across segments, a trailing `/**` also\nmatches the bare prefix. Everything else — including `(`, `)`, `[`, `]` — is literal. Later entries win.\n\n## Precedence\n\nPer field: **CLI flag > config file > built-in default**. One exception — `--rules` and `--ignore`\nare selection, not configuration: `--rules` narrows the run to the ids it names and overrides a\nconfig-file `off` for them, but keeps their declared severity and options; `--ignore` adds `off`\nentries for the ids it names, layered on top of whatever `rules` resolved to, and beats `--rules`\nwhen both name the same rule.\n\n`overrides` has no CLI flag; route policy belongs in a committed file.\n\n## Validation\n\nAn unknown rule id or category, a negative weight, a malformed `overrides` entry, or an invalid\nrule setting is a **hard error (exit `2`)** — a typo must not silently un-gate CI. An unrecognized\n`treatDynamicAs`/`failOn` value, or an unknown top-level key, only warns.\n\n## Related\n\n- `svelte-vitals explain --list` — every rule id\n- `svelte-vitals docs show scoping` — accepting an existing backlog instead of disabling rules"
20
20
  },
21
21
  {
22
22
  name: "monorepo",
@@ -117,7 +117,7 @@ const DOCS_SHOW_ARGS = {
117
117
  * (`docs/superpowers/specs/2026-08-11-cli-ja-help-design.md`).
118
118
  */
119
119
  async function buildDocsHelpText(rootCommand, locale) {
120
- const ja = locale === "ja" ? await import("./ja-CULhsFnk.js").then((n) => n.n) : void 0;
120
+ const ja = locale === "ja" ? await import("./ja-BDLP3KLG.js").then((n) => n.n) : void 0;
121
121
  const optionsSection = stripAutoVersionLine(await localizedOptionsSection(rootCommand, "svelte-vitals docs", locale, ja?.JA_ARG_DESCRIPTIONS.docs ?? {}));
122
122
  if (locale === "ja") return ja.docsHelpJa(optionsSection);
123
123
  return `svelte-vitals docs — read the bundled guides without leaving the terminal
@@ -1,5 +1,5 @@
1
- import { l as knownRuleIds } from "./src-BSphfbnn.js";
2
- import { a as guardArgs, c as stripUnknownFlags, l as suggestClosest, m as consoleIO, o as splitAtTerminator, r as localizedOptionsSection, s as stripAutoVersionLine } from "./analyze-BaQfhf-Q.js";
1
+ import { l as knownRuleIds } from "./src-hgEeAWHb.js";
2
+ import { a as guardArgs, c as stripUnknownFlags, l as suggestClosest, m as consoleIO, o as splitAtTerminator, r as localizedOptionsSection, s as stripAutoVersionLine } from "./analyze-njXPaqmd.js";
3
3
  import { CATEGORIES } from "@svelte-vitals/core";
4
4
  import { allRules, explainRule } from "@svelte-vitals/core/internal";
5
5
  import { cli } from "gunshi/bone";
@@ -82,7 +82,7 @@ const EXPLAIN_ARGS = {
82
82
  * (`docs/superpowers/specs/2026-08-11-cli-ja-help-design.md`).
83
83
  */
84
84
  async function buildExplainHelpText(explainCommand, locale) {
85
- const ja = locale === "ja" ? await import("./ja-CULhsFnk.js").then((n) => n.n) : void 0;
85
+ const ja = locale === "ja" ? await import("./ja-BDLP3KLG.js").then((n) => n.n) : void 0;
86
86
  const optionsSection = stripAutoVersionLine(await localizedOptionsSection(explainCommand, "svelte-vitals explain", locale, ja?.JA_ARG_DESCRIPTIONS.explain ?? {}));
87
87
  if (locale === "ja") return ja.explainHelpJa(optionsSection);
88
88
  return `svelte-vitals explain — print a rule's rationale, fix, and configurable options
@@ -1,4 +1,4 @@
1
- import { t as ROOT_ARGS } from "./analyze-BaQfhf-Q.js";
2
- import { t as INSTALL_ARGS } from "./install-ckW1HTAL.js";
3
- import { t as JA_ARG_DESCRIPTIONS } from "./ja-CULhsFnk.js";
1
+ import { t as ROOT_ARGS } from "./analyze-njXPaqmd.js";
2
+ import { t as INSTALL_ARGS } from "./install-CzogylBI.js";
3
+ import { t as JA_ARG_DESCRIPTIONS } from "./ja-BDLP3KLG.js";
4
4
  export { INSTALL_ARGS, JA_ARG_DESCRIPTIONS, ROOT_ARGS };
package/dist/index.d.ts CHANGED
@@ -142,6 +142,14 @@ declare function findUnknownRuleIds(ids: string[]): string[];
142
142
  declare function knownRuleIds(): string[];
143
143
  /** The options a rule declares, or undefined when it takes none. */
144
144
  declare function ruleOptionsSpec(id: string): RuleOptionsSpec | undefined;
145
+ /**
146
+ * Stamps the rule registry's provenance onto error messages that list known ids.
147
+ * Two copies of svelte-vitals can coexist in one tree (e.g. the Vite plugin's
148
+ * resolved copy and a directly installed CLI, issue #583); without the versions,
149
+ * a rule that exists in one copy but not the other reads as a mystery rather
150
+ * than a version skew.
151
+ */
152
+ declare function registryTag(): string;
145
153
  //#endregion
146
154
  //#region src/index.d.ts
147
155
  interface RunOptions {
@@ -362,4 +370,4 @@ declare function applyScope(results: Result[], opts: ApplyScopeOptions): Promise
362
370
  */
363
371
  declare function run(opts?: RunOptions): Promise<number>;
364
372
  //#endregion
365
- export { AnalyzeOptions, AnalyzeResult, ApplyScopeOptions, CONFIG_FILENAMES, type LoadedConfigFile, type ParseCache, ProjectError, RunOptions, analyzeProject, applyScope, defineConfig, findUnknownRuleIds, knownRuleIds, loadConfigFile, routeMatcher, ruleOptionsSpec, run, spinnerEnabled };
373
+ export { AnalyzeOptions, AnalyzeResult, ApplyScopeOptions, CONFIG_FILENAMES, type LoadedConfigFile, type ParseCache, ProjectError, RunOptions, analyzeProject, applyScope, defineConfig, findUnknownRuleIds, knownRuleIds, loadConfigFile, registryTag, routeMatcher, ruleOptionsSpec, run, spinnerEnabled };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as spinnerEnabled, c as findUnknownRuleIds, i as run, l as knownRuleIds, n as applyScope, o as CONFIG_FILENAMES, r as defineConfig, s as loadConfigFile, t as analyzeProject, u as ruleOptionsSpec, v as routeMatcher, y as ProjectError } from "./src-BSphfbnn.js";
2
- export { CONFIG_FILENAMES, ProjectError, analyzeProject, applyScope, defineConfig, findUnknownRuleIds, knownRuleIds, loadConfigFile, routeMatcher, ruleOptionsSpec, run, spinnerEnabled };
1
+ import { a as spinnerEnabled, b as ProjectError, c as findUnknownRuleIds, d as ruleOptionsSpec, i as run, l as knownRuleIds, n as applyScope, o as CONFIG_FILENAMES, r as defineConfig, s as loadConfigFile, t as analyzeProject, u as registryTag, y as routeMatcher } from "./src-hgEeAWHb.js";
2
+ export { CONFIG_FILENAMES, ProjectError, analyzeProject, applyScope, defineConfig, findUnknownRuleIds, knownRuleIds, loadConfigFile, registryTag, routeMatcher, ruleOptionsSpec, run, spinnerEnabled };
@@ -118,6 +118,11 @@ otherwise the app directory. Every \`npx svelte-vitals\` below then runs that in
118
118
  of \`node_modules/.bin\`, at the version the lockfile pins rather than whatever the registry serves;
119
119
  the package manager's own runner (\`pnpm exec\`, \`yarn\`, \`bun run\`) is equivalent.
120
120
 
121
+ If \`@svelte-vitals/vite\` is already installed, keep the two on the same rule registry: install a
122
+ \`svelte-vitals\` version the plugin's \`peerDependencies\` range accepts, or update both together —
123
+ a CLI ahead of the plugin accepts config the plugin's copy rejects, so the scan passes and
124
+ \`vite build\` fails on the same file.
125
+
121
126
  Ask nothing yet. Read:
122
127
 
123
128
  | Read | Where | What it decides |
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { _ as readPkg, g as hasDep, h as discoverApps, m as readPackageVersion, o as CONFIG_FILENAMES } from "./src-BSphfbnn.js";
3
- import { a as guardArgs, c as stripUnknownFlags, g as realIO, h as clackPrompts, m as consoleIO, o as splitAtTerminator, p as toList, r as localizedOptionsSection, s as stripAutoVersionLine } from "./analyze-BaQfhf-Q.js";
2
+ import { _ as hasDep, g as discoverApps, h as readPackageVersion, o as CONFIG_FILENAMES, v as readPkg } from "./src-hgEeAWHb.js";
3
+ import { a as guardArgs, c as stripUnknownFlags, g as realIO, h as clackPrompts, m as consoleIO, o as splitAtTerminator, p as toList, r as localizedOptionsSection, s as stripAutoVersionLine } from "./analyze-njXPaqmd.js";
4
4
  import { a as planWorkflowWrite, i as buildWorkflowYaml, n as ACTION_VERSION, r as WORKFLOW_PATH, t as ACTION_SHA } from "./action-pin.generated-C2XJJ3NW.js";
5
5
  import { buildCursorRules, installHeader } from "./install/skill-content.js";
6
6
  import { join } from "node:path";
@@ -71,7 +71,7 @@ function isKind(id, kind) {
71
71
  function buildConfigFileTemplate(opts = {}) {
72
72
  const header = "// svelte-vitals config file — https://oekazuma.github.io/svelte-vitals/guides/configuration/\n";
73
73
  const options = ` // treatDynamicAs: 'pass', // 'pass' | 'warn' | 'fail' — how {data.title}-style dynamic values are scored
74
- // metaComponents: ['Seo'], // component names that resolve SEO tags into <head>
74
+ // metaComponents: ['Seo'], // head-metadata components the analyzer can't resolve (e.g. from an npm package)
75
75
  // rules: {}, // e.g. { 'seo/title-presence': 'off' } to disable a rule
76
76
  // failOn: 'critical', // 'critical' | 'warning' | 'info'
77
77
  // weights: {} // e.g. { seo: 2 } — per-category weight for the combined Health score`;
@@ -818,7 +818,7 @@ const INSTALL_ARGS = {
818
818
  * ja-localized when `locale` is 'ja' (`docs/superpowers/specs/2026-08-11-cli-ja-help-design.md`).
819
819
  */
820
820
  async function buildInstallHelpText(installCommand, locale) {
821
- const ja = locale === "ja" ? await import("./ja-CULhsFnk.js").then((n) => n.n) : void 0;
821
+ const ja = locale === "ja" ? await import("./ja-BDLP3KLG.js").then((n) => n.n) : void 0;
822
822
  const optionsSection = stripAutoVersionLine(await localizedOptionsSection(installCommand, "svelte-vitals install", locale, ja?.JA_ARG_DESCRIPTIONS.install ?? {}));
823
823
  if (locale === "ja") return ja.installHelpJa(optionsSection);
824
824
  return `svelte-vitals install — set up the svelte-vitals Vite integration, Cursor rules, config file, and CI
@@ -34,7 +34,7 @@ var ja_exports = /* @__PURE__ */ __exportAll({
34
34
  */
35
35
  const JA_ARG_DESCRIPTIONS = {
36
36
  root: {
37
- "meta-components": "head メタデータを出力するコンポーネント名(カンマ区切り)",
37
+ "meta-components": "解析が追跡できない head メタデータ出力コンポーネント名(カンマ区切り)",
38
38
  "treat-dynamic-as": "pass | warn | fail(デフォルト: pass)",
39
39
  route: "指定した glob に一致するルートのみ解析",
40
40
  diff: "ref と比較して変更されたファイルの検出結果のみ報告(デフォルト HEAD。例: --diff main)",
@@ -1102,10 +1102,6 @@ async function resolveFileTags(rt, cwd, fileRel, parsed, config, depth, visited,
1102
1102
  broad = broad || result.broad;
1103
1103
  continue;
1104
1104
  }
1105
- if (config.metaComponents.includes(use.name)) {
1106
- broad = true;
1107
- continue;
1108
- }
1109
1105
  const childRel = info ? resolveComponentPath(info.source, fileRel, aliases) : void 0;
1110
1106
  if (childRel && depth > 0 && !visited.has(childRel)) {
1111
1107
  const abs = rt.join(cwd, childRel);
@@ -1120,8 +1116,13 @@ async function resolveFileTags(rt, cwd, fileRel, parsed, config, depth, visited,
1120
1116
  file: childRel
1121
1117
  });
1122
1118
  headings.push(...child.headings);
1119
+ continue;
1123
1120
  }
1124
1121
  }
1122
+ if (config.metaComponents.includes(use.name)) {
1123
+ broad = true;
1124
+ continue;
1125
+ }
1125
1126
  }
1126
1127
  return {
1127
1128
  tags,
@@ -1248,7 +1249,7 @@ function offsetPath(path, base) {
1248
1249
  /**
1249
1250
  * One file's contribution to the route: its own occurrences plus, inline at each component
1250
1251
  * usage, that component's contribution carrying the usage's branch address and repeatability.
1251
- * Anything that cannot be followed (package/adapter/meta component, `<svelte:component>`,
1252
+ * Anything that cannot be followed (package/adapter import, `<svelte:component>`,
1252
1253
  * a cycle, MAX_DEPTH) contributes nothing and opens the world — existential rules stay sound,
1253
1254
  * `no-missing-id-ref` skips the route.
1254
1255
  */
@@ -1277,7 +1278,7 @@ async function composeA11y(ctx, fileRel, parsed, depth, visited, chain) {
1277
1278
  });
1278
1279
  continue;
1279
1280
  }
1280
- const info = ctx.config.metaComponents.includes(node.key) ? void 0 : parsed.imports.get(node.key);
1281
+ const info = parsed.imports.get(node.key);
1281
1282
  const childRel = info ? resolveComponentPath(info.source, fileRel, ctx.aliases) : void 0;
1282
1283
  if (!childRel || depth <= 0 || visited.has(childRel) || !await rt.exists(rt.join(cwd, childRel))) {
1283
1284
  state.fullyResolved = false;
@@ -1347,7 +1348,6 @@ async function resolveRoute(rt, cwd, pageRel, config, layouts, cache, aliases, a
1347
1348
  const a11yCtx = {
1348
1349
  rt,
1349
1350
  cwd,
1350
- config,
1351
1351
  cache,
1352
1352
  aliases,
1353
1353
  state: {
@@ -1947,6 +1947,16 @@ function knownRuleIds() {
1947
1947
  function ruleOptionsSpec(id) {
1948
1948
  return RULE_BY_ID.get(id)?.options;
1949
1949
  }
1950
+ /**
1951
+ * Stamps the rule registry's provenance onto error messages that list known ids.
1952
+ * Two copies of svelte-vitals can coexist in one tree (e.g. the Vite plugin's
1953
+ * resolved copy and a directly installed CLI, issue #583); without the versions,
1954
+ * a rule that exists in one copy but not the other reads as a mystery rather
1955
+ * than a version skew.
1956
+ */
1957
+ function registryTag() {
1958
+ return `svelte-vitals ${readPackageVersion()}, core ${readCoreVersion()}`;
1959
+ }
1950
1960
  //#endregion
1951
1961
  //#region src/config-file.ts
1952
1962
  /**
@@ -2035,7 +2045,7 @@ function validateConfigFile(raw, path) {
2035
2045
  if (!isPlainObject(raw.rules)) throw new Error(`${path}: rules must be an object of rule-id → setting.`);
2036
2046
  const rules = raw.rules;
2037
2047
  const unknown = findUnknownRuleIds(Object.keys(rules));
2038
- if (unknown.length > 0) throw new Error(`${path}: unknown rule id(s) in rules: ${unknown.join(", ")}. Known rule ids: ${knownRuleIds().join(", ")}`);
2048
+ if (unknown.length > 0) throw new Error(`${path}: unknown rule id(s) in rules: ${unknown.join(", ")}. Known rule ids (${registryTag()}): ${knownRuleIds().join(", ")}`);
2039
2049
  for (const [key, setting] of Object.entries(rules)) validateSetting(path, "rules", key, setting, true);
2040
2050
  config.rules = rules;
2041
2051
  }
@@ -2053,7 +2063,7 @@ function validateConfigFile(raw, path) {
2053
2063
  if (!isPlainObject(entry.rules)) throw new Error(`${path}: overrides[${i}].rules must be an object of rule-id/category → setting.`);
2054
2064
  if (Object.keys(entry.rules).length === 0) throw new Error(`${path}: overrides[${i}].rules must contain at least one rule id or category.`);
2055
2065
  const unknown = findUnknownRuleIds(Object.keys(entry.rules).filter((k) => !CATEGORIES.includes(k)));
2056
- if (unknown.length > 0) throw new Error(`${path}: unknown rule id(s) or categories in overrides[${i}].rules: ${unknown.join(", ")}. Known categories: ${CATEGORIES.join(", ")}. Known rule ids: ${knownRuleIds().join(", ")}`);
2066
+ if (unknown.length > 0) throw new Error(`${path}: unknown rule id(s) or categories in overrides[${i}].rules: ${unknown.join(", ")}. Known categories: ${CATEGORIES.join(", ")}. Known rule ids (${registryTag()}): ${knownRuleIds().join(", ")}`);
2057
2067
  for (const [key, setting] of Object.entries(entry.rules)) {
2058
2068
  const isCategory = CATEGORIES.includes(key);
2059
2069
  const baseline = isCategory ? void 0 : resolveRuleOptions(key, ruleOptionsSpec(key), {
@@ -3143,4 +3153,4 @@ async function run(opts = {}) {
3143
3153
  }
3144
3154
  }
3145
3155
  //#endregion
3146
- export { readPkg as _, spinnerEnabled as a, findUnknownRuleIds as c, REPORTER_NAMES as d, isReporterName as f, hasDep as g, discoverApps as h, run as i, knownRuleIds as l, readPackageVersion as m, applyScope as n, CONFIG_FILENAMES as o, readCoreVersion as p, defineConfig$1 as r, loadConfigFile as s, analyzeProject as t, ruleOptionsSpec as u, routeMatcher as v, ProjectError as y };
3156
+ export { hasDep as _, spinnerEnabled as a, ProjectError as b, findUnknownRuleIds as c, ruleOptionsSpec as d, REPORTER_NAMES as f, discoverApps as g, readPackageVersion as h, run as i, knownRuleIds as l, readCoreVersion as m, applyScope as n, CONFIG_FILENAMES as o, isReporterName as p, defineConfig$1 as r, loadConfigFile as s, analyzeProject as t, registryTag as u, readPkg as v, routeMatcher as y };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte-vitals",
3
- "version": "0.51.0",
3
+ "version": "0.52.0",
4
4
  "description": "A deterministic SvelteKit code-health scanner (SEO, performance, correctness, security, architecture, accessibility).",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -51,7 +51,7 @@
51
51
  "magicast": "^0.5.4",
52
52
  "svelte": "^5.56.10",
53
53
  "tinyglobby": "^0.2.17",
54
- "@svelte-vitals/core": "0.47.2"
54
+ "@svelte-vitals/core": "0.48.0"
55
55
  },
56
56
  "devDependencies": {
57
57
  "@gunshi/docs": "0.37.1",