@svelte-vitals/vite 0.31.1 → 0.32.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.
@@ -41,8 +41,10 @@ function collectA11y(root) {
41
41
  const nestedLandmarks = [];
42
42
  const ids = [];
43
43
  const idRefs = [];
44
+ const elementTags = /* @__PURE__ */ new Set();
44
45
  const walk = (el, ctx) => {
45
46
  const tag = el.rawTagName?.toLowerCase();
47
+ if (tag && ctx.inBody) elementTags.add(tag);
46
48
  const roleAttr = el.getAttribute("role");
47
49
  let landmark;
48
50
  if (roleAttr !== void 0) {
@@ -76,12 +78,13 @@ function collectA11y(root) {
76
78
  const nextCtx = {
77
79
  sectioning: ctx.sectioning + (SECTIONING_TAGS.has(tag) ? 1 : 0),
78
80
  asideDemoting: ctx.asideDemoting + (ASIDE_DEMOTING_TAGS.has(tag) ? 1 : 0),
79
- landmarks: landmark ? [...ctx.landmarks, landmark] : ctx.landmarks
81
+ landmarks: landmark ? [...ctx.landmarks, landmark] : ctx.landmarks,
82
+ inBody: ctx.inBody || tag === "body"
80
83
  };
81
84
  for (const child of el.children) walk(child, nextCtx);
82
85
  };
83
- for (const child of root.children) walk(child, { sectioning: 0, asideDemoting: 0, landmarks: [] });
84
- return { landmarks, nestedLandmarks, ids, idRefs };
86
+ for (const child of root.children) walk(child, { sectioning: 0, asideDemoting: 0, landmarks: [], inBody: false });
87
+ return { landmarks, nestedLandmarks, ids, idRefs, elementTags: [...elementTags] };
85
88
  }
86
89
  function parseHtmlHead(html) {
87
90
  const root = parse(html);
@@ -228,7 +231,10 @@ async function collectRenderedHeads(prerenderPagesDir) {
228
231
  idCandidates: [...new Set(parsed.ids)],
229
232
  // The prerendered document IS the closed world: every id/landmark/reference it can ever
230
233
  // have is already in it, unlike source mode which may hit an unresolved component.
231
- fullyResolved: true
234
+ fullyResolved: true,
235
+ elementTags: parsed.elementTags,
236
+ elementsClosed: true,
237
+ file: rel
232
238
  });
233
239
  }
234
240
  return { heads, headings, images, a11y, htmlLang };
@@ -9,7 +9,7 @@ interface SvelteVitalsHookOptions {
9
9
  rules?: Record<string, RuleSetting>;
10
10
  }
11
11
  /**
12
- * SvelteKit `handle` that analyzes each visited page's rendered `<head>`, in dev only,
12
+ * SvelteKit `handle` that analyzes each visited page's rendered HTML, in dev only,
13
13
  * and (when the live dashboard is enabled) feeds the results in — upgrading that
14
14
  * route's dashboard findings from static (source-only) to `measured` (real rendered
15
15
  * HTML). Add it to `src/hooks.server.ts`, e.g. `sequence(svelteVitalsHandle())`.
@@ -2,7 +2,7 @@ import {
2
2
  isLoopbackOrigin,
3
3
  parseHtmlHead,
4
4
  toOccurrenceMap
5
- } from "../chunk-LTT4HAEL.js";
5
+ } from "../chunk-KII3SU3J.js";
6
6
 
7
7
  // src/hooks/handle.ts
8
8
  import { DEV } from "esm-env";
@@ -10,6 +10,7 @@ import { defineConfig } from "@svelte-vitals/core";
10
10
  import {
11
11
  allRules,
12
12
  applyRuleSeverities,
13
+ defaultProject,
13
14
  effectiveSeverity,
14
15
  formatFailedRuleWarning,
15
16
  isPenalized,
@@ -43,16 +44,7 @@ async function postIngest(origin, route, results, failedRuleIds) {
43
44
  }
44
45
  async function analyzeAndIngest(html, route, origin, rules, config, lastSignature) {
45
46
  try {
46
- const {
47
- tags,
48
- htmlLang,
49
- headings: levels,
50
- images: imgs,
51
- landmarks,
52
- nestedLandmarks,
53
- ids,
54
- idRefs
55
- } = parseHtmlHead(html);
47
+ const { tags, headings: levels, images: imgs, landmarks, nestedLandmarks, ids, idRefs } = parseHtmlHead(html);
56
48
  const head = { route, source: "rendered", tags, file: route };
57
49
  const headings = [
58
50
  { route, headings: levels.map((level) => ({ level, line: 0, file: route })) }
@@ -69,13 +61,12 @@ async function analyzeAndIngest(html, route, origin, rules, config, lastSignatur
69
61
  fullyResolved: true
70
62
  }
71
63
  ];
72
- const project = { hasRobotsTxt: true, hasSitemap: true, htmlLang };
73
64
  const { results: ruleResults, failedRules } = await runRules(rules, {
74
65
  heads: [head],
75
66
  headings,
76
67
  images,
77
68
  a11y,
78
- project,
69
+ project: defaultProject,
79
70
  config
80
71
  });
81
72
  const results = applyRuleSeverities(ruleResults, config);
@@ -99,7 +90,7 @@ function svelteVitalsHandle(options = {}) {
99
90
  metaComponents: options.metaComponents ?? [],
100
91
  rules: options.rules ?? {}
101
92
  });
102
- const rules = selectRules(allRules, config);
93
+ const rules = selectRules(allRules, config).filter((r) => r.scope === "route" && !r.crossRoute);
103
94
  const lastSignature = /* @__PURE__ */ new Map();
104
95
  return ({ event, resolve }) => {
105
96
  let buffer = "";
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  collectRenderedHeads,
3
3
  isLoopbackOrigin
4
- } from "./chunk-LTT4HAEL.js";
4
+ } from "./chunk-KII3SU3J.js";
5
5
 
