svelte-vitals 0.17.0 → 0.18.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 +263 -23
- package/dist/{chunk-VG647TPJ.js → chunk-NLQZ3CMZ.js} +19 -375
- package/dist/index.js +1 -1
- package/package.json +6 -5
package/dist/bin.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
knownRuleIds,
|
|
7
7
|
readPackageVersion,
|
|
8
8
|
run
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-NLQZ3CMZ.js";
|
|
10
10
|
|
|
11
11
|
// src/bin.ts
|
|
12
12
|
import mri2 from "mri";
|
|
@@ -79,9 +79,13 @@ function resolveArgs(argv) {
|
|
|
79
79
|
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
80
80
|
import { dirname } from "path";
|
|
81
81
|
import { homedir } from "os";
|
|
82
|
+
import { spawnSync } from "child_process";
|
|
82
83
|
import mri from "mri";
|
|
83
84
|
import * as p from "@clack/prompts";
|
|
84
85
|
|
|
86
|
+
// src/install/index.ts
|
|
87
|
+
import { join as join3 } from "path";
|
|
88
|
+
|
|
85
89
|
// src/install/clients.ts
|
|
86
90
|
import { join } from "path";
|
|
87
91
|
var MCP_ENTRY = { command: "npx", args: ["-y", "@svelte-vitals/mcp"] };
|
|
@@ -165,12 +169,192 @@ function mergeToml(existing, entry, force) {
|
|
|
165
169
|
return { content: stringifyToml(root), status };
|
|
166
170
|
}
|
|
167
171
|
|
|
172
|
+
// src/install/vite-targets.ts
|
|
173
|
+
var VITE_TARGETS = [
|
|
174
|
+
{
|
|
175
|
+
id: "vite-plugin",
|
|
176
|
+
label: "Vite plugin (build gate)",
|
|
177
|
+
hint: "Fails `vite build` when prerendered pages cross the SEO/Performance threshold"
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
id: "vite-dev-overlay",
|
|
181
|
+
label: "Dev overlay",
|
|
182
|
+
hint: "Live warnings in `vite dev` only \u2014 never fails a build or CI"
|
|
183
|
+
}
|
|
184
|
+
];
|
|
185
|
+
function viteTargetById(id) {
|
|
186
|
+
return VITE_TARGETS.find((t) => t.id === id);
|
|
187
|
+
}
|
|
188
|
+
function isViteTargetId(id) {
|
|
189
|
+
return VITE_TARGETS.some((t) => t.id === id);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// src/install/codemod-vite-config.ts
|
|
193
|
+
import { parseModule, generateCode, builders, MagicastError } from "magicast";
|
|
194
|
+
var MANUAL_SNIPPET = `import { svelteVitals } from '@svelte-vitals/vite';
|
|
195
|
+
// add svelteVitals() to your \`plugins\` array`;
|
|
196
|
+
function codemodViteConfig(existing) {
|
|
197
|
+
if (existing === void 0) {
|
|
198
|
+
return { status: "manual", snippet: MANUAL_SNIPPET };
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
const mod = parseModule(existing);
|
|
202
|
+
const def = mod.exports.default;
|
|
203
|
+
const configObj = def?.$type === "function-call" ? def.$args[0] : def;
|
|
204
|
+
if (!configObj || configObj.$type !== "object" || configObj.plugins?.$type !== "array") {
|
|
205
|
+
return { status: "manual", snippet: MANUAL_SNIPPET };
|
|
206
|
+
}
|
|
207
|
+
const already = configObj.plugins.find(
|
|
208
|
+
(p2) => p2?.$type === "function-call" && p2?.$callee === "svelteVitals"
|
|
209
|
+
);
|
|
210
|
+
if (already !== void 0) {
|
|
211
|
+
return { status: "exists" };
|
|
212
|
+
}
|
|
213
|
+
if (!mod.imports.svelteVitals) {
|
|
214
|
+
mod.imports.$append({ imported: "svelteVitals", local: "svelteVitals", from: "@svelte-vitals/vite" });
|
|
215
|
+
}
|
|
216
|
+
configObj.plugins.unshift(builders.functionCall("svelteVitals"));
|
|
217
|
+
return { status: "added", content: generateCode(mod, { format: { objectCurlySpacing: true } }).code };
|
|
218
|
+
} catch (err) {
|
|
219
|
+
if (err instanceof MagicastError) {
|
|
220
|
+
return { status: "manual", snippet: MANUAL_SNIPPET };
|
|
221
|
+
}
|
|
222
|
+
throw err;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// src/install/codemod-hooks.ts
|
|
227
|
+
import { parseModule as parseModule2, generateCode as generateCode2, builders as builders2, MagicastError as MagicastError2 } from "magicast";
|
|
228
|
+
var FRESH_HANDLE = `import { svelteVitalsHandle } from '@svelte-vitals/vite/hooks';
|
|
229
|
+
import { sequence } from '@sveltejs/kit/hooks';
|
|
230
|
+
|
|
231
|
+
export const handle = sequence(svelteVitalsHandle());
|
|
232
|
+
`;
|
|
233
|
+
var MANUAL_SNIPPET2 = `import { svelteVitalsHandle } from '@svelte-vitals/vite/hooks';
|
|
234
|
+
import { sequence } from '@sveltejs/kit/hooks';
|
|
235
|
+
// wrap your existing \`handle\` in sequence(yourHandle, svelteVitalsHandle())`;
|
|
236
|
+
function addImports(mod) {
|
|
237
|
+
if (!mod.imports.sequence) {
|
|
238
|
+
mod.imports.$append({ imported: "sequence", local: "sequence", from: "@sveltejs/kit/hooks" });
|
|
239
|
+
}
|
|
240
|
+
if (!mod.imports.svelteVitalsHandle) {
|
|
241
|
+
mod.imports.$append({
|
|
242
|
+
imported: "svelteVitalsHandle",
|
|
243
|
+
local: "svelteVitalsHandle",
|
|
244
|
+
from: "@svelte-vitals/vite/hooks"
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function codemodHooksServer(existing) {
|
|
249
|
+
if (existing === void 0) {
|
|
250
|
+
return { status: "created", content: FRESH_HANDLE };
|
|
251
|
+
}
|
|
252
|
+
try {
|
|
253
|
+
const mod = parseModule2(existing);
|
|
254
|
+
const handle = mod.exports.handle;
|
|
255
|
+
if (handle === void 0) {
|
|
256
|
+
addImports(mod);
|
|
257
|
+
mod.exports.handle = builders2.functionCall("sequence", builders2.functionCall("svelteVitalsHandle"));
|
|
258
|
+
return {
|
|
259
|
+
status: "added",
|
|
260
|
+
content: generateCode2(mod, { format: { objectCurlySpacing: true, quote: "single" } }).code
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
if (handle.$type === "function-call" && handle.$callee === "sequence") {
|
|
264
|
+
const already = handle.$args.find(
|
|
265
|
+
(a) => a?.$type === "function-call" && a?.$callee === "svelteVitalsHandle"
|
|
266
|
+
);
|
|
267
|
+
if (already !== void 0) {
|
|
268
|
+
return { status: "exists" };
|
|
269
|
+
}
|
|
270
|
+
if (!mod.imports.svelteVitalsHandle) {
|
|
271
|
+
mod.imports.$append({
|
|
272
|
+
imported: "svelteVitalsHandle",
|
|
273
|
+
local: "svelteVitalsHandle",
|
|
274
|
+
from: "@svelte-vitals/vite/hooks"
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
handle.$args.push(builders2.functionCall("svelteVitalsHandle"));
|
|
278
|
+
return {
|
|
279
|
+
status: "added",
|
|
280
|
+
content: generateCode2(mod, { format: { objectCurlySpacing: true, quote: "single" } }).code
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
addImports(mod);
|
|
284
|
+
mod.exports.handle = builders2.functionCall("sequence", handle, builders2.functionCall("svelteVitalsHandle"));
|
|
285
|
+
return {
|
|
286
|
+
status: "updated",
|
|
287
|
+
content: generateCode2(mod, { format: { objectCurlySpacing: true, quote: "single" } }).code
|
|
288
|
+
};
|
|
289
|
+
} catch (err) {
|
|
290
|
+
if (err instanceof MagicastError2) {
|
|
291
|
+
return { status: "manual", snippet: MANUAL_SNIPPET2 };
|
|
292
|
+
}
|
|
293
|
+
throw err;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// src/install/package-manager.ts
|
|
298
|
+
import { join as join2 } from "path";
|
|
299
|
+
var LOCKFILE_TO_PM = {
|
|
300
|
+
"pnpm-lock.yaml": "pnpm",
|
|
301
|
+
"yarn.lock": "yarn",
|
|
302
|
+
"bun.lock": "bun",
|
|
303
|
+
"bun.lockb": "bun"
|
|
304
|
+
};
|
|
305
|
+
function detectPackageManager(io) {
|
|
306
|
+
for (const [file, pm] of Object.entries(LOCKFILE_TO_PM)) {
|
|
307
|
+
if (io.readFile(join2(io.cwd, file)) !== void 0) return pm;
|
|
308
|
+
}
|
|
309
|
+
return "npm";
|
|
310
|
+
}
|
|
311
|
+
function hasVitePackage(io) {
|
|
312
|
+
const raw = io.readFile(join2(io.cwd, "package.json"));
|
|
313
|
+
if (raw === void 0) return false;
|
|
314
|
+
try {
|
|
315
|
+
const pkg = JSON.parse(raw);
|
|
316
|
+
return Boolean(pkg.dependencies?.["@svelte-vitals/vite"] || pkg.devDependencies?.["@svelte-vitals/vite"]);
|
|
317
|
+
} catch {
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
function installCommand(pm) {
|
|
322
|
+
const action = pm === "npm" ? "install" : "add";
|
|
323
|
+
return { command: pm, args: [action, "-D", "@svelte-vitals/vite"] };
|
|
324
|
+
}
|
|
325
|
+
|
|
168
326
|
// src/install/index.ts
|
|
169
|
-
function
|
|
327
|
+
function planForClient(client, scope, io, force) {
|
|
170
328
|
const path = client.resolvePath(scope, io.cwd, io.home);
|
|
171
329
|
const existing = io.readFile(path);
|
|
172
330
|
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 };
|
|
331
|
+
return { id: client.id, label: client.label, scope, path, status: merged.status, content: merged.content };
|
|
332
|
+
}
|
|
333
|
+
function resolveCandidate(io, candidates) {
|
|
334
|
+
for (const rel of candidates) {
|
|
335
|
+
const path = join3(io.cwd, rel);
|
|
336
|
+
const content = io.readFile(path);
|
|
337
|
+
if (content !== void 0) return { path, content };
|
|
338
|
+
}
|
|
339
|
+
return { path: join3(io.cwd, candidates[0]), content: void 0 };
|
|
340
|
+
}
|
|
341
|
+
function planForVitePlugin(io) {
|
|
342
|
+
const { path, content } = resolveCandidate(io, ["vite.config.ts", "vite.config.js", "vite.config.mjs"]);
|
|
343
|
+
const result = codemodViteConfig(content);
|
|
344
|
+
return { id: "vite-plugin", label: viteTargetById("vite-plugin").label, path, ...result };
|
|
345
|
+
}
|
|
346
|
+
function planForDevOverlay(io) {
|
|
347
|
+
const { path, content } = resolveCandidate(io, ["src/hooks.server.ts", "src/hooks.server.js"]);
|
|
348
|
+
const result = codemodHooksServer(content);
|
|
349
|
+
return { id: "vite-dev-overlay", label: viteTargetById("vite-dev-overlay").label, path, ...result };
|
|
350
|
+
}
|
|
351
|
+
function indent(text) {
|
|
352
|
+
return text.split("\n").map((l) => ` ${l}`).join("\n");
|
|
353
|
+
}
|
|
354
|
+
function rowLine(r) {
|
|
355
|
+
const head = ` ${r.label}${r.scope ? ` (${r.scope})` : ""} \u2192 ${r.path} [${r.status}]`;
|
|
356
|
+
return r.status === "manual" && r.snippet ? `${head}
|
|
357
|
+
${indent(r.snippet)}` : head;
|
|
174
358
|
}
|
|
175
359
|
async function runInstall(flags, io, prompts) {
|
|
176
360
|
let ids;
|
|
@@ -184,22 +368,33 @@ async function runInstall(flags, io, prompts) {
|
|
|
184
368
|
return false;
|
|
185
369
|
}
|
|
186
370
|
};
|
|
187
|
-
const
|
|
188
|
-
(c) => c.
|
|
371
|
+
const detectedClients = CLIENTS.filter(
|
|
372
|
+
(c) => c.scopes.some((s) => configExists(c.resolvePath(s, io.cwd, io.home)))
|
|
373
|
+
).map((c) => c.id);
|
|
374
|
+
const viteConfigExists = ["vite.config.ts", "vite.config.js", "vite.config.mjs"].some(
|
|
375
|
+
(f) => configExists(join3(io.cwd, f))
|
|
189
376
|
);
|
|
190
|
-
const
|
|
377
|
+
const detected = [...detectedClients, ...viteConfigExists ? VITE_TARGETS.map((t) => t.id) : []];
|
|
378
|
+
const options = [
|
|
379
|
+
...CLIENTS.map((c) => ({ id: c.id, label: c.label })),
|
|
380
|
+
...VITE_TARGETS.map((t) => ({ id: t.id, label: t.label, hint: t.hint }))
|
|
381
|
+
];
|
|
382
|
+
const picked = await prompts.selectClients(options, detected);
|
|
191
383
|
if (picked === null) {
|
|
192
384
|
io.log("Cancelled.");
|
|
193
385
|
return 0;
|
|
194
386
|
}
|
|
195
387
|
ids = picked;
|
|
196
388
|
} else {
|
|
197
|
-
io.errorLog(
|
|
389
|
+
io.errorLog(
|
|
390
|
+
"svelte-vitals: no TTY; pass --client <claude-code,cursor,codex,vite-plugin,vite-dev-overlay> to install non-interactively."
|
|
391
|
+
);
|
|
198
392
|
return 2;
|
|
199
393
|
}
|
|
200
394
|
const clients = ids.map(clientById).filter((c) => c !== void 0);
|
|
201
|
-
|
|
202
|
-
|
|
395
|
+
const viteIds = ids.filter(isViteTargetId);
|
|
396
|
+
if (clients.length === 0 && viteIds.length === 0) {
|
|
397
|
+
io.errorLog("svelte-vitals: no valid clients or targets selected.");
|
|
203
398
|
return 2;
|
|
204
399
|
}
|
|
205
400
|
const rows = [];
|
|
@@ -220,7 +415,7 @@ async function runInstall(flags, io, prompts) {
|
|
|
220
415
|
scope = "project";
|
|
221
416
|
}
|
|
222
417
|
try {
|
|
223
|
-
rows.push(
|
|
418
|
+
rows.push(planForClient(client, scope, io, flags.force ?? false));
|
|
224
419
|
} catch (err) {
|
|
225
420
|
const path = client.resolvePath(scope, io.cwd, io.home);
|
|
226
421
|
io.errorLog(
|
|
@@ -229,7 +424,10 @@ async function runInstall(flags, io, prompts) {
|
|
|
229
424
|
return 2;
|
|
230
425
|
}
|
|
231
426
|
}
|
|
232
|
-
|
|
427
|
+
for (const viteId of viteIds) {
|
|
428
|
+
rows.push(viteId === "vite-plugin" ? planForVitePlugin(io) : planForDevOverlay(io));
|
|
429
|
+
}
|
|
430
|
+
const planText = rows.map(rowLine).join("\n");
|
|
233
431
|
io.log("Plan:");
|
|
234
432
|
io.log(planText);
|
|
235
433
|
if (flags.dryRun) {
|
|
@@ -244,41 +442,63 @@ async function runInstall(flags, io, prompts) {
|
|
|
244
442
|
}
|
|
245
443
|
}
|
|
246
444
|
let hadFailure = false;
|
|
445
|
+
let viteWasWritten = false;
|
|
247
446
|
for (const r of rows) {
|
|
248
447
|
if (r.status === "exists") {
|
|
249
|
-
|
|
448
|
+
const hint = isViteTargetId(r.id) ? "" : " \u2014 use --force to overwrite";
|
|
449
|
+
io.log(`= ${r.label}: already configured (${r.path})${hint}.`);
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
if (r.status === "manual") {
|
|
453
|
+
io.log(`! ${r.label}: couldn't safely modify ${r.path} \u2014 add this by hand:
|
|
454
|
+
${indent(r.snippet ?? "")}`);
|
|
250
455
|
continue;
|
|
251
456
|
}
|
|
252
457
|
try {
|
|
253
|
-
io.writeFile(r.path, r.content);
|
|
254
|
-
io.log(`\u2713 ${r.
|
|
458
|
+
io.writeFile(r.path, r.content ?? "");
|
|
459
|
+
io.log(`\u2713 ${r.label}: ${r.status} ${r.path}`);
|
|
460
|
+
if (isViteTargetId(r.id)) viteWasWritten = true;
|
|
255
461
|
} catch (err) {
|
|
256
462
|
hadFailure = true;
|
|
257
463
|
io.errorLog(`svelte-vitals: failed to write ${r.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
258
464
|
}
|
|
259
465
|
}
|
|
466
|
+
if (viteWasWritten && io.runCommand && !hasVitePackage(io)) {
|
|
467
|
+
const pm = detectPackageManager(io);
|
|
468
|
+
const { command, args } = installCommand(pm);
|
|
469
|
+
io.log(`Installing @svelte-vitals/vite via ${pm}...`);
|
|
470
|
+
const code = io.runCommand(command, args, io.cwd);
|
|
471
|
+
if (code !== 0) {
|
|
472
|
+
io.errorLog(
|
|
473
|
+
`svelte-vitals: failed to install @svelte-vitals/vite (${command} ${args.join(" ")} exited ${code}). Install it manually.`
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
260
477
|
if (hadFailure) return 2;
|
|
261
478
|
io.log("");
|
|
262
|
-
io.log("
|
|
479
|
+
if (clients.length > 0) io.log("Restart your client to load the svelte-vitals MCP server.");
|
|
480
|
+
if (viteWasWritten) io.log("Restart `vite dev` (or your build) to pick up the change.");
|
|
481
|
+
io.log("Done.");
|
|
263
482
|
return 0;
|
|
264
483
|
}
|
|
265
484
|
|
|
266
485
|
// src/install/args.ts
|
|
267
|
-
var
|
|
486
|
+
var VALID_TARGETS = [...CLIENTS.map((c) => c.id), ...VITE_TARGETS.map((t) => t.id)];
|
|
487
|
+
var EXPECTED_TARGETS = VALID_TARGETS.join("|");
|
|
268
488
|
function resolveInstallArgs(argv) {
|
|
269
489
|
const warnings = [];
|
|
270
490
|
const errors = [];
|
|
271
491
|
const rawClients = typeof argv.client === "string" ? argv.client.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
272
492
|
const client = [];
|
|
273
493
|
for (const c of rawClients) {
|
|
274
|
-
if (
|
|
494
|
+
if (VALID_TARGETS.includes(c)) {
|
|
275
495
|
if (!client.includes(c)) client.push(c);
|
|
276
496
|
} else {
|
|
277
|
-
warnings.push(`svelte-vitals: unknown --client '${c}'; expected
|
|
497
|
+
warnings.push(`svelte-vitals: unknown --client '${c}'; expected ${EXPECTED_TARGETS}. Skipping.`);
|
|
278
498
|
}
|
|
279
499
|
}
|
|
280
500
|
if (rawClients.length > 0 && client.length === 0) {
|
|
281
|
-
errors.push(
|
|
501
|
+
errors.push(`svelte-vitals: no valid --client values; expected ${EXPECTED_TARGETS}.`);
|
|
282
502
|
}
|
|
283
503
|
let scope;
|
|
284
504
|
const rawScope = argv.scope;
|
|
@@ -307,7 +527,10 @@ Usage:
|
|
|
307
527
|
svelte-vitals install [options]
|
|
308
528
|
|
|
309
529
|
Options:
|
|
310
|
-
--client <ids> Comma-separated: claude-code,cursor,codex (skips the interactive picker)
|
|
530
|
+
--client <ids> Comma-separated: claude-code,cursor,codex,vite-plugin,vite-dev-overlay (skips the interactive picker)
|
|
531
|
+
vite-plugin registers the build-mode plugin in vite.config.{ts,js,mjs}; vite-dev-overlay
|
|
532
|
+
wires up the dev-overlay hook in src/hooks.server.{ts,js}. --force does not apply
|
|
533
|
+
to either \u2014 an existing registration is always left as-is.
|
|
311
534
|
--scope <scope> project | global (applies to all selected clients; codex is always global)
|
|
312
535
|
--yes, -y Skip the confirmation prompt
|
|
313
536
|
--dry-run Print the planned changes and exit without writing
|
|
@@ -331,15 +554,32 @@ function realIO() {
|
|
|
331
554
|
home: homedir(),
|
|
332
555
|
isTTY: Boolean(process.stdout.isTTY),
|
|
333
556
|
log: (line) => console.log(line),
|
|
334
|
-
errorLog: (line) => console.error(line)
|
|
557
|
+
errorLog: (line) => console.error(line),
|
|
558
|
+
runCommand: (command, args, cwd) => {
|
|
559
|
+
const result = spawnSync(command, args, {
|
|
560
|
+
cwd,
|
|
561
|
+
stdio: "inherit",
|
|
562
|
+
shell: process.platform === "win32",
|
|
563
|
+
timeout: 12e4
|
|
564
|
+
});
|
|
565
|
+
if (result.error) {
|
|
566
|
+
console.error(`svelte-vitals: ${command} failed to start: ${result.error.message}`);
|
|
567
|
+
return 1;
|
|
568
|
+
}
|
|
569
|
+
if (result.signal) {
|
|
570
|
+
console.error(`svelte-vitals: ${command} was terminated (${result.signal}) \u2014 it may have timed out.`);
|
|
571
|
+
return 1;
|
|
572
|
+
}
|
|
573
|
+
return result.status ?? 1;
|
|
574
|
+
}
|
|
335
575
|
};
|
|
336
576
|
}
|
|
337
577
|
function clackPrompts() {
|
|
338
578
|
return {
|
|
339
579
|
selectClients: async (all, defaults) => {
|
|
340
580
|
const res = await p.multiselect({
|
|
341
|
-
message: "Which clients should svelte-vitals be installed for?",
|
|
342
|
-
options: all.map((
|
|
581
|
+
message: "Which clients/targets should svelte-vitals be installed for?",
|
|
582
|
+
options: all.map((o) => ({ value: o.id, label: o.label, hint: o.hint })),
|
|
343
583
|
initialValues: defaults,
|
|
344
584
|
required: true
|
|
345
585
|
});
|
|
@@ -124,6 +124,15 @@ async function collectProjectFacts(rt, cwd) {
|
|
|
124
124
|
|
|
125
125
|
// src/providers/source/parse.ts
|
|
126
126
|
import { parse } from "svelte/compiler";
|
|
127
|
+
import {
|
|
128
|
+
CHILD_NODE_KEYS,
|
|
129
|
+
lineOf,
|
|
130
|
+
findAttr,
|
|
131
|
+
valueFromNodes,
|
|
132
|
+
textFromNodes,
|
|
133
|
+
attrText,
|
|
134
|
+
attrValue
|
|
135
|
+
} from "@svelte-vitals/core";
|
|
127
136
|
|
|
128
137
|
// src/providers/source/imports.ts
|
|
129
138
|
function addImportsFromProgram(program, map) {
|
|
@@ -150,58 +159,6 @@ function collectImports(ast) {
|
|
|
150
159
|
}
|
|
151
160
|
|
|
152
161
|
// src/providers/source/parse.ts
|
|
153
|
-
var CHILD_NODE_KEYS = [
|
|
154
|
-
"fragment",
|
|
155
|
-
"nodes",
|
|
156
|
-
"consequent",
|
|
157
|
-
"alternate",
|
|
158
|
-
"body",
|
|
159
|
-
"pending",
|
|
160
|
-
"then",
|
|
161
|
-
"catch",
|
|
162
|
-
"fallback"
|
|
163
|
-
];
|
|
164
|
-
function valueFromNodes(nodes) {
|
|
165
|
-
if (!Array.isArray(nodes)) return "absent";
|
|
166
|
-
if (nodes.some((n) => n?.type === "ExpressionTag")) return "dynamic";
|
|
167
|
-
const text = nodes.filter((n) => n?.type === "Text").map((n) => String(n.data ?? "")).join("");
|
|
168
|
-
return text.trim().length > 0 ? "static" : "absent";
|
|
169
|
-
}
|
|
170
|
-
function textFromNodes(nodes) {
|
|
171
|
-
if (!Array.isArray(nodes) || nodes.some((n) => n?.type === "ExpressionTag")) return void 0;
|
|
172
|
-
const text = nodes.filter((n) => n?.type === "Text").map((n) => String(n.data ?? "")).join("");
|
|
173
|
-
return text.trim().length > 0 ? text : void 0;
|
|
174
|
-
}
|
|
175
|
-
function attrText(attributes, name) {
|
|
176
|
-
const attr = findAttr(attributes, name);
|
|
177
|
-
if (!attr) return void 0;
|
|
178
|
-
const v = attr.value;
|
|
179
|
-
if (v === true) return "";
|
|
180
|
-
if (Array.isArray(v)) {
|
|
181
|
-
return v.filter((n) => n?.type === "Text").map((n) => String(n.data ?? "")).join("");
|
|
182
|
-
}
|
|
183
|
-
return void 0;
|
|
184
|
-
}
|
|
185
|
-
function attrValue(attributes, name) {
|
|
186
|
-
const attr = findAttr(attributes, name);
|
|
187
|
-
if (!attr) return "absent";
|
|
188
|
-
const v = attr.value;
|
|
189
|
-
if (v === true) return "absent";
|
|
190
|
-
if (Array.isArray(v)) return valueFromNodes(v);
|
|
191
|
-
if (v && v.type === "ExpressionTag") return "dynamic";
|
|
192
|
-
return "absent";
|
|
193
|
-
}
|
|
194
|
-
function lineOf(source, offset) {
|
|
195
|
-
if (typeof offset !== "number" || offset < 0) return 0;
|
|
196
|
-
let line = 1;
|
|
197
|
-
const end = Math.min(offset, source.length);
|
|
198
|
-
for (let i = 0; i < end; i++) if (source[i] === "\n") line++;
|
|
199
|
-
return line;
|
|
200
|
-
}
|
|
201
|
-
function findAttr(attributes, name) {
|
|
202
|
-
if (!Array.isArray(attributes)) return void 0;
|
|
203
|
-
return attributes.find((a) => a?.type === "Attribute" && a.name === name);
|
|
204
|
-
}
|
|
205
162
|
function collectSvelteHeads(node, acc) {
|
|
206
163
|
if (Array.isArray(node)) {
|
|
207
164
|
for (const child of node) collectSvelteHeads(child, acc);
|
|
@@ -279,19 +236,6 @@ function tagsFromHead(head) {
|
|
|
279
236
|
}
|
|
280
237
|
return tags;
|
|
281
238
|
}
|
|
282
|
-
function attrValueOf(attr) {
|
|
283
|
-
const v = attr?.value;
|
|
284
|
-
if (v === true) return "absent";
|
|
285
|
-
if (Array.isArray(v)) return valueFromNodes(v);
|
|
286
|
-
if (v && v.type === "ExpressionTag") return "dynamic";
|
|
287
|
-
return "absent";
|
|
288
|
-
}
|
|
289
|
-
function attrTextOf(attr) {
|
|
290
|
-
const v = attr?.value;
|
|
291
|
-
if (!Array.isArray(v) || v.some((n) => n?.type === "ExpressionTag")) return void 0;
|
|
292
|
-
const text = v.filter((n) => n?.type === "Text").map((n) => String(n.data ?? "")).join("");
|
|
293
|
-
return text.trim().length > 0 ? text : void 0;
|
|
294
|
-
}
|
|
295
239
|
function collectComponents(node, acc) {
|
|
296
240
|
if (Array.isArray(node)) {
|
|
297
241
|
for (const child of node) collectComponents(child, acc);
|
|
@@ -366,312 +310,9 @@ function parseFile(source, filename) {
|
|
|
366
310
|
headings
|
|
367
311
|
};
|
|
368
312
|
}
|
|
369
|
-
function isConstantListEach(node) {
|
|
370
|
-
const expr = node?.expression;
|
|
371
|
-
return expr?.type === "ArrayExpression" && Array.isArray(expr.elements) && !expr.elements.some((el) => el?.type === "SpreadElement");
|
|
372
|
-
}
|
|
373
|
-
function collectEachBlocks(node, source, acc) {
|
|
374
|
-
if (Array.isArray(node)) {
|
|
375
|
-
for (const child of node) collectEachBlocks(child, source, acc);
|
|
376
|
-
return;
|
|
377
|
-
}
|
|
378
|
-
if (!node || typeof node !== "object") return;
|
|
379
|
-
if (node.type === "EachBlock" && !isConstantListEach(node)) {
|
|
380
|
-
acc.push({ hasKey: node.key != null, line: lineOf(source, node.start) });
|
|
381
|
-
}
|
|
382
|
-
for (const key of CHILD_NODE_KEYS) {
|
|
383
|
-
if (key in node) collectEachBlocks(node[key], source, acc);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
function walkEstree(node, visit) {
|
|
387
|
-
if (Array.isArray(node)) {
|
|
388
|
-
for (const child of node) walkEstree(child, visit);
|
|
389
|
-
return;
|
|
390
|
-
}
|
|
391
|
-
if (!node || typeof node !== "object" || typeof node.type !== "string") return;
|
|
392
|
-
visit(node);
|
|
393
|
-
for (const key of Object.keys(node)) {
|
|
394
|
-
if (key === "type" || key === "start" || key === "end" || key === "loc" || key === "range") continue;
|
|
395
|
-
walkEstree(node[key], visit);
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
function isEffectCall(node) {
|
|
399
|
-
const c = node?.callee;
|
|
400
|
-
if (c?.type === "Identifier") return c.name === "$effect";
|
|
401
|
-
if (c?.type === "MemberExpression" && c.object?.type === "Identifier" && c.object.name === "$effect") {
|
|
402
|
-
return c.property?.type === "Identifier" && c.property.name === "pre";
|
|
403
|
-
}
|
|
404
|
-
return false;
|
|
405
|
-
}
|
|
406
|
-
function isStateDeclaration(node) {
|
|
407
|
-
const c = node?.callee;
|
|
408
|
-
if (c?.type === "Identifier") return c.name === "$state";
|
|
409
|
-
if (c?.type === "MemberExpression" && c.object?.type === "Identifier" && c.object.name === "$state") {
|
|
410
|
-
return c.property?.type === "Identifier" && (c.property.name === "raw" || c.property.name === "frozen");
|
|
411
|
-
}
|
|
412
|
-
return false;
|
|
413
|
-
}
|
|
414
|
-
function bodyOnlyAssignsState(fn, stateNames) {
|
|
415
|
-
const isStateAssign = (expr) => expr?.type === "AssignmentExpression" && expr.operator === "=" && expr.left?.type === "Identifier" && stateNames.has(expr.left.name);
|
|
416
|
-
const body = fn?.body;
|
|
417
|
-
if (!body) return false;
|
|
418
|
-
if (body.type !== "BlockStatement") return isStateAssign(body);
|
|
419
|
-
if (body.body.length === 0) return false;
|
|
420
|
-
return body.body.every((s) => s?.type === "ExpressionStatement" && isStateAssign(s.expression));
|
|
421
|
-
}
|
|
422
|
-
function isDerivedDeclaration(node) {
|
|
423
|
-
const c = node?.callee;
|
|
424
|
-
if (c?.type === "Identifier") return c.name === "$derived";
|
|
425
|
-
if (c?.type === "MemberExpression" && c.object?.type === "Identifier" && c.object.name === "$derived") {
|
|
426
|
-
return c.property?.type === "Identifier" && c.property.name === "by";
|
|
427
|
-
}
|
|
428
|
-
return false;
|
|
429
|
-
}
|
|
430
|
-
function addBoundNames(id, acc) {
|
|
431
|
-
if (!id) return;
|
|
432
|
-
switch (id.type) {
|
|
433
|
-
case "Identifier":
|
|
434
|
-
acc.add(id.name);
|
|
435
|
-
break;
|
|
436
|
-
case "ObjectPattern":
|
|
437
|
-
for (const p of id.properties ?? []) {
|
|
438
|
-
if (p?.type === "Property") addBoundNames(p.value, acc);
|
|
439
|
-
else if (p?.type === "RestElement") addBoundNames(p.argument, acc);
|
|
440
|
-
}
|
|
441
|
-
break;
|
|
442
|
-
case "ArrayPattern":
|
|
443
|
-
for (const el of id.elements ?? []) addBoundNames(el, acc);
|
|
444
|
-
break;
|
|
445
|
-
case "AssignmentPattern":
|
|
446
|
-
addBoundNames(id.left, acc);
|
|
447
|
-
break;
|
|
448
|
-
case "RestElement":
|
|
449
|
-
addBoundNames(id.argument, acc);
|
|
450
|
-
break;
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
function rootObjectName(node) {
|
|
454
|
-
let cur = node;
|
|
455
|
-
while (cur?.type === "MemberExpression") cur = cur.object;
|
|
456
|
-
return cur?.type === "Identifier" ? cur.name : void 0;
|
|
457
|
-
}
|
|
458
|
-
function collectStateWrites(root, stateNames, acc) {
|
|
459
|
-
walkEstree(root, (n) => {
|
|
460
|
-
if (n?.type === "AssignmentExpression") {
|
|
461
|
-
if (n.left?.type === "Identifier" && stateNames.has(n.left.name)) acc.add(n.left.name);
|
|
462
|
-
else if (n.left?.type === "MemberExpression") {
|
|
463
|
-
const r = rootObjectName(n.left);
|
|
464
|
-
if (r && stateNames.has(r)) acc.add(r);
|
|
465
|
-
} else if (n.left?.type === "ObjectPattern" || n.left?.type === "ArrayPattern") {
|
|
466
|
-
const bound = /* @__PURE__ */ new Set();
|
|
467
|
-
addBoundNames(n.left, bound);
|
|
468
|
-
for (const name of bound) if (stateNames.has(name)) acc.add(name);
|
|
469
|
-
}
|
|
470
|
-
} else if (n?.type === "UpdateExpression") {
|
|
471
|
-
const r = rootObjectName(n.argument);
|
|
472
|
-
if (r && stateNames.has(r)) acc.add(r);
|
|
473
|
-
} else if (n?.type === "UnaryExpression" && n.operator === "delete") {
|
|
474
|
-
const r = rootObjectName(n.argument);
|
|
475
|
-
if (r && stateNames.has(r)) acc.add(r);
|
|
476
|
-
} else if (n?.type === "CallExpression") {
|
|
477
|
-
if (n.callee?.type === "MemberExpression") {
|
|
478
|
-
const r = rootObjectName(n.callee);
|
|
479
|
-
if (r && stateNames.has(r)) acc.add(r);
|
|
480
|
-
}
|
|
481
|
-
for (const a of n.arguments ?? []) {
|
|
482
|
-
const arg = a?.type === "SpreadElement" ? a.argument : a;
|
|
483
|
-
const r = rootObjectName(arg);
|
|
484
|
-
if (r && stateNames.has(r)) acc.add(r);
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
});
|
|
488
|
-
}
|
|
489
|
-
var COMPONENT_LIKE_TYPES = /* @__PURE__ */ new Set(["Component", "SvelteComponent", "SvelteSelf"]);
|
|
490
|
-
function collectTemplateEscapes(node, stateNames, acc) {
|
|
491
|
-
if (Array.isArray(node)) {
|
|
492
|
-
for (const c of node) collectTemplateEscapes(c, stateNames, acc);
|
|
493
|
-
return;
|
|
494
|
-
}
|
|
495
|
-
if (!node || typeof node !== "object" || typeof node.type !== "string") return;
|
|
496
|
-
if (Array.isArray(node.attributes)) {
|
|
497
|
-
for (const attr of node.attributes) {
|
|
498
|
-
if (attr?.type === "BindDirective") {
|
|
499
|
-
const r = rootObjectName(attr.expression);
|
|
500
|
-
if (r && stateNames.has(r)) acc.add(r);
|
|
501
|
-
} else if (COMPONENT_LIKE_TYPES.has(node.type)) {
|
|
502
|
-
walkEstree(attr, (m) => {
|
|
503
|
-
if (m?.type === "Identifier" && stateNames.has(m.name)) acc.add(m.name);
|
|
504
|
-
});
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
for (const key of CHILD_NODE_KEYS) {
|
|
509
|
-
if (key in node) collectTemplateEscapes(node[key], stateNames, acc);
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
var RUNE_NAMES = /* @__PURE__ */ new Set(["$state", "$derived", "$effect", "$props", "$bindable", "$inspect", "$host"]);
|
|
513
|
-
function bodyReadsReactive(fn, reactiveNames) {
|
|
514
|
-
let reads = false;
|
|
515
|
-
const IGNORED_KEYS = /* @__PURE__ */ new Set(["type", "start", "end", "loc", "range"]);
|
|
516
|
-
const visit = (n) => {
|
|
517
|
-
if (reads || !n) return;
|
|
518
|
-
if (Array.isArray(n)) {
|
|
519
|
-
for (const c of n) visit(c);
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
522
|
-
if (typeof n !== "object" || typeof n.type !== "string") return;
|
|
523
|
-
if (n.type === "Identifier") {
|
|
524
|
-
if (reactiveNames.has(n.name) || n.name.startsWith("$") && !RUNE_NAMES.has(n.name)) reads = true;
|
|
525
|
-
return;
|
|
526
|
-
}
|
|
527
|
-
if (n.type === "CallExpression" && n.callee?.type === "Identifier") {
|
|
528
|
-
reads = true;
|
|
529
|
-
return;
|
|
530
|
-
}
|
|
531
|
-
if (n.type === "MemberExpression") {
|
|
532
|
-
visit(n.object);
|
|
533
|
-
if (n.computed) visit(n.property);
|
|
534
|
-
return;
|
|
535
|
-
}
|
|
536
|
-
if (n.type === "Property") {
|
|
537
|
-
if (n.computed) visit(n.key);
|
|
538
|
-
visit(n.value);
|
|
539
|
-
return;
|
|
540
|
-
}
|
|
541
|
-
for (const key of Object.keys(n)) {
|
|
542
|
-
if (!IGNORED_KEYS.has(key)) visit(n[key]);
|
|
543
|
-
}
|
|
544
|
-
};
|
|
545
|
-
visit(fn.body);
|
|
546
|
-
return reads;
|
|
547
|
-
}
|
|
548
|
-
function bodyIsEmpty(fn) {
|
|
549
|
-
const body = fn?.body;
|
|
550
|
-
if (!body) return true;
|
|
551
|
-
if (body.type === "BlockStatement") return (body.body ?? []).length === 0;
|
|
552
|
-
return false;
|
|
553
|
-
}
|
|
554
|
-
var URL_ATTRS = ["href", "src", "action", "formaction"];
|
|
555
|
-
function collectSecurityFacts(node, source, htmlTags, jsUrls) {
|
|
556
|
-
if (Array.isArray(node)) {
|
|
557
|
-
for (const child of node) collectSecurityFacts(child, source, htmlTags, jsUrls);
|
|
558
|
-
return;
|
|
559
|
-
}
|
|
560
|
-
if (!node || typeof node !== "object") return;
|
|
561
|
-
if (node.type === "HtmlTag") htmlTags.push({ line: lineOf(source, node.start) });
|
|
562
|
-
if ((node.type === "RegularElement" || node.type === "SvelteElement") && Array.isArray(node.attributes)) {
|
|
563
|
-
for (const name of URL_ATTRS) {
|
|
564
|
-
const attr = findAttr(node.attributes, name);
|
|
565
|
-
if (!attr) continue;
|
|
566
|
-
const value = attrTextOf(attr);
|
|
567
|
-
if (value !== void 0 && /^\s*javascript:/i.test(value)) {
|
|
568
|
-
jsUrls.push({ line: lineOf(source, attr.start ?? node.start) });
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
for (const key of CHILD_NODE_KEYS) {
|
|
573
|
-
if (key in node) collectSecurityFacts(node[key], source, htmlTags, jsUrls);
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
function isPropsCall(node) {
|
|
577
|
-
return node?.type === "CallExpression" && node.callee?.type === "Identifier" && node.callee.name === "$props";
|
|
578
|
-
}
|
|
579
|
-
function countProps(program) {
|
|
580
|
-
let count = 0;
|
|
581
|
-
let seen = 0;
|
|
582
|
-
let uncountable = false;
|
|
583
|
-
walkEstree(program, (n) => {
|
|
584
|
-
if (n.type !== "VariableDeclarator" || !n.init || !isPropsCall(n.init)) return;
|
|
585
|
-
seen++;
|
|
586
|
-
const props = n.id?.type === "ObjectPattern" ? n.id.properties : void 0;
|
|
587
|
-
if (!Array.isArray(props) || props.some((p) => p?.type === "RestElement")) {
|
|
588
|
-
uncountable = true;
|
|
589
|
-
return;
|
|
590
|
-
}
|
|
591
|
-
count = props.filter((p) => p?.type === "Property").length;
|
|
592
|
-
});
|
|
593
|
-
return uncountable || seen > 1 ? 0 : count;
|
|
594
|
-
}
|
|
595
|
-
function countLines(source) {
|
|
596
|
-
if (source.length === 0) return 0;
|
|
597
|
-
return source.split("\n").length - (source.endsWith("\n") ? 1 : 0);
|
|
598
|
-
}
|
|
599
|
-
function collectImportSources(program, acc) {
|
|
600
|
-
walkEstree(program, (n) => {
|
|
601
|
-
if (n.type === "ImportDeclaration" && typeof n.source?.value === "string") acc.push(n.source.value);
|
|
602
|
-
});
|
|
603
|
-
}
|
|
604
|
-
function isBareSpecifier(s) {
|
|
605
|
-
return !/^[./$#]/.test(s);
|
|
606
|
-
}
|
|
607
|
-
function collectNamespaceImports(program, source, acc) {
|
|
608
|
-
walkEstree(program, (n) => {
|
|
609
|
-
if (n.type !== "ImportDeclaration" || n.importKind === "type") return;
|
|
610
|
-
const spec = n.source?.value;
|
|
611
|
-
if (typeof spec !== "string" || !isBareSpecifier(spec)) return;
|
|
612
|
-
if (Array.isArray(n.specifiers) && n.specifiers.some((s) => s?.type === "ImportNamespaceSpecifier")) {
|
|
613
|
-
acc.push({ source: spec, line: lineOf(source, n.start) });
|
|
614
|
-
}
|
|
615
|
-
});
|
|
616
|
-
}
|
|
617
|
-
function parseComponentFacts(source, filename) {
|
|
618
|
-
const ast = parse(source, { modern: true, filename });
|
|
619
|
-
const eachBlocks = [];
|
|
620
|
-
collectEachBlocks(ast.fragment ?? ast, source, eachBlocks);
|
|
621
|
-
const htmlTags = [];
|
|
622
|
-
const javascriptUrls = [];
|
|
623
|
-
collectSecurityFacts(ast.fragment ?? ast, source, htmlTags, javascriptUrls);
|
|
624
|
-
const loc = countLines(source);
|
|
625
|
-
const imports = [];
|
|
626
|
-
const namespaceImports = [];
|
|
627
|
-
if (ast.module?.content) {
|
|
628
|
-
collectImportSources(ast.module.content, imports);
|
|
629
|
-
collectNamespaceImports(ast.module.content, source, namespaceImports);
|
|
630
|
-
}
|
|
631
|
-
const effects = [];
|
|
632
|
-
const constableStates = [];
|
|
633
|
-
let propCount = 0;
|
|
634
|
-
const program = ast.instance?.content;
|
|
635
|
-
if (program) {
|
|
636
|
-
collectImportSources(program, imports);
|
|
637
|
-
collectNamespaceImports(program, source, namespaceImports);
|
|
638
|
-
propCount = countProps(program);
|
|
639
|
-
const stateNames = /* @__PURE__ */ new Set();
|
|
640
|
-
const reactiveNames = /* @__PURE__ */ new Set();
|
|
641
|
-
const stateDecls = [];
|
|
642
|
-
walkEstree(program, (n) => {
|
|
643
|
-
if (n.type !== "VariableDeclarator" || !n.init) return;
|
|
644
|
-
if (isStateDeclaration(n.init) && n.id?.type === "Identifier") {
|
|
645
|
-
stateNames.add(n.id.name);
|
|
646
|
-
stateDecls.push({ name: n.id.name, line: lineOf(source, n.start) });
|
|
647
|
-
}
|
|
648
|
-
if (isStateDeclaration(n.init) || isDerivedDeclaration(n.init) || isPropsCall(n.init))
|
|
649
|
-
addBoundNames(n.id, reactiveNames);
|
|
650
|
-
});
|
|
651
|
-
walkEstree(program, (n) => {
|
|
652
|
-
if (n.type !== "CallExpression" || !isEffectCall(n)) return;
|
|
653
|
-
const fn = n.arguments?.[0];
|
|
654
|
-
const isFn = fn?.type === "ArrowFunctionExpression" || fn?.type === "FunctionExpression";
|
|
655
|
-
effects.push({
|
|
656
|
-
line: lineOf(source, n.start),
|
|
657
|
-
assignsOnlyState: isFn ? bodyOnlyAssignsState(fn, stateNames) : false,
|
|
658
|
-
mountOnly: isFn ? !bodyIsEmpty(fn) && !bodyReadsReactive(fn, reactiveNames) : false
|
|
659
|
-
});
|
|
660
|
-
});
|
|
661
|
-
const writtenOrEscaped = /* @__PURE__ */ new Set();
|
|
662
|
-
collectStateWrites(program, stateNames, writtenOrEscaped);
|
|
663
|
-
if (ast.fragment) {
|
|
664
|
-
collectStateWrites(ast.fragment, stateNames, writtenOrEscaped);
|
|
665
|
-
collectTemplateEscapes(ast.fragment, stateNames, writtenOrEscaped);
|
|
666
|
-
}
|
|
667
|
-
for (const d of stateDecls) {
|
|
668
|
-
if (!writtenOrEscaped.has(d.name)) constableStates.push(d);
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
return { eachBlocks, effects, htmlTags, javascriptUrls, loc, propCount, imports, namespaceImports, constableStates };
|
|
672
|
-
}
|
|
673
313
|
|
|
674
314
|
// src/providers/source/adapters/svelte-meta-tags.ts
|
|
315
|
+
import { attrValueOf, attrTextOf } from "@svelte-vitals/core";
|
|
675
316
|
function findAttr2(attributes, name) {
|
|
676
317
|
return attributes.find((a) => a?.type === "Attribute" && a.name === name);
|
|
677
318
|
}
|
|
@@ -709,6 +350,7 @@ var svelteMetaTagsAdapter = {
|
|
|
709
350
|
};
|
|
710
351
|
|
|
711
352
|
// src/providers/source/adapters/svelte-seo.ts
|
|
353
|
+
import { attrValueOf as attrValueOf2, attrTextOf as attrTextOf2 } from "@svelte-vitals/core";
|
|
712
354
|
function findAttr3(attributes, name) {
|
|
713
355
|
return attributes.find((a) => a?.type === "Attribute" && a.name === name);
|
|
714
356
|
}
|
|
@@ -721,18 +363,18 @@ var svelteSeoAdapter = {
|
|
|
721
363
|
const attrs = use.attributes;
|
|
722
364
|
const title = findAttr3(attrs, "title");
|
|
723
365
|
if (title) {
|
|
724
|
-
const value =
|
|
725
|
-
const text = value === "static" ?
|
|
366
|
+
const value = attrValueOf2(title);
|
|
367
|
+
const text = value === "static" ? attrTextOf2(title) : void 0;
|
|
726
368
|
tags.push({ kind: "title", value, ...text !== void 0 ? { text } : {} });
|
|
727
369
|
}
|
|
728
370
|
const description = findAttr3(attrs, "description");
|
|
729
371
|
if (description) {
|
|
730
|
-
const value =
|
|
731
|
-
const text = value === "static" ?
|
|
372
|
+
const value = attrValueOf2(description);
|
|
373
|
+
const text = value === "static" ? attrTextOf2(description) : void 0;
|
|
732
374
|
tags.push({ kind: "meta", name: "description", value, ...text !== void 0 ? { text } : {} });
|
|
733
375
|
}
|
|
734
376
|
const canonical = findAttr3(attrs, "canonical");
|
|
735
|
-
if (canonical) tags.push({ kind: "link", rel: "canonical", value:
|
|
377
|
+
if (canonical) tags.push({ kind: "link", rel: "canonical", value: attrValueOf2(canonical) });
|
|
736
378
|
const openGraph = findAttr3(attrs, "openGraph");
|
|
737
379
|
const broad = use.hasSpread || Boolean(openGraph);
|
|
738
380
|
return { tags, broad };
|
|
@@ -936,6 +578,7 @@ async function collectRoutes(rt, cwd, config = defaultConfig) {
|
|
|
936
578
|
}
|
|
937
579
|
|
|
938
580
|
// src/providers/source/components.ts
|
|
581
|
+
import { parseComponentFacts } from "@svelte-vitals/core";
|
|
939
582
|
async function collectComponentFacts(rt, cwd) {
|
|
940
583
|
const files = await rt.glob("src/**/*.svelte", cwd);
|
|
941
584
|
return Promise.all(
|
|
@@ -954,7 +597,8 @@ async function collectComponentFacts(rt, cwd) {
|
|
|
954
597
|
propCount: 0,
|
|
955
598
|
imports: [],
|
|
956
599
|
namespaceImports: [],
|
|
957
|
-
constableStates: []
|
|
600
|
+
constableStates: [],
|
|
601
|
+
suppressions: []
|
|
958
602
|
};
|
|
959
603
|
}
|
|
960
604
|
})
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelte-vitals",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.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",
|
|
@@ -39,15 +39,16 @@
|
|
|
39
39
|
"dist"
|
|
40
40
|
],
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@clack/prompts": "^
|
|
42
|
+
"@clack/prompts": "^1.6.0",
|
|
43
|
+
"magicast": "^0.5.3",
|
|
43
44
|
"mri": "^1.2.0",
|
|
44
45
|
"smol-toml": "^1.7.0",
|
|
45
|
-
"svelte": "^5.56.
|
|
46
|
+
"svelte": "^5.56.4",
|
|
46
47
|
"tinyglobby": "^0.2.17",
|
|
47
|
-
"@svelte-vitals/core": "0.
|
|
48
|
+
"@svelte-vitals/core": "0.19.0"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
50
|
-
"@types/node": "^24.
|
|
51
|
+
"@types/node": "^24.13.2"
|
|
51
52
|
},
|
|
52
53
|
"scripts": {
|
|
53
54
|
"build": "tsup",
|