svelte-vitals 0.17.0 → 0.19.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 +317 -25
- package/dist/{chunk-VG647TPJ.js → chunk-ZE3M3T6U.js} +293 -491
- package/dist/index.d.ts +42 -4
- package/dist/index.js +5 -1
- package/package.json +7 -6
package/dist/bin.js
CHANGED
|
@@ -6,12 +6,55 @@ import {
|
|
|
6
6
|
knownRuleIds,
|
|
7
7
|
readPackageVersion,
|
|
8
8
|
run
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-ZE3M3T6U.js";
|
|
10
10
|
|
|
11
11
|
// src/bin.ts
|
|
12
12
|
import mri2 from "mri";
|
|
13
13
|
|
|
14
14
|
// src/resolve-args.ts
|
|
15
|
+
var CATEGORIES = ["seo", "performance", "correctness", "security", "architecture"];
|
|
16
|
+
function parseWeights(raw, errors) {
|
|
17
|
+
if (typeof raw !== "string" || raw.trim() === "") return void 0;
|
|
18
|
+
const weights = {};
|
|
19
|
+
const unknownCategories = [];
|
|
20
|
+
const invalidValues = [];
|
|
21
|
+
for (const pair of raw.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
22
|
+
const eq = pair.indexOf("=");
|
|
23
|
+
if (eq === -1) {
|
|
24
|
+
invalidValues.push(pair);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const category = pair.slice(0, eq).trim().toLowerCase();
|
|
28
|
+
const valueRaw = pair.slice(eq + 1).trim();
|
|
29
|
+
if (!CATEGORIES.includes(category)) {
|
|
30
|
+
unknownCategories.push(category);
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (valueRaw === "") {
|
|
34
|
+
invalidValues.push(pair);
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const value = Number(valueRaw);
|
|
38
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
39
|
+
invalidValues.push(pair);
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
weights[category] = value;
|
|
43
|
+
}
|
|
44
|
+
if (unknownCategories.length > 0) {
|
|
45
|
+
errors.push(`svelte-vitals: unknown category(ies) in --weights: ${unknownCategories.join(", ")}`);
|
|
46
|
+
errors.push(`Known categories: ${CATEGORIES.join(", ")}`);
|
|
47
|
+
}
|
|
48
|
+
if (invalidValues.length > 0) {
|
|
49
|
+
errors.push(
|
|
50
|
+
`svelte-vitals: invalid --weights entry(ies): ${invalidValues.join(", ")}; expected category=number with a finite number >= 0.`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
if (unknownCategories.length === 0 && invalidValues.length === 0 && Object.keys(weights).length === 0) {
|
|
54
|
+
errors.push("svelte-vitals: --weights was passed but contains no category=number pairs.");
|
|
55
|
+
}
|
|
56
|
+
return weights;
|
|
57
|
+
}
|
|
15
58
|
var toList = (v) => typeof v === "string" ? v.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
16
59
|
function resolveArgs(argv) {
|
|
17
60
|
const warnings = [];
|
|
@@ -55,6 +98,9 @@ function resolveArgs(argv) {
|
|
|
55
98
|
);
|
|
56
99
|
}
|
|
57
100
|
const failOn = argv["fail-on-warning"] ? "warning" : failOnValid ? failOnRaw : void 0;
|
|
101
|
+
const weights = parseWeights(argv.weights, errors);
|
|
102
|
+
const rulesConfig = buildRulesConfig(allow, ignore);
|
|
103
|
+
const rules = Object.keys(rulesConfig).length > 0 ? rulesConfig : void 0;
|
|
58
104
|
if (errors.length > 0) return { options: null, warnings, errors };
|
|
59
105
|
return {
|
|
60
106
|
options: {
|
|
@@ -66,7 +112,8 @@ function resolveArgs(argv) {
|
|
|
66
112
|
outFile: typeof argv["out-file"] === "string" ? argv["out-file"] : void 0,
|
|
67
113
|
byRoute: Boolean(argv["by-route"]),
|
|
68
114
|
failOn,
|
|
69
|
-
rules
|
|
115
|
+
rules,
|
|
116
|
+
...weights !== void 0 ? { weights } : {},
|
|
70
117
|
...diffBase !== void 0 ? { diffBase } : {},
|
|
71
118
|
...staged ? { staged } : {}
|
|
72
119
|
},
|
|
@@ -79,9 +126,13 @@ function resolveArgs(argv) {
|
|
|
79
126
|
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
80
127
|
import { dirname } from "path";
|
|
81
128
|
import { homedir } from "os";
|
|
129
|
+
import { spawnSync } from "child_process";
|
|
82
130
|
import mri from "mri";
|
|
83
131
|
import * as p from "@clack/prompts";
|
|
84
132
|
|
|
133
|
+
// src/install/index.ts
|
|
134
|
+
import { join as join3 } from "path";
|
|
135
|
+
|
|
85
136
|
// src/install/clients.ts
|
|
86
137
|
import { join } from "path";
|
|
87
138
|
var MCP_ENTRY = { command: "npx", args: ["-y", "@svelte-vitals/mcp"] };
|
|
@@ -165,12 +216,192 @@ function mergeToml(existing, entry, force) {
|
|
|
165
216
|
return { content: stringifyToml(root), status };
|
|
166
217
|
}
|
|
167
218
|
|
|
219
|
+
// src/install/vite-targets.ts
|
|
220
|
+
var VITE_TARGETS = [
|
|
221
|
+
{
|
|
222
|
+
id: "vite-plugin",
|
|
223
|
+
label: "Vite plugin (build gate)",
|
|
224
|
+
hint: "Fails `vite build` when prerendered pages cross the SEO/Performance threshold"
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
id: "vite-dev-overlay",
|
|
228
|
+
label: "Dev overlay",
|
|
229
|
+
hint: "Live warnings in `vite dev` only \u2014 never fails a build or CI"
|
|
230
|
+
}
|
|
231
|
+
];
|
|
232
|
+
function viteTargetById(id) {
|
|
233
|
+
return VITE_TARGETS.find((t) => t.id === id);
|
|
234
|
+
}
|
|
235
|
+
function isViteTargetId(id) {
|
|
236
|
+
return VITE_TARGETS.some((t) => t.id === id);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// src/install/codemod-vite-config.ts
|
|
240
|
+
import { parseModule, generateCode, builders, MagicastError } from "magicast";
|
|
241
|
+
var MANUAL_SNIPPET = `import { svelteVitals } from '@svelte-vitals/vite';
|
|
242
|
+
// add svelteVitals() to your \`plugins\` array`;
|
|
243
|
+
function codemodViteConfig(existing) {
|
|
244
|
+
if (existing === void 0) {
|
|
245
|
+
return { status: "manual", snippet: MANUAL_SNIPPET };
|
|
246
|
+
}
|
|
247
|
+
try {
|
|
248
|
+
const mod = parseModule(existing);
|
|
249
|
+
const def = mod.exports.default;
|
|
250
|
+
const configObj = def?.$type === "function-call" ? def.$args[0] : def;
|
|
251
|
+
if (!configObj || configObj.$type !== "object" || configObj.plugins?.$type !== "array") {
|
|
252
|
+
return { status: "manual", snippet: MANUAL_SNIPPET };
|
|
253
|
+
}
|
|
254
|
+
const already = configObj.plugins.find(
|
|
255
|
+
(p2) => p2?.$type === "function-call" && p2?.$callee === "svelteVitals"
|
|
256
|
+
);
|
|
257
|
+
if (already !== void 0) {
|
|
258
|
+
return { status: "exists" };
|
|
259
|
+
}
|
|
260
|
+
if (!mod.imports.svelteVitals) {
|
|
261
|
+
mod.imports.$append({ imported: "svelteVitals", local: "svelteVitals", from: "@svelte-vitals/vite" });
|
|
262
|
+
}
|
|
263
|
+
configObj.plugins.unshift(builders.functionCall("svelteVitals"));
|
|
264
|
+
return { status: "added", content: generateCode(mod, { format: { objectCurlySpacing: true } }).code };
|
|
265
|
+
} catch (err) {
|
|
266
|
+
if (err instanceof MagicastError) {
|
|
267
|
+
return { status: "manual", snippet: MANUAL_SNIPPET };
|
|
268
|
+
}
|
|
269
|
+
throw err;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// src/install/codemod-hooks.ts
|
|
274
|
+
import { parseModule as parseModule2, generateCode as generateCode2, builders as builders2, MagicastError as MagicastError2 } from "magicast";
|
|
275
|
+
var FRESH_HANDLE = `import { svelteVitalsHandle } from '@svelte-vitals/vite/hooks';
|
|
276
|
+
import { sequence } from '@sveltejs/kit/hooks';
|
|
277
|
+
|
|
278
|
+
export const handle = sequence(svelteVitalsHandle());
|
|
279
|
+
`;
|
|
280
|
+
var MANUAL_SNIPPET2 = `import { svelteVitalsHandle } from '@svelte-vitals/vite/hooks';
|
|
281
|
+
import { sequence } from '@sveltejs/kit/hooks';
|
|
282
|
+
// wrap your existing \`handle\` in sequence(yourHandle, svelteVitalsHandle())`;
|
|
283
|
+
function addImports(mod) {
|
|
284
|
+
if (!mod.imports.sequence) {
|
|
285
|
+
mod.imports.$append({ imported: "sequence", local: "sequence", from: "@sveltejs/kit/hooks" });
|
|
286
|
+
}
|
|
287
|
+
if (!mod.imports.svelteVitalsHandle) {
|
|
288
|
+
mod.imports.$append({
|
|
289
|
+
imported: "svelteVitalsHandle",
|
|
290
|
+
local: "svelteVitalsHandle",
|
|
291
|
+
from: "@svelte-vitals/vite/hooks"
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function codemodHooksServer(existing) {
|
|
296
|
+
if (existing === void 0) {
|
|
297
|
+
return { status: "created", content: FRESH_HANDLE };
|
|
298
|
+
}
|
|
299
|
+
try {
|
|
300
|
+
const mod = parseModule2(existing);
|
|
301
|
+
const handle = mod.exports.handle;
|
|
302
|
+
if (handle === void 0) {
|
|
303
|
+
addImports(mod);
|
|
304
|
+
mod.exports.handle = builders2.functionCall("sequence", builders2.functionCall("svelteVitalsHandle"));
|
|
305
|
+
return {
|
|
306
|
+
status: "added",
|
|
307
|
+
content: generateCode2(mod, { format: { objectCurlySpacing: true, quote: "single" } }).code
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
if (handle.$type === "function-call" && handle.$callee === "sequence") {
|
|
311
|
+
const already = handle.$args.find(
|
|
312
|
+
(a) => a?.$type === "function-call" && a?.$callee === "svelteVitalsHandle"
|
|
313
|
+
);
|
|
314
|
+
if (already !== void 0) {
|
|
315
|
+
return { status: "exists" };
|
|
316
|
+
}
|
|
317
|
+
if (!mod.imports.svelteVitalsHandle) {
|
|
318
|
+
mod.imports.$append({
|
|
319
|
+
imported: "svelteVitalsHandle",
|
|
320
|
+
local: "svelteVitalsHandle",
|
|
321
|
+
from: "@svelte-vitals/vite/hooks"
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
handle.$args.push(builders2.functionCall("svelteVitalsHandle"));
|
|
325
|
+
return {
|
|
326
|
+
status: "added",
|
|
327
|
+
content: generateCode2(mod, { format: { objectCurlySpacing: true, quote: "single" } }).code
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
addImports(mod);
|
|
331
|
+
mod.exports.handle = builders2.functionCall("sequence", handle, builders2.functionCall("svelteVitalsHandle"));
|
|
332
|
+
return {
|
|
333
|
+
status: "updated",
|
|
334
|
+
content: generateCode2(mod, { format: { objectCurlySpacing: true, quote: "single" } }).code
|
|
335
|
+
};
|
|
336
|
+
} catch (err) {
|
|
337
|
+
if (err instanceof MagicastError2) {
|
|
338
|
+
return { status: "manual", snippet: MANUAL_SNIPPET2 };
|
|
339
|
+
}
|
|
340
|
+
throw err;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// src/install/package-manager.ts
|
|
345
|
+
import { join as join2 } from "path";
|
|
346
|
+
var LOCKFILE_TO_PM = {
|
|
347
|
+
"pnpm-lock.yaml": "pnpm",
|
|
348
|
+
"yarn.lock": "yarn",
|
|
349
|
+
"bun.lock": "bun",
|
|
350
|
+
"bun.lockb": "bun"
|
|
351
|
+
};
|
|
352
|
+
function detectPackageManager(io) {
|
|
353
|
+
for (const [file, pm] of Object.entries(LOCKFILE_TO_PM)) {
|
|
354
|
+
if (io.readFile(join2(io.cwd, file)) !== void 0) return pm;
|
|
355
|
+
}
|
|
356
|
+
return "npm";
|
|
357
|
+
}
|
|
358
|
+
function hasVitePackage(io) {
|
|
359
|
+
const raw = io.readFile(join2(io.cwd, "package.json"));
|
|
360
|
+
if (raw === void 0) return false;
|
|
361
|
+
try {
|
|
362
|
+
const pkg = JSON.parse(raw);
|
|
363
|
+
return Boolean(pkg.dependencies?.["@svelte-vitals/vite"] || pkg.devDependencies?.["@svelte-vitals/vite"]);
|
|
364
|
+
} catch {
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
function installCommand(pm) {
|
|
369
|
+
const action = pm === "npm" ? "install" : "add";
|
|
370
|
+
return { command: pm, args: [action, "-D", "@svelte-vitals/vite"] };
|
|
371
|
+
}
|
|
372
|
+
|
|
168
373
|
// src/install/index.ts
|
|
169
|
-
function
|
|
374
|
+
function planForClient(client, scope, io, force) {
|
|
170
375
|
const path = client.resolvePath(scope, io.cwd, io.home);
|
|
171
376
|
const existing = io.readFile(path);
|
|
172
377
|
const merged = client.format === "toml" ? mergeToml(existing, MCP_ENTRY, force) : mergeJson(existing, MCP_ENTRY, force);
|
|
173
|
-
return { client, scope, path, status: merged.status, content: merged.content };
|
|
378
|
+
return { id: client.id, label: client.label, scope, path, status: merged.status, content: merged.content };
|
|
379
|
+
}
|
|
380
|
+
function resolveCandidate(io, candidates) {
|
|
381
|
+
for (const rel of candidates) {
|
|
382
|
+
const path = join3(io.cwd, rel);
|
|
383
|
+
const content = io.readFile(path);
|
|
384
|
+
if (content !== void 0) return { path, content };
|
|
385
|
+
}
|
|
386
|
+
return { path: join3(io.cwd, candidates[0]), content: void 0 };
|
|
387
|
+
}
|
|
388
|
+
function planForVitePlugin(io) {
|
|
389
|
+
const { path, content } = resolveCandidate(io, ["vite.config.ts", "vite.config.js", "vite.config.mjs"]);
|
|
390
|
+
const result = codemodViteConfig(content);
|
|
391
|
+
return { id: "vite-plugin", label: viteTargetById("vite-plugin").label, path, ...result };
|
|
392
|
+
}
|
|
393
|
+
function planForDevOverlay(io) {
|
|
394
|
+
const { path, content } = resolveCandidate(io, ["src/hooks.server.ts", "src/hooks.server.js"]);
|
|
395
|
+
const result = codemodHooksServer(content);
|
|
396
|
+
return { id: "vite-dev-overlay", label: viteTargetById("vite-dev-overlay").label, path, ...result };
|
|
397
|
+
}
|
|
398
|
+
function indent(text) {
|
|
399
|
+
return text.split("\n").map((l) => ` ${l}`).join("\n");
|
|
400
|
+
}
|
|
401
|
+
function rowLine(r) {
|
|
402
|
+
const head = ` ${r.label}${r.scope ? ` (${r.scope})` : ""} \u2192 ${r.path} [${r.status}]`;
|
|
403
|
+
return r.status === "manual" && r.snippet ? `${head}
|
|
404
|
+
${indent(r.snippet)}` : head;
|
|
174
405
|
}
|
|
175
406
|
async function runInstall(flags, io, prompts) {
|
|
176
407
|
let ids;
|
|
@@ -184,22 +415,33 @@ async function runInstall(flags, io, prompts) {
|
|
|
184
415
|
return false;
|
|
185
416
|
}
|
|
186
417
|
};
|
|
187
|
-
const
|
|
188
|
-
(c) => c.
|
|
418
|
+
const detectedClients = CLIENTS.filter(
|
|
419
|
+
(c) => c.scopes.some((s) => configExists(c.resolvePath(s, io.cwd, io.home)))
|
|
420
|
+
).map((c) => c.id);
|
|
421
|
+
const viteConfigExists = ["vite.config.ts", "vite.config.js", "vite.config.mjs"].some(
|
|
422
|
+
(f) => configExists(join3(io.cwd, f))
|
|
189
423
|
);
|
|
190
|
-
const
|
|
424
|
+
const detected = [...detectedClients, ...viteConfigExists ? VITE_TARGETS.map((t) => t.id) : []];
|
|
425
|
+
const options = [
|
|
426
|
+
...CLIENTS.map((c) => ({ id: c.id, label: c.label })),
|
|
427
|
+
...VITE_TARGETS.map((t) => ({ id: t.id, label: t.label, hint: t.hint }))
|
|
428
|
+
];
|
|
429
|
+
const picked = await prompts.selectClients(options, detected);
|
|
191
430
|
if (picked === null) {
|
|
192
431
|
io.log("Cancelled.");
|
|
193
432
|
return 0;
|
|
194
433
|
}
|
|
195
434
|
ids = picked;
|
|
196
435
|
} else {
|
|
197
|
-
io.errorLog(
|
|
436
|
+
io.errorLog(
|
|
437
|
+
"svelte-vitals: no TTY; pass --client <claude-code,cursor,codex,vite-plugin,vite-dev-overlay> to install non-interactively."
|
|
438
|
+
);
|
|
198
439
|
return 2;
|
|
199
440
|
}
|
|
200
441
|
const clients = ids.map(clientById).filter((c) => c !== void 0);
|
|
201
|
-
|
|
202
|
-
|
|
442
|
+
const viteIds = ids.filter(isViteTargetId);
|
|
443
|
+
if (clients.length === 0 && viteIds.length === 0) {
|
|
444
|
+
io.errorLog("svelte-vitals: no valid clients or targets selected.");
|
|
203
445
|
return 2;
|
|
204
446
|
}
|
|
205
447
|
const rows = [];
|
|
@@ -220,7 +462,7 @@ async function runInstall(flags, io, prompts) {
|
|
|
220
462
|
scope = "project";
|
|
221
463
|
}
|
|
222
464
|
try {
|
|
223
|
-
rows.push(
|
|
465
|
+
rows.push(planForClient(client, scope, io, flags.force ?? false));
|
|
224
466
|
} catch (err) {
|
|
225
467
|
const path = client.resolvePath(scope, io.cwd, io.home);
|
|
226
468
|
io.errorLog(
|
|
@@ -229,7 +471,10 @@ async function runInstall(flags, io, prompts) {
|
|
|
229
471
|
return 2;
|
|
230
472
|
}
|
|
231
473
|
}
|
|
232
|
-
|
|
474
|
+
for (const viteId of viteIds) {
|
|
475
|
+
rows.push(viteId === "vite-plugin" ? planForVitePlugin(io) : planForDevOverlay(io));
|
|
476
|
+
}
|
|
477
|
+
const planText = rows.map(rowLine).join("\n");
|
|
233
478
|
io.log("Plan:");
|
|
234
479
|
io.log(planText);
|
|
235
480
|
if (flags.dryRun) {
|
|
@@ -244,41 +489,63 @@ async function runInstall(flags, io, prompts) {
|
|
|
244
489
|
}
|
|
245
490
|
}
|
|
246
491
|
let hadFailure = false;
|
|
492
|
+
let viteWasWritten = false;
|
|
247
493
|
for (const r of rows) {
|
|
248
494
|
if (r.status === "exists") {
|
|
249
|
-
|
|
495
|
+
const hint = isViteTargetId(r.id) ? "" : " \u2014 use --force to overwrite";
|
|
496
|
+
io.log(`= ${r.label}: already configured (${r.path})${hint}.`);
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
if (r.status === "manual") {
|
|
500
|
+
io.log(`! ${r.label}: couldn't safely modify ${r.path} \u2014 add this by hand:
|
|
501
|
+
${indent(r.snippet ?? "")}`);
|
|
250
502
|
continue;
|
|
251
503
|
}
|
|
252
504
|
try {
|
|
253
|
-
io.writeFile(r.path, r.content);
|
|
254
|
-
io.log(`\u2713 ${r.
|
|
505
|
+
io.writeFile(r.path, r.content ?? "");
|
|
506
|
+
io.log(`\u2713 ${r.label}: ${r.status} ${r.path}`);
|
|
507
|
+
if (isViteTargetId(r.id)) viteWasWritten = true;
|
|
255
508
|
} catch (err) {
|
|
256
509
|
hadFailure = true;
|
|
257
510
|
io.errorLog(`svelte-vitals: failed to write ${r.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
258
511
|
}
|
|
259
512
|
}
|
|
513
|
+
if (viteWasWritten && io.runCommand && !hasVitePackage(io)) {
|
|
514
|
+
const pm = detectPackageManager(io);
|
|
515
|
+
const { command, args } = installCommand(pm);
|
|
516
|
+
io.log(`Installing @svelte-vitals/vite via ${pm}...`);
|
|
517
|
+
const code = io.runCommand(command, args, io.cwd);
|
|
518
|
+
if (code !== 0) {
|
|
519
|
+
io.errorLog(
|
|
520
|
+
`svelte-vitals: failed to install @svelte-vitals/vite (${command} ${args.join(" ")} exited ${code}). Install it manually.`
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
260
524
|
if (hadFailure) return 2;
|
|
261
525
|
io.log("");
|
|
262
|
-
io.log("
|
|
526
|
+
if (clients.length > 0) io.log("Restart your client to load the svelte-vitals MCP server.");
|
|
527
|
+
if (viteWasWritten) io.log("Restart `vite dev` (or your build) to pick up the change.");
|
|
528
|
+
io.log("Done.");
|
|
263
529
|
return 0;
|
|
264
530
|
}
|
|
265
531
|
|
|
266
532
|
// src/install/args.ts
|
|
267
|
-
var
|
|
533
|
+
var VALID_TARGETS = [...CLIENTS.map((c) => c.id), ...VITE_TARGETS.map((t) => t.id)];
|
|
534
|
+
var EXPECTED_TARGETS = VALID_TARGETS.join("|");
|
|
268
535
|
function resolveInstallArgs(argv) {
|
|
269
536
|
const warnings = [];
|
|
270
537
|
const errors = [];
|
|
271
538
|
const rawClients = typeof argv.client === "string" ? argv.client.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
272
539
|
const client = [];
|
|
273
540
|
for (const c of rawClients) {
|
|
274
|
-
if (
|
|
541
|
+
if (VALID_TARGETS.includes(c)) {
|
|
275
542
|
if (!client.includes(c)) client.push(c);
|
|
276
543
|
} else {
|
|
277
|
-
warnings.push(`svelte-vitals: unknown --client '${c}'; expected
|
|
544
|
+
warnings.push(`svelte-vitals: unknown --client '${c}'; expected ${EXPECTED_TARGETS}. Skipping.`);
|
|
278
545
|
}
|
|
279
546
|
}
|
|
280
547
|
if (rawClients.length > 0 && client.length === 0) {
|
|
281
|
-
errors.push(
|
|
548
|
+
errors.push(`svelte-vitals: no valid --client values; expected ${EXPECTED_TARGETS}.`);
|
|
282
549
|
}
|
|
283
550
|
let scope;
|
|
284
551
|
const rawScope = argv.scope;
|
|
@@ -307,7 +574,10 @@ Usage:
|
|
|
307
574
|
svelte-vitals install [options]
|
|
308
575
|
|
|
309
576
|
Options:
|
|
310
|
-
--client <ids> Comma-separated: claude-code,cursor,codex (skips the interactive picker)
|
|
577
|
+
--client <ids> Comma-separated: claude-code,cursor,codex,vite-plugin,vite-dev-overlay (skips the interactive picker)
|
|
578
|
+
vite-plugin registers the build-mode plugin in vite.config.{ts,js,mjs}; vite-dev-overlay
|
|
579
|
+
wires up the dev-overlay hook in src/hooks.server.{ts,js}. --force does not apply
|
|
580
|
+
to either \u2014 an existing registration is always left as-is.
|
|
311
581
|
--scope <scope> project | global (applies to all selected clients; codex is always global)
|
|
312
582
|
--yes, -y Skip the confirmation prompt
|
|
313
583
|
--dry-run Print the planned changes and exit without writing
|
|
@@ -331,15 +601,32 @@ function realIO() {
|
|
|
331
601
|
home: homedir(),
|
|
332
602
|
isTTY: Boolean(process.stdout.isTTY),
|
|
333
603
|
log: (line) => console.log(line),
|
|
334
|
-
errorLog: (line) => console.error(line)
|
|
604
|
+
errorLog: (line) => console.error(line),
|
|
605
|
+
runCommand: (command, args, cwd) => {
|
|
606
|
+
const result = spawnSync(command, args, {
|
|
607
|
+
cwd,
|
|
608
|
+
stdio: "inherit",
|
|
609
|
+
shell: process.platform === "win32",
|
|
610
|
+
timeout: 12e4
|
|
611
|
+
});
|
|
612
|
+
if (result.error) {
|
|
613
|
+
console.error(`svelte-vitals: ${command} failed to start: ${result.error.message}`);
|
|
614
|
+
return 1;
|
|
615
|
+
}
|
|
616
|
+
if (result.signal) {
|
|
617
|
+
console.error(`svelte-vitals: ${command} was terminated (${result.signal}) \u2014 it may have timed out.`);
|
|
618
|
+
return 1;
|
|
619
|
+
}
|
|
620
|
+
return result.status ?? 1;
|
|
621
|
+
}
|
|
335
622
|
};
|
|
336
623
|
}
|
|
337
624
|
function clackPrompts() {
|
|
338
625
|
return {
|
|
339
626
|
selectClients: async (all, defaults) => {
|
|
340
627
|
const res = await p.multiselect({
|
|
341
|
-
message: "Which clients should svelte-vitals be installed for?",
|
|
342
|
-
options: all.map((
|
|
628
|
+
message: "Which clients/targets should svelte-vitals be installed for?",
|
|
629
|
+
options: all.map((o) => ({ value: o.id, label: o.label, hint: o.hint })),
|
|
343
630
|
initialValues: defaults,
|
|
344
631
|
required: true
|
|
345
632
|
});
|
|
@@ -399,10 +686,14 @@ Options:
|
|
|
399
686
|
--min-health <0-100> Fail (exit 1) when the combined Health score is below this value
|
|
400
687
|
--rules <ids> Comma-separated rule ids to enable (all others disabled)
|
|
401
688
|
--ignore <ids> Comma-separated rule ids to disable
|
|
689
|
+
--weights <pairs> Per-category Health weight overrides, e.g. seo=2,performance=1 (unlisted categories default to 1)
|
|
402
690
|
--no-color Disable ANSI color in console output
|
|
403
691
|
-h, --help Show this help
|
|
404
692
|
-v, --version Show version
|
|
405
693
|
|
|
694
|
+
Config file:
|
|
695
|
+
svelte-vitals.config.{mjs,js,ts} in the analyzed directory; flags override it.
|
|
696
|
+
|
|
406
697
|
Exit codes:
|
|
407
698
|
0 no failing findings
|
|
408
699
|
1 critical finding present (or --fail-on threshold reached)
|
|
@@ -427,7 +718,8 @@ async function main() {
|
|
|
427
718
|
"ignore",
|
|
428
719
|
"min-health",
|
|
429
720
|
"out-file",
|
|
430
|
-
"diff"
|
|
721
|
+
"diff",
|
|
722
|
+
"weights"
|
|
431
723
|
]
|
|
432
724
|
});
|
|
433
725
|
if (argv.help) {
|