@svelte-vitals/vite 0.30.1 → 0.31.1

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.
package/README.md CHANGED
@@ -7,7 +7,7 @@ Vite/SvelteKit plugin for [svelte-vitals](https://github.com/oekazuma/svelte-vit
7
7
  - **Build-time gate** — piggybacks on `vite build`, parses the **prerendered HTML** (head metadata, images, headings, landmarks and ids), and runs the same rule engine as the CLI (SEO, Performance, Correctness, Security, Architecture, Accessibility) — library-agnostic, because it inspects the real output. Fails the build when findings reach `failOn`.
8
8
  - **Live dev dashboard** — on by default during `vite dev`: a searchable, filterable code-health dashboard at `/__svelte-vitals/`, covering the whole project from startup via static analysis, refined to real rendered values as you browse.
9
9
 
10
- > **ESM-only** (Node 22.13+). Ships ES modules only; `require()` is unsupported by design.
10
+ > **ESM-only** (Node 24.16+). Ships ES modules only; `require()` is unsupported by design.
11
11
 
12
12
  ## Usage
13
13
 
@@ -34,7 +34,7 @@ export default {
34
34
  - `cwd` — project root (defaults to the Vite config root).
35
35
  - `ui` — serve the live dev dashboard (default `true`); pass `false` to keep only the build-time gate.
36
36
 
37
- Only **prerendered** routes are analyzed at build time; for SSR/dynamic routes use the `svelte-vitals` CLI, or browse them in the live dashboard during `vite dev`.
37
+ At build time the **HTML** check covers prerendered routes only; the source scan applies project-wide. SSR and dynamic routes have no build output, so nothing checks their rendered HTML here — the `svelte-vitals` CLI covers them by source analysis, and browsing them during `vite dev` gets the live dashboard a rendered reading.
38
38
 
39
39
  See [Live dashboard](https://oekazuma.github.io/svelte-vitals/guides/dev-dashboard/) and [Plugin mode](https://oekazuma.github.io/svelte-vitals/guides/plugin-mode/) for the full picture.
40
40
 
@@ -1,6 +1,13 @@
1
1
  // src/providers/rendered/parse-html.ts
2
2
  import { parse } from "node-html-parser";
3
- import { decodeFragmentId, splitTokens, isTopFragment, LANDMARK_ROLES, IDREF_ATTRS } from "@svelte-vitals/core";
3
+ import {
4
+ decodeFragmentId,
5
+ splitTokens,
6
+ isTopFragment,
7
+ stripTextDirective,
8
+ LANDMARK_ROLES,
9
+ IDREF_ATTRS
10
+ } from "@svelte-vitals/core/internal";
4
11
  function attrValue(v) {
5
12
  return v !== void 0 && v.trim().length > 0 ? "static" : "absent";
6
13
  }
@@ -28,6 +35,7 @@ function isClassicScriptType(type) {
28
35
  return normalized === "" || JS_MIME_TYPES.has(normalized);
29
36
  }
30
37
  var SECTIONING_TAGS = /* @__PURE__ */ new Set(["article", "aside", "main", "nav", "section"]);
38
+ var ASIDE_DEMOTING_TAGS = /* @__PURE__ */ new Set(["article", "aside", "nav", "section"]);
31
39
  function collectA11y(root) {
32
40
  const landmarks = [];
33
41
  const nestedLandmarks = [];
@@ -44,6 +52,9 @@ function collectA11y(root) {
44
52
  landmark = "main";
45
53
  } else if ((tag === "header" || tag === "footer") && ctx.sectioning === 0) {
46
54
  landmark = tag === "header" ? "banner" : "contentinfo";
55
+ } else if (tag === "aside") {
56
+ const named = ["aria-label", "aria-labelledby"].some((a) => (el.getAttribute(a) ?? "").trim().length > 0);
57
+ landmark = ctx.asideDemoting === 0 || named ? "complementary" : void 0;
47
58
  }
48
59
  if (landmark) {
49
60
  landmarks.push(landmark);
@@ -53,8 +64,9 @@ function collectA11y(root) {
53
64
  const id = el.getAttribute("id");
54
65
  if (id && id.trim()) ids.push(id);
55
66
  const href = el.getAttribute("href");
56
- if (href && href.startsWith("#") && href.length > 1) {
57
- const fragment = decodeFragmentId(href.slice(1));
67
+ const target = href?.startsWith("#") ? stripTextDirective(href.slice(1)) : "";
68
+ if (target) {
69
+ const fragment = decodeFragmentId(target);
58
70
  if (!isTopFragment(fragment)) idRefs.push({ id: fragment, attr: "href" });
59
71
  }
60
72
  for (const attr of IDREF_ATTRS) {
@@ -63,11 +75,12 @@ function collectA11y(root) {
63
75
  if (tag === "template") return;
64
76
  const nextCtx = {
65
77
  sectioning: ctx.sectioning + (SECTIONING_TAGS.has(tag) ? 1 : 0),
78
+ asideDemoting: ctx.asideDemoting + (ASIDE_DEMOTING_TAGS.has(tag) ? 1 : 0),
66
79
  landmarks: landmark ? [...ctx.landmarks, landmark] : ctx.landmarks
67
80
  };
68
81
  for (const child of el.children) walk(child, nextCtx);
69
82
  };
70
- for (const child of root.children) walk(child, { sectioning: 0, landmarks: [] });
83
+ for (const child of root.children) walk(child, { sectioning: 0, asideDemoting: 0, landmarks: [] });
71
84
  return { landmarks, nestedLandmarks, ids, idRefs };
72
85
  }
73
86
  function parseHtmlHead(html) {
@@ -2,21 +2,21 @@ import {
2
2
  isLoopbackOrigin,
3
3
  parseHtmlHead,
4
4
  toOccurrenceMap
5
- } from "../chunk-6FDRI4Q6.js";
5
+ } from "../chunk-LTT4HAEL.js";
6
6
 
7
7
  // src/hooks/handle.ts
8
8
  import { DEV } from "esm-env";
9
+ import { defineConfig } from "@svelte-vitals/core";
9
10
  import {
10
11
  allRules,
11
12
  applyRuleSeverities,
12
- defineConfig,
13
13
  effectiveSeverity,
14
14
  formatFailedRuleWarning,
15
15
  isPenalized,
16
16
  runRules,
17
17
  selectRules,
18
18
  terminalSafe
19
- } from "@svelte-vitals/core";
19
+ } from "@svelte-vitals/core/internal";
20
20
  function findingSignature(results, config) {
21
21
  return results.filter((r) => isPenalized(r.detection, config.treatDynamicAs)).map((r) => `${r.id}:${effectiveSeverity(r, config)}:${r.detection.presence}:${r.detection.value}`).sort().join("|");
22
22
  }
package/dist/index.js CHANGED
@@ -1,23 +1,24 @@
1
1
  import {
2
2
  collectRenderedHeads,
3
3
  isLoopbackOrigin
4
- } from "./chunk-6FDRI4Q6.js";
4
+ } from "./chunk-LTT4HAEL.js";
5
5
 
6
6
  // src/plugin.ts
7
7
  import { existsSync } from "fs";
8
8
  import { writeFile, mkdir } from "fs/promises";
9
9
  import { join as join3, isAbsolute, dirname, relative as relative3, basename, sep as sep2 } from "path";
10
+ import { CATEGORIES as CATEGORIES2 } from "@svelte-vitals/core";
10
11
  import {
11
- CATEGORIES as CATEGORIES2,
12
12
  defaultConfig,
13
13
  resolveRuleOptions,
14
14
  shouldSkipRangeCheck,
15
15
  terminalSafe,
16
16
  validateRuleSetting
17
- } from "@svelte-vitals/core";
18
- import { findUnknownRuleIds, knownRuleIds, ruleOptionsSpec } from "svelte-vitals";
17
+ } from "@svelte-vitals/core/internal";
18
+ import { CONFIG_FILENAMES, findUnknownRuleIds, knownRuleIds, ruleOptionsSpec } from "svelte-vitals";
19
19
 
20
20
  // src/analyze.ts
21
+ import { defineConfig } from "@svelte-vitals/core";
21
22
  import {
22
23
  allRules,
23
24
  selectRules,
@@ -26,18 +27,19 @@ import {
26
27
  runRules,
27
28
  withFailedRulesOff,
28
29
  formatFailedRuleWarning,
30
+ skippedFileWarnings,
29
31
  computeScore,
30
32
  summarize,
31
33
  hasFailureAtOrAbove,
32
34
  formatConsoleReport,
33
- formatJsonReport,
34
- defineConfig
35
- } from "@svelte-vitals/core";
35
+ formatJsonReport
36
+ } from "@svelte-vitals/core/internal";
36
37
  import { loadConfigFile } from "svelte-vitals";
37
38
 
38
39
  // src/providers/rendered/project.ts
39
40
  import { access, readFile } from "fs/promises";
40
41
  import { join } from "path";
42
+ import "@svelte-vitals/core";
41
43
  import {
42
44
  ROBOTS_SOURCE_PATHS,
43
45
  SITEMAP_SOURCE_PATHS,
@@ -45,7 +47,7 @@ import {
45
47
  VITE_CONFIG_FILES,
46
48
  resolveKitAliases,
47
49
  resolveKitPathsBase
48
- } from "@svelte-vitals/core";
50
+ } from "@svelte-vitals/core/internal";
49
51
  async function exists(path) {
50
52
  try {
51
53
  await access(path);
@@ -104,10 +106,12 @@ import { glob } from "tinyglobby";
104
106
  import {
105
107
  collectComponentFacts as collect,
106
108
  collectKitModuleFacts as collectKit,
107
- collectSourceFiles as collectFiles
108
- } from "@svelte-vitals/core";
109
+ collectSourceFiles as collectFiles,
110
+ withReadLimit
111
+ } from "@svelte-vitals/core/internal";
112
+ var boundedRead = withReadLimit((path) => readFile2(path, "utf8"));
109
113
  var nodeRuntime = {
110
- readFile: (path) => readFile2(path, "utf8"),
114
+ readFile: (path) => boundedRead(path),
111
115
  async exists(path) {
112
116
  try {
113
117
  await access2(path);
@@ -196,6 +200,7 @@ async function analyze(prerenderPagesDir, cwd, options, extraProjectFacts, resol
196
200
  sourceFiles
197
201
  });
198
202
  const results = applyOverrides(applyRuleSeverities(rawResults, config), config);
203
+ warnings.push(...skippedFileWarnings([...components, ...kitModules]));
199
204
  for (const f of failedRules) warnings.push(formatFailedRuleWarning(f));
200
205
  const scoringConfig = withFailedRulesOff(
201
206
  config,
@@ -230,7 +235,7 @@ Scanned ${components.length} component(s) under src/ for Correctness/Security/Ar
230
235
  // src/minify-flag.ts
231
236
  import { readFile as readFile3 } from "fs/promises";
232
237
  import { relative } from "path";
233
- import { findMinifyDisabled } from "@svelte-vitals/core";
238
+ import { findMinifyDisabled } from "@svelte-vitals/core/internal";
234
239
  async function resolveMinifyDisabled(minify, configFile, root) {
235
240
  if (minify !== false) return void 0;
236
241
  if (!configFile) return {};
@@ -245,14 +250,12 @@ async function resolveMinifyDisabled(minify, configFile, root) {
245
250
  }
246
251
 
247
252
  // src/ui/middleware.ts
248
- import { CATEGORIES, renderAppShell } from "@svelte-vitals/core";
253
+ import { CATEGORIES } from "@svelte-vitals/core";
254
+ import { renderAppShell } from "@svelte-vitals/core/internal";
249
255
 
250
256
  // src/ui/snapshot.ts
251
- import {
252
- buildJsonReport,
253
- safeHref,
254
- withFailedRulesOff as withFailedRulesOff2
255
- } from "@svelte-vitals/core";
257
+ import "@svelte-vitals/core";
258
+ import { buildJsonReport, safeHref, withFailedRulesOff as withFailedRulesOff2 } from "@svelte-vitals/core/internal";
256
259
  function sanitizeDocsUrl(issue) {
257
260
  if (issue.docsUrl === void 0) return issue;
258
261
  if (safeHref(issue.docsUrl) !== null) return issue;
@@ -522,9 +525,9 @@ function createAnalysisRunner(opts) {
522
525
  var CONFIG_BASENAMES = /* @__PURE__ */ new Set([
523
526
  "svelte.config.js",
524
527
  "svelte.config.ts",
525
- "svelte-vitals.config.mjs",
526
- "svelte-vitals.config.js",
527
- "svelte-vitals.config.ts",
528
+ // The svelte-vitals config names come from the CLI's own loader list, so the watcher
529
+ // can never drift from what analyze() actually loads.
530
+ ...CONFIG_FILENAMES,
528
531
  "vite.config.js",
529
532
  "vite.config.mjs",
530
533
  "vite.config.ts",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svelte-vitals/vite",
3
- "version": "0.30.1",
3
+ "version": "0.31.1",
4
4
  "description": "Vite/SvelteKit plugin for svelte-vitals — analyzes prerendered HTML during vite build, plus a live dev dashboard.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -22,7 +22,7 @@
22
22
  },
23
23
  "homepage": "https://github.com/oekazuma/svelte-vitals#readme",
24
24
  "engines": {
25
- "node": ">=22.13.0"
25
+ "node": ">=24.16.0"
26
26
  },
27
27
  "sideEffects": false,
28
28
  "exports": {
@@ -42,8 +42,8 @@
42
42
  "esm-env": "^1.2.2",
43
43
  "node-html-parser": "^9.0.1",
44
44
  "tinyglobby": "^0.2.17",
45
- "@svelte-vitals/core": "0.43.1",
46
- "svelte-vitals": "0.47.1"
45
+ "@svelte-vitals/core": "0.45.0",
46
+ "svelte-vitals": "0.48.1"
47
47
  },
48
48
  "peerDependencies": {
49
49
  "@sveltejs/kit": "^2.70.2",
@@ -58,12 +58,12 @@
58
58
  "@sveltejs/kit": "^2.70.2",
59
59
  "@types/node": "^24.13.3",
60
60
  "jsdom": "^30.0.1",
61
- "svelte": "^5.56.8",
61
+ "svelte": "^5.56.9",
62
62
  "vite": "^8.2.1"
63
63
  },
64
64
  "scripts": {
65
65
  "build": "tsup",
66
- "bench": "node scripts/bench/bench.mjs",
66
+ "bench": "node scripts/bench/bench.js",
67
67
  "typecheck": "tsc --noEmit",
68
68
  "test": "vitest run"
69
69
  }