@svelte-vitals/vite 0.32.1 → 0.32.3

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,12 +1,12 @@
1
- import { Handle } from '@sveltejs/kit';
2
- import { RuleSetting } from '@svelte-vitals/core';
3
-
1
+ import { RuleSetting } from "@svelte-vitals/core";
2
+ import { Handle } from "@sveltejs/kit";
3
+ //#region src/hooks/handle.d.ts
4
4
  /** Options for the dev-time SvelteKit handle. A focused subset of the plugin options. */
5
5
  interface SvelteVitalsHookOptions {
6
- /** Component names treated as meta sources (design §11 layer 4). Mirrors the plugin option. */
7
- metaComponents?: string[];
8
- /** Per-rule overrides keyed by rule id, e.g. `{ 'seo/json-ld': 'off' }`. Mirrors the plugin option. */
9
- rules?: Record<string, RuleSetting>;
6
+ /** Component names treated as meta sources (design §11 layer 4). Mirrors the plugin option. */
7
+ metaComponents?: string[];
8
+ /** Per-rule overrides keyed by rule id, e.g. `{ 'seo/json-ld': 'off' }`. Mirrors the plugin option. */
9
+ rules?: Record<string, RuleSetting>;
10
10
  }
11
11
  /**
12
12
  * SvelteKit `handle` that analyzes each visited page's rendered HTML, in dev only,
@@ -15,5 +15,5 @@ interface SvelteVitalsHookOptions {
15
15
  * HTML). Add it to `src/hooks.server.ts`, e.g. `sequence(svelteVitalsHandle())`.
16
16
  */
17
17
  declare function svelteVitalsHandle(options?: SvelteVitalsHookOptions): Handle;
18
-
19
- export { type SvelteVitalsHookOptions, svelteVitalsHandle };
18
+ //#endregion
19
+ export { type SvelteVitalsHookOptions, svelteVitalsHandle };
@@ -1,109 +1,112 @@
1
- import {
2
- isLoopbackOrigin,
3
- parseHtmlHead,
4
- toOccurrenceMap
5
- } from "../chunk-KII3SU3J.js";
6
-
7
- // src/hooks/handle.ts
8
- import { DEV } from "esm-env";
1
+ import { i as parseHtmlHead, r as toOccurrenceMap, t as isLoopbackOrigin } from "../loopback-dIwCgCTG.js";
9
2
  import { defineConfig } from "@svelte-vitals/core";
10
- import {
11
- allRules,
12
- applyRuleSeverities,
13
- defaultProject,
14
- effectiveSeverity,
15
- formatFailedRuleWarning,
16
- isPenalized,
17
- runRules,
18
- selectRules,
19
- terminalSafe
20
- } from "@svelte-vitals/core/internal";
3
+ import { allRules, applyRuleSeverities, defaultProject, effectiveSeverity, formatFailedRuleWarning, isPenalized, runRules, selectRules, terminalSafe } from "@svelte-vitals/core/internal";
4
+ import { DEV } from "esm-env";
5
+ //#region src/hooks/handle.ts
6
+ /** Stable signature of a route's penalized findings, so ingest is skipped when a repeat visit finds nothing new. */
21
7
  function findingSignature(results, config) {
22
- return results.filter((r) => isPenalized(r.detection, config.treatDynamicAs)).map((r) => `${r.id}:${effectiveSeverity(r, config)}:${r.detection.presence}:${r.detection.value}`).sort().join("|");
8
+ return results.filter((r) => isPenalized(r.detection, config.treatDynamicAs)).map((r) => `${r.id}:${effectiveSeverity(r, config)}:${r.detection.presence}:${r.detection.value}`).sort().join("|");
23
9
  }
