@svelte-vitals/vite 0.29.6 → 0.29.7
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/{chunk-2LDMPITM.js → chunk-EHCC47NO.js} +1 -5
- package/dist/hooks/index.js +15 -9
- package/dist/index.js +46 -31
- package/package.json +3 -3
|
@@ -130,12 +130,8 @@ function isLoopbackOrigin(origin) {
|
|
|
130
130
|
return false;
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
|
-
function isLoopbackHost(host) {
|
|
134
|
-
return host !== void 0 && isLoopbackOrigin(`http://${host}`);
|
|
135
|
-
}
|
|
136
133
|
|
|
137
134
|
export {
|
|
138
135
|
parseHtmlHead,
|
|
139
|
-
isLoopbackOrigin
|
|
140
|
-
isLoopbackHost
|
|
136
|
+
isLoopbackOrigin
|
|
141
137
|
};
|
package/dist/hooks/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isLoopbackOrigin,
|
|
3
3
|
parseHtmlHead
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-EHCC47NO.js";
|
|
5
5
|
|
|
6
6
|
// src/hooks/handle.ts
|
|
7
7
|
import { DEV } from "esm-env";
|
|
@@ -10,17 +10,20 @@ import {
|
|
|
10
10
|
applyRuleSeverities,
|
|
11
11
|
defineConfig,
|
|
12
12
|
effectiveSeverity,
|
|
13
|
+
formatFailedRuleWarning,
|
|
13
14
|
isPenalized,
|
|
14
15
|
runRules,
|
|
15
|
-
selectRules
|
|
16
|
+
selectRules,
|
|
17
|
+
terminalSafe
|
|
16
18
|
} from "@svelte-vitals/core";
|
|
17
19
|
function findingSignature(results, config) {
|
|
18
20
|
return results.filter((r) => isPenalized(r.detection, config.treatDynamicAs)).map((r) => `${r.id}:${effectiveSeverity(r, config)}:${r.detection.presence}:${r.detection.value}`).sort().join("|");
|
|
19
21
|
}
|
|
20
|
-
|
|
22
|
+
var warn = (line) => console.warn(terminalSafe(line));
|
|
23
|
+
async function postIngest(origin, route, results, failedRuleIds) {
|
|
21
24
|
if (!isLoopbackOrigin(origin)) {
|
|
22
25
|
if (globalThis.process?.env?.SVELTE_VITALS_DEBUG) {
|
|
23
|
-
|
|
26
|
+
warn(
|
|
24
27
|
`[svelte-vitals] live UI ingest skipped for non-loopback origin ${origin} \u2014 open the dashboard via localhost`
|
|
25
28
|
);
|
|
26
29
|
}
|
|
@@ -30,7 +33,9 @@ async function postIngest(origin, route, results) {
|
|
|
30
33
|
await fetch(`${origin}/__svelte-vitals/ingest`, {
|
|
31
34
|
method: "POST",
|
|
32
35
|
headers: { "content-type": "application/json" },
|
|
33
|
-
|
|
36
|
+
// failedRuleIds is always sent, empty array included, so a route that recovers from
|
|
37
|
+
// a previously-crashing rule clears its stale entry on the receiving store.
|
|
38
|
+
body: JSON.stringify({ route, results, failedRuleIds })
|
|
34
39
|
});
|
|
35
40
|
} catch {
|
|
36
41
|
}
|
|
@@ -52,16 +57,17 @@ async function analyzeAndIngest(html, route, origin, rules, config, lastSignatur
|
|
|
52
57
|
config
|
|
53
58
|
});
|
|
54
59
|
const results = applyRuleSeverities(ruleResults, config);
|
|
60
|
+
const failedRuleIds = failedRules.map((f) => f.id);
|
|
55
61
|
if (failedRules.length > 0 && globalThis.process?.env?.SVELTE_VITALS_DEBUG) {
|
|
56
|
-
for (const f of failedRules)
|
|
62
|
+
for (const f of failedRules) warn(formatFailedRuleWarning(f));
|
|
57
63
|
}
|
|
58
|
-
const signature = findingSignature(results, config)
|
|
64
|
+
const signature = `${findingSignature(results, config)}|failed:${[...failedRuleIds].sort().join(",")}`;
|
|
59
65
|
if (lastSignature.get(route) === signature) return;
|
|
60
66
|
lastSignature.set(route, signature);
|
|
61
|
-
if (globalThis.process?.env?.SVELTE_VITALS_UI) void postIngest(origin, route, results);
|
|
67
|
+
if (globalThis.process?.env?.SVELTE_VITALS_UI) void postIngest(origin, route, results, failedRuleIds);
|
|
62
68
|
} catch (err) {
|
|
63
69
|
if (globalThis.process?.env?.SVELTE_VITALS_DEBUG) {
|
|
64
|
-
|
|
70
|
+
warn(`[svelte-vitals] dev analysis failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
65
71
|
}
|
|
66
72
|
}
|
|
67
73
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
-
isLoopbackHost,
|
|
3
2
|
isLoopbackOrigin,
|
|
4
3
|
parseHtmlHead
|
|
5
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-EHCC47NO.js";
|
|
6
5
|
|
|
7
6
|
// src/plugin.ts
|
|
8
7
|
import { existsSync } from "fs";
|
|
@@ -13,6 +12,7 @@ import {
|
|
|
13
12
|
defaultConfig,
|
|
14
13
|
resolveRuleOptions,
|
|
15
14
|
shouldSkipRangeCheck,
|
|
15
|
+
terminalSafe,
|
|
16
16
|
validateRuleSetting
|
|
17
17
|
} from "@svelte-vitals/core";
|
|
18
18
|
import { findUnknownRuleIds, knownRuleIds, ruleOptionsSpec } from "svelte-vitals";
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
applyOverrides,
|
|
26
26
|
runRules,
|
|
27
27
|
withFailedRulesOff,
|
|
28
|
+
formatFailedRuleWarning,
|
|
28
29
|
computeScore,
|
|
29
30
|
summarize,
|
|
30
31
|
hasFailureAtOrAbove,
|
|
@@ -232,7 +233,7 @@ async function analyze(prerenderPagesDir, cwd, options, extraProjectFacts, resol
|
|
|
232
233
|
sourceFiles
|
|
233
234
|
});
|
|
234
235
|
const results = applyOverrides(applyRuleSeverities(rawResults, config), config);
|
|
235
|
-
for (const f of failedRules) warnings.push(
|
|
236
|
+
for (const f of failedRules) warnings.push(formatFailedRuleWarning(f));
|
|
236
237
|
const scoringConfig = withFailedRulesOff(
|
|
237
238
|
config,
|
|
238
239
|
failedRules.map((f) => f.id)
|
|
@@ -284,7 +285,11 @@ async function resolveMinifyDisabled(minify, configFile, root) {
|
|
|
284
285
|
import { CATEGORIES, renderAppShell } from "@svelte-vitals/core";
|
|
285
286
|
|
|
286
287
|
// src/ui/snapshot.ts
|
|
287
|
-
import {
|
|
288
|
+
import {
|
|
289
|
+
buildJsonReport,
|
|
290
|
+
safeHref,
|
|
291
|
+
withFailedRulesOff as withFailedRulesOff2
|
|
292
|
+
} from "@svelte-vitals/core";
|
|
288
293
|
function sanitizeDocsUrl(issue) {
|
|
289
294
|
if (issue.docsUrl === void 0) return issue;
|
|
290
295
|
if (safeHref(issue.docsUrl) !== null) return issue;
|
|
@@ -297,12 +302,14 @@ function sanitizeReport(report) {
|
|
|
297
302
|
siteIssues: report.siteIssues.map(sanitizeDocsUrl)
|
|
298
303
|
};
|
|
299
304
|
}
|
|
300
|
-
function buildSnapshot(store, config, meta) {
|
|
305
|
+
function buildSnapshot(store, config, meta, staticFailedRuleIds) {
|
|
306
|
+
const failedRuleIds = [.../* @__PURE__ */ new Set([...staticFailedRuleIds ?? [], ...store.failedRuleIds()])];
|
|
307
|
+
const scoringConfig = withFailedRulesOff2(config, failedRuleIds);
|
|
301
308
|
return {
|
|
302
309
|
// No rule-id list threaded through: `report.rules` is seeded from `store.snapshot()`
|
|
303
310
|
// alone here, so presence means "produced a result", not "was selected" — unlike the
|
|
304
311
|
// `json` reporter (design doc 2026-08-03-json-rule-evidence-design.md, Not in scope).
|
|
305
|
-
report: sanitizeReport(buildJsonReport(store.snapshot(),
|
|
312
|
+
report: sanitizeReport(buildJsonReport(store.snapshot(), scoringConfig, meta)),
|
|
306
313
|
badges: store.badges(),
|
|
307
314
|
analyzing: store.isAnalyzing(),
|
|
308
315
|
sequence: store.sequence(),
|
|
@@ -324,7 +331,7 @@ function isResultLike(x) {
|
|
|
324
331
|
const f = r.fix;
|
|
325
332
|
return typeof r.id === "string" && typeof r.message === "string" && typeof r.severity === "string" && SEVERITIES.has(r.severity) && typeof d === "object" && d !== null && typeof d.presence === "string" && typeof d.value === "string" && (r.category === void 0 || typeof r.category === "string" && CATEGORY_SET.has(r.category)) && isOptionalString(r.location) && isOptionalString(r.recommendation) && isOptionalString(r.docsUrl) && (f === void 0 || typeof f === "object" && f !== null && typeof f.description === "string" && isOptionalString(f.snippet) && isOptionalString(f.lang)) && (r.line === void 0 || typeof r.line === "number") && isOptionalString(r.route);
|
|
326
333
|
}
|
|
327
|
-
function installUiMiddleware(server, config, version, store, coreVersion) {
|
|
334
|
+
function installUiMiddleware(server, config, version, store, coreVersion, getStaticFailedRuleIds) {
|
|
328
335
|
const clients = /* @__PURE__ */ new Set();
|
|
329
336
|
store.subscribe(() => {
|
|
330
337
|
for (const res of clients) {
|
|
@@ -343,7 +350,7 @@ function installUiMiddleware(server, config, version, store, coreVersion) {
|
|
|
343
350
|
const url = req.url ?? "/";
|
|
344
351
|
const origin = req.headers.origin;
|
|
345
352
|
const host = req.headers.host;
|
|
346
|
-
if (typeof origin === "string" && !isLoopbackOrigin(origin) || !
|
|
353
|
+
if (typeof origin === "string" && !isLoopbackOrigin(origin) || host === void 0 || !isLoopbackOrigin(`http://${host}`)) {
|
|
347
354
|
req.resume();
|
|
348
355
|
res.statusCode = 403;
|
|
349
356
|
res.end("svelte-vitals dev UI is only available from localhost");
|
|
@@ -354,9 +361,10 @@ function installUiMiddleware(server, config, version, store, coreVersion) {
|
|
|
354
361
|
req.on("data", (c) => chunks.push(c));
|
|
355
362
|
req.on("end", () => {
|
|
356
363
|
try {
|
|
357
|
-
const { route, results } = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
364
|
+
const { route, results, failedRuleIds } = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
358
365
|
if (typeof route === "string" && Array.isArray(results)) {
|
|
359
|
-
|
|
366
|
+
const failedIds = Array.isArray(failedRuleIds) ? failedRuleIds.filter((id) => typeof id === "string") : [];
|
|
367
|
+
store.set(route, results.filter(isResultLike), failedIds);
|
|
360
368
|
}
|
|
361
369
|
} catch {
|
|
362
370
|
}
|
|
@@ -378,7 +386,7 @@ function installUiMiddleware(server, config, version, store, coreVersion) {
|
|
|
378
386
|
}
|
|
379
387
|
if (url.startsWith("/data.json")) {
|
|
380
388
|
try {
|
|
381
|
-
const snapshot = buildSnapshot(store, config, { version, coreVersion });
|
|
389
|
+
const snapshot = buildSnapshot(store, config, { version, coreVersion }, getStaticFailedRuleIds?.());
|
|
382
390
|
res.setHeader("Content-Type", "application/json");
|
|
383
391
|
res.end(JSON.stringify(snapshot));
|
|
384
392
|
} catch {
|
|
@@ -388,7 +396,7 @@ function installUiMiddleware(server, config, version, store, coreVersion) {
|
|
|
388
396
|
return;
|
|
389
397
|
}
|
|
390
398
|
try {
|
|
391
|
-
const html = renderAppShell(buildSnapshot(store, config, { version, coreVersion }));
|
|
399
|
+
const html = renderAppShell(buildSnapshot(store, config, { version, coreVersion }, getStaticFailedRuleIds?.()));
|
|
392
400
|
res.setHeader("Content-Type", "text/html");
|
|
393
401
|
res.end(html);
|
|
394
402
|
} catch {
|
|
@@ -432,6 +440,7 @@ function composeBadges(staticResults, liveByRoute) {
|
|
|
432
440
|
function createStore() {
|
|
433
441
|
let staticResults = [];
|
|
434
442
|
const liveByRoute = /* @__PURE__ */ new Map();
|
|
443
|
+
const liveFailedByRoute = /* @__PURE__ */ new Map();
|
|
435
444
|
const subs = /* @__PURE__ */ new Set();
|
|
436
445
|
let analyzing = false;
|
|
437
446
|
let seq = 0;
|
|
@@ -440,11 +449,13 @@ function createStore() {
|
|
|
440
449
|
for (const fn of subs) fn();
|
|
441
450
|
}
|
|
442
451
|
return {
|
|
443
|
-
set(route, results) {
|
|
452
|
+
set(route, results, failedRuleIds) {
|
|
444
453
|
liveByRoute.set(
|
|
445
454
|
route,
|
|
446
455
|
results.map((r) => r.route ? r : { ...r, route })
|
|
447
456
|
);
|
|
457
|
+
if (failedRuleIds && failedRuleIds.length > 0) liveFailedByRoute.set(route, failedRuleIds);
|
|
458
|
+
else liveFailedByRoute.delete(route);
|
|
448
459
|
notify();
|
|
449
460
|
},
|
|
450
461
|
setStatic(results) {
|
|
@@ -464,6 +475,9 @@ function createStore() {
|
|
|
464
475
|
badges() {
|
|
465
476
|
return composeBadges(staticResults, liveByRoute);
|
|
466
477
|
},
|
|
478
|
+
failedRuleIds() {
|
|
479
|
+
return [...new Set([...liveFailedByRoute.values()].flat())].sort();
|
|
480
|
+
},
|
|
467
481
|
sequence() {
|
|
468
482
|
return seq;
|
|
469
483
|
},
|
|
@@ -476,28 +490,21 @@ function createStore() {
|
|
|
476
490
|
|
|
477
491
|
// src/ui/analysis.ts
|
|
478
492
|
import { relative as relative2, sep } from "path";
|
|
493
|
+
import { analyzeProject } from "svelte-vitals";
|
|
479
494
|
function createAnalysisRunner(opts) {
|
|
480
495
|
const debounceMs = opts.debounceMs ?? 500;
|
|
481
|
-
|
|
496
|
+
const analyze2 = opts.analyze ?? analyzeProject;
|
|
482
497
|
const parseCache = /* @__PURE__ */ new Map();
|
|
483
498
|
let stopped = false;
|
|
484
499
|
let running = false;
|
|
485
500
|
let pending = false;
|
|
486
501
|
let timer;
|
|
487
|
-
async function getAnalyze() {
|
|
488
|
-
if (cachedAnalyze) return cachedAnalyze;
|
|
489
|
-
const mod = await import("svelte-vitals");
|
|
490
|
-
const fn = (o) => mod.analyzeProject(o);
|
|
491
|
-
cachedAnalyze = fn;
|
|
492
|
-
return fn;
|
|
493
|
-
}
|
|
494
502
|
async function runOnce() {
|
|
495
503
|
if (stopped) return;
|
|
496
504
|
running = true;
|
|
497
505
|
opts.onStatusChange?.(true);
|
|
498
506
|
try {
|
|
499
|
-
const
|
|
500
|
-
const { results } = await analyze2({
|
|
507
|
+
const { results, failedRuleIds } = await analyze2({
|
|
501
508
|
cwd: opts.root,
|
|
502
509
|
treatDynamicAs: opts.treatDynamicAs,
|
|
503
510
|
metaComponents: opts.metaComponents,
|
|
@@ -505,7 +512,10 @@ function createAnalysisRunner(opts) {
|
|
|
505
512
|
failOn: opts.failOn,
|
|
506
513
|
parseCache
|
|
507
514
|
});
|
|
508
|
-
if (!stopped)
|
|
515
|
+
if (!stopped) {
|
|
516
|
+
if (failedRuleIds !== void 0) opts.onResults(results, failedRuleIds);
|
|
517
|
+
else opts.onResults(results);
|
|
518
|
+
}
|
|
509
519
|
} catch (err) {
|
|
510
520
|
if (!stopped) opts.onError(err);
|
|
511
521
|
} finally {
|
|
@@ -560,6 +570,7 @@ var CONFIG_BASENAMES = /* @__PURE__ */ new Set([
|
|
|
560
570
|
"vite.config.cts"
|
|
561
571
|
]);
|
|
562
572
|
var IGNORED_SEGMENTS = /* @__PURE__ */ new Set(["node_modules", ".svelte-kit", "build", "dist"]);
|
|
573
|
+
var warn = (line) => console.warn(terminalSafe(line));
|
|
563
574
|
function isRelevant(file, root) {
|
|
564
575
|
const rel = relative3(root, file);
|
|
565
576
|
if (rel === "" || rel.startsWith("..")) return false;
|
|
@@ -640,10 +651,10 @@ function svelteVitals(options = {}) {
|
|
|
640
651
|
resolvedConfig
|
|
641
652
|
);
|
|
642
653
|
} catch (err) {
|
|
643
|
-
|
|
654
|
+
warn(`svelte-vitals: skipped \u2014 analysis failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
644
655
|
return;
|
|
645
656
|
}
|
|
646
|
-
for (const w of result.warnings)
|
|
657
|
+
for (const w of result.warnings) warn(`svelte-vitals: ${w}`);
|
|
647
658
|
if (result.routeCount === 0) return;
|
|
648
659
|
if (options.report !== false) {
|
|
649
660
|
const out = options.report === "json" ? result.jsonReport : result.consoleReport;
|
|
@@ -671,22 +682,26 @@ function svelteVitals(options = {}) {
|
|
|
671
682
|
try {
|
|
672
683
|
({ config, warnings } = await resolveConfig(uiRoot, options));
|
|
673
684
|
} catch (err) {
|
|
674
|
-
|
|
685
|
+
warn(
|
|
675
686
|
`svelte-vitals: config file invalid \u2014 dashboard using plugin options/defaults: ${err instanceof Error ? err.message : String(err)}`
|
|
676
687
|
);
|
|
677
688
|
config = mergeConfig(options, void 0);
|
|
678
689
|
warnings = [];
|
|
679
690
|
}
|
|
680
|
-
for (const w of warnings)
|
|
691
|
+
for (const w of warnings) warn(`svelte-vitals: ${w}`);
|
|
681
692
|
const store = createStore();
|
|
693
|
+
let staticFailedRuleIds = [];
|
|
682
694
|
const runner = createAnalysisRunner({
|
|
683
695
|
root: uiRoot,
|
|
684
696
|
treatDynamicAs: options.treatDynamicAs,
|
|
685
697
|
metaComponents: options.metaComponents,
|
|
686
698
|
rules: options.rules,
|
|
687
699
|
failOn: options.failOn,
|
|
688
|
-
onResults: (results) =>
|
|
689
|
-
|
|
700
|
+
onResults: (results, failedRuleIds) => {
|
|
701
|
+
store.setStatic(results);
|
|
702
|
+
staticFailedRuleIds = failedRuleIds ?? [];
|
|
703
|
+
},
|
|
704
|
+
onError: (err) => warn(`[svelte-vitals] dev analysis failed: ${err instanceof Error ? err.message : String(err)}`),
|
|
690
705
|
onStatusChange: (analyzing) => store.setAnalyzing(analyzing)
|
|
691
706
|
});
|
|
692
707
|
runner.start();
|
|
@@ -697,7 +712,7 @@ function svelteVitals(options = {}) {
|
|
|
697
712
|
delete process.env.SVELTE_VITALS_UI;
|
|
698
713
|
runner.stop();
|
|
699
714
|
});
|
|
700
|
-
installUiMiddleware(server, config, readPackageVersion(), store, readCoreVersion());
|
|
715
|
+
installUiMiddleware(server, config, readPackageVersion(), store, readCoreVersion(), () => staticFailedRuleIds);
|
|
701
716
|
if (typeof server.printUrls === "function") {
|
|
702
717
|
const printUrls = server.printUrls.bind(server);
|
|
703
718
|
server.printUrls = () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@svelte-vitals/vite",
|
|
3
|
-
"version": "0.29.
|
|
3
|
+
"version": "0.29.7",
|
|
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,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.
|
|
46
|
-
"svelte-vitals": "0.
|
|
45
|
+
"@svelte-vitals/core": "0.42.0",
|
|
46
|
+
"svelte-vitals": "0.46.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"@sveltejs/kit": "^2.70.2",
|