svelte-vitals 0.3.0 → 0.5.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/bin.js CHANGED
@@ -1,32 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ buildRulesConfig,
4
+ findUnknownRuleIds,
3
5
  isReporterName,
6
+ knownRuleIds,
4
7
  readPackageVersion,
5
8
  run
6
- } from "./chunk-LYX2RVRM.js";
9
+ } from "./chunk-RI6LBHFR.js";
7
10
 
8
11
  // src/bin.ts
9
12
  import mri from "mri";
10
-
11
- // src/rules-config.ts
12
- import { allRules } from "@svelte-vitals/core";
13
- var KNOWN_IDS = new Set(allRules.map((r) => r.id));
14
- function findUnknownRuleIds(ids) {
15
- return [...new Set(ids.filter((id) => !KNOWN_IDS.has(id)))];
16
- }
17
- function knownRuleIds() {
18
- return [...KNOWN_IDS].sort();
19
- }
20
- function buildRulesConfig(allow, ignore) {
21
- const rules = {};
22
- if (allow.length > 0) {
23
- for (const r of allRules) if (!allow.includes(r.id)) rules[r.id] = "off";
24
- }
25
- for (const id of ignore) rules[id] = "off";
26
- return rules;
27
- }
28
-
29
- // src/bin.ts
30
13
  var HELP = `svelte-vitals \u2014 a SvelteKit SEO checker (static mode)
31
14
 
32
15
  Usage:
@@ -37,7 +20,7 @@ Options:
37
20
  --treat-dynamic-as <mode> pass | warn | fail (default: pass)
38
21
  --route <glob> Only analyze routes matching this glob
39
22
  --by-route Show per-route score breakdown in console output
40
- --reporter <fmt> console | json | agent (auto: agent under AI-agent envs)
23
+ --reporter <fmt> console | json | agent | sarif | github (auto: agent under AI-agent envs, github under GitHub Actions)
41
24
  --json Alias for --reporter=json
42
25
  --fail-on <severity> Fail (exit 1) when any finding reaches this severity: critical | warning | info
43
26
  --fail-on-warning Alias for --fail-on=warning
@@ -84,7 +67,9 @@ async function main() {
84
67
  reporter = "json";
85
68
  } else if (typeof argv.reporter === "string") {
86
69
  if (!isReporterName(argv.reporter)) {
87
- console.error(`svelte-vitals: unknown reporter '${argv.reporter}'. Valid values: console, json, agent.`);
70
+ console.error(
71
+ `svelte-vitals: unknown reporter '${argv.reporter}'. Valid values: console, json, agent, sarif, github.`
72
+ );
88
73
  process.exit(2);
89
74
  }
90
75
  reporter = argv.reporter;
@@ -1,10 +1,12 @@
1
1
  // src/index.ts
2
2
  import {
3
- allRules,
3
+ allRules as allRules2,
4
4
  runRules,
5
5
  formatConsoleReport,
6
6
  formatJsonReport,
7
7
  formatAgentReport,
8
+ formatSarifReport,
9
+ formatGithubReport,
8
10
  summarize,
9
11
  hasFailureAtOrAbove,
10
12
  defineConfig,
@@ -449,7 +451,7 @@ function readPackageVersion() {
449
451
  // src/reporter-resolve.ts
450
452
  var AGENT_ENV_VARS = ["CLAUDECODE", "SVELTE_VITALS_AGENT"];
451
453
  function isReporterName(value) {
452
- return value === "console" || value === "json" || value === "agent";
454
+ return value === "console" || value === "json" || value === "agent" || value === "sarif" || value === "github";
453
455
  }
454
456
  function isAgentEnv(env = process.env) {
455
457
  return AGENT_ENV_VARS.some((key) => {
@@ -457,16 +459,41 @@ function isAgentEnv(env = process.env) {
457
459
  return v !== void 0 && v !== "";
458
460
  });
459
461
  }
462
+ function isGithubActionsEnv(env = process.env) {
463
+ return env.GITHUB_ACTIONS === "true";
464
+ }
460
465
  function resolveReporter(explicit, env = process.env) {
461
466
  if (explicit) return explicit;
462
467
  const fromEnv = env.SVELTE_VITALS_REPORTER;
463
468
  if (isReporterName(fromEnv)) return fromEnv;
464
469
  if (isAgentEnv(env)) return "agent";
470
+ if (isGithubActionsEnv(env)) return "github";
465
471
  return "console";
466
472
  }
467
473
  function isAutoDetectedAgent(explicit, env = process.env) {
468
474
  return !explicit && !isReporterName(env.SVELTE_VITALS_REPORTER) && isAgentEnv(env);
469
475
  }
476
+ function isAutoDetectedGithub(explicit, env = process.env) {
477
+ return !explicit && !isReporterName(env.SVELTE_VITALS_REPORTER) && !isAgentEnv(env) && isGithubActionsEnv(env);
478
+ }
479
+
480
+ // src/rules-config.ts
481
+ import { allRules } from "@svelte-vitals/core";
482
+ var KNOWN_IDS = new Set(allRules.map((r) => r.id));
483
+ function findUnknownRuleIds(ids) {
484
+ return [...new Set(ids.filter((id) => !KNOWN_IDS.has(id)))];
485
+ }
486
+ function knownRuleIds() {
487
+ return [...KNOWN_IDS].sort();
488
+ }
489
+ function buildRulesConfig(allow, ignore) {
490
+ const rules = {};
491
+ if (allow.length > 0) {
492
+ for (const r of allRules) if (!allow.includes(r.id)) rules[r.id] = "off";
493
+ }
494
+ for (const id of ignore) rules[id] = "off";
495
+ return rules;
496
+ }
470
497
 
471
498
  // src/index.ts
472
499
  function routeMatcher(glob) {
@@ -475,10 +502,8 @@ function routeMatcher(glob) {
475
502
  const re = new RegExp(`^${body}$`);
476
503
  return (route) => re.test(route.replace(/^\//, ""));
477
504
  }
478
- async function run(opts = {}) {
505
+ async function analyzeProject(opts = {}) {
479
506
  const cwd = opts.cwd ?? process.cwd();
480
- const log = opts.log ?? ((line) => console.log(line));
481
- const errorLog = opts.errorLog ?? ((line) => console.error(line));
482
507
  const rt = createNodeRuntime();
483
508
  const config = defineConfig({
484
509
  treatDynamicAs: opts.treatDynamicAs ?? "pass",
@@ -486,21 +511,37 @@ async function run(opts = {}) {
486
511
  rules: opts.rules ?? {},
487
512
  failOn: opts.failOn ?? "critical"
488
513
  });
514
+ await detectProject(rt, cwd);
515
+ const matches = routeMatcher(opts.route);
516
+ const heads = (await sourceHeadProvider.collect(rt, cwd, config)).filter((h) => matches(h.route));
517
+ const project = await collectProjectFacts(rt, cwd);
518
+ const rules = selectRules(allRules2, config);
519
+ const results = applyRuleSeverities(await runRules(rules, { heads, project, config }), config);
520
+ return { results, config, version: readPackageVersion() };
521
+ }
522
+ async function run(opts = {}) {
523
+ const log = opts.log ?? ((line) => console.log(line));
524
+ const errorLog = opts.errorLog ?? ((line) => console.error(line));
525
+ let analysis;
489
526
  try {
490
- await detectProject(rt, cwd);
527
+ analysis = await analyzeProject({
528
+ cwd: opts.cwd ?? process.cwd(),
529
+ metaComponents: opts.metaComponents,
530
+ treatDynamicAs: opts.treatDynamicAs,
531
+ route: opts.route,
532
+ failOn: opts.failOn,
533
+ rules: opts.rules
534
+ });
491
535
  } catch (err) {
492
536
  if (err instanceof ProjectError) {
493
537
  errorLog(err.message);
494
538
  return 2;
495
539
  }
496
- throw err;
540
+ errorLog(`svelte-vitals: ${err instanceof Error ? err.message : String(err)}`);
541
+ return 2;
497
542
  }
498
543
  try {
499
- const matches = routeMatcher(opts.route);
500
- const heads = (await sourceHeadProvider.collect(rt, cwd, config)).filter((h) => matches(h.route));
501
- const project = await collectProjectFacts(rt, cwd);
502
- const rules = selectRules(allRules, config);
503
- const results = applyRuleSeverities(await runRules(rules, { heads, project, config }), config);
544
+ const { results, config, version } = analysis;
504
545
  const env = opts.env ?? process.env;
505
546
  const reporter = resolveReporter(opts.reporter, env);
506
547
  if (reporter === "agent" && isAutoDetectedAgent(opts.reporter, env)) {
@@ -508,10 +549,20 @@ async function run(opts = {}) {
508
549
  "svelte-vitals: agent reporter auto-selected (AI-agent env detected); override with --reporter console|json."
509
550
  );
510
551
  }
552
+ if (reporter === "github" && isAutoDetectedGithub(opts.reporter, env)) {
553
+ errorLog(
554
+ "svelte-vitals: github reporter auto-selected (GitHub Actions detected); override with --reporter console|json|sarif."
555
+ );
556
+ }
511
557
  if (reporter === "json") {
512
- log(formatJsonReport(results, config, { version: readPackageVersion() }));
558
+ log(formatJsonReport(results, config, { version }));
513
559
  } else if (reporter === "agent") {
514
560
  log(formatAgentReport(results, config));
561
+ } else if (reporter === "sarif") {
562
+ log(formatSarifReport(results, config, { version }));
563
+ } else if (reporter === "github") {
564
+ const output = formatGithubReport(results, config);
565
+ if (output) log(output);
515
566
  } else {
516
567
  log(formatConsoleReport(results, config, { byRoute: opts.byRoute ?? false }));
517
568
  }
@@ -524,8 +575,13 @@ async function run(opts = {}) {
524
575
  }
525
576
 
526
577
  export {
578
+ ProjectError,
527
579
  readPackageVersion,
528
580
  isReporterName,
581
+ findUnknownRuleIds,
582
+ knownRuleIds,
583
+ buildRulesConfig,
529
584
  routeMatcher,
585
+ analyzeProject,
530
586
  run
531
587
  };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,23 @@
1
- import { Severity, RuleSetting } from '@svelte-vitals/core';
1
+ import { RuleSetting, Severity, Result, Config } from '@svelte-vitals/core';
2
2
 
3
- type ReporterName = 'console' | 'json' | 'agent';
3
+ type ReporterName = 'console' | 'json' | 'agent' | 'sarif' | 'github';
4
+
5
+ /** Thrown when the target directory is not a SvelteKit project (CLI maps to exit 2). */
6
+ declare class ProjectError extends Error {
7
+ constructor(message: string);
8
+ }
9
+
10
+ /** Rule ids passed to --rules/--ignore that aren't part of the built-in registry. */
11
+ declare function findUnknownRuleIds(ids: string[]): string[];
12
+ /** All built-in rule ids, sorted — for help and error messages. */
13
+ declare function knownRuleIds(): string[];
14
+ /**
15
+ * Build the per-rule config map from an allow-list (--rules) and a deny-list
16
+ * (--ignore). An allow-list disables every rule not listed; deny always wins.
17
+ * Callers should reject unknown ids first (see findUnknownRuleIds) so a typo in
18
+ * --rules can't silently disable every rule.
19
+ */
20
+ declare function buildRulesConfig(allow: string[], ignore: string[]): Record<string, RuleSetting>;
4
21
 
5
22
  interface RunOptions {
6
23
  cwd?: string;
@@ -18,10 +35,30 @@ interface RunOptions {
18
35
  env?: NodeJS.ProcessEnv;
19
36
  }
20
37
  declare function routeMatcher(glob: string | undefined): (route: string) => boolean;
38
+ interface AnalyzeOptions {
39
+ cwd?: string;
40
+ metaComponents?: string[];
41
+ treatDynamicAs?: 'pass' | 'warn' | 'fail';
42
+ /** Restrict analysis to routes whose path matches this glob (matched against the route path without leading slash). */
43
+ route?: string;
44
+ failOn?: Severity;
45
+ rules?: Record<string, RuleSetting>;
46
+ }
47
+ interface AnalyzeResult {
48
+ results: Result[];
49
+ config: Config;
50
+ version: string;
51
+ }
52
+ /**
53
+ * Run static-mode analysis and return the structured findings + resolved config.
54
+ * Throws ProjectError when `cwd` is not a SvelteKit project. Shared by the CLI's
55
+ * run() and by @svelte-vitals/mcp (issue #24).
56
+ */
57
+ declare function analyzeProject(opts?: AnalyzeOptions): Promise<AnalyzeResult>;
21
58
  /**
22
59
  * Run static-mode analysis once and return the process exit code (design §6):
23
60
  * 0 = no failing findings, 1 = critical finding present, 2 = execution error.
24
61
  */
25
62
  declare function run(opts?: RunOptions): Promise<number>;
26
63
 
27
- export { type RunOptions, routeMatcher, run };
64
+ export { type AnalyzeOptions, type AnalyzeResult, ProjectError, type RunOptions, analyzeProject, buildRulesConfig, findUnknownRuleIds, knownRuleIds, routeMatcher, run };
package/dist/index.js CHANGED
@@ -1,8 +1,18 @@
1
1
  import {
2
+ ProjectError,
3
+ analyzeProject,
4
+ buildRulesConfig,
5
+ findUnknownRuleIds,
6
+ knownRuleIds,
2
7
  routeMatcher,
3
8
  run
4
- } from "./chunk-LYX2RVRM.js";
9
+ } from "./chunk-RI6LBHFR.js";
5
10
  export {
11
+ ProjectError,
12
+ analyzeProject,
13
+ buildRulesConfig,
14
+ findUnknownRuleIds,
15
+ knownRuleIds,
6
16
  routeMatcher,
7
17
  run
8
18
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte-vitals",
3
- "version": "0.3.0",
3
+ "version": "0.5.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",
@@ -38,7 +38,7 @@
38
38
  "mri": "^1.2.0",
39
39
  "svelte": "^5.56.3",
40
40
  "tinyglobby": "^0.2.17",
41
- "@svelte-vitals/core": "0.4.0"
41
+ "@svelte-vitals/core": "0.6.0"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/node": "^24.7.0"