24
- var warn = (line) => console.warn(terminalSafe(line));
10
+ const warn = (line) => console.warn(terminalSafe(line));
25
11
  async function postIngest(origin, route, results, failedRuleIds) {
26
- if (!isLoopbackOrigin(origin)) {
27
- if (globalThis.process?.env?.SVELTE_VITALS_DEBUG) {
28
- warn(
29
- `[svelte-vitals] live UI ingest skipped for non-loopback origin ${origin} \u2014 open the dashboard via localhost`
30
- );
31
- }
32
- return;
33
- }
34
- try {
35
- await fetch(`${origin}/__svelte-vitals/ingest`, {
36
- method: "POST",
37
- headers: { "content-type": "application/json" },
38
- // failedRuleIds is always sent, empty array included, so a route that recovers from
39
- // a previously-crashing rule clears its stale entry on the receiving store.
40
- body: JSON.stringify({ route, results, failedRuleIds })
41
- });
42
- } catch {
43
- }
12
+ if (!isLoopbackOrigin(origin)) {
13
+ if (globalThis.process?.env?.SVELTE_VITALS_DEBUG) warn(`svelte-vitals: live UI ingest skipped for non-loopback origin ${origin} — open the dashboard via localhost`);
14
+ return;
15
+ }
16
+ try {
17
+ await fetch(`${origin}/__svelte-vitals/ingest`, {
18
+ method: "POST",
19
+ headers: { "content-type": "application/json" },
20
+ body: JSON.stringify({
21
+ route,
22
+ results,
23
+ failedRuleIds
24
+ })
25
+ });
26
+ } catch {}
44
27
  }
45
28
  async function analyzeAndIngest(html, route, origin, rules, config, lastSignature) {
46
- try {
47
- const { tags, headings: levels, images: imgs, landmarks, nestedLandmarks, ids, idRefs } = parseHtmlHead(html);
48
- const head = { route, source: "rendered", tags, file: route };
49
- const headings = [
50
- { route, headings: levels.map((level) => ({ level, line: 0, file: route })) }
51
- ];
52
- const images = [{ route, images: imgs.map((img) => ({ ...img, file: route })) }];
53
- const a11y = [
54
- {
55
- route,
56
- landmarks: toOccurrenceMap(landmarks, route),
57
- nestedLandmarks: nestedLandmarks.map((n) => ({ ...n, file: route, line: 0 })),
58
- ids: toOccurrenceMap(ids, route),
59
- idRefs: idRefs.map((r) => ({ ...r, file: route, line: 0 })),
60
- idCandidates: [...new Set(ids)],
61
- fullyResolved: true
62
- }
63
- ];
64
- const { results: ruleResults, failedRules } = await runRules(rules, {
65
- heads: [head],
66
- headings,
67
- images,
68
- a11y,
69
- project: defaultProject,
70
- config
71
- });
72
- const results = applyRuleSeverities(ruleResults, config);
73
- const failedRuleIds = failedRules.map((f) => f.id);
74
- if (failedRules.length > 0 && globalThis.process?.env?.SVELTE_VITALS_DEBUG) {
75
- for (const f of failedRules) warn(formatFailedRuleWarning(f));
76
- }
77
- const signature = `${findingSignature(results, config)}|failed:${[...failedRuleIds].sort().join(",")}`;
78
- if (lastSignature.get(route) === signature) return;
79
- lastSignature.set(route, signature);
80
- if (globalThis.process?.env?.SVELTE_VITALS_UI) void postIngest(origin, route, results, failedRuleIds);
81
- } catch (err) {
82
- if (globalThis.process?.env?.SVELTE_VITALS_DEBUG) {
83
- warn(`[svelte-vitals] dev analysis failed: ${err instanceof Error ? err.message : String(err)}`);
84
- }
85
- }
29
+ try {
30
+ const { tags, headings: levels, images: imgs, landmarks, nestedLandmarks, ids, idRefs } = parseHtmlHead(html);
31
+ const head = {
32
+ route,
33
+ source: "rendered",
34
+ tags,
35
+ file: route
36
+ };
37
+ const headings = [{
38
+ route,
39
+ headings: levels.map((level) => ({
40
+ level,
41
+ line: 0,
42
+ file: route
43
+ }))
44
+ }];
45
+ const images = [{
46
+ route,
47
+ images: imgs.map((img) => ({
48
+ ...img,
49
+ file: route
50
+ }))
51
+ }];
52
+ const a11y = [{
53
+ route,
54
+ landmarks: toOccurrenceMap(landmarks, route),
55
+ nestedLandmarks: nestedLandmarks.map((n) => ({
56
+ ...n,
57
+ file: route,
58
+ line: 0
59
+ })),
60
+ ids: toOccurrenceMap(ids, route),
61
+ idRefs: idRefs.map((r) => ({
62
+ ...r,
63
+ file: route,
64
+ line: 0
65
+ })),
66
+ idCandidates: [...new Set(ids)],
67
+ fullyResolved: true
68
+ }];
69
+ const { results: ruleResults, failedRules } = await runRules(rules, {
70
+ heads: [head],
71
+ headings,
72
+ images,
73
+ a11y,
74
+ project: defaultProject,
75
+ config
76
+ });
77
+ const results = applyRuleSeverities(ruleResults, config);
78
+ const failedRuleIds = failedRules.map((f) => f.id);
79
+ if (failedRules.length > 0 && globalThis.process?.env?.SVELTE_VITALS_DEBUG) for (const f of failedRules) warn(formatFailedRuleWarning(f));
80
+ const signature = `${findingSignature(results, config)}|failed:${[...failedRuleIds].sort().join(",")}`;
81
+ if (lastSignature.get(route) === signature) return;
82
+ lastSignature.set(route, signature);
83
+ if (globalThis.process?.env?.SVELTE_VITALS_UI) postIngest(origin, route, results, failedRuleIds);
84
+ } catch (err) {
85
+ if (globalThis.process?.env?.SVELTE_VITALS_DEBUG) warn(`svelte-vitals: dev analysis failed: ${err instanceof Error ? err.message : String(err)}`);
86
+ }
86
87
  }
