@svelte-vitals/vite 0.54.6 → 0.54.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/README.md +2 -0
- package/dist/index.js +68 -9
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -34,6 +34,8 @@ 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
|
+
A `svelte-vitals.config.*` and a `svelte-vitals-suppressions.json` in the project root are read automatically (explicit options win over the config file). Only source-scan suppression entries apply; route-level entries are skipped on every plugin surface (rendered route findings anchor to the built HTML) and the plugin says how many.
|
|
38
|
+
|
|
37
39
|
At build time the HTML check covers prerendered routes only; the source scan applies project-wide. Routes that are not prerendered (server-rendered, or dynamic routes without prerendered entries) have no build output, so nothing checks their rendered HTML here. A prerendered route with `ssr = false` has build output, but only the empty app shell, so the HTML check skips it too (a `+layout` with `ssr = false` skips its subtree) and prints one warning listing the skipped routes. The `svelte-vitals` CLI covers them by source analysis, and browsing them during `vite dev` gets the live dashboard a rendered reading.
|
|
38
40
|
|
|
39
41
|
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.
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
|
4
4
|
import { basename, dirname, isAbsolute, join, relative, sep } from "node:path";
|
|
5
5
|
import { CATEGORIES, defineConfig } from "@svelte-vitals/core";
|
|
6
6
|
import { ROBOTS_SOURCE_PATHS, SITEMAP_SOURCE_PATHS, SVELTE_CONFIG_FILES, VITE_CONFIG_FILES, addFactsDirectives, allRules, buildJsonReport, collectComponentFacts, collectKitModuleFacts, collectSourceFiles, collectSuppressions, computeScore, defaultConfig, findMinifyDisabled, formatConsoleReport, formatFailedRuleWarning, formatJsonReport, hasFailureAtOrAbove, renderAppShell, resolveKitAliases, resolveKitPathsBase, resolveRuleOptions, runAnalysis, safeHref, selectRules, shouldSkipRangeCheck, skippedFileWarnings, summarize, terminalSafe, unknownDirectiveIds, validateRuleSetting, withFailedRulesOff } from "@svelte-vitals/core/internal";
|
|
7
|
-
import { CONFIG_FILENAMES, analyzeProject, createNodeRuntime, findUnknownRuleIds, knownRuleIds, loadConfigFile, registryTag, ruleOptionsSpec } from "svelte-vitals";
|
|
7
|
+
import { CONFIG_FILENAMES, SUPPRESSIONS_FILE, analyzeProject, applySuppressions, createNodeRuntime, findUnknownRuleIds, knownRuleIds, loadConfigFile, loadSuppressions, registryTag, ruleOptionsSpec } from "svelte-vitals";
|
|
8
8
|
//#region src/providers/rendered/collect.ts
|
|
9
9
|
/** Map a prerendered HTML path (relative to pages/, POSIX) to its route. */
|
|
10
10
|
function deriveRouteFromHtmlPath(relPath) {
|
|
@@ -157,6 +157,29 @@ function collectSourceFiles$1(root) {
|
|
|
157
157
|
return collectSourceFiles(nodeRuntime, root);
|
|
158
158
|
}
|
|
159
159
|
//#endregion
|
|
160
|
+
//#region src/suppressions.ts
|
|
161
|
+
/**
|
|
162
|
+
* Apply `svelte-vitals-suppressions.json` the way every plugin surface must: source-scan entries
|
|
163
|
+
* only. A route-level entry (route starting with `/`) is written by the CLI against
|
|
164
|
+
* `src/routes/…/+page.svelte`, while the build gate's rendered findings anchor to the built HTML
|
|
165
|
+
* and the dashboard's live layer to the route itself — the gate could never match it, and the
|
|
166
|
+
* dashboard's static layer would drop it only until the route was browsed and the live result
|
|
167
|
+
* re-surfaced it. Skipping them everywhere keeps Health identical across surfaces; the caller
|
|
168
|
+
* gets one line per fact to print. Stale entries are not counted: pruning stays with the CLI.
|
|
169
|
+
*/
|
|
170
|
+
function applySourceSuppressions(results, entries, config) {
|
|
171
|
+
const notices = [];
|
|
172
|
+
const source = entries.filter((e) => !e.route?.startsWith("/"));
|
|
173
|
+
const routeLevel = entries.length - source.length;
|
|
174
|
+
const applied = applySuppressions(results, source, config);
|
|
175
|
+
if (applied.suppressed > 0) notices.push(`${applied.suppressed} finding(s) suppressed by ${SUPPRESSIONS_FILE}.`);
|
|
176
|
+
if (routeLevel > 0) notices.push(`${routeLevel} route-level ${routeLevel === 1 ? "entry does" : "entries do"} not apply to the plugin (${SUPPRESSIONS_FILE}) — rendered route findings anchor to the built HTML; use \`overrides\` for those.`);
|
|
177
|
+
return {
|
|
178
|
+
results: applied.results,
|
|
179
|
+
notices
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
//#endregion
|
|
160
183
|
//#region src/version.ts
|
|
161
184
|
/** Read this package's version from its own package.json so the report never drifts. */
|
|
162
185
|
function readPackageVersion() {
|
|
@@ -261,11 +284,13 @@ function ssrDisabledRouteMatcher(kitModules) {
|
|
|
261
284
|
/**
|
|
262
285
|
* Collect prerendered heads + project facts + component facts, run the core pipeline, and
|
|
263
286
|
* format reports. Config precedence: see `resolveConfig`. Pass `resolved` when the caller
|
|
264
|
-
* already resolved config itself (build mode
|
|
265
|
-
*
|
|
287
|
+
* already resolved config and loaded `svelte-vitals-suppressions.json` itself (build mode does
|
|
288
|
+
* both outside `analyze`'s try/catch so a validation error fails the build instead of being
|
|
289
|
+
* caught as an analysis error); `suppressions: undefined` means the file does not exist.
|
|
266
290
|
*/
|
|
267
291
|
async function analyze(prerenderPagesDir, cwd, options, extraProjectFacts, resolved) {
|
|
268
292
|
const { config, warnings } = resolved ?? await resolveConfig(cwd, options);
|
|
293
|
+
const suppressions = resolved ? resolved.suppressions : loadSuppressions(cwd);
|
|
269
294
|
const [rendered, components, sourceFiles] = await Promise.all([
|
|
270
295
|
collectRenderedHeads(prerenderPagesDir),
|
|
271
296
|
collectComponentFacts$1(cwd),
|
|
@@ -296,7 +321,7 @@ async function analyze(prerenderPagesDir, cwd, options, extraProjectFacts, resol
|
|
|
296
321
|
kitModules,
|
|
297
322
|
viteMinifyDisabled: project.viteMinifyDisabled
|
|
298
323
|
});
|
|
299
|
-
const
|
|
324
|
+
const analysis = await runAnalysis(selected, {
|
|
300
325
|
heads,
|
|
301
326
|
headings,
|
|
302
327
|
images,
|
|
@@ -307,6 +332,13 @@ async function analyze(prerenderPagesDir, cwd, options, extraProjectFacts, resol
|
|
|
307
332
|
kitModules,
|
|
308
333
|
sourceFiles
|
|
309
334
|
}, directives);
|
|
335
|
+
const { examined, failedRules, scoringConfig } = analysis;
|
|
336
|
+
let results = analysis.results;
|
|
337
|
+
if (suppressions !== void 0) {
|
|
338
|
+
const applied = applySourceSuppressions(results, suppressions, config);
|
|
339
|
+
results = applied.results;
|
|
340
|
+
warnings.push(...applied.notices);
|
|
341
|
+
}
|
|
310
342
|
warnings.push(...skippedFileWarnings([...components, ...kitModules]));
|
|
311
343
|
warnings.push(...unknownDirectiveIds(directives, allRules));
|
|
312
344
|
for (const f of failedRules) warnings.push(formatFailedRuleWarning(f));
|
|
@@ -315,12 +347,14 @@ async function analyze(prerenderPagesDir, cwd, options, extraProjectFacts, resol
|
|
|
315
347
|
const failed = hasFailureAtOrAbove(summary, scoringConfig.failOn);
|
|
316
348
|
const coverageNote = `Analyzed ${heads.length} prerendered route(s)${shells.length > 0 ? ` (skipped ${shells.length} with ssr = false)` : ""}. SSR/dynamic routes are not covered — run \`npx svelte-vitals\` for those.
|
|
317
349
|
Scanned ${components.length} component(s) under src/ for Correctness/Security/Architecture/Accessibility/Bundle findings.`;
|
|
350
|
+
const consoleReport = formatConsoleReport(results, scoringConfig, { mode: "rendered / plugin" }) + "\n" + coverageNote + "\n";
|
|
351
|
+
const jsonReport = formatJsonReport(results, scoringConfig, { version: readPackageVersion() }, selected.map((r) => r.id), examined);
|
|
318
352
|
return {
|
|
319
353
|
score,
|
|
320
354
|
summary,
|
|
321
355
|
results,
|
|
322
|
-
consoleReport
|
|
323
|
-
jsonReport
|
|
356
|
+
consoleReport,
|
|
357
|
+
jsonReport,
|
|
324
358
|
routeCount: rendered.heads.length,
|
|
325
359
|
failed,
|
|
326
360
|
failOn: scoringConfig.failOn,
|
|
@@ -635,6 +669,26 @@ function createStore() {
|
|
|
635
669
|
//#endregion
|
|
636
670
|
//#region src/ui/analysis.ts
|
|
637
671
|
/**
|
|
672
|
+
* Apply `svelte-vitals-suppressions.json` to the whole-project layer, same entries as the build
|
|
673
|
+
* gate (see `applySourceSuppressions`). A malformed file is a warning here, not an error: dev
|
|
674
|
+
* must keep serving. Returns the results unchanged when the file is absent or `config` is
|
|
675
|
+
* unknown (an injected `analyze` that returns no config).
|
|
676
|
+
*/
|
|
677
|
+
function suppress(root, results, config, warnings) {
|
|
678
|
+
if (!config) return results;
|
|
679
|
+
let entries;
|
|
680
|
+
try {
|
|
681
|
+
entries = loadSuppressions(root);
|
|
682
|
+
} catch (err) {
|
|
683
|
+
warnings.push(`${err instanceof Error ? err.message : String(err)} — ${SUPPRESSIONS_FILE} ignored.`);
|
|
684
|
+
return results;
|
|
685
|
+
}
|
|
686
|
+
if (entries === void 0) return results;
|
|
687
|
+
const applied = applySourceSuppressions(results, entries, config);
|
|
688
|
+
warnings.push(...applied.notices);
|
|
689
|
+
return applied.results;
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
638
692
|
* Owns the dev-dashboard's whole-project analysis lifecycle: an async run at startup,
|
|
639
693
|
* a debounced re-run on source changes, and coalescing so a change that arrives mid-run
|
|
640
694
|
* produces exactly one follow-up run rather than one per change (design doc §1).
|
|
@@ -661,12 +715,13 @@ function createAnalysisRunner(opts) {
|
|
|
661
715
|
failOn: opts.failOn,
|
|
662
716
|
parseCache
|
|
663
717
|
});
|
|
664
|
-
const {
|
|
718
|
+
const { failedRuleIds } = result;
|
|
719
|
+
const warnings = [...result.warnings ?? []];
|
|
720
|
+
const results = suppress(opts.root, result.results, result.config, warnings);
|
|
665
721
|
if (!stopped) {
|
|
666
722
|
if (failedRuleIds !== void 0) opts.onResults(results, failedRuleIds);
|
|
667
723
|
else opts.onResults(results);
|
|
668
724
|
}
|
|
669
|
-
const warnings = result.warnings ?? [];
|
|
670
725
|
const key = warnings.join("\n");
|
|
671
726
|
if (!stopped && warnings.length > 0 && key !== lastWarningsKey) opts.onWarnings?.(warnings);
|
|
672
727
|
lastWarningsKey = key;
|
|
@@ -716,6 +771,7 @@ const CONFIG_BASENAMES = /* @__PURE__ */ new Set([
|
|
|
716
771
|
"svelte.config.js",
|
|
717
772
|
"svelte.config.ts",
|
|
718
773
|
...CONFIG_FILENAMES,
|
|
774
|
+
SUPPRESSIONS_FILE,
|
|
719
775
|
"vite.config.js",
|
|
720
776
|
"vite.config.mjs",
|
|
721
777
|
"vite.config.ts",
|
|
@@ -841,7 +897,10 @@ function svelteVitals(options = {}) {
|
|
|
841
897
|
const pagesDir = options.prerenderDir ? options.prerenderDir : join(root, DEFAULT_PRERENDER_DIR);
|
|
842
898
|
const resolved = isAbsolute(pagesDir) ? pagesDir : join(root, pagesDir);
|
|
843
899
|
if (!existsSync(resolved)) return;
|
|
844
|
-
const resolvedConfig =
|
|
900
|
+
const resolvedConfig = {
|
|
901
|
+
...await resolveConfig(root, options),
|
|
902
|
+
suppressions: loadSuppressions(root)
|
|
903
|
+
};
|
|
845
904
|
let result;
|
|
846
905
|
try {
|
|
847
906
|
const viteMinifyDisabled = await resolveMinifyDisabled(minify, configFile, root);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@svelte-vitals/vite",
|
|
3
|
-
"version": "0.54.
|
|
3
|
+
"version": "0.54.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",
|
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
"node-html-parser": "^9.0.4"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
|
-
"@svelte-vitals/core": "^0.54.
|
|
46
|
+
"@svelte-vitals/core": "^0.54.7",
|
|
47
47
|
"@sveltejs/kit": "^2.70.3",
|
|
48
|
-
"svelte-vitals": "^0.54.
|
|
48
|
+
"svelte-vitals": "^0.54.7",
|
|
49
49
|
"vite": "^8.3.0"
|
|
50
50
|
},
|
|
51
51
|
"peerDependenciesMeta": {
|
|
@@ -54,12 +54,12 @@
|
|
|
54
54
|
}
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@svelte-vitals/core": "0.54.
|
|
57
|
+
"@svelte-vitals/core": "0.54.7",
|
|
58
58
|
"@sveltejs/kit": "^2.70.3",
|
|
59
59
|
"@types/node": "^24.13.4",
|
|
60
60
|
"happy-dom": "^20.14.3",
|
|
61
61
|
"svelte": "^5.57.0",
|
|
62
|
-
"svelte-vitals": "0.54.
|
|
62
|
+
"svelte-vitals": "0.54.7",
|
|
63
63
|
"vite": "^8.3.0"
|
|
64
64
|
},
|
|
65
65
|
"scripts": {
|