@svelte-vitals/vite 0.13.1 → 0.14.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.
- package/dist/hooks/index.d.ts +4 -2
- package/dist/hooks/index.js +2 -17
- package/dist/index.d.ts +6 -1
- package/dist/index.js +46 -11
- package/package.json +7 -7
package/dist/hooks/index.d.ts
CHANGED
|
@@ -10,8 +10,10 @@ interface SvelteVitalsHookOptions {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* SvelteKit `handle` that
|
|
14
|
-
*
|
|
13
|
+
* SvelteKit `handle` that analyzes each visited page's rendered `<head>`, in dev only,
|
|
14
|
+
* and (when the live dashboard is enabled) feeds the results in — upgrading that
|
|
15
|
+
* route's dashboard findings from static (source-only) to `measured` (real rendered
|
|
16
|
+
* HTML). Add it to `src/hooks.server.ts`, e.g. `sequence(svelteVitalsHandle())`.
|
|
15
17
|
*/
|
|
16
18
|
declare function svelteVitalsHandle(options?: SvelteVitalsHookOptions): Handle;
|
|
17
19
|
|
package/dist/hooks/index.js
CHANGED
|
@@ -15,22 +15,9 @@ import {
|
|
|
15
15
|
|
|
16
16
|
// src/hooks/format.ts
|
|
17
17
|
import { isPenalized, effectiveSeverity } from "@svelte-vitals/core";
|
|
18
|
-
var GLYPH = { critical: "\u2717", warning: "\u26A0", info: "\xB7" };
|
|
19
|
-
var RANK = { critical: 0, warning: 1, info: 2 };
|
|
20
18
|
function penalized(results, config) {
|
|
21
19
|
return results.filter((r) => isPenalized(r.detection, config.treatDynamicAs));
|
|
22
20
|
}
|
|
23
|
-
function formatDevReport(route, results, config) {
|
|
24
|
-
const failing = penalized(results, config).sort(
|
|
25
|
-
(a, b) => RANK[effectiveSeverity(a, config)] - RANK[effectiveSeverity(b, config)] || a.id.localeCompare(b.id)
|
|
26
|
-
);
|
|
27
|
-
if (failing.length === 0) return "";
|
|
28
|
-
const lines = [`[svelte-vitals] ${route}`];
|
|
29
|
-
for (const r of failing) {
|
|
30
|
-
lines.push(` ${GLYPH[effectiveSeverity(r, config)]} ${r.id} ${r.message}`);
|
|
31
|
-
}
|
|
32
|
-
return lines.join("\n");
|
|
33
|
-
}
|
|
34
21
|
function findingSignature(results, config) {
|
|
35
22
|
return penalized(results, config).map((r) => `${r.id}:${effectiveSeverity(r, config)}:${r.detection.presence}:${r.detection.value}`).sort().join("|");
|
|
36
23
|
}
|
|
@@ -54,7 +41,7 @@ async function postIngest(origin, route, results) {
|
|
|
54
41
|
} catch {
|
|
55
42
|
}
|
|
56
43
|
}
|
|
57
|
-
async function
|
|
44
|
+
async function analyzeAndIngest(html, route, origin, rules, config, lastSignature) {
|
|
58
45
|
try {
|
|
59
46
|
const { tags, htmlLang, headings: levels, images: imgs } = parseHtmlHead(html);
|
|
60
47
|
const head = { route, source: "rendered", tags, file: route };
|
|
@@ -70,8 +57,6 @@ async function analyzeAndWarn(html, route, origin, rules, config, lastSignature)
|
|
|
70
57
|
const signature = findingSignature(results, config);
|
|
71
58
|
if (lastSignature.get(route) === signature) return;
|
|
72
59
|
lastSignature.set(route, signature);
|
|
73
|
-
const report = formatDevReport(route, results, config);
|
|
74
|
-
if (report) console.warn(report);
|
|
75
60
|
if (globalThis.process?.env?.SVELTE_VITALS_UI) void postIngest(origin, route, results);
|
|
76
61
|
} catch (err) {
|
|
77
62
|
if (globalThis.process?.env?.SVELTE_VITALS_DEBUG) {
|
|
@@ -93,7 +78,7 @@ function svelteVitalsHandle(options = {}) {
|
|
|
93
78
|
transformPageChunk: ({ html, done }) => {
|
|
94
79
|
buffer += html;
|
|
95
80
|
if (done)
|
|
96
|
-
void
|
|
81
|
+
void analyzeAndIngest(
|
|
97
82
|
buffer,
|
|
98
83
|
event.route.id ?? event.url.pathname,
|
|
99
84
|
event.url.origin,
|
package/dist/index.d.ts
CHANGED
|
@@ -15,7 +15,12 @@ interface SvelteVitalsOptions {
|
|
|
15
15
|
outFile?: string;
|
|
16
16
|
/** Override the prerendered-pages directory (default: .svelte-kit/output/prerendered/pages). */
|
|
17
17
|
prerenderDir?: string;
|
|
18
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* Serve a live dashboard at /__svelte-vitals/ during `vite dev` (add
|
|
20
|
+
* svelteVitalsHandle to hooks.server.ts for accurate, per-route `measured` results
|
|
21
|
+
* as you browse — the dashboard still works without it, from whole-project static
|
|
22
|
+
* analysis alone). Default: `true`. Pass `false` to keep only the build-time gate.
|
|
23
|
+
*/
|
|
19
24
|
ui?: boolean;
|
|
20
25
|
}
|
|
21
26
|
/** svelte-vitals Vite/SvelteKit plugin. */
|
package/dist/index.js
CHANGED
|
@@ -210,8 +210,9 @@ body{background:var(--ground);color:var(--ink);font-family:var(--sans);line-heig
|
|
|
210
210
|
.dv-app{display:grid;grid-template-rows:auto 1fr;grid-template-columns:280px 1fr;grid-template-areas:"top top" "side main";height:100vh}
|
|
211
211
|
.dv-topbar{grid-area:top;border-bottom:1px solid var(--line);background:var(--panel)}
|
|
212
212
|
.dv-topbar-inner{display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;padding:12px 20px}
|
|
213
|
-
.dv-brand{display:flex;align-items:
|
|
214
|
-
.dv-brand
|
|
213
|
+
.dv-brand{display:flex;align-items:center;background:none;border:none;padding:0;cursor:pointer;border-radius:6px}
|
|
214
|
+
.dv-brand svg{height:28px;width:auto;display:block}
|
|
215
|
+
.dv-brand:focus-visible{outline:2px solid var(--accent);outline-offset:4px}
|
|
215
216
|
.dv-meta{font-family:var(--mono);font-size:12px;color:var(--muted);display:flex;gap:12px;flex-wrap:wrap}
|
|
216
217
|
.dv-status{display:flex;align-items:center;gap:10px}
|
|
217
218
|
.dv-analyzing{font-size:12px;color:var(--accent);font-weight:600}
|
|
@@ -268,6 +269,9 @@ body{background:var(--ground);color:var(--ink);font-family:var(--sans);line-heig
|
|
|
268
269
|
.dv-sev-critical{color:var(--poor)}
|
|
269
270
|
.dv-sev-warning{color:var(--warn)}
|
|
270
271
|
.dv-sev-info{color:var(--faint)}
|
|
272
|
+
.dv-f-route{display:block;font:inherit;font-family:var(--mono);font-size:12.5px;font-weight:600;color:var(--accent);background:none;border:none;padding:0;margin:8px 0 0;cursor:pointer;text-align:left}
|
|
273
|
+
.dv-f-route:hover{text-decoration:underline}
|
|
274
|
+
.dv-f-route:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
|
|
271
275
|
.dv-f-loc{font-family:var(--mono);font-size:12.5px;color:var(--muted);margin:8px 0 0}
|
|
272
276
|
.dv-f-rec{font-size:14px;margin:10px 0 0}
|
|
273
277
|
.dv-fix{margin:12px 0 0;background:var(--code-bg);border-radius:8px;overflow:hidden}
|
|
@@ -293,6 +297,12 @@ var DASHBOARD_SCRIPT = `
|
|
|
293
297
|
var BAND_COLOR = { good: '#2fa968', warn: '#e8a317', poor: '#e5484d' };
|
|
294
298
|
function scoreBand(score) { return score >= 90 ? 'good' : score >= 50 ? 'warn' : 'poor'; }
|
|
295
299
|
|
|
300
|
+
// Same mark as the docs site's hero wordmark (docs/public/wordmark.svg) \u2014 an inline
|
|
301
|
+
// copy, not an <img src>, since the dashboard is a single self-contained HTML response
|
|
302
|
+
// with no other static assets to serve alongside it. Fixed brand colors (not CSS custom
|
|
303
|
+
// properties), matching the docs usage: the wordmark reads the same in both themes.
|
|
304
|
+
var WORDMARK_SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 56" role="img" aria-labelledby="dv-wordmark-title"><title id="dv-wordmark-title">svelte-vitals</title><defs><clipPath id="dv-wordmark-clip"><rect x="2" y="2" width="52" height="52" rx="14"/></clipPath></defs><rect x="2" y="2" width="52" height="52" rx="14" fill="#FF3E00"/><polyline clip-path="url(#dv-wordmark-clip)" points="4,28 15,28 17.5,23.5 20,28 23,28 26,7 29,49 32,28 35,28 37,24.5 39.5,28 52,28" fill="none" stroke="#fff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><text x="70" y="38" font-family="ui-sans-serif, system-ui, -apple-system, \\'Segoe UI\\', Roboto, sans-serif" font-size="30" font-weight="700" fill="#FF3E00">svelte-vitals</text></svg>';
|
|
305
|
+
|
|
296
306
|
function h(tag, attrs, kids) {
|
|
297
307
|
var n = document.createElement(tag);
|
|
298
308
|
if (attrs) {
|
|
@@ -418,12 +428,18 @@ var DASHBOARD_SCRIPT = `
|
|
|
418
428
|
if (sb) sb.classList.toggle('open');
|
|
419
429
|
}
|
|
420
430
|
|
|
431
|
+
function brandEl() {
|
|
432
|
+
var el = h('button', { type: 'button', class: 'dv-brand', 'aria-label': 'Go to Overview', onclick: function () { selectItem('overview'); } }, []);
|
|
433
|
+
el.innerHTML = WORDMARK_SVG;
|
|
434
|
+
return el;
|
|
435
|
+
}
|
|
436
|
+
|
|
421
437
|
function renderTopbar() {
|
|
422
438
|
var s = state.snapshot;
|
|
423
439
|
var findings = s.report.routes.reduce(function (n, r) { return n + r.issues.length; }, 0) + s.report.siteIssues.length;
|
|
424
440
|
var kids = [
|
|
425
441
|
h('button', { type: 'button', class: 'dv-menu-toggle', 'aria-label': 'Toggle route list', onclick: toggleSidebar, text: '\u2261' }, []),
|
|
426
|
-
|
|
442
|
+
brandEl(),
|
|
427
443
|
h('div', { class: 'dv-meta' }, [
|
|
428
444
|
h('span', { text: 'v' + s.meta.version }, []),
|
|
429
445
|
s.meta.coreVersion ? h('span', { title: '@svelte-vitals/core version', text: 'core v' + s.meta.coreVersion }, []) : null,
|
|
@@ -567,7 +583,9 @@ var DASHBOARD_SCRIPT = `
|
|
|
567
583
|
return f === 'all' || issue.severity === f || issue.category === f;
|
|
568
584
|
}
|
|
569
585
|
|
|
570
|
-
|
|
586
|
+
var SEVERITY_ORDER = { critical: 0, warning: 1, info: 2 };
|
|
587
|
+
|
|
588
|
+
function renderFinding(issue, route) {
|
|
571
589
|
var kids = [
|
|
572
590
|
h('div', { class: 'dv-f-head' }, [
|
|
573
591
|
h('span', { class: 'dv-ruleid', text: issue.id }, []),
|
|
@@ -575,6 +593,9 @@ var DASHBOARD_SCRIPT = `
|
|
|
575
593
|
h('span', { class: 'dv-sev-tag dv-sev-' + issue.severity, text: issue.severity }, [])
|
|
576
594
|
])
|
|
577
595
|
];
|
|
596
|
+
if (route) {
|
|
597
|
+
kids.push(h('button', { type: 'button', class: 'dv-f-route', onclick: function () { selectItem(route); }, text: route }, []));
|
|
598
|
+
}
|
|
578
599
|
if (issue.location) {
|
|
579
600
|
kids.push(h('p', { class: 'dv-f-loc', text: issue.location + (issue.line !== undefined ? ':' + issue.line : '') }, []));
|
|
580
601
|
}
|
|
@@ -631,11 +652,23 @@ var DASHBOARD_SCRIPT = `
|
|
|
631
652
|
]);
|
|
632
653
|
});
|
|
633
654
|
var chips = renderFilterChips(s.report.categories);
|
|
634
|
-
var
|
|
635
|
-
var
|
|
636
|
-
|
|
637
|
-
:
|
|
638
|
-
|
|
655
|
+
var totalCount = s.report.routes.reduce(function (n, r) { return n + r.issues.length; }, 0) + s.report.siteIssues.length;
|
|
656
|
+
var entries = [];
|
|
657
|
+
s.report.routes.forEach(function (r) {
|
|
658
|
+
r.issues.forEach(function (issue) { entries.push({ issue: issue, route: r.route }); });
|
|
659
|
+
});
|
|
660
|
+
s.report.siteIssues.forEach(function (issue) { entries.push({ issue: issue, route: null }); });
|
|
661
|
+
entries = entries.filter(function (e) { return passesFilter(e.issue); });
|
|
662
|
+
entries.sort(function (a, b) {
|
|
663
|
+
var sd = SEVERITY_ORDER[a.issue.severity] - SEVERITY_ORDER[b.issue.severity];
|
|
664
|
+
if (sd !== 0) return sd;
|
|
665
|
+
return (a.route || '').localeCompare(b.route || '');
|
|
666
|
+
});
|
|
667
|
+
var body = entries.length
|
|
668
|
+
? entries.map(function (e) { return renderFinding(e.issue, e.route); })
|
|
669
|
+
: [h('p', { class: 'dv-empty', text: totalCount ? 'No issues match the current filter.' : 'No issues found \u2014 nice work!' }, [])];
|
|
670
|
+
var findings = h('section', { class: 'dv-section' }, [h('h2', { text: 'Findings' }, [])].concat(body));
|
|
671
|
+
return h('div', { class: 'dv-overview' }, [gauge, h('div', { class: 'dv-cats' }, cats), chips, findings].filter(Boolean));
|
|
639
672
|
}
|
|
640
673
|
|
|
641
674
|
function renderRouteDetail(route) {
|
|
@@ -648,7 +681,9 @@ var DASHBOARD_SCRIPT = `
|
|
|
648
681
|
].filter(Boolean));
|
|
649
682
|
var chips = renderFilterChips(state.snapshot.report.categories);
|
|
650
683
|
var findings = route.issues.filter(passesFilter);
|
|
651
|
-
var body = findings.length
|
|
684
|
+
var body = findings.length
|
|
685
|
+
? findings.map(function (issue) { return renderFinding(issue); })
|
|
686
|
+
: [h('p', { class: 'dv-empty', text: 'No issues match the current filter.' }, [])];
|
|
652
687
|
return h('div', { class: 'dv-route-detail' }, [header, chips].concat(body));
|
|
653
688
|
}
|
|
654
689
|
|
|
@@ -1004,7 +1039,7 @@ function svelteVitals(options = {}) {
|
|
|
1004
1039
|
}
|
|
1005
1040
|
}
|
|
1006
1041
|
};
|
|
1007
|
-
if (
|
|
1042
|
+
if (options.ui === false) return buildPlugin;
|
|
1008
1043
|
const uiPlugin = {
|
|
1009
1044
|
name: "svelte-vitals:ui",
|
|
1010
1045
|
apply: "serve",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@svelte-vitals/vite",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Vite/SvelteKit plugin for svelte-vitals — analyzes prerendered HTML during vite build.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -43,11 +43,11 @@
|
|
|
43
43
|
"node-html-parser": "^9.0.0",
|
|
44
44
|
"tinyglobby": "^0.2.17",
|
|
45
45
|
"@svelte-vitals/core": "0.23.0",
|
|
46
|
-
"svelte-vitals": "0.
|
|
46
|
+
"svelte-vitals": "0.24.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"@sveltejs/kit": "^2.69.
|
|
50
|
-
"vite": "^8.1.
|
|
49
|
+
"@sveltejs/kit": "^2.69.2",
|
|
50
|
+
"vite": "^8.1.4"
|
|
51
51
|
},
|
|
52
52
|
"peerDependenciesMeta": {
|
|
53
53
|
"@sveltejs/kit": {
|
|
@@ -55,10 +55,10 @@
|
|
|
55
55
|
}
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@sveltejs/kit": "^2.69.
|
|
59
|
-
"@types/node": "^24.13.
|
|
58
|
+
"@sveltejs/kit": "^2.69.2",
|
|
59
|
+
"@types/node": "^24.13.3",
|
|
60
60
|
"svelte": "^5.56.4",
|
|
61
|
-
"vite": "^8.1.
|
|
61
|
+
"vite": "^8.1.4"
|
|
62
62
|
},
|
|
63
63
|
"scripts": {
|
|
64
64
|
"build": "tsup",
|