88
+ /**
89
+ * SvelteKit `handle` that analyzes each visited page's rendered HTML, in dev only,
90
+ * and (when the live dashboard is enabled) feeds the results in — upgrading that
91
+ * route's dashboard findings from static (source-only) to `measured` (real rendered
92
+ * HTML). Add it to `src/hooks.server.ts`, e.g. `sequence(svelteVitalsHandle())`.
93
+ */
87
94
  function svelteVitalsHandle(options = {}) {
88
- if (!DEV) return ({ event, resolve }) => resolve(event);
89
- const config = defineConfig({
90
- metaComponents: options.metaComponents ?? [],
91
- rules: options.rules ?? {}
92
- });
93
- const rules = selectRules(allRules, config).filter((r) => r.scope === "route" && !r.crossRoute);
94
- const lastSignature = /* @__PURE__ */ new Map();
95
- return ({ event, resolve }) => {
96
- let buffer = "";
97
- return resolve(event, {
98
- transformPageChunk: ({ html, done }) => {
99
- buffer += html;
100
- if (done && event.route.id != null)
101
- void analyzeAndIngest(buffer, event.route.id, event.url.origin, rules, config, lastSignature);
102
- return html;
103
- }
104
- });
105
- };
95
+ if (!DEV) return ({ event, resolve }) => resolve(event);
96
+ const config = defineConfig({
97
+ metaComponents: options.metaComponents ?? [],
98
+ rules: options.rules ?? {}
99
+ });
100
+ const rules = selectRules(allRules, config).filter((r) => r.scope === "route" && !r.crossRoute);
101
+ const lastSignature = /* @__PURE__ */ new Map();
102
+ return ({ event, resolve }) => {
103
+ let buffer = "";
104
+ return resolve(event, { transformPageChunk: ({ html, done }) => {
105
+ buffer += html;
106
+ if (done && event.route.id != null) analyzeAndIngest(buffer, event.route.id, event.url.origin, rules, config, lastSignature);
107
+ return html;
108
+ } });
109
+ };
106
110
  }
