svelte-vitals 0.22.1 → 0.23.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/README.md CHANGED
@@ -34,14 +34,12 @@ Critical (1)
34
34
  src/routes/none/+page.svelte
35
35
 
36
36
  Passed (3)
37
- ────────────────────────
38
- ✓ SEO001 <title> /blog
39
- ✓ SEO001 <title> ↯ dynamic /dynamic
40
- ✓ SEO001 <title> /static
41
37
 
42
38
  ↯ = set dynamically (verified at runtime).
43
39
  ```
44
40
 
41
+ By default, console output groups failures by rule (top 5 per severity, each with one example location and an "…and N more" count) and collapses the Passed section to a bare count, so large projects don't flood the terminal. Pass `--verbose` to see every finding uncapped and ungrouped, with each passed item listed individually. On an interactive, color-capable terminal the Health score plays a short reveal animation; pass `--no-animation` to disable it.
42
+
45
43
  ### Exit codes
46
44
 
47
45
  | Code | Meaning |
package/dist/bin.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  readCoreVersion,
8
8
  readPackageVersion,
9
9
  run
10
- } from "./chunk-KSJZLF5U.js";
10
+ } from "./chunk-HYTPZVSU.js";
11
11
 
12
12
  // src/bin.ts
13
13
  import mri3 from "mri";
@@ -134,6 +134,7 @@ function resolveArgs(argv) {
134
134
  if (score && (argv.json || typeof argv.reporter === "string")) {
135
135
  warnings.push("svelte-vitals: --score overrides --reporter; reporter output suppressed.");
136
136
  }
137
+ const verbose = Boolean(argv["verbose"]);
137
138
  const rulesConfig = buildRulesConfig(allow, ignore);
138
139
  const rules = Object.keys(rulesConfig).length > 0 ? rulesConfig : void 0;
139
140
  if (errors.length > 0) return { options: null, warnings, errors };
@@ -154,6 +155,7 @@ function resolveArgs(argv) {
154
155
  ...weights !== void 0 ? { weights } : {},
155
156
  ...categories !== void 0 ? { categories } : {},
156
157
  ...score ? { score } : {},
158
+ ...verbose ? { verbose } : {},
157
159
  ...diffBase !== void 0 ? { diffBase } : {},
158
160
  ...staged ? { staged } : {},
159
161
  ...baselineRef !== void 0 ? { baseline: baselineRef } : {}
@@ -880,8 +882,8 @@ function buildWorkflowYaml(opts) {
880
882
  }
881
883
 
882
884
  // src/ci/action-pin.generated.ts
883
- var ACTION_SHA = "f16e414797a2ee42c3b1fb6a14dc6fdb9dca4132";
884
- var ACTION_VERSION = "0.2.1";
885
+ var ACTION_SHA = "caf278cf4030801b017d31b108fa46abc15e8105";
886
+ var ACTION_VERSION = "0.2.2";
885
887
 
886
888
  // src/ci/cli.ts
887
889
  var CI_HELP = `svelte-vitals ci \u2014 scaffold CI integration
@@ -969,6 +971,8 @@ Options:
969
971
  --weights <pairs> Per-category Health weight overrides, e.g. seo=2,performance=1 (unlisted categories default to 1)
970
972
  --score Print only the combined Health score (works with --min-health for gating)
971
973
  --no-color Disable ANSI color in console output
974
+ --no-animation Disable the Health-score reveal animation on an interactive terminal
975
+ --verbose Show every finding uncapped and ungrouped (default: capped, grouped by rule)
972
976
  -h, --help Show this help
973
977
  -v, --version Show version
974
978
 
@@ -999,7 +1003,7 @@ async function main() {
999
1003
  }
