svelte-vitals 0.43.0 → 0.44.1
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 +228 -231
- package/dist/{chunk-6Y2E7QCQ.js → chunk-4MC7STFI.js} +308 -248
- package/dist/chunk-5Q2PT47V.js +27 -0
- package/dist/{cli-J5V65YIN.js → cli-LGD6NDAO.js} +4 -6
- package/dist/index.d.ts +36 -32
- package/dist/index.js +1 -3
- package/package.json +8 -5
- package/dist/chunk-SLUMRYUD.js +0 -9
package/dist/bin.js
CHANGED
|
@@ -3,19 +3,19 @@ import {
|
|
|
3
3
|
CONFIG_FILENAMES,
|
|
4
4
|
discoverApps,
|
|
5
5
|
findUnknownRuleIds,
|
|
6
|
+
hasDep,
|
|
6
7
|
isReporterName,
|
|
7
8
|
knownRuleIds,
|
|
8
9
|
readCoreVersion,
|
|
9
10
|
readPackageVersion,
|
|
11
|
+
readPkg,
|
|
10
12
|
run
|
|
11
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-4MC7STFI.js";
|
|
12
14
|
import {
|
|
13
|
-
consoleIO
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
import mri4 from "mri";
|
|
18
|
-
import * as p2 from "@clack/prompts";
|
|
15
|
+
consoleIO,
|
|
16
|
+
parseCliArgs,
|
|
17
|
+
toList
|
|
18
|
+
} from "./chunk-5Q2PT47V.js";
|
|
19
19
|
|
|
20
20
|
// src/resolve-args.ts
|
|
21
21
|
var CATEGORIES = ["seo", "performance", "correctness", "security", "architecture"];
|
|
@@ -24,7 +24,7 @@ function parseWeights(raw, errors) {
|
|
|
24
24
|
const weights = {};
|
|
25
25
|
const unknownCategories = [];
|
|
26
26
|
const invalidValues = [];
|
|
27
|
-
for (const pair of raw
|
|
27
|
+
for (const pair of toList(raw)) {
|
|
28
28
|
const eq = pair.indexOf("=");
|
|
29
29
|
if (eq === -1) {
|
|
30
30
|
invalidValues.push(pair);
|
|
@@ -65,7 +65,7 @@ function parseCategories(raw, errors) {
|
|
|
65
65
|
if (typeof raw !== "string" || raw.trim() === "") return void 0;
|
|
66
66
|
const categories = [];
|
|
67
67
|
const unknownCategories = [];
|
|
68
|
-
for (const entry of raw
|
|
68
|
+
for (const entry of toList(raw).map((s) => s.toLowerCase())) {
|
|
69
69
|
if (!CATEGORIES.includes(entry)) {
|
|
70
70
|
unknownCategories.push(entry);
|
|
71
71
|
continue;
|
|
@@ -81,12 +81,74 @@ function parseCategories(raw, errors) {
|
|
|
81
81
|
}
|
|
82
82
|
return categories;
|
|
83
83
|
}
|
|
84
|
-
var
|
|
84
|
+
var VALUE_FLAGS = [
|
|
85
|
+
"meta-components",
|
|
86
|
+
"treat-dynamic-as",
|
|
87
|
+
"route",
|
|
88
|
+
"fail-on",
|
|
89
|
+
"reporter",
|
|
90
|
+
"rules",
|
|
91
|
+
"ignore",
|
|
92
|
+
"min-health",
|
|
93
|
+
"out-file",
|
|
94
|
+
"weights",
|
|
95
|
+
"category"
|
|
96
|
+
];
|
|
97
|
+
function parseRunArgs(args) {
|
|
98
|
+
const patched = args.map((a, i) => a === "--diff" && (args[i + 1] ?? "--").startsWith("-") ? "--diff=HEAD" : a);
|
|
99
|
+
return parseCliArgs(patched, {
|
|
100
|
+
boolean: [
|
|
101
|
+
"by-route",
|
|
102
|
+
"staged",
|
|
103
|
+
"score",
|
|
104
|
+
"verbose",
|
|
105
|
+
"update-suppressions",
|
|
106
|
+
"no-suppressions",
|
|
107
|
+
"no-color",
|
|
108
|
+
"no-animation",
|
|
109
|
+
"help",
|
|
110
|
+
"version"
|
|
111
|
+
],
|
|
112
|
+
string: [
|
|
113
|
+
"meta-components",
|
|
114
|
+
"treat-dynamic-as",
|
|
115
|
+
"route",
|
|
116
|
+
"fail-on",
|
|
117
|
+
"reporter",
|
|
118
|
+
"rules",
|
|
119
|
+
"ignore",
|
|
120
|
+
"min-health",
|
|
121
|
+
"out-file",
|
|
122
|
+
"diff",
|
|
123
|
+
"baseline",
|
|
124
|
+
"weights",
|
|
125
|
+
"category"
|
|
126
|
+
],
|
|
127
|
+
short: { h: "help", v: "version" }
|
|
128
|
+
});
|
|
129
|
+
}
|
|
85
130
|
function resolveArgs(argv) {
|
|
86
131
|
const warnings = [];
|
|
87
132
|
const errors = [];
|
|
133
|
+
for (const flag of VALUE_FLAGS) {
|
|
134
|
+
const v = argv[flag];
|
|
135
|
+
if (flag === "out-file" && v === "-") continue;
|
|
136
|
+
if (v !== void 0 && (typeof v !== "string" || v.trim() === "" || v.startsWith("-"))) {
|
|
137
|
+
errors.push(`svelte-vitals: --${flag} requires a value.`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
let minHealth;
|
|
141
|
+
const minHealthRaw = argv["min-health"];
|
|
142
|
+
if (minHealthRaw !== void 0) {
|
|
143
|
+
const n = Number(minHealthRaw);
|
|
144
|
+
if (!Number.isFinite(n) || n < 0 || n > 100) {
|
|
145
|
+
errors.push(`svelte-vitals: invalid --min-health '${minHealthRaw}'; expected a number 0-100.`);
|
|
146
|
+
} else {
|
|
147
|
+
minHealth = n;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
88
150
|
const positional = argv._[0];
|
|
89
|
-
const metaComponents = typeof argv["meta-components"] === "string" ? argv["meta-components"]
|
|
151
|
+
const metaComponents = typeof argv["meta-components"] === "string" ? toList(argv["meta-components"]) : void 0;
|
|
90
152
|
const treatRaw = argv["treat-dynamic-as"];
|
|
91
153
|
const treatDynamicAs = treatRaw === "warn" || treatRaw === "fail" || treatRaw === "pass" ? treatRaw : void 0;
|
|
92
154
|
if (typeof treatRaw === "string" && treatDynamicAs === void 0) {
|
|
@@ -98,8 +160,8 @@ function resolveArgs(argv) {
|
|
|
98
160
|
const diffBase = typeof argv.diff === "string" ? argv.diff || "HEAD" : void 0;
|
|
99
161
|
const staged = Boolean(argv.staged);
|
|
100
162
|
let baselineRef;
|
|
101
|
-
if (
|
|
102
|
-
if (argv.baseline.trim() === "") {
|
|
163
|
+
if (argv.baseline !== void 0) {
|
|
164
|
+
if (typeof argv.baseline !== "string" || argv.baseline.trim() === "" || argv.baseline.startsWith("-")) {
|
|
103
165
|
errors.push("svelte-vitals: --baseline requires a git ref (e.g. --baseline origin/main).");
|
|
104
166
|
} else {
|
|
105
167
|
baselineRef = argv.baseline;
|
|
@@ -132,14 +194,23 @@ function resolveArgs(argv) {
|
|
|
132
194
|
const failOn = failOnValid ? failOnRaw : void 0;
|
|
133
195
|
const weights = parseWeights(argv.weights, errors);
|
|
134
196
|
const categories = parseCategories(argv.category, errors);
|
|
197
|
+
if (categories !== void 0 && categories.length > 0 && allow.length > 0) {
|
|
198
|
+
const excluded = allow.filter((id) => !unknown.includes(id)).filter((id) => !categories.includes(id.split("/")[0]));
|
|
199
|
+
if (excluded.length > 0) {
|
|
200
|
+
errors.push(
|
|
201
|
+
`svelte-vitals: --rules id(s) excluded by --category ${categories.join(", ")}: ${excluded.join(", ")}`
|
|
202
|
+
);
|
|
203
|
+
errors.push("Add the rule's category to --category, or drop the rule from --rules.");
|
|
204
|
+
}
|
|
205
|
+
}
|
|
135
206
|
const score = Boolean(argv.score);
|
|
136
207
|
if (score && typeof argv.reporter === "string") {
|
|
137
208
|
warnings.push("svelte-vitals: --score overrides --reporter; reporter output suppressed.");
|
|
138
209
|
}
|
|
139
210
|
const verbose = Boolean(argv["verbose"]);
|
|
140
|
-
const noColor = argv
|
|
141
|
-
const noAnimation = argv
|
|
142
|
-
const noSuppressions = argv
|
|
211
|
+
const noColor = Boolean(argv["no-color"]);
|
|
212
|
+
const noAnimation = Boolean(argv["no-animation"]);
|
|
213
|
+
const noSuppressions = Boolean(argv["no-suppressions"]);
|
|
143
214
|
const updateSuppressions = Boolean(argv["update-suppressions"]);
|
|
144
215
|
if (updateSuppressions && noSuppressions) {
|
|
145
216
|
errors.push("svelte-vitals: --update-suppressions and --no-suppressions cannot be used together.");
|
|
@@ -175,7 +246,8 @@ function resolveArgs(argv) {
|
|
|
175
246
|
...updateSuppressions ? { updateSuppressions } : {}
|
|
176
247
|
},
|
|
177
248
|
warnings,
|
|
178
|
-
errors
|
|
249
|
+
errors,
|
|
250
|
+
minHealth
|
|
179
251
|
};
|
|
180
252
|
}
|
|
181
253
|
|
|
@@ -183,83 +255,11 @@ function resolveArgs(argv) {
|
|
|
183
255
|
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
184
256
|
import { dirname } from "path";
|
|
185
257
|
import { spawnSync } from "child_process";
|
|
186
|
-
import mri from "mri";
|
|
187
258
|
import * as p from "@clack/prompts";
|
|
188
259
|
|
|
189
260
|
// src/install/index.ts
|
|
190
261
|
import { join as join3 } from "path";
|
|
191
262
|
|
|
192
|
-
// src/install/vite-targets.ts
|
|
193
|
-
var VITE_TARGETS = [
|
|
194
|
-
{
|
|
195
|
-
id: "vite-plugin",
|
|
196
|
-
label: "Vite plugin (build gate)",
|
|
197
|
-
hint: "Fails `vite build` when prerendered pages cross the SEO/Performance threshold"
|
|
198
|
-
},
|
|
199
|
-
{
|
|
200
|
-
id: "vite-hooks",
|
|
201
|
-
label: "Live dashboard accuracy",
|
|
202
|
-
hint: "Feeds real rendered results into the live dashboard as you browse \u2014 improves per-route accuracy, never fails a build"
|
|
203
|
-
}
|
|
204
|
-
];
|
|
205
|
-
function viteTargetById(id) {
|
|
206
|
-
return VITE_TARGETS.find((t) => t.id === id);
|
|
207
|
-
}
|
|
208
|
-
function isViteTargetId(id) {
|
|
209
|
-
return VITE_TARGETS.some((t) => t.id === id);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// src/install/agent-targets.ts
|
|
213
|
-
var AGENT_TARGETS = [
|
|
214
|
-
{
|
|
215
|
-
id: "claude-skill",
|
|
216
|
-
label: "Agent skill: svelte-vitals",
|
|
217
|
-
hint: "Teaches the agent svelte-vitals rules + when to run the scanner (Claude Code, Codex, Cursor)",
|
|
218
|
-
relPaths: [
|
|
219
|
-
".claude/skills/svelte-vitals/SKILL.md",
|
|
220
|
-
".agents/skills/svelte-vitals/SKILL.md",
|
|
221
|
-
".cursor/skills/svelte-vitals/SKILL.md"
|
|
222
|
-
]
|
|
223
|
-
},
|
|
224
|
-
{
|
|
225
|
-
id: "cursor-rules",
|
|
226
|
-
label: "Cursor rules",
|
|
227
|
-
hint: "Project rules file so Cursor avoids flagged patterns up front",
|
|
228
|
-
relPaths: [".cursor/rules/svelte-vitals.mdc"]
|
|
229
|
-
},
|
|
230
|
-
{
|
|
231
|
-
id: "claude-skill-improve",
|
|
232
|
-
label: "Agent skill: improve-svelte",
|
|
233
|
-
hint: "Senior-advisor audit \u2192 implementation plans (read-only), for a project-wide improvement roadmap (Claude Code, Codex, Cursor)",
|
|
234
|
-
relPaths: [
|
|
235
|
-
".claude/skills/improve-svelte/SKILL.md",
|
|
236
|
-
".agents/skills/improve-svelte/SKILL.md",
|
|
237
|
-
".cursor/skills/improve-svelte/SKILL.md"
|
|
238
|
-
]
|
|
239
|
-
}
|
|
240
|
-
];
|
|
241
|
-
function agentTargetById(id) {
|
|
242
|
-
return AGENT_TARGETS.find((t) => t.id === id);
|
|
243
|
-
}
|
|
244
|
-
function isAgentTargetId(id) {
|
|
245
|
-
return AGENT_TARGETS.some((t) => t.id === id);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// src/install/config-targets.ts
|
|
249
|
-
var CONFIG_TARGETS = [
|
|
250
|
-
{
|
|
251
|
-
id: "config-file",
|
|
252
|
-
label: "Config file",
|
|
253
|
-
hint: "Scaffolds svelte-vitals.config.{mjs,ts} (auto-picks the best one) with every option commented out"
|
|
254
|
-
}
|
|
255
|
-
];
|
|
256
|
-
function configTargetById(id) {
|
|
257
|
-
return CONFIG_TARGETS.find((t) => t.id === id);
|
|
258
|
-
}
|
|
259
|
-
function isConfigTargetId(id) {
|
|
260
|
-
return CONFIG_TARGETS.some((t) => t.id === id);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
263
|
// src/ci/workflow.ts
|
|
264
264
|
var WORKFLOW_PATH = ".github/workflows/svelte-vitals.yml";
|
|
265
265
|
var CHECKOUT_SHA = "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0";
|
|
@@ -298,20 +298,74 @@ function buildWorkflowYaml(opts) {
|
|
|
298
298
|
].join("\n");
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
-
// src/install/
|
|
302
|
-
var
|
|
301
|
+
// src/install/targets.ts
|
|
302
|
+
var INSTALL_TARGETS = [
|
|
303
|
+
{
|
|
304
|
+
id: "vite-plugin",
|
|
305
|
+
kind: "vite",
|
|
306
|
+
label: "Vite plugin (build gate)",
|
|
307
|
+
hint: "Fails `vite build` when prerendered pages cross the SEO/Performance threshold",
|
|
308
|
+
relPaths: []
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
id: "vite-hooks",
|
|
312
|
+
kind: "vite",
|
|
313
|
+
label: "Live dashboard accuracy",
|
|
314
|
+
hint: "Feeds real rendered results into the live dashboard as you browse \u2014 improves per-route accuracy, never fails a build",
|
|
315
|
+
relPaths: []
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
id: "claude-skill",
|
|
319
|
+
kind: "agent",
|
|
320
|
+
label: "Agent skill: svelte-vitals",
|
|
321
|
+
hint: "Teaches the agent svelte-vitals rules + when to run the scanner (Claude Code, Codex, Cursor)",
|
|
322
|
+
relPaths: [
|
|
323
|
+
".claude/skills/svelte-vitals/SKILL.md",
|
|
324
|
+
".agents/skills/svelte-vitals/SKILL.md",
|
|
325
|
+
".cursor/skills/svelte-vitals/SKILL.md"
|
|
326
|
+
]
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
id: "cursor-rules",
|
|
330
|
+
kind: "agent",
|
|
331
|
+
label: "Cursor rules",
|
|
332
|
+
hint: "Project rules file so Cursor avoids flagged patterns up front",
|
|
333
|
+
relPaths: [".cursor/rules/svelte-vitals.mdc"]
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
id: "claude-skill-improve",
|
|
337
|
+
kind: "agent",
|
|
338
|
+
label: "Agent skill: improve-svelte",
|
|
339
|
+
hint: "Senior-advisor audit \u2192 implementation plans (read-only), for a project-wide improvement roadmap (Claude Code, Codex, Cursor)",
|
|
340
|
+
relPaths: [
|
|
341
|
+
".claude/skills/improve-svelte/SKILL.md",
|
|
342
|
+
".agents/skills/improve-svelte/SKILL.md",
|
|
343
|
+
".cursor/skills/improve-svelte/SKILL.md"
|
|
344
|
+
]
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
id: "config-file",
|
|
348
|
+
kind: "config",
|
|
349
|
+
label: "Config file",
|
|
350
|
+
hint: "Scaffolds svelte-vitals.config.{mjs,ts} (auto-picks the best one) with every option commented out",
|
|
351
|
+
relPaths: []
|
|
352
|
+
},
|
|
303
353
|
{
|
|
304
354
|
id: "ci-workflow",
|
|
355
|
+
kind: "ci",
|
|
305
356
|
label: "GitHub Actions CI",
|
|
306
357
|
hint: "Scaffolds a workflow that runs @svelte-vitals/action on pull requests \u2014 inline annotations, job summary, sticky PR comment",
|
|
307
|
-
|
|
358
|
+
relPaths: [WORKFLOW_PATH]
|
|
308
359
|
}
|
|
309
360
|
];
|
|
310
|
-
function
|
|
311
|
-
return
|
|
361
|
+
function targetById(id) {
|
|
362
|
+
return INSTALL_TARGETS.find((t) => t.id === id);
|
|
312
363
|
}
|
|
313
|
-
function
|
|
314
|
-
return
|
|
364
|
+
function targetsOfKind(kind) {
|
|
365
|
+
return INSTALL_TARGETS.filter((t) => t.kind === kind);
|
|
366
|
+
}
|
|
367
|
+
function isKind(id, kind) {
|
|
368
|
+
return targetById(id)?.kind === kind;
|
|
315
369
|
}
|
|
316
370
|
|
|
317
371
|
// src/install/skill-content.ts
|
|
@@ -351,7 +405,7 @@ ${lines}`;
|
|
|
351
405
|
}).join("\n\n");
|
|
352
406
|
}
|
|
353
407
|
function sharedBody(version) {
|
|
354
|
-
return `<!-- Generated by \`svelte-vitals install\` (svelte-vitals ${version}). Re-run
|
|
408
|
+
return `<!-- Generated by \`svelte-vitals install\` (svelte-vitals ${version}). Re-run \`svelte-vitals install --refresh\` to regenerate. -->
|
|
355
409
|
|
|
356
410
|
# svelte-vitals
|
|
357
411
|
|
|
@@ -398,7 +452,7 @@ name: improve-svelte
|
|
|
398
452
|
description: Survey a whole SvelteKit codebase as a senior Svelte/SvelteKit engineer, using svelte-vitals' scan as evidence, then produce a prioritized audit and self-contained implementation plans for other agents (or cheaper models) to execute. Read-only on source code \u2014 it plans improvements, it does not apply them. Use when the user asks to "improve this SvelteKit app", "audit this codebase", "make this app more SEO/performance/security solid", or wants a roadmap of fixes rather than a review of a single diff. For routine regression checks while writing code, use the \`svelte-vitals\` skill instead.
|
|
399
453
|
---
|
|
400
454
|
|
|
401
|
-
<!-- Generated by \`svelte-vitals install\` (svelte-vitals ${version}). Re-run
|
|
455
|
+
<!-- Generated by \`svelte-vitals install\` (svelte-vitals ${version}). Re-run \`svelte-vitals install --refresh\` to regenerate. -->
|
|
402
456
|
|
|
403
457
|
# improve-svelte
|
|
404
458
|
|
|
@@ -755,23 +809,10 @@ function findExistingConfigFile(readFile, cwd) {
|
|
|
755
809
|
return CONFIG_FILENAMES.find((rel) => readFile(join(cwd, rel)) !== void 0);
|
|
756
810
|
}
|
|
757
811
|
function hasSvelteVitalsDependency(readFile, cwd) {
|
|
758
|
-
|
|
759
|
-
if (raw === void 0) return false;
|
|
760
|
-
try {
|
|
761
|
-
const pkg = JSON.parse(raw);
|
|
762
|
-
return Boolean(pkg.dependencies?.["svelte-vitals"] ?? pkg.devDependencies?.["svelte-vitals"]);
|
|
763
|
-
} catch {
|
|
764
|
-
return false;
|
|
765
|
-
}
|
|
812
|
+
return hasDep(readPkg(readFile, cwd), "svelte-vitals");
|
|
766
813
|
}
|
|
767
814
|
function isEsmProject(readFile, cwd) {
|
|
768
|
-
|
|
769
|
-
if (raw === void 0) return false;
|
|
770
|
-
try {
|
|
771
|
-
return JSON.parse(raw).type === "module";
|
|
772
|
-
} catch {
|
|
773
|
-
return false;
|
|
774
|
-
}
|
|
815
|
+
return readPkg(readFile, cwd)?.type === "module";
|
|
775
816
|
}
|
|
776
817
|
function detectBestConfigExtension(opts) {
|
|
777
818
|
if (!nodeSupportsNativeTypeScript(opts.nodeVersion)) return "mjs";
|
|
@@ -796,7 +837,7 @@ function codemodViteConfig(existing) {
|
|
|
796
837
|
return { status: "manual", snippet: MANUAL_SNIPPET };
|
|
797
838
|
}
|
|
798
839
|
const already = configObj.plugins.find(
|
|
799
|
-
(
|
|
840
|
+
(p2) => p2?.$type === "function-call" && p2?.$callee === "svelteVitals"
|
|
800
841
|
);
|
|
801
842
|
if (already !== void 0) {
|
|
802
843
|
return { status: "exists" };
|
|
@@ -904,14 +945,10 @@ function detectPackageManager(io) {
|
|
|
904
945
|
return detectPackageManagerFromLockfile(io) ?? "npm";
|
|
905
946
|
}
|
|
906
947
|
function hasVitePackage(io) {
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
return Boolean(pkg.dependencies?.["@svelte-vitals/vite"] || pkg.devDependencies?.["@svelte-vitals/vite"]);
|
|
912
|
-
} catch {
|
|
913
|
-
return false;
|
|
914
|
-
}
|
|
948
|
+
return hasDep(
|
|
949
|
+
readPkg((p2) => io.readFile(p2), io.cwd),
|
|
950
|
+
"@svelte-vitals/vite"
|
|
951
|
+
);
|
|
915
952
|
}
|
|
916
953
|
function installCommand(pm) {
|
|
917
954
|
const action = pm === "npm" ? "install" : "add";
|
|
@@ -946,12 +983,12 @@ function resolveCandidate(io, baseDir, candidates) {
|
|
|
946
983
|
function planForVitePlugin(io, appDir) {
|
|
947
984
|
const { path, content } = resolveCandidate(io, appDir, ["vite.config.ts", "vite.config.js", "vite.config.mjs"]);
|
|
948
985
|
const result = codemodViteConfig(content);
|
|
949
|
-
return { id: "vite-plugin", label:
|
|
986
|
+
return { id: "vite-plugin", label: targetById("vite-plugin").label, path, ...result };
|
|
950
987
|
}
|
|
951
988
|
function planForViteHooks(io, appDir) {
|
|
952
989
|
const { path, content } = resolveCandidate(io, appDir, ["src/hooks.server.ts", "src/hooks.server.js"]);
|
|
953
990
|
const result = codemodHooksServer(content);
|
|
954
|
-
return { id: "vite-hooks", label:
|
|
991
|
+
return { id: "vite-hooks", label: targetById("vite-hooks").label, path, ...result };
|
|
955
992
|
}
|
|
956
993
|
function agentTargetContent(id, version) {
|
|
957
994
|
switch (id) {
|
|
@@ -967,8 +1004,9 @@ function agentTargetContent(id, version) {
|
|
|
967
1004
|
}
|
|
968
1005
|
}
|
|
969
1006
|
}
|
|
970
|
-
function planForAgentTarget(
|
|
971
|
-
const
|
|
1007
|
+
function planForAgentTarget(id, io, force, version) {
|
|
1008
|
+
const target = targetById(id);
|
|
1009
|
+
const content = agentTargetContent(id, version);
|
|
972
1010
|
return target.relPaths.map((relPath) => {
|
|
973
1011
|
const path = join3(io.cwd, relPath);
|
|
974
1012
|
const existing = io.readFile(path);
|
|
@@ -976,7 +1014,8 @@ function planForAgentTarget(target, io, force, version) {
|
|
|
976
1014
|
return { id: target.id, label: target.label, path, status, content };
|
|
977
1015
|
});
|
|
978
1016
|
}
|
|
979
|
-
function planForConfigTarget(
|
|
1017
|
+
function planForConfigTarget(io, force, appDir) {
|
|
1018
|
+
const target = targetById("config-file");
|
|
980
1019
|
const existingRel = findExistingConfigFile(io.readFile, appDir);
|
|
981
1020
|
if (existingRel !== void 0) {
|
|
982
1021
|
const path2 = join3(appDir, existingRel);
|
|
@@ -996,8 +1035,9 @@ function planForConfigTarget(target, io, force, appDir) {
|
|
|
996
1035
|
const content = buildConfigFileTemplate({ useDefineConfig: ext === "ts" });
|
|
997
1036
|
return { id: target.id, label: target.label, path, status: "created", content };
|
|
998
1037
|
}
|
|
999
|
-
function planForCiTarget(
|
|
1000
|
-
const
|
|
1038
|
+
function planForCiTarget(io, force) {
|
|
1039
|
+
const target = targetById("ci-workflow");
|
|
1040
|
+
const path = join3(io.cwd, target.relPaths[0]);
|
|
1001
1041
|
const existing = io.readFile(path);
|
|
1002
1042
|
const plan = planWorkflowWrite(existing, force);
|
|
1003
1043
|
const content = plan.status === "exists" ? void 0 : buildWorkflowYaml({ actionSha: ACTION_SHA, actionVersion: ACTION_VERSION });
|
|
@@ -1014,7 +1054,7 @@ ${indent(r.snippet)}` : head;
|
|
|
1014
1054
|
async function runRefresh(io, flags, version) {
|
|
1015
1055
|
let hadFailure = false;
|
|
1016
1056
|
const rows = [];
|
|
1017
|
-
for (const target of
|
|
1057
|
+
for (const target of targetsOfKind("agent")) {
|
|
1018
1058
|
const content = agentTargetContent(target.id, version);
|
|
1019
1059
|
for (const relPath of target.relPaths) {
|
|
1020
1060
|
const path = join3(io.cwd, relPath);
|
|
@@ -1079,25 +1119,26 @@ async function runInstall(flags, io, prompts, version = "0.0.0") {
|
|
|
1079
1119
|
".cursor/environment.json",
|
|
1080
1120
|
".cursorrules",
|
|
1081
1121
|
".cursorignore",
|
|
1082
|
-
|
|
1122
|
+
targetById("cursor-rules").relPaths[0]
|
|
1083
1123
|
].some((rel) => configExists(join3(io.cwd, rel)));
|
|
1084
1124
|
const detectedAgents = [
|
|
1085
1125
|
...claudeSkillDetected ? ["claude-skill"] : [],
|
|
1086
1126
|
...cursorRulesDetected ? ["cursor-rules"] : []
|
|
1087
1127
|
];
|
|
1088
|
-
const ciWorkflowDetected = configExists(join3(io.cwd,
|
|
1089
|
-
const configFileDetected = findExistingConfigFile((
|
|
1128
|
+
const ciWorkflowDetected = configExists(join3(io.cwd, targetById("ci-workflow").relPaths[0]));
|
|
1129
|
+
const configFileDetected = findExistingConfigFile((p2) => configExists(p2) ? "" : void 0, io.cwd) !== void 0;
|
|
1090
1130
|
const detected = [
|
|
1091
|
-
...viteConfigExists ?
|
|
1131
|
+
...viteConfigExists ? targetsOfKind("vite").map((t) => t.id) : [],
|
|
1092
1132
|
...detectedAgents,
|
|
1093
|
-
...ciWorkflowDetected ?
|
|
1094
|
-
...configFileDetected ?
|
|
1133
|
+
...ciWorkflowDetected ? ["ci-workflow"] : [],
|
|
1134
|
+
...configFileDetected ? ["config-file"] : []
|
|
1095
1135
|
];
|
|
1136
|
+
const asOption = (t) => ({ id: t.id, label: t.label, hint: t.hint });
|
|
1096
1137
|
const groups = {
|
|
1097
|
-
"Vite integration":
|
|
1098
|
-
"Agent Skills & rules":
|
|
1099
|
-
"CI (GitHub Actions)":
|
|
1100
|
-
"Config file":
|
|
1138
|
+
"Vite integration": targetsOfKind("vite").map(asOption),
|
|
1139
|
+
"Agent Skills & rules": targetsOfKind("agent").map(asOption),
|
|
1140
|
+
"CI (GitHub Actions)": targetsOfKind("ci").map(asOption),
|
|
1141
|
+
"Config file": targetsOfKind("config").map(asOption)
|
|
1101
1142
|
};
|
|
1102
1143
|
const picked = await prompts.selectClients(groups, detected);
|
|
1103
1144
|
if (picked === null) {
|
|
@@ -1111,10 +1152,10 @@ async function runInstall(flags, io, prompts, version = "0.0.0") {
|
|
|
1111
1152
|
);
|
|
1112
1153
|
return 2;
|
|
1113
1154
|
}
|
|
1114
|
-
const viteIds = ids.filter(
|
|
1115
|
-
const agentIds = ids.filter(
|
|
1116
|
-
const configIds = ids.filter(
|
|
1117
|
-
const ciIds = ids.filter(
|
|
1155
|
+
const viteIds = ids.filter((id) => isKind(id, "vite"));
|
|
1156
|
+
const agentIds = ids.filter((id) => isKind(id, "agent"));
|
|
1157
|
+
const configIds = ids.filter((id) => isKind(id, "config"));
|
|
1158
|
+
const ciIds = ids.filter((id) => isKind(id, "ci"));
|
|
1118
1159
|
if (viteIds.length === 0 && agentIds.length === 0 && configIds.length === 0 && ciIds.length === 0) {
|
|
1119
1160
|
io.errorLog("svelte-vitals: no valid targets selected.");
|
|
1120
1161
|
return 2;
|
|
@@ -1124,10 +1165,10 @@ async function runInstall(flags, io, prompts, version = "0.0.0") {
|
|
|
1124
1165
|
if (io.readFile(join3(dir, "svelte.config.js")) !== void 0 || io.readFile(join3(dir, "svelte.config.ts")) !== void 0) {
|
|
1125
1166
|
return true;
|
|
1126
1167
|
}
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1168
|
+
return hasDep(
|
|
1169
|
+
readPkg((p2) => io.readFile(p2), dir),
|
|
1170
|
+
"@sveltejs/kit"
|
|
1171
|
+
);
|
|
1131
1172
|
} catch {
|
|
1132
1173
|
return false;
|
|
1133
1174
|
}
|
|
@@ -1177,20 +1218,18 @@ async function runInstall(flags, io, prompts, version = "0.0.0") {
|
|
|
1177
1218
|
}
|
|
1178
1219
|
}
|
|
1179
1220
|
for (const agentId of agentIds) {
|
|
1180
|
-
const target = agentTargetById(agentId);
|
|
1181
1221
|
try {
|
|
1182
|
-
rows.push(...planForAgentTarget(
|
|
1222
|
+
rows.push(...planForAgentTarget(agentId, io, flags.force ?? false, version));
|
|
1183
1223
|
} catch (err) {
|
|
1184
1224
|
io.errorLog(
|
|
1185
|
-
`svelte-vitals: could not check existing agent target ${
|
|
1225
|
+
`svelte-vitals: could not check existing agent target ${agentId}: ${err instanceof Error ? err.message : String(err)}`
|
|
1186
1226
|
);
|
|
1187
1227
|
return 2;
|
|
1188
1228
|
}
|
|
1189
1229
|
}
|
|
1190
|
-
|
|
1191
|
-
const target = configTargetById(configId);
|
|
1230
|
+
if (configIds.length > 0) {
|
|
1192
1231
|
try {
|
|
1193
|
-
rows.push(planForConfigTarget(
|
|
1232
|
+
rows.push(planForConfigTarget(io, flags.force ?? false, appDir));
|
|
1194
1233
|
} catch (err) {
|
|
1195
1234
|
io.errorLog(
|
|
1196
1235
|
`svelte-vitals: could not check existing config file: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -1198,13 +1237,12 @@ async function runInstall(flags, io, prompts, version = "0.0.0") {
|
|
|
1198
1237
|
return 2;
|
|
1199
1238
|
}
|
|
1200
1239
|
}
|
|
1201
|
-
|
|
1202
|
-
const target = ciTargetById(ciId);
|
|
1240
|
+
if (ciIds.length > 0) {
|
|
1203
1241
|
try {
|
|
1204
|
-
rows.push(planForCiTarget(
|
|
1242
|
+
rows.push(planForCiTarget(io, flags.force ?? false));
|
|
1205
1243
|
} catch (err) {
|
|
1206
1244
|
io.errorLog(
|
|
1207
|
-
`svelte-vitals: could not check existing workflow at ${join3(io.cwd,
|
|
1245
|
+
`svelte-vitals: could not check existing workflow at ${join3(io.cwd, targetById("ci-workflow").relPaths[0])}: ${err instanceof Error ? err.message : String(err)}`
|
|
1208
1246
|
);
|
|
1209
1247
|
return 2;
|
|
1210
1248
|
}
|
|
@@ -1227,7 +1265,7 @@ async function runInstall(flags, io, prompts, version = "0.0.0") {
|
|
|
1227
1265
|
let viteWasWritten = false;
|
|
1228
1266
|
for (const r of rows) {
|
|
1229
1267
|
if (r.status === "exists") {
|
|
1230
|
-
const hint =
|
|
1268
|
+
const hint = isKind(r.id, "vite") ? "" : " \u2014 use --force to overwrite";
|
|
1231
1269
|
io.log(`= ${r.label}: already configured (${r.path})${hint}.`);
|
|
1232
1270
|
continue;
|
|
1233
1271
|
}
|
|
@@ -1239,8 +1277,8 @@ ${indent(r.snippet ?? "")}`);
|
|
|
1239
1277
|
try {
|
|
1240
1278
|
io.writeFile(r.path, r.content ?? "");
|
|
1241
1279
|
io.log(`\u2713 ${r.label}: ${r.status} ${r.path}`);
|
|
1242
|
-
if (
|
|
1243
|
-
if (
|
|
1280
|
+
if (isKind(r.id, "vite")) viteWasWritten = true;
|
|
1281
|
+
if (isKind(r.id, "config") && r.path.endsWith(".ts")) {
|
|
1244
1282
|
io.log(
|
|
1245
1283
|
"svelte-vitals: note \u2014 a .ts config needs Node 22.18+ (or 23.6+) everywhere svelte-vitals runs, CI included; rename to .mjs if that is not guaranteed."
|
|
1246
1284
|
);
|
|
@@ -1276,17 +1314,21 @@ ${indent(r.snippet ?? "")}`);
|
|
|
1276
1314
|
}
|
|
1277
1315
|
|
|
1278
1316
|
// src/install/args.ts
|
|
1279
|
-
var VALID_TARGETS =
|
|
1280
|
-
...VITE_TARGETS.map((t) => t.id),
|
|
1281
|
-
...AGENT_TARGETS.map((t) => t.id),
|
|
1282
|
-
...CONFIG_TARGETS.map((t) => t.id),
|
|
1283
|
-
...CI_TARGETS.map((t) => t.id)
|
|
1284
|
-
];
|
|
1317
|
+
var VALID_TARGETS = INSTALL_TARGETS.map((t) => t.id);
|
|
1285
1318
|
var EXPECTED_TARGETS = VALID_TARGETS.join("|");
|
|
1319
|
+
function parseInstallArgs(args) {
|
|
1320
|
+
return parseCliArgs(args, {
|
|
1321
|
+
boolean: ["yes", "dry-run", "force", "refresh", "help"],
|
|
1322
|
+
// `scope` is still declared although the flag is gone: it keeps `--scope global` from
|
|
1323
|
+
// parsing its value as a positional, so resolveInstallArgs can warn and carry on.
|
|
1324
|
+
string: ["client", "scope", "app"],
|
|
1325
|
+
short: { y: "yes", h: "help" }
|
|
1326
|
+
});
|
|
1327
|
+
}
|
|
1286
1328
|
function resolveInstallArgs(argv) {
|
|
1287
1329
|
const warnings = [];
|
|
1288
1330
|
const errors = [];
|
|
1289
|
-
const rawClients =
|
|
1331
|
+
const rawClients = toList(argv.client);
|
|
1290
1332
|
const client = [];
|
|
1291
1333
|
for (const c of rawClients) {
|
|
1292
1334
|
if (VALID_TARGETS.includes(c)) {
|
|
@@ -1408,6 +1450,14 @@ function realIO() {
|
|
|
1408
1450
|
}
|
|
1409
1451
|
};
|
|
1410
1452
|
}
|
|
1453
|
+
async function selectAppPrompt(apps, message) {
|
|
1454
|
+
const res = await p.select({
|
|
1455
|
+
message,
|
|
1456
|
+
options: apps.map((a) => ({ value: a, label: a })),
|
|
1457
|
+
initialValue: apps[0]
|
|
1458
|
+
});
|
|
1459
|
+
return p.isCancel(res) ? null : res;
|
|
1460
|
+
}
|
|
1411
1461
|
function clackPrompts() {
|
|
1412
1462
|
return {
|
|
1413
1463
|
selectClients: async (groups, defaults) => {
|
|
@@ -1424,14 +1474,7 @@ function clackPrompts() {
|
|
|
1424
1474
|
});
|
|
1425
1475
|
return p.isCancel(res) ? null : res;
|
|
1426
1476
|
},
|
|
1427
|
-
selectApp:
|
|
1428
|
-
const res = await p.select({
|
|
1429
|
-
message: "Multiple SvelteKit apps found \u2014 which one should the Vite/config targets go into?",
|
|
1430
|
-
options: apps.map((a) => ({ value: a, label: a })),
|
|
1431
|
-
initialValue: apps[0]
|
|
1432
|
-
});
|
|
1433
|
-
return p.isCancel(res) ? null : res;
|
|
1434
|
-
},
|
|
1477
|
+
selectApp: (apps) => selectAppPrompt(apps, "Multiple SvelteKit apps found \u2014 which one should the Vite/config targets go into?"),
|
|
1435
1478
|
confirm: async (planText) => {
|
|
1436
1479
|
const res = await p.confirm({ message: `Apply this plan?
|
|
1437
1480
|
${planText}` });
|
|
@@ -1440,13 +1483,7 @@ ${planText}` });
|
|
|
1440
1483
|
};
|
|
1441
1484
|
}
|
|
1442
1485
|
async function runInstallCli(args) {
|
|
1443
|
-
const argv =
|
|
1444
|
-
boolean: ["yes", "dry-run", "force", "refresh", "help"],
|
|
1445
|
-
// `scope` is still declared although the flag is gone: it keeps `--scope global` from
|
|
1446
|
-
// parsing its value as a positional, so resolveInstallArgs can warn and carry on.
|
|
1447
|
-
string: ["client", "scope", "app"],
|
|
1448
|
-
alias: { y: "yes", h: "help" }
|
|
1449
|
-
});
|
|
1486
|
+
const argv = parseInstallArgs(args);
|
|
1450
1487
|
if (argv.help) {
|
|
1451
1488
|
console.log(INSTALL_HELP);
|
|
1452
1489
|
return 0;
|
|
@@ -1460,7 +1497,6 @@ async function runInstallCli(args) {
|
|
|
1460
1497
|
|
|
1461
1498
|
// src/ci/cli.ts
|
|
1462
1499
|
import { join as join4 } from "path";
|
|
1463
|
-
import mri2 from "mri";
|
|
1464
1500
|
|
|
1465
1501
|
// src/ci/upgrade.ts
|
|
1466
1502
|
var CANONICAL_PATH = "oekazuma/svelte-vitals-action";
|
|
@@ -1529,10 +1565,7 @@ async function runCiCli(args, io = realIO()) {
|
|
|
1529
1565
|
io.log(CI_HELP);
|
|
1530
1566
|
return 2;
|
|
1531
1567
|
}
|
|
1532
|
-
const argv =
|
|
1533
|
-
boolean: ["force", "dry-run", "help"],
|
|
1534
|
-
alias: { h: "help" }
|
|
1535
|
-
});
|
|
1568
|
+
const argv = parseCliArgs(args.slice(1), { boolean: ["force", "dry-run", "help"], short: { h: "help" } });
|
|
1536
1569
|
if (argv.help) {
|
|
1537
1570
|
io.log(CI_HELP);
|
|
1538
1571
|
return 0;
|
|
@@ -1563,10 +1596,7 @@ async function runCiCli(args, io = realIO()) {
|
|
|
1563
1596
|
return 0;
|
|
1564
1597
|
}
|
|
1565
1598
|
async function runCiUpgrade(args, io) {
|
|
1566
|
-
const argv =
|
|
1567
|
-
boolean: ["dry-run", "help"],
|
|
1568
|
-
alias: { h: "help" }
|
|
1569
|
-
});
|
|
1599
|
+
const argv = parseCliArgs(args, { boolean: ["dry-run", "help"], short: { h: "help" } });
|
|
1570
1600
|
if (argv.help) {
|
|
1571
1601
|
io.log(CI_HELP);
|
|
1572
1602
|
return 0;
|
|
@@ -1602,7 +1632,6 @@ async function runCiUpgrade(args, io) {
|
|
|
1602
1632
|
}
|
|
1603
1633
|
|
|
1604
1634
|
// src/explain.ts
|
|
1605
|
-
import mri3 from "mri";
|
|
1606
1635
|
import { allRules as allRules2, CATEGORIES as CATEGORIES2, explainRule } from "@svelte-vitals/core";
|
|
1607
1636
|
var EXPLAIN_HELP = `svelte-vitals explain \u2014 print a rule's rationale, fix, and configurable options
|
|
1608
1637
|
|
|
@@ -1652,7 +1681,7 @@ function renderRuleList() {
|
|
|
1652
1681
|
);
|
|
1653
1682
|
}
|
|
1654
1683
|
function runExplainCli(args, io = consoleIO) {
|
|
1655
|
-
const argv =
|
|
1684
|
+
const argv = parseCliArgs(args, { boolean: ["json", "list", "help"], short: { h: "help" } });
|
|
1656
1685
|
if (argv.help) {
|
|
1657
1686
|
io.log(EXPLAIN_HELP);
|
|
1658
1687
|
return 0;
|
|
@@ -1750,17 +1779,13 @@ If you are an AI agent:
|
|
|
1750
1779
|
naming the flag to pass. \`install\` is the exception \u2014 non-interactively it skips its
|
|
1751
1780
|
confirmation and writes, so pass \`--dry-run\` first if you need to see the plan.`;
|
|
1752
1781
|
var VERSION = readPackageVersion();
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
message: "Multiple SvelteKit apps found \u2014 which one should svelte-vitals analyze?",
|
|
1756
|
-
options: apps.map((a) => ({ value: a, label: a }))
|
|
1757
|
-
});
|
|
1758
|
-
return p2.isCancel(res) ? null : res;
|
|
1782
|
+
function selectApp(apps) {
|
|
1783
|
+
return selectAppPrompt(apps, "Multiple SvelteKit apps found \u2014 which one should svelte-vitals analyze?");
|
|
1759
1784
|
}
|
|
1760
1785
|
async function main() {
|
|
1761
1786
|
const rawArgs = process.argv.slice(2);
|
|
1762
1787
|
if (rawArgs[0] === "docs") {
|
|
1763
|
-
const { runDocsCli } = await import("./cli-
|
|
1788
|
+
const { runDocsCli } = await import("./cli-LGD6NDAO.js");
|
|
1764
1789
|
process.exitCode = runDocsCli(rawArgs.slice(1));
|
|
1765
1790
|
return;
|
|
1766
1791
|
}
|
|
@@ -1776,25 +1801,7 @@ async function main() {
|
|
|
1776
1801
|
const code2 = await runCiCli(rawArgs.slice(1));
|
|
1777
1802
|
process.exit(code2);
|
|
1778
1803
|
}
|
|
1779
|
-
const argv =
|
|
1780
|
-
alias: { h: "help", v: "version" },
|
|
1781
|
-
boolean: ["by-route", "staged", "score", "verbose", "update-suppressions"],
|
|
1782
|
-
string: [
|
|
1783
|
-
"meta-components",
|
|
1784
|
-
"treat-dynamic-as",
|
|
1785
|
-
"route",
|
|
1786
|
-
"fail-on",
|
|
1787
|
-
"reporter",
|
|
1788
|
-
"rules",
|
|
1789
|
-
"ignore",
|
|
1790
|
-
"min-health",
|
|
1791
|
-
"out-file",
|
|
1792
|
-
"diff",
|
|
1793
|
-
"baseline",
|
|
1794
|
-
"weights",
|
|
1795
|
-
"category"
|
|
1796
|
-
]
|
|
1797
|
-
});
|
|
1804
|
+
const argv = parseRunArgs(rawArgs);
|
|
1798
1805
|
if (argv.help) {
|
|
1799
1806
|
console.log(HELP);
|
|
1800
1807
|
return;
|
|
@@ -1804,20 +1811,10 @@ async function main() {
|
|
|
1804
1811
|
console.error("svelte-vitals: run `svelte-vitals docs list` for the bundled guides.");
|
|
1805
1812
|
return;
|
|
1806
1813
|
}
|
|
1807
|
-
const { options, warnings, errors } = resolveArgs(argv);
|
|
1814
|
+
const { options, warnings, errors, minHealth } = resolveArgs(argv);
|
|
1808
1815
|
for (const w of warnings) console.error(w);
|
|
1809
1816
|
for (const e of errors) console.error(e);
|
|
1810
1817
|
if (!options) process.exit(2);
|
|
1811
|
-
const minHealthRaw = argv["min-health"];
|
|
1812
|
-
let minHealth;
|
|
1813
|
-
if (minHealthRaw !== void 0) {
|
|
1814
|
-
const n = Number(minHealthRaw);
|
|
1815
|
-
if (!Number.isFinite(n) || n < 0 || n > 100) {
|
|
1816
|
-
console.error(`svelte-vitals: invalid --min-health '${minHealthRaw}'; expected a number 0-100.`);
|
|
1817
|
-
process.exit(2);
|
|
1818
|
-
}
|
|
1819
|
-
minHealth = n;
|
|
1820
|
-
}
|
|
1821
1818
|
const code = await run({
|
|
1822
1819
|
...options,
|
|
1823
1820
|
minHealth,
|