107
- export {
108
- svelteVitalsHandle
109
- };
111
+ //#endregion
112
+ export { svelteVitalsHandle };
package/dist/index.d.ts CHANGED
@@ -1,33 +1,33 @@
1
- import { Plugin } from 'vite';
2
- import { TreatDynamicAs, RuleSetting, RuleOverride, Severity, Category } from '@svelte-vitals/core';
3
-
1
+ import { Category, RuleOverride, RuleSetting, Severity, TreatDynamicAs } from "@svelte-vitals/core";
2
+ import { Plugin } from "vite";
3
+ //#region src/plugin.d.ts
4
4
  interface SvelteVitalsOptions {
5
- /** Project root (defaults to the Vite config root / cwd). */
6
- cwd?: string;
7
- treatDynamicAs?: TreatDynamicAs;
8
- metaComponents?: string[];
9
- rules?: Record<string, RuleSetting>;
10
- /** Route-/file-scoped rule overrides applied to build-gate results (option > config file). */
11
- overrides?: RuleOverride[];
12
- /** Minimum severity that fails the build (default: 'critical'). */
13
- failOn?: Severity;
14
- /** Per-category weights for the combined Health score shown in the JSON/console report (flag > config file > default 1 each). */
15
- weights?: Partial<Record<Category, number>>;
16
- /** Report output (default: 'console'). */
17
- report?: 'console' | 'json' | false;
18
- /** Write the JSON report to this path. */
19
- outFile?: string;
20
- /** Override the prerendered-pages directory (default: .svelte-kit/output/prerendered/pages). */
21
- prerenderDir?: string;
22
- /**
23
- * Serve a live dashboard at /__svelte-vitals/ during `vite dev` (add
24
- * svelteVitalsHandle to hooks.server.ts for accurate, per-route `measured` results
25
- * as you browse — the dashboard still works without it, from whole-project static
26
- * analysis alone). Default: `true`. Pass `false` to keep only the build-time gate.
27
- */
28
- ui?: boolean;
5
+ /** Project root (defaults to the Vite config root / cwd). */
6
+ cwd?: string;
7
+ treatDynamicAs?: TreatDynamicAs;
8
+ metaComponents?: string[];
9
+ rules?: Record<string, RuleSetting>;
10
+ /** Route-/file-scoped rule overrides applied to build-gate results (option > config file). */
11
+ overrides?: RuleOverride[];
12
+ /** Minimum severity that fails the build (default: 'critical'). */
13
+ failOn?: Severity;
14
+ /** Per-category weights for the combined Health score shown in the JSON/console report (flag > config file > default 1 each). */
15
+ weights?: Partial<Record<Category, number>>;
16
+ /** Report output (default: 'console'). */
17
+ report?: 'console' | 'json' | false;
18
+ /** Write the JSON report to this path. */
19
+ outFile?: string;
20
+ /** Override the prerendered-pages directory (default: .svelte-kit/output/prerendered/pages). */
21
+ prerenderDir?: string;
22
+ /**
23
+ * Serve a live dashboard at /__svelte-vitals/ during `vite dev` (add
24
+ * svelteVitalsHandle to hooks.server.ts for accurate, per-route `measured` results
25
+ * as you browse — the dashboard still works without it, from whole-project static
26
+ * analysis alone). Default: `true`. Pass `false` to keep only the build-time gate.
27
+ */
28
+ ui?: boolean;
29
29
  }
30
30
  /** svelte-vitals Vite/SvelteKit plugin. */
31
31
  declare function svelteVitals(options?: SvelteVitalsOptions): Plugin | Plugin[];
32
-
33
- export { type SvelteVitalsOptions, svelteVitals };
32
+ //#endregion
33
+ export { type SvelteVitalsOptions, svelteVitals };