@svelte-vitals/vite 0.55.4 → 0.55.5
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 +1 -1
- package/dist/hooks/index.d.ts +24 -3
- package/dist/index.js +57 -38
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ export default {
|
|
|
38
38
|
|
|
39
39
|
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.
|
|
40
40
|
|
|
41
|
-
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
|
|
41
|
+
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 the hash router every route) 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. When the build prerenders nothing at all (an SPA served from a fallback page, say), the plugin prints one warning that route analysis was skipped and still runs and gates the source scan; `seo/html-lang`, which reads the rendered `<html>`, is skipped with it.
|
|
42
42
|
|
|
43
43
|
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.
|
|
44
44
|
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Handle } from "@sveltejs/kit";
|
|
1
|
+
import { RuleSetting } from "@svelte-vitals/core";
|
|
3
2
|
/** Options for the dev-time SvelteKit handle. A focused subset of the plugin options. */
|
|
4
3
|
interface SvelteVitalsHookOptions {
|
|
5
4
|
/** Component names treated as meta sources (design §11 layer 4). Mirrors the plugin option. */
|
|
@@ -7,11 +6,33 @@ interface SvelteVitalsHookOptions {
|
|
|
7
6
|
/** Per-rule overrides keyed by rule id, e.g. `{ 'seo/json-ld': 'off' }`. Mirrors the plugin option. */
|
|
8
7
|
rules?: Record<string, RuleSetting>;
|
|
9
8
|
}
|
|
9
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
10
|
+
/** The part of a SvelteKit 2 or 3 `RequestEvent` this handle reads. */
|
|
11
|
+
interface HandleEvent {
|
|
12
|
+
route: {
|
|
13
|
+
id: string | null;
|
|
14
|
+
};
|
|
15
|
+
url: URL;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Structural, so the package imports no SvelteKit type: `Handle` lives in a different module in
|
|
19
|
+
* each major. Generic in the event, because `resolve`'s parameter is checked contravariantly
|
|
20
|
+
* against SvelteKit's `RequestEvent`.
|
|
21
|
+
*/
|
|
22
|
+
type SvelteVitalsHandle = <E extends HandleEvent>(input: {
|
|
23
|
+
event: E;
|
|
24
|
+
resolve: (event: E, opts?: {
|
|
25
|
+
transformPageChunk?: (input: {
|
|
26
|
+
html: string;
|
|
27
|
+
done: boolean;
|
|
28
|
+
}) => MaybePromise<string | undefined>;
|
|
29
|
+
}) => MaybePromise<Response>;
|
|
30
|
+
}) => MaybePromise<Response>;
|
|
10
31
|
/**
|
|
11
32
|
* SvelteKit `handle` that analyzes each visited page's rendered HTML, in dev only,
|
|
12
33
|
* and (when the live dashboard is enabled) feeds the results in — upgrading that
|
|
13
34
|
* route's dashboard findings from static (source-only) to `measured` (real rendered
|
|
14
35
|
* HTML). Add it to `src/hooks.server.ts`, e.g. `sequence(svelteVitalsHandle())`.
|
|
15
36
|
*/
|
|
16
|
-
export declare function svelteVitalsHandle(options?: SvelteVitalsHookOptions):
|
|
37
|
+
export declare function svelteVitalsHandle(options?: SvelteVitalsHookOptions): SvelteVitalsHandle;
|
|
17
38
|
export type { SvelteVitalsHookOptions };
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
4
4
|
import { access, mkdir, readFile, readdir, writeFile } from "node:fs/promises";
|
|
5
5
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
6
6
|
import { CATEGORIES, defineConfig } from "@svelte-vitals/core";
|
|
7
|
-
import { ROBOTS_SOURCE_PATHS, SITEMAP_SOURCE_PATHS, SVELTE_CONFIG_FILES, VITE_CONFIG_FILES, addFactsDirectives, allRules, buildJsonReport, collectComponentFacts, collectKitModuleFacts, collectSourceFiles, collectSuppressions, computeScore, defaultConfig,
|
|
7
|
+
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, formatJsonReportChunks, hasFailureAtOrAbove, isPlainObject, renderAppShell, resolveKitAliases, resolveKitHashRouter, resolveKitPathsBase, resolveRuleOptions, routeNeverSsr, runAnalysis, safeHref, selectRules, servesRootFile, shouldSkipRangeCheck, skippedFileWarnings, summarize, terminalSafe, unknownDirectiveIds, validateRuleSetting, withFailedRulesOff, withIndexableOff, withPackageImports, withWorkspacePackages } from "@svelte-vitals/core/internal";
|
|
8
8
|
import { CONFIG_FILENAMES, SUPPRESSIONS_FILE, analyzeProject, applySuppressions, createNodeRuntime, dropConstantListEachBlocks, findUnknownRuleIds, findWorkspacePackages, knownRuleIds, loadConfigFile, loadSuppressions, registryTag, ruleOptionsSpec } from "svelte-vitals";
|
|
9
9
|
// src/providers/rendered/collect.ts
|
|
10
10
|
/** Map a prerendered HTML path (relative to pages/, POSIX) to its route. */
|
|
@@ -150,7 +150,7 @@ async function collectRenderedProject(cwd, htmlLang) {
|
|
|
150
150
|
htmlLang,
|
|
151
151
|
...robotsReferencesSitemap !== void 0 ? { robotsReferencesSitemap } : {},
|
|
152
152
|
...kitPathsBase ? { kitPathsBase } : {},
|
|
153
|
-
...
|
|
153
|
+
...resolveKitHashRouter(viteConfig, svelteConfig) ? { kitHashRouter: true } : {},
|
|
154
154
|
...kitAliases ? { kitAliases } : {},
|
|
155
155
|
...componentAliases !== kitAliases ? { componentAliases } : {}
|
|
156
156
|
};
|
|
@@ -942,7 +942,50 @@ function svelteVitals(options = {}) {
|
|
|
942
942
|
let minify;
|
|
943
943
|
let configFile;
|
|
944
944
|
let ssrBuild = false;
|
|
945
|
+
let appBuild = false;
|
|
945
946
|
let prerenderDir = DEFAULT_PRERENDER_DIR;
|
|
947
|
+
const readPrerenderDir = (base, outDir) => {
|
|
948
|
+
const out = outDir ? resolve(base, outDir) : "";
|
|
949
|
+
if (/[\\/]output[\\/](?:server|client)$/.test(out)) prerenderDir = join(dirname(out), "prerendered/pages");
|
|
950
|
+
};
|
|
951
|
+
/** `afterPrerender`: an absent pages dir means "nothing was prerendered" rather than "not yet". */
|
|
952
|
+
const analyzeAndGate = async (afterPrerender) => {
|
|
953
|
+
const pagesDir = options.prerenderDir ? options.prerenderDir : prerenderDir;
|
|
954
|
+
const resolved = isAbsolute(pagesDir) ? pagesDir : join(root, pagesDir);
|
|
955
|
+
if (!afterPrerender && !existsSync(resolved)) return;
|
|
956
|
+
const resolvedConfig = {
|
|
957
|
+
...await resolveConfig(root, options),
|
|
958
|
+
suppressions: loadSuppressions(root)
|
|
959
|
+
};
|
|
960
|
+
let result;
|
|
961
|
+
try {
|
|
962
|
+
const viteMinifyDisabled = await resolveMinifyDisabled(minify, configFile, root);
|
|
963
|
+
result = await analyze(resolved, root, options, viteMinifyDisabled ? { viteMinifyDisabled } : void 0, resolvedConfig);
|
|
964
|
+
} catch (err) {
|
|
965
|
+
warn(`svelte-vitals: skipped — analysis failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
966
|
+
return;
|
|
967
|
+
}
|
|
968
|
+
for (const w of result.warnings) warn(`svelte-vitals: ${w}`);
|
|
969
|
+
if (result.routeCount === 0) {
|
|
970
|
+
if (!afterPrerender) return;
|
|
971
|
+
warn(`svelte-vitals: no prerendered pages found in ${relative(root, resolved) || "."} — route analysis skipped; the source scan still ran. Prerender routes to check their HTML, or run \`npx svelte-vitals\` to check every route from source.`);
|
|
972
|
+
}
|
|
973
|
+
if (options.report !== false) {
|
|
974
|
+
if (options.report === "json") {
|
|
975
|
+
for (const chunk of result.jsonReportChunks()) {
|
|
976
|
+
if (process.stdout.destroyed) break;
|
|
977
|
+
if (!process.stdout.write(chunk)) await once(process.stdout, "drain");
|
|
978
|
+
}
|
|
979
|
+
process.stdout.write("\n");
|
|
980
|
+
} else console.log(result.consoleReport);
|
|
981
|
+
}
|
|
982
|
+
if (options.outFile) {
|
|
983
|
+
const outPath = isAbsolute(options.outFile) ? options.outFile : join(root, options.outFile);
|
|
984
|
+
await mkdir(dirname(outPath), { recursive: true });
|
|
985
|
+
await writeFile(outPath, result.jsonReportChunks());
|
|
986
|
+
}
|
|
987
|
+
if (result.failed) throw new Error(`svelte-vitals: build failed — findings at or above "${result.failOn}".`);
|
|
988
|
+
};
|
|
946
989
|
const buildPlugin = {
|
|
947
990
|
name: "svelte-vitals",
|
|
948
991
|
apply: "build",
|
|
@@ -954,45 +997,21 @@ function svelteVitals(options = {}) {
|
|
|
954
997
|
if (!options.cwd) root = config.root;
|
|
955
998
|
configFile = config.configFile;
|
|
956
999
|
ssrBuild = Boolean(config.build?.ssr);
|
|
957
|
-
|
|
958
|
-
|
|
1000
|
+
appBuild = Boolean(config.builder);
|
|
1001
|
+
readPrerenderDir(config.root ?? root, config.build?.outDir);
|
|
959
1002
|
},
|
|
960
1003
|
async closeBundle() {
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
result = await analyze(resolved, root, options, viteMinifyDisabled ? { viteMinifyDisabled } : void 0, resolvedConfig);
|
|
972
|
-
} catch (err) {
|
|
973
|
-
warn(`svelte-vitals: skipped — analysis failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
974
|
-
return;
|
|
975
|
-
}
|
|
976
|
-
for (const w of result.warnings) warn(`svelte-vitals: ${w}`);
|
|
977
|
-
if (result.routeCount === 0) {
|
|
978
|
-
if (!ssrBuild) return;
|
|
979
|
-
warn(`svelte-vitals: no prerendered pages found in ${relative(root, resolved) || "."} — route analysis skipped; the source scan still ran. Prerender routes to check their HTML, or run \`npx svelte-vitals\` to check every route from source.`);
|
|
980
|
-
}
|
|
981
|
-
if (options.report !== false) {
|
|
982
|
-
if (options.report === "json") {
|
|
983
|
-
for (const chunk of result.jsonReportChunks()) {
|
|
984
|
-
if (process.stdout.destroyed) break;
|
|
985
|
-
if (!process.stdout.write(chunk)) await once(process.stdout, "drain");
|
|
986
|
-
}
|
|
987
|
-
process.stdout.write("\n");
|
|
988
|
-
} else console.log(result.consoleReport);
|
|
989
|
-
}
|
|
990
|
-
if (options.outFile) {
|
|
991
|
-
const outPath = isAbsolute(options.outFile) ? options.outFile : join(root, options.outFile);
|
|
992
|
-
await mkdir(dirname(outPath), { recursive: true });
|
|
993
|
-
await writeFile(outPath, result.jsonReportChunks());
|
|
1004
|
+
if (appBuild) return;
|
|
1005
|
+
await analyzeAndGate(ssrBuild);
|
|
1006
|
+
},
|
|
1007
|
+
buildApp: {
|
|
1008
|
+
order: "post",
|
|
1009
|
+
async handler(builder) {
|
|
1010
|
+
if (!appBuild) return;
|
|
1011
|
+
readPrerenderDir(root, builder.environments.ssr?.config.build.outDir);
|
|
1012
|
+
minify = builder.environments.client?.config.build.minify ?? minify;
|
|
1013
|
+
await analyzeAndGate(true);
|
|
994
1014
|
}
|
|
995
|
-
if (result.failed) throw new Error(`svelte-vitals: build failed — findings at or above "${result.failOn}".`);
|
|
996
1015
|
}
|
|
997
1016
|
};
|
|
998
1017
|
if (options.ui === false) return buildPlugin;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@svelte-vitals/vite",
|
|
3
|
-
"version": "0.55.
|
|
3
|
+
"version": "0.55.5",
|
|
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.7"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
|
-
"@svelte-vitals/core": "^0.55.
|
|
47
|
-
"@sveltejs/kit": "^2.0.0",
|
|
48
|
-
"svelte-vitals": "^0.55.
|
|
46
|
+
"@svelte-vitals/core": "^0.55.5",
|
|
47
|
+
"@sveltejs/kit": "^2.0.0 || ^3.0.0",
|
|
48
|
+
"svelte-vitals": "^0.55.5",
|
|
49
49
|
"vite": "^5.0.3 || ^6.0.0 || ^7.0.0 || ^8.0.0"
|
|
50
50
|
},
|
|
51
51
|
"peerDependenciesMeta": {
|
|
@@ -54,12 +54,12 @@
|
|
|
54
54
|
}
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@svelte-vitals/core": "0.55.
|
|
57
|
+
"@svelte-vitals/core": "0.55.5",
|
|
58
58
|
"@sveltejs/kit": "^2.70.4",
|
|
59
59
|
"@types/node": "^24.19.2",
|
|
60
60
|
"happy-dom": "^21.0.4",
|
|
61
61
|
"svelte": "^5.57.3",
|
|
62
|
-
"svelte-vitals": "0.55.
|
|
62
|
+
"svelte-vitals": "0.55.5",
|
|
63
63
|
"vite": "^8.3.6"
|
|
64
64
|
},
|
|
65
65
|
"scripts": {
|