1000
1004
  const argv = mri3(process.argv.slice(2), {
1001
1005
  alias: { h: "help", v: "version" },
1002
- boolean: ["by-route", "json", "fail-on-warning", "staged", "no-color", "score"],
1006
+ boolean: ["by-route", "json", "fail-on-warning", "staged", "no-color", "score", "verbose", "no-animation"],
1003
1007
  string: [
1004
1008
  "meta-components",
1005
1009
  "treat-dynamic-as",
@@ -1038,7 +1042,13 @@ async function main() {
1038
1042
  }
1039
1043
  minHealth = n;
1040
1044
  }
1041
- const code = await run({ ...options, minHealth, noColor: argv["no-color"], selectApp });
1045
+ const code = await run({
1046
+ ...options,
1047
+ minHealth,
1048
+ noColor: argv["no-color"],
1049
+ noAnimation: argv["no-animation"],
1050
+ selectApp
1051
+ });
1042
1052
  process.exit(code);
1043
1053
  }
1044
1054
  void main();
@@ -702,6 +702,9 @@ function isAgentEnv(env = process.env) {
702
702
  function isGithubActionsEnv(env = process.env) {
703
703
  return env.GITHUB_ACTIONS === "true";
704
704
  }
705
+ function isCiEnv(env = process.env) {
706
+ return env.CI === "true" || env.CI === "1";
707
+ }
705
708
  function resolveReporter(explicit, env = process.env) {
706
709
  if (explicit) return explicit;
707
710
  const fromEnv = env.SVELTE_VITALS_REPORTER;
@@ -953,6 +956,40 @@ async function loadConfigFile(cwd) {
953
956
  return validateConfigFile(mod.default, found);
954
957
  }
955
958
 
959
+ // src/pulse-animation.ts
960
+ import { scoreColor } from "@svelte-vitals/core";
961
+ var FRAME_COUNT = 6;
962
+ var FRAME_DELAY_MS = 200;
963
+ var WAVE_FRAMES = [
964
+ "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2571\u2572\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
965
+ "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2571\u2572\u2500\u2500\u2571\u2572\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
966
+ "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2571\u2572\u2500\u2500\u2500\u2500\u2500\u2500\u2571\u2572\u2500\u2500\u2500\u2500\u2500\u2500",
967
+ "\u2500\u2500\u2500\u2500\u2500\u2500\u2571\u2572\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2572\u2500\u2500\u2500\u2500\u2500",
968
+ "\u2500\u2500\u2500\u2500\u2571\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2572\u2500\u2500\u2500\u2500",
969
+ "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
970
+ ];
971
+ function sleep(ms) {
972
+ return new Promise((resolve) => setTimeout(resolve, ms));
973
+ }
974
+ async function playScoreAnimation(opts) {
975
+ const frameDelayMs = opts.frameDelayMs ?? FRAME_DELAY_MS;
976
+ for (let frame = 0; frame < FRAME_COUNT; frame++) {
977
+ const progress = frame / (FRAME_COUNT - 1);
978
+ const displayScore = Math.round(opts.score * progress);
979
+ const isFinalFrame = frame === FRAME_COUNT - 1;
980
+ const wave = WAVE_FRAMES[frame];
981
+ const scoreText = isFinalFrame ? scoreColor(opts.palette, opts.score)(`${displayScore}/100`) : opts.palette.dim(`${displayScore}/100`);
982
+ const cursorUp = frame === 0 ? "" : "\x1B[2A";
983
+ opts.stream.write(`${cursorUp}\r ${wave}\x1B[K
984
+ \r Health: ${scoreText}\x1B[K
985
+ `);
986
+ if (!isFinalFrame) await sleep(frameDelayMs);
987
+ }
988
+ }
989
+ function scoreAnimationEnabled(opts) {
990
+ return opts.reporter === "console" && opts.stdoutIsTTY && !opts.noAnimationFlag && !isAgentEnv(opts.env) && !isCiEnv(opts.env) && colorEnabled({ reporter: opts.reporter, isTTY: opts.stdoutIsTTY, env: opts.env, noColorFlag: opts.noColorFlag });
991
+ }
992
+
956
993
  // src/index.ts
957
994
  import { defineConfig as defineConfig2 } from "@svelte-vitals/core";
958
995
  function spinnerEnabled(opts) {
@@ -1172,13 +1209,37 @@ async function run(opts = {}) {
1172
1209
  } else if (reporter === "md") {
1173
1210
  log(formatMarkdownReport(results, config, { version }));
1174
1211
  } else {
1212
+ const stdoutIsTTY = opts.stdoutIsTTY ?? !!process.stdout.isTTY;
1175
1213
  const colorOn = colorEnabled({
1176
1214
  reporter,
1177
- isTTY: opts.stdoutIsTTY ?? !!process.stdout.isTTY,
1215
+ isTTY: stdoutIsTTY,
1178
1216
  env,
1179
1217
  noColorFlag: opts.noColor
1180
1218
  });
1181
- log(formatConsoleReport(results, config, { byRoute: opts.byRoute ?? false, palette: paletteFor(colorOn) }));
1219
+ const palette = paletteFor(colorOn);
1220
+ const animate = scoreAnimationEnabled({
1221
+ reporter,
1222
+ stdoutIsTTY,
1223
+ env,
1224
+ noColorFlag: opts.noColor,
1225
+ noAnimationFlag: opts.noAnimation
1226
+ });
1227
+ if (animate) {
1228
+ await playScoreAnimation({
1229
+ score: computeHealth(results, config).health,
1230
+ palette,
1231
+ stream: opts.stdoutStream ?? process.stdout,
1232
+ frameDelayMs: opts.animationFrameDelayMs
1233
+ });
1234
+ }
1235
+ log(
1236
+ formatConsoleReport(results, config, {
1237
+ byRoute: opts.byRoute ?? false,
1238
+ verbose: opts.verbose ?? false,
1239
+ palette,
1240
+ omitHeader: animate
1241
+ })
1242
+ );
1182
1243
  }
1183
1244
  }
1184
1245
  const summary = summarize(results, config);
package/dist/index.d.ts CHANGED
@@ -89,6 +89,14 @@ interface RunOptions {
89
89
  explicitPath?: boolean;
90
90
  /** Injected picker for the monorepo app selector (bin.ts wires a clack implementation; null = cancelled). */
91
91
  selectApp?: (apps: string[]) => Promise<string | null>;
92
+ /** Show every finding uncapped and ungrouped in console output (default false — capped, grouped by rule). */
93
+ verbose?: boolean;
94
+ /** Disable the Health-score reveal animation even on an interactive stdout. */
95
+ noAnimation?: boolean;
96
+ /** Override the stream the score animation writes to (tests). Defaults to process.stdout. */
97
+ stdoutStream?: NodeJS.WriteStream;
98
+ /** Override the animation's per-frame delay in ms (tests — 0 runs the real frame loop near-instantly). Defaults to the animation module's own constant. */
99
+ animationFrameDelayMs?: number;
92
100
  }
93
101
  /**
94
102
  * Whether the "Analyzing…" spinner should run. Unlike color, the spinner animates
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  routeMatcher,
11
11
  run,
12
12
  spinnerEnabled
13
- } from "./chunk-KSJZLF5U.js";
13
+ } from "./chunk-HYTPZVSU.js";
14
14
  export {
15
15
  ProjectError,
16
16
  analyzeProject,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte-vitals",
3
- "version": "0.22.1",
3
+ "version": "0.23.0",
4
4
  "description": "A SvelteKit SEO checker — not a runtime Web Vitals reporter. Static analysis of your routes' head metadata.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -45,7 +45,7 @@
45
45
  "smol-toml": "^1.7.0",
46
46
  "svelte": "^5.56.4",
47
47
  "tinyglobby": "^0.2.17",
48
- "@svelte-vitals/core": "0.22.1"
48
+ "@svelte-vitals/core": "0.23.0"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/node": "^24.13.2"