6
6
  // src/plugin.ts
7
7
  import { existsSync } from "fs";
@@ -24,6 +24,8 @@ import {
24
24
  selectRules,
25
25
  applyRuleSeverities,
26
26
  applyOverrides,
27
+ applyInlineDirectives,
28
+ unknownDirectiveIds,
27
29
  runRules,
28
30
  withFailedRulesOff,
29
31
  formatFailedRuleWarning,
@@ -184,6 +186,11 @@ async function analyze(prerenderPagesDir, cwd, options, extraProjectFacts, resol
184
186
  };
185
187
  const kitModules = await collectKitModuleFacts(cwd, project.kitAliases);
186
188
  const selected = selectRules(allRules, config);
189
+ if (selected.some((r) => r.id === "a11y/unverified-id-ref")) {
190
+ warnings.push(
191
+ "a11y/unverified-id-ref has no effect in rendered mode \u2014 the prerendered document is always fully resolved."
192
+ );
193
+ }
187
194
  const {
188
195
  results: rawResults,
189
196
  examined,
@@ -199,8 +206,19 @@ async function analyze(prerenderPagesDir, cwd, options, extraProjectFacts, resol
199
206
  kitModules,
200
207
  sourceFiles
201
208
  });
202
- const results = applyOverrides(applyRuleSeverities(rawResults, config), config);
209
+ const directives = /* @__PURE__ */ new Map();
210
+ for (const c of components) directives.set(c.file, c.suppressions ?? []);
211
+ for (const m of kitModules) directives.set(m.file, m.suppressions ?? []);
212
+ if (project.viteMinifyDisabled?.file)
213
+ directives.set(project.viteMinifyDisabled.file, project.viteMinifyDisabled.suppressions ?? []);
214
+ const results = applyInlineDirectives(
215
+ applyOverrides(applyRuleSeverities(rawResults, config), config),
216
+ directives,
217
+ selected,
218
+ config
219
+ );
203
220
  warnings.push(...skippedFileWarnings([...components, ...kitModules]));
221
+ warnings.push(...unknownDirectiveIds(directives, allRules));
204
222
  for (const f of failedRules) warnings.push(formatFailedRuleWarning(f));
205
223
  const scoringConfig = withFailedRulesOff(
206
224
  config,
@@ -235,18 +253,28 @@ Scanned ${components.length} component(s) under src/ for Correctness/Security/Ar
235
253
  // src/minify-flag.ts
236
254
  import { readFile as readFile3 } from "fs/promises";
237
255
  import { relative } from "path";
238
- import { findMinifyDisabled } from "@svelte-vitals/core/internal";
256
+ import {
257
+ collectSuppressions,
258
+ findMinifyDisabled
259
+ } from "@svelte-vitals/core/internal";
239
260
  async function resolveMinifyDisabled(minify, configFile, root) {
240
261
  if (minify !== false) return void 0;
241
262
  if (!configFile) return {};
242
263
  const rel = relative(root, configFile).split("\\").join("/");
243
264
  const file = rel === "" ? configFile.split("\\").join("/") : rel;
244
265
  let line;
266
+ let suppressions;
245
267
  try {
246
- line = findMinifyDisabled(await readFile3(configFile, "utf8"))?.line;
268
+ const source = await readFile3(configFile, "utf8");
269
+ line = findMinifyDisabled(source)?.line;
270
+ suppressions = collectSuppressions(source);
247
271
  } catch {
248
272
  }
249
- return { file, ...line !== void 0 ? { line } : {} };
273
+ return {
274
+ file,
275
+ ...line !== void 0 ? { line } : {},
276
+ ...suppressions !== void 0 ? { suppressions } : {}
277
+ };
250
278
  }
251
279
 
252
280
  // src/ui/middleware.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svelte-vitals/vite",
3
- "version": "0.31.1",
3
+ "version": "0.32.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",
@@ -42,11 +42,11 @@
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.45.0",
46
- "svelte-vitals": "0.48.1"
45
+ "svelte-vitals": "0.50.0",
46
+ "@svelte-vitals/core": "0.47.0"
47
47
  },
48
48
  "peerDependencies": {
49
- "@sveltejs/kit": "^2.70.2",
49
+ "@sveltejs/kit": "^2.70.3",
50
50
  "vite": "^8.2.1"
51
51
  },
52
52
  "peerDependenciesMeta": {
@@ -55,7 +55,7 @@
55
55
  }
56
56
  },
57
57
  "devDependencies": {
58
- "@sveltejs/kit": "^2.70.2",
58
+ "@sveltejs/kit": "^2.70.3",
59
59
  "@types/node": "^24.13.3",
60
60
  "jsdom": "^30.0.1",
61
61
  "svelte": "^5.56.9",