svelte-vitals 0.18.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 +55 -3
- package/dist/{chunk-NLQZ3CMZ.js → chunk-ZE3M3T6U.js} +285 -127
- package/dist/index.d.ts +42 -4
- package/dist/index.js +5 -1
- package/package.json +4 -4
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
|
},
|
|
@@ -639,10 +686,14 @@ Options:
|
|
|
639
686
|
--min-health <0-100> Fail (exit 1) when the combined Health score is below this value
|
|
640
687
|
--rules <ids> Comma-separated rule ids to enable (all others disabled)
|
|
641
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)
|
|
642
690
|
--no-color Disable ANSI color in console output
|
|
643
691
|
-h, --help Show this help
|
|
644
692
|
-v, --version Show version
|
|
645
693
|
|
|
694
|
+
Config file:
|
|
695
|
+
svelte-vitals.config.{mjs,js,ts} in the analyzed directory; flags override it.
|
|
696
|
+
|
|
646
697
|
Exit codes:
|
|
647
698
|
0 no failing findings
|
|
648
699
|
1 critical finding present (or --fail-on threshold reached)
|
|
@@ -667,7 +718,8 @@ async function main() {
|
|
|
667
718
|
"ignore",
|
|
668
719
|
"min-health",
|
|
669
720
|
"out-file",
|
|
670
|
-
"diff"
|
|
721
|
+
"diff",
|
|
722
|
+
"weights"
|
|
671
723
|
]
|
|
672
724
|
});
|
|
673
725
|
if (argv.help) {
|
|
@@ -122,12 +122,145 @@ async function collectProjectFacts(rt, cwd) {
|
|
|
122
122
|
};
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
// src/providers/source/adapters/svelte-meta-tags.ts
|
|
126
|
+
import { attrValueOf, attrTextOf } from "@svelte-vitals/core";
|
|
127
|
+
|
|
128
|
+
// src/providers/source/adapters/meta-object.ts
|
|
129
|
+
function exprValue(node) {
|
|
130
|
+
if (!node) return "absent";
|
|
131
|
+
if (node.type === "Literal") {
|
|
132
|
+
if (typeof node.value === "string") return node.value.trim().length > 0 ? "static" : "absent";
|
|
133
|
+
return "static";
|
|
134
|
+
}
|
|
135
|
+
return "dynamic";
|
|
136
|
+
}
|
|
137
|
+
function resolveMetaObject(attr, keyMap) {
|
|
138
|
+
if (!attr) return { tags: [], opaque: false };
|
|
139
|
+
const expr = attr.value?.type === "ExpressionTag" ? attr.value.expression : void 0;
|
|
140
|
+
if (!expr || expr.type !== "ObjectExpression") return { tags: [], opaque: true };
|
|
141
|
+
const tags = [];
|
|
142
|
+
let opaque = false;
|
|
143
|
+
for (const prop of expr.properties ?? []) {
|
|
144
|
+
if (prop?.type !== "Property" || prop.computed) {
|
|
145
|
+
opaque = true;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const key = prop.key?.name ?? prop.key?.value;
|
|
149
|
+
const make = typeof key === "string" ? keyMap[key] : void 0;
|
|
150
|
+
if (make) tags.push(make(exprValue(prop.value)));
|
|
151
|
+
}
|
|
152
|
+
return { tags, opaque };
|
|
153
|
+
}
|
|
154
|
+
var OPEN_GRAPH_KEYS = {
|
|
155
|
+
title: (value) => ({ kind: "meta", property: "og:title", value }),
|
|
156
|
+
description: (value) => ({ kind: "meta", property: "og:description", value }),
|
|
157
|
+
url: (value) => ({ kind: "meta", property: "og:url", value }),
|
|
158
|
+
images: (value) => ({ kind: "meta", property: "og:image", value }),
|
|
159
|
+
type: (value) => ({ kind: "meta", property: "og:type", value })
|
|
160
|
+
};
|
|
161
|
+
var TWITTER_KEYS = {
|
|
162
|
+
cardType: (value) => ({ kind: "meta", name: "twitter:card", value }),
|
|
163
|
+
card: (value) => ({ kind: "meta", name: "twitter:card", value })
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// src/providers/source/adapters/svelte-meta-tags.ts
|
|
167
|
+
function findAttr(attributes, name) {
|
|
168
|
+
return attributes.find((a) => a?.type === "Attribute" && a.name === name);
|
|
169
|
+
}
|
|
170
|
+
var svelteMetaTagsAdapter = {
|
|
171
|
+
match(info) {
|
|
172
|
+
if (info.source === "svelte-meta-tags") return info.imported === "MetaTags";
|
|
173
|
+
if (info.source === "svelte-meta-tags/MetaTags.svelte") return info.imported === "default";
|
|
174
|
+
return false;
|
|
175
|
+
},
|
|
176
|
+
resolve(use) {
|
|
177
|
+
const tags = [];
|
|
178
|
+
const attrs = use.attributes;
|
|
179
|
+
const titleAttr = findAttr(attrs, "title");
|
|
180
|
+
const templateAttr = findAttr(attrs, "titleTemplate");
|
|
181
|
+
const title = titleAttr ?? templateAttr;
|
|
182
|
+
if (title) {
|
|
183
|
+
const value = attrValueOf(title);
|
|
184
|
+
const text = titleAttr && !templateAttr && value === "static" ? attrTextOf(titleAttr) : void 0;
|
|
185
|
+
tags.push({ kind: "title", value, ...text !== void 0 ? { text } : {} });
|
|
186
|
+
}
|
|
187
|
+
const description = findAttr(attrs, "description");
|
|
188
|
+
if (description) {
|
|
189
|
+
const value = attrValueOf(description);
|
|
190
|
+
const text = value === "static" ? attrTextOf(description) : void 0;
|
|
191
|
+
tags.push({ kind: "meta", name: "description", value, ...text !== void 0 ? { text } : {} });
|
|
192
|
+
}
|
|
193
|
+
const canonical = findAttr(attrs, "canonical");
|
|
194
|
+
if (canonical) tags.push({ kind: "link", rel: "canonical", value: attrValueOf(canonical) });
|
|
195
|
+
const robots = findAttr(attrs, "robots");
|
|
196
|
+
if (robots) tags.push({ kind: "meta", name: "robots", value: attrValueOf(robots) });
|
|
197
|
+
const og = resolveMetaObject(findAttr(attrs, "openGraph"), OPEN_GRAPH_KEYS);
|
|
198
|
+
const tw = resolveMetaObject(findAttr(attrs, "twitter"), TWITTER_KEYS);
|
|
199
|
+
tags.push(...og.tags, ...tw.tags);
|
|
200
|
+
const broad = use.hasSpread || og.opaque || tw.opaque;
|
|
201
|
+
return { tags, broad };
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// src/providers/source/adapters/svelte-meta-tags-jsonld.ts
|
|
206
|
+
var svelteMetaTagsJsonLdAdapter = {
|
|
207
|
+
match(info) {
|
|
208
|
+
if (info.source === "svelte-meta-tags") return info.imported === "JsonLd";
|
|
209
|
+
if (info.source === "svelte-meta-tags/JsonLd.svelte") return info.imported === "default";
|
|
210
|
+
return false;
|
|
211
|
+
},
|
|
212
|
+
resolve() {
|
|
213
|
+
const tags = [{ kind: "jsonld", value: "dynamic" }];
|
|
214
|
+
return { tags, broad: false };
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
// src/providers/source/adapters/svelte-seo.ts
|
|
219
|
+
import { attrValueOf as attrValueOf2, attrTextOf as attrTextOf2 } from "@svelte-vitals/core";
|
|
220
|
+
function findAttr2(attributes, name) {
|
|
221
|
+
return attributes.find((a) => a?.type === "Attribute" && a.name === name);
|
|
222
|
+
}
|
|
223
|
+
var svelteSeoAdapter = {
|
|
224
|
+
match(info) {
|
|
225
|
+
return info.source === "svelte-seo" && info.imported === "default";
|
|
226
|
+
},
|
|
227
|
+
resolve(use) {
|
|
228
|
+
const tags = [];
|
|
229
|
+
const attrs = use.attributes;
|
|
230
|
+
const title = findAttr2(attrs, "title");
|
|
231
|
+
if (title) {
|
|
232
|
+
const value = attrValueOf2(title);
|
|
233
|
+
const text = value === "static" ? attrTextOf2(title) : void 0;
|
|
234
|
+
tags.push({ kind: "title", value, ...text !== void 0 ? { text } : {} });
|
|
235
|
+
}
|
|
236
|
+
const description = findAttr2(attrs, "description");
|
|
237
|
+
if (description) {
|
|
238
|
+
const value = attrValueOf2(description);
|
|
239
|
+
const text = value === "static" ? attrTextOf2(description) : void 0;
|
|
240
|
+
tags.push({ kind: "meta", name: "description", value, ...text !== void 0 ? { text } : {} });
|
|
241
|
+
}
|
|
242
|
+
const canonical = findAttr2(attrs, "canonical");
|
|
243
|
+
if (canonical) tags.push({ kind: "link", rel: "canonical", value: attrValueOf2(canonical) });
|
|
244
|
+
const og = resolveMetaObject(findAttr2(attrs, "openGraph"), OPEN_GRAPH_KEYS);
|
|
245
|
+
const tw = resolveMetaObject(findAttr2(attrs, "twitter"), TWITTER_KEYS);
|
|
246
|
+
tags.push(...og.tags, ...tw.tags);
|
|
247
|
+
const broad = use.hasSpread || og.opaque || tw.opaque;
|
|
248
|
+
return { tags, broad };
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// src/providers/source/adapters/index.ts
|
|
253
|
+
var builtinAdapters = [svelteMetaTagsAdapter, svelteMetaTagsJsonLdAdapter, svelteSeoAdapter];
|
|
254
|
+
function findAdapter(info) {
|
|
255
|
+
return builtinAdapters.find((adapter) => adapter.match(info));
|
|
256
|
+
}
|
|
257
|
+
|
|
125
258
|
// src/providers/source/parse.ts
|
|
126
259
|
import { parse } from "svelte/compiler";
|
|
127
260
|
import {
|
|
128
261
|
CHILD_NODE_KEYS,
|
|
129
262
|
lineOf,
|
|
130
|
-
findAttr,
|
|
263
|
+
findAttr as findAttr3,
|
|
131
264
|
valueFromNodes,
|
|
132
265
|
textFromNodes,
|
|
133
266
|
attrText,
|
|
@@ -203,9 +336,9 @@ function tagsFromHead(head) {
|
|
|
203
336
|
});
|
|
204
337
|
} else if (node.name === "link") {
|
|
205
338
|
const rel = attrText(node.attributes, "rel");
|
|
206
|
-
const hasAs =
|
|
339
|
+
const hasAs = findAttr3(node.attributes, "as") !== void 0;
|
|
207
340
|
const asLiteral = attrText(node.attributes, "as");
|
|
208
|
-
const hasCrossorigin =
|
|
341
|
+
const hasCrossorigin = findAttr3(node.attributes, "crossorigin") !== void 0;
|
|
209
342
|
const hreflang = attrText(node.attributes, "hreflang");
|
|
210
343
|
const href = attrText(node.attributes, "href");
|
|
211
344
|
tags.push({
|
|
@@ -228,7 +361,7 @@ function tagsFromHead(head) {
|
|
|
228
361
|
} else {
|
|
229
362
|
const src = attrText(node.attributes, "src");
|
|
230
363
|
if (src) {
|
|
231
|
-
const blocking =
|
|
364
|
+
const blocking = findAttr3(node.attributes, "defer") === void 0 && findAttr3(node.attributes, "async") === void 0 && type !== "module";
|
|
232
365
|
tags.push({ kind: "script", value: "static", href: src, ...blocking ? { blocking: true } : {} });
|
|
233
366
|
}
|
|
234
367
|
}
|
|
@@ -264,13 +397,13 @@ function collectImages(node, source, acc) {
|
|
|
264
397
|
const attrs = node.attributes ?? [];
|
|
265
398
|
const hasSpread = attrs.some((a) => a?.type === "SpreadAttribute");
|
|
266
399
|
acc.push({
|
|
267
|
-
hasWidth: hasSpread || Boolean(
|
|
268
|
-
hasHeight: hasSpread || Boolean(
|
|
269
|
-
hasLoading: hasSpread || Boolean(
|
|
270
|
-
hasAlt: hasSpread || Boolean(
|
|
400
|
+
hasWidth: hasSpread || Boolean(findAttr3(attrs, "width")),
|
|
401
|
+
hasHeight: hasSpread || Boolean(findAttr3(attrs, "height")),
|
|
402
|
+
hasLoading: hasSpread || Boolean(findAttr3(attrs, "loading")),
|
|
403
|
+
hasAlt: hasSpread || Boolean(findAttr3(attrs, "alt")),
|
|
271
404
|
// A literal loading="lazy" only — a spread or dynamic loading={…} must not be flagged.
|
|
272
405
|
lazy: attrText(attrs, "loading") === "lazy",
|
|
273
|
-
hasSrcset: hasSpread || Boolean(
|
|
406
|
+
hasSrcset: hasSpread || Boolean(findAttr3(attrs, "srcset")),
|
|
274
407
|
line: lineOf(source, node.start)
|
|
275
408
|
});
|
|
276
409
|
}
|
|
@@ -311,90 +444,27 @@ function parseFile(source, filename) {
|
|
|
311
444
|
};
|
|
312
445
|
}
|
|
313
446
|
|
|
314
|
-
// src/providers/source/
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
match(info) {
|
|
321
|
-
if (info.source === "svelte-meta-tags") return info.imported === "MetaTags";
|
|
322
|
-
if (info.source === "svelte-meta-tags/MetaTags.svelte") return info.imported === "default";
|
|
323
|
-
return false;
|
|
324
|
-
},
|
|
325
|
-
resolve(use) {
|
|
326
|
-
const tags = [];
|
|
327
|
-
const attrs = use.attributes;
|
|
328
|
-
const titleAttr = findAttr2(attrs, "title");
|
|
329
|
-
const templateAttr = findAttr2(attrs, "titleTemplate");
|
|
330
|
-
const title = titleAttr ?? templateAttr;
|
|
331
|
-
if (title) {
|
|
332
|
-
const value = attrValueOf(title);
|
|
333
|
-
const text = titleAttr && !templateAttr && value === "static" ? attrTextOf(titleAttr) : void 0;
|
|
334
|
-
tags.push({ kind: "title", value, ...text !== void 0 ? { text } : {} });
|
|
335
|
-
}
|
|
336
|
-
const description = findAttr2(attrs, "description");
|
|
337
|
-
if (description) {
|
|
338
|
-
const value = attrValueOf(description);
|
|
339
|
-
const text = value === "static" ? attrTextOf(description) : void 0;
|
|
340
|
-
tags.push({ kind: "meta", name: "description", value, ...text !== void 0 ? { text } : {} });
|
|
341
|
-
}
|
|
342
|
-
const canonical = findAttr2(attrs, "canonical");
|
|
343
|
-
if (canonical) tags.push({ kind: "link", rel: "canonical", value: attrValueOf(canonical) });
|
|
344
|
-
const robots = findAttr2(attrs, "robots");
|
|
345
|
-
if (robots) tags.push({ kind: "meta", name: "robots", value: attrValueOf(robots) });
|
|
346
|
-
const openGraph = findAttr2(attrs, "openGraph");
|
|
347
|
-
const broad = use.hasSpread || Boolean(openGraph);
|
|
348
|
-
return { tags, broad };
|
|
349
|
-
}
|
|
350
|
-
};
|
|
351
|
-
|
|
352
|
-
// src/providers/source/adapters/svelte-seo.ts
|
|
353
|
-
import { attrValueOf as attrValueOf2, attrTextOf as attrTextOf2 } from "@svelte-vitals/core";
|
|
354
|
-
function findAttr3(attributes, name) {
|
|
355
|
-
return attributes.find((a) => a?.type === "Attribute" && a.name === name);
|
|
356
|
-
}
|
|
357
|
-
var svelteSeoAdapter = {
|
|
358
|
-
match(info) {
|
|
359
|
-
return info.source === "svelte-seo" && info.imported === "default";
|
|
360
|
-
},
|
|
361
|
-
resolve(use) {
|
|
362
|
-
const tags = [];
|
|
363
|
-
const attrs = use.attributes;
|
|
364
|
-
const title = findAttr3(attrs, "title");
|
|
365
|
-
if (title) {
|
|
366
|
-
const value = attrValueOf2(title);
|
|
367
|
-
const text = value === "static" ? attrTextOf2(title) : void 0;
|
|
368
|
-
tags.push({ kind: "title", value, ...text !== void 0 ? { text } : {} });
|
|
369
|
-
}
|
|
370
|
-
const description = findAttr3(attrs, "description");
|
|
371
|
-
if (description) {
|
|
372
|
-
const value = attrValueOf2(description);
|
|
373
|
-
const text = value === "static" ? attrTextOf2(description) : void 0;
|
|
374
|
-
tags.push({ kind: "meta", name: "description", value, ...text !== void 0 ? { text } : {} });
|
|
375
|
-
}
|
|
376
|
-
const canonical = findAttr3(attrs, "canonical");
|
|
377
|
-
if (canonical) tags.push({ kind: "link", rel: "canonical", value: attrValueOf2(canonical) });
|
|
378
|
-
const openGraph = findAttr3(attrs, "openGraph");
|
|
379
|
-
const broad = use.hasSpread || Boolean(openGraph);
|
|
380
|
-
return { tags, broad };
|
|
447
|
+
// src/providers/source/resolve.ts
|
|
448
|
+
function readAndParse(rt, cwd, rel, cache) {
|
|
449
|
+
let hit = cache.get(rel);
|
|
450
|
+
if (!hit) {
|
|
451
|
+
hit = rt.readFile(rt.join(cwd, rel)).then((source) => parseFile(source, rel));
|
|
452
|
+
cache.set(rel, hit);
|
|
381
453
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
// src/providers/source/adapters/index.ts
|
|
385
|
-
var builtinAdapters = [svelteMetaTagsAdapter, svelteSeoAdapter];
|
|
386
|
-
function findAdapter(info) {
|
|
387
|
-
return builtinAdapters.find((adapter) => adapter.match(info));
|
|
454
|
+
return hit;
|
|
388
455
|
}
|
|
389
|
-
|
|
390
|
-
// src/providers/source/resolve.ts
|
|
391
456
|
var BROAD_KINDS = [
|
|
392
457
|
{ kind: "title", value: "dynamic" },
|
|
393
458
|
{ kind: "meta", name: "description", value: "dynamic" },
|
|
394
459
|
{ kind: "link", rel: "canonical", value: "dynamic" },
|
|
395
460
|
{ kind: "meta", property: "og:title", value: "dynamic" },
|
|
461
|
+
{ kind: "meta", property: "og:description", value: "dynamic" },
|
|
396
462
|
{ kind: "meta", property: "og:image", value: "dynamic" },
|
|
463
|
+
{ kind: "meta", property: "og:url", value: "dynamic" },
|
|
464
|
+
{ kind: "meta", name: "twitter:card", value: "dynamic" },
|
|
397
465
|
{ kind: "meta", name: "robots", value: "dynamic" }
|
|
466
|
+
// jsonld is intentionally omitted: structured data is a distinct concern, not a
|
|
467
|
+
// meta-tag family a broad meta source implies (JsonLd has its own adapter).
|
|
398
468
|
];
|
|
399
469
|
function tagKey(tag) {
|
|
400
470
|
switch (tag.kind) {
|
|
@@ -431,7 +501,7 @@ function resolveComponentPath(source, fromFileRel) {
|
|
|
431
501
|
if (/\.[^/]+$/.test(path)) return void 0;
|
|
432
502
|
return `${path}.svelte`;
|
|
433
503
|
}
|
|
434
|
-
async function resolveFileTags(rt, cwd, fileRel, parsed, config, depth, visited) {
|
|
504
|
+
async function resolveFileTags(rt, cwd, fileRel, parsed, config, depth, visited, cache = /* @__PURE__ */ new Map()) {
|
|
435
505
|
const tags = [...parsed.headTags];
|
|
436
506
|
let broad = false;
|
|
437
507
|
for (const use of parsed.components) {
|
|
@@ -451,9 +521,9 @@ async function resolveFileTags(rt, cwd, fileRel, parsed, config, depth, visited)
|
|
|
451
521
|
if (childRel && depth > 0 && !visited.has(childRel)) {
|
|
452
522
|
const abs = rt.join(cwd, childRel);
|
|
453
523
|
if (await rt.exists(abs)) {
|
|
454
|
-
const childParsed =
|
|
524
|
+
const childParsed = await readAndParse(rt, cwd, childRel, cache);
|
|
455
525
|
const childVisited = new Set(visited).add(childRel);
|
|
456
|
-
const child = await resolveFileTags(rt, cwd, childRel, childParsed, config, depth - 1, childVisited);
|
|
526
|
+
const child = await resolveFileTags(rt, cwd, childRel, childParsed, config, depth - 1, childVisited, cache);
|
|
457
527
|
tags.push(...child.tags);
|
|
458
528
|
broad = broad || child.broad;
|
|
459
529
|
}
|
|
@@ -528,7 +598,7 @@ function chainFiles(pageRel, layouts) {
|
|
|
528
598
|
}
|
|
529
599
|
return [...chain.map((rel) => ({ rel, isPage: false })), { rel: pageRel, isPage: true }];
|
|
530
600
|
}
|
|
531
|
-
async function resolveRoute(rt, cwd, pageRel, config, layouts) {
|
|
601
|
+
async function resolveRoute(rt, cwd, pageRel, config, layouts, cache) {
|
|
532
602
|
const files = chainFiles(pageRel, layouts);
|
|
533
603
|
const composed = /* @__PURE__ */ new Map();
|
|
534
604
|
let broadOwn = false;
|
|
@@ -536,15 +606,14 @@ async function resolveRoute(rt, cwd, pageRel, config, layouts) {
|
|
|
536
606
|
const images = [];
|
|
537
607
|
const headings = [];
|
|
538
608
|
for (const { rel, isPage } of files) {
|
|
539
|
-
const
|
|
540
|
-
const parsed = parseFile(source, rel);
|
|
609
|
+
const parsed = await readAndParse(rt, cwd, rel, cache);
|
|
541
610
|
for (const img of parsed.images) {
|
|
542
611
|
images.push({ ...img, file: rel });
|
|
543
612
|
}
|
|
544
613
|
for (const heading of parsed.headings) {
|
|
545
614
|
headings.push({ ...heading, file: rel });
|
|
546
615
|
}
|
|
547
|
-
const resolved = await resolveFileTags(rt, cwd, rel, parsed, config, MAX_DEPTH, /* @__PURE__ */ new Set([rel]));
|
|
616
|
+
const resolved = await resolveFileTags(rt, cwd, rel, parsed, config, MAX_DEPTH, /* @__PURE__ */ new Set([rel]), cache);
|
|
548
617
|
for (const tag of resolved.tags) {
|
|
549
618
|
composed.set(tagKey(tag), { ...tag, presence: isPage ? "own" : "inherited", file: rel });
|
|
550
619
|
}
|
|
@@ -569,7 +638,8 @@ async function resolveRoute(rt, cwd, pageRel, config, layouts) {
|
|
|
569
638
|
}
|
|
570
639
|
async function collectRoutes(rt, cwd, config = defaultConfig) {
|
|
571
640
|
const [pages, layouts] = await Promise.all([enumerateRoutePages(rt, cwd), collectLayouts(rt, cwd)]);
|
|
572
|
-
const
|
|
641
|
+
const cache = /* @__PURE__ */ new Map();
|
|
642
|
+
const facts = await Promise.all(pages.map((page) => resolveRoute(rt, cwd, page, config, layouts, cache)));
|
|
573
643
|
return {
|
|
574
644
|
heads: facts.map((f) => f.head),
|
|
575
645
|
images: facts.map((f) => f.images),
|
|
@@ -578,32 +648,7 @@ async function collectRoutes(rt, cwd, config = defaultConfig) {
|
|
|
578
648
|
}
|
|
579
649
|
|
|
580
650
|
// src/providers/source/components.ts
|
|
581
|
-
import {
|
|
582
|
-
async function collectComponentFacts(rt, cwd) {
|
|
583
|
-
const files = await rt.glob("src/**/*.svelte", cwd);
|
|
584
|
-
return Promise.all(
|
|
585
|
-
files.sort().map(async (rel) => {
|
|
586
|
-
try {
|
|
587
|
-
const source = await rt.readFile(rt.join(cwd, rel));
|
|
588
|
-
return { file: rel, ...parseComponentFacts(source, rel) };
|
|
589
|
-
} catch {
|
|
590
|
-
return {
|
|
591
|
-
file: rel,
|
|
592
|
-
eachBlocks: [],
|
|
593
|
-
effects: [],
|
|
594
|
-
htmlTags: [],
|
|
595
|
-
javascriptUrls: [],
|
|
596
|
-
loc: 0,
|
|
597
|
-
propCount: 0,
|
|
598
|
-
imports: [],
|
|
599
|
-
namespaceImports: [],
|
|
600
|
-
constableStates: [],
|
|
601
|
-
suppressions: []
|
|
602
|
-
};
|
|
603
|
-
}
|
|
604
|
-
})
|
|
605
|
-
);
|
|
606
|
-
}
|
|
651
|
+
import { collectComponentFacts } from "@svelte-vitals/core";
|
|
607
652
|
|
|
608
653
|
// src/version.ts
|
|
609
654
|
import { readFileSync } from "fs";
|
|
@@ -652,8 +697,8 @@ function git(args, cwd) {
|
|
|
652
697
|
}
|
|
653
698
|
function getChangedFiles(cwd, opts) {
|
|
654
699
|
try {
|
|
655
|
-
const files = opts.staged ? git(["diff", "--name-only", "--cached", "--diff-filter=d"], cwd) : [
|
|
656
|
-
...git(["diff", "--name-only", "--diff-filter=d", "--merge-base", opts.base ?? "HEAD"], cwd),
|
|
700
|
+
const files = opts.staged ? git(["diff", "--name-only", "--relative", "--cached", "--diff-filter=d"], cwd) : [
|
|
701
|
+
...git(["diff", "--name-only", "--relative", "--diff-filter=d", "--merge-base", opts.base ?? "HEAD"], cwd),
|
|
657
702
|
...git(["ls-files", "--others", "--exclude-standard"], cwd)
|
|
658
703
|
// untracked / new files
|
|
659
704
|
];
|
|
@@ -708,6 +753,11 @@ function startSpinner(text, opts) {
|
|
|
708
753
|
};
|
|
709
754
|
}
|
|
710
755
|
|
|
756
|
+
// src/config-file.ts
|
|
757
|
+
import { existsSync } from "fs";
|
|
758
|
+
import { join as join2 } from "path";
|
|
759
|
+
import { pathToFileURL } from "url";
|
|
760
|
+
|
|
711
761
|
// src/rules-config.ts
|
|
712
762
|
import { allRules } from "@svelte-vitals/core";
|
|
713
763
|
var KNOWN_IDS = new Set(allRules.map((r) => r.id));
|
|
@@ -726,7 +776,106 @@ function buildRulesConfig(allow, ignore) {
|
|
|
726
776
|
return rules;
|
|
727
777
|
}
|
|
728
778
|
|
|
779
|
+
// src/config-file.ts
|
|
780
|
+
var CONFIG_FILENAMES = ["svelte-vitals.config.mjs", "svelte-vitals.config.js", "svelte-vitals.config.ts"];
|
|
781
|
+
var CATEGORIES = ["seo", "performance", "correctness", "security", "architecture"];
|
|
782
|
+
var TREAT_DYNAMIC_AS_VALUES = ["pass", "warn", "fail"];
|
|
783
|
+
var FAIL_ON_VALUES = ["critical", "warning", "info"];
|
|
784
|
+
var KNOWN_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set(["treatDynamicAs", "metaComponents", "rules", "failOn", "weights"]);
|
|
785
|
+
function isPlainObject(value) {
|
|
786
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
787
|
+
}
|
|
788
|
+
function isMissingExtensionLoaderError(err) {
|
|
789
|
+
return err instanceof Error && ("code" in err && err.code === "ERR_UNKNOWN_FILE_EXTENSION" || /Unknown file extension/.test(err.message));
|
|
790
|
+
}
|
|
791
|
+
function validateConfigFile(raw, path) {
|
|
792
|
+
const warnings = [];
|
|
793
|
+
const config = {};
|
|
794
|
+
for (const key of Object.keys(raw)) {
|
|
795
|
+
if (!KNOWN_TOP_LEVEL_KEYS.has(key)) {
|
|
796
|
+
warnings.push(`${path}: unknown config key '${key}' ignored.`);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
if (raw.treatDynamicAs !== void 0) {
|
|
800
|
+
if (TREAT_DYNAMIC_AS_VALUES.includes(raw.treatDynamicAs)) {
|
|
801
|
+
config.treatDynamicAs = raw.treatDynamicAs;
|
|
802
|
+
} else {
|
|
803
|
+
warnings.push(
|
|
804
|
+
`${path}: unknown treatDynamicAs '${String(raw.treatDynamicAs)}'; expected pass|warn|fail. Ignoring.`
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
if (raw.failOn !== void 0) {
|
|
809
|
+
if (FAIL_ON_VALUES.includes(raw.failOn)) {
|
|
810
|
+
config.failOn = raw.failOn;
|
|
811
|
+
} else {
|
|
812
|
+
warnings.push(`${path}: unknown failOn '${String(raw.failOn)}'; expected critical|warning|info. Ignoring.`);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
if (raw.metaComponents !== void 0) {
|
|
816
|
+
if (Array.isArray(raw.metaComponents) && raw.metaComponents.every((c) => typeof c === "string")) {
|
|
817
|
+
config.metaComponents = raw.metaComponents;
|
|
818
|
+
} else {
|
|
819
|
+
warnings.push(`${path}: metaComponents must be an array of strings. Ignoring.`);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
if (raw.rules !== void 0) {
|
|
823
|
+
if (!isPlainObject(raw.rules)) {
|
|
824
|
+
throw new Error(`${path}: rules must be an object of rule-id \u2192 setting.`);
|
|
825
|
+
}
|
|
826
|
+
const rules = raw.rules;
|
|
827
|
+
const unknown = findUnknownRuleIds(Object.keys(rules));
|
|
828
|
+
if (unknown.length > 0) {
|
|
829
|
+
throw new Error(
|
|
830
|
+
`${path}: unknown rule id(s) in rules: ${unknown.join(", ")}. Known rule ids: ${knownRuleIds().join(", ")}`
|
|
831
|
+
);
|
|
832
|
+
}
|
|
833
|
+
config.rules = rules;
|
|
834
|
+
}
|
|
835
|
+
if (raw.weights !== void 0) {
|
|
836
|
+
if (!isPlainObject(raw.weights)) {
|
|
837
|
+
throw new Error(`${path}: weights must be an object of category \u2192 number.`);
|
|
838
|
+
}
|
|
839
|
+
const weights = {};
|
|
840
|
+
for (const [rawCat, w] of Object.entries(raw.weights)) {
|
|
841
|
+
const cat = rawCat.toLowerCase();
|
|
842
|
+
if (!CATEGORIES.includes(cat)) {
|
|
843
|
+
throw new Error(`${path}: unknown category '${rawCat}' in weights. Known categories: ${CATEGORIES.join(", ")}`);
|
|
844
|
+
}
|
|
845
|
+
if (typeof w !== "number" || !Number.isFinite(w) || w < 0) {
|
|
846
|
+
throw new Error(`${path}: invalid weight for '${cat}': ${String(w)}; expected a finite number >= 0.`);
|
|
847
|
+
}
|
|
848
|
+
weights[cat] = w;
|
|
849
|
+
}
|
|
850
|
+
config.weights = weights;
|
|
851
|
+
}
|
|
852
|
+
return { config, warnings };
|
|
853
|
+
}
|
|
854
|
+
async function loadConfigFile(cwd) {
|
|
855
|
+
const found = CONFIG_FILENAMES.map((name) => join2(cwd, name)).find((path) => existsSync(path));
|
|
856
|
+
if (!found) return void 0;
|
|
857
|
+
let mod;
|
|
858
|
+
try {
|
|
859
|
+
mod = await import(pathToFileURL(found).href);
|
|
860
|
+
} catch (err) {
|
|
861
|
+
if (found.endsWith(".ts") && isMissingExtensionLoaderError(err)) {
|
|
862
|
+
throw new Error(
|
|
863
|
+
`could not load ${found} \u2014 this Node runtime does not support TypeScript config files without a flag. Native type-stripping is unflagged from Node 22.18 / 23.6+: upgrade Node to 22.18+, re-run with --experimental-strip-types, or rename the file to .mjs/.js.`,
|
|
864
|
+
{ cause: err }
|
|
865
|
+
);
|
|
866
|
+
}
|
|
867
|
+
throw err;
|
|
868
|
+
}
|
|
869
|
+
if (!isPlainObject(mod.default)) {
|
|
870
|
+
throw new Error(
|
|
871
|
+
`${found} must have a default export that is a plain object (e.g. \`export default defineConfig({...})\` or a plain object literal).`
|
|
872
|
+
);
|
|
873
|
+
}
|
|
874
|
+
return validateConfigFile(mod.default, found);
|
|
875
|
+
}
|
|
876
|
+
|
|
729
877
|
// src/index.ts
|
|
878
|
+
import { defineConfig as defineConfig2 } from "@svelte-vitals/core";
|
|
730
879
|
function spinnerEnabled(opts) {
|
|
731
880
|
return opts.reporter === "console" && opts.stderrIsTTY && !isAutoDetectedAgent(opts.rawReporter, opts.env) && colorEnabled({ reporter: opts.reporter, isTTY: opts.stderrIsTTY, env: opts.env, noColorFlag: opts.noColorFlag });
|
|
732
881
|
}
|
|
@@ -739,11 +888,16 @@ function routeMatcher(glob) {
|
|
|
739
888
|
async function analyzeProject(opts = {}) {
|
|
740
889
|
const cwd = opts.cwd ?? process.cwd();
|
|
741
890
|
const rt = createNodeRuntime();
|
|
891
|
+
const loaded = await loadConfigFile(cwd);
|
|
892
|
+
const file = loaded?.config;
|
|
893
|
+
const warnings = loaded?.warnings ?? [];
|
|
894
|
+
const weights = opts.weights ?? file?.weights;
|
|
742
895
|
const config = defineConfig({
|
|
743
|
-
treatDynamicAs: opts.treatDynamicAs ?? "pass",
|
|
744
|
-
metaComponents: opts.metaComponents ?? [],
|
|
745
|
-
rules: opts.rules ?? {},
|
|
746
|
-
failOn: opts.failOn ?? "critical"
|
|
896
|
+
treatDynamicAs: opts.treatDynamicAs ?? file?.treatDynamicAs ?? "pass",
|
|
897
|
+
metaComponents: opts.metaComponents ?? file?.metaComponents ?? [],
|
|
898
|
+
rules: opts.rules ?? file?.rules ?? {},
|
|
899
|
+
failOn: opts.failOn ?? file?.failOn ?? "critical",
|
|
900
|
+
...weights !== void 0 ? { weights } : {}
|
|
747
901
|
});
|
|
748
902
|
await detectProject(rt, cwd);
|
|
749
903
|
const matches = routeMatcher(opts.route);
|
|
@@ -758,7 +912,7 @@ async function analyzeProject(opts = {}) {
|
|
|
758
912
|
await runRules(rules, { heads, images, headings, components, project, config }),
|
|
759
913
|
config
|
|
760
914
|
);
|
|
761
|
-
return { results, config, version: readPackageVersion() };
|
|
915
|
+
return { results, config, version: readPackageVersion(), warnings };
|
|
762
916
|
}
|
|
763
917
|
async function run(opts = {}) {
|
|
764
918
|
const log = opts.log ?? ((line) => console.log(line));
|
|
@@ -786,7 +940,8 @@ async function run(opts = {}) {
|
|
|
786
940
|
treatDynamicAs: opts.treatDynamicAs,
|
|
787
941
|
route: opts.route,
|
|
788
942
|
failOn: opts.failOn,
|
|
789
|
-
rules: opts.rules
|
|
943
|
+
rules: opts.rules,
|
|
944
|
+
weights: opts.weights
|
|
790
945
|
});
|
|
791
946
|
} catch (err) {
|
|
792
947
|
spinner.stop();
|
|
@@ -798,6 +953,7 @@ async function run(opts = {}) {
|
|
|
798
953
|
return 2;
|
|
799
954
|
}
|
|
800
955
|
spinner.stop();
|
|
956
|
+
for (const w of analysis.warnings) errorLog(`svelte-vitals: ${w}`);
|
|
801
957
|
try {
|
|
802
958
|
const { config, version } = analysis;
|
|
803
959
|
let results = analysis.results;
|
|
@@ -870,8 +1026,10 @@ export {
|
|
|
870
1026
|
findUnknownRuleIds,
|
|
871
1027
|
knownRuleIds,
|
|
872
1028
|
buildRulesConfig,
|
|
1029
|
+
loadConfigFile,
|
|
873
1030
|
spinnerEnabled,
|
|
874
1031
|
routeMatcher,
|
|
875
1032
|
analyzeProject,
|
|
876
|
-
run
|
|
1033
|
+
run,
|
|
1034
|
+
defineConfig2 as defineConfig
|
|
877
1035
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { RuleSetting, Severity,
|
|
1
|
+
import { RuleSetting, Config, Severity, Category, Result } from '@svelte-vitals/core';
|
|
2
|
+
export { defineConfig } from '@svelte-vitals/core';
|
|
2
3
|
|
|
3
4
|
type ReporterName = 'console' | 'json' | 'agent' | 'sarif' | 'github' | 'html';
|
|
4
5
|
|
|
@@ -19,6 +20,31 @@ declare function knownRuleIds(): string[];
|
|
|
19
20
|
*/
|
|
20
21
|
declare function buildRulesConfig(allow: string[], ignore: string[]): Record<string, RuleSetting>;
|
|
21
22
|
|
|
23
|
+
/** Result of loading and validating a config file. */
|
|
24
|
+
interface LoadedConfigFile {
|
|
25
|
+
/** The file's contents after validation; invalid optional fields are dropped (see `warnings`). */
|
|
26
|
+
config: Partial<Config>;
|
|
27
|
+
/** Non-fatal issues: unknown top-level keys, invalid enum values (the field is ignored). */
|
|
28
|
+
warnings: string[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Find and load `svelte-vitals.config.{mjs,js,ts}` from `cwd` (only `cwd`, no
|
|
32
|
+
* upward search). Returns `undefined` when no candidate file exists.
|
|
33
|
+
*
|
|
34
|
+
* Loader mechanism (design doc §2): plain native `import()`. `.mjs`/`.js` always
|
|
35
|
+
* work (zero dependencies, no Node-version dependency). `.ts` depends on the host
|
|
36
|
+
* Node's TypeScript type-stripping support: unflagged in Node 23.6.0, backported
|
|
37
|
+
* to 22.18.0; on 22.13–22.17 (this repo's floor is >=22.13.0) it requires
|
|
38
|
+
* `--experimental-strip-types` and otherwise fails with
|
|
39
|
+
* `ERR_UNKNOWN_FILE_EXTENSION` — this is caught here and rethrown as a
|
|
40
|
+
* descriptive, actionable error instead of surfacing Node's raw error.
|
|
41
|
+
*
|
|
42
|
+
* Throws when: the file exists but has no usable default export; (`.ts` only)
|
|
43
|
+
* the host Node can't load TypeScript without a flag; or the loaded config
|
|
44
|
+
* fails validation (unknown rule ids in `rules`, invalid `weights` entries).
|
|
45
|
+
*/
|
|
46
|
+
declare function loadConfigFile(cwd: string): Promise<LoadedConfigFile | undefined>;
|
|
47
|
+
|
|
22
48
|
interface RunOptions {
|
|
23
49
|
cwd?: string;
|
|
24
50
|
log?: (line: string) => void;
|
|
@@ -31,6 +57,8 @@ interface RunOptions {
|
|
|
31
57
|
byRoute?: boolean;
|
|
32
58
|
failOn?: Severity;
|
|
33
59
|
rules?: Record<string, RuleSetting>;
|
|
60
|
+
/** Per-category weights for the combined Health score (flag > config file > default 1 each). */
|
|
61
|
+
weights?: Partial<Record<Category, number>>;
|
|
34
62
|
/** Override process.env for reporter auto-detection (mainly useful in tests). */
|
|
35
63
|
env?: NodeJS.ProcessEnv;
|
|
36
64
|
/** Fail (exit 1) when the combined Health score is below this value (0–100). */
|
|
@@ -73,16 +101,26 @@ interface AnalyzeOptions {
|
|
|
73
101
|
route?: string;
|
|
74
102
|
failOn?: Severity;
|
|
75
103
|
rules?: Record<string, RuleSetting>;
|
|
104
|
+
/** Per-category weights for the combined Health score (flag > config file > default 1 each). */
|
|
105
|
+
weights?: Partial<Record<Category, number>>;
|
|
76
106
|
}
|
|
77
107
|
interface AnalyzeResult {
|
|
78
108
|
results: Result[];
|
|
79
109
|
config: Config;
|
|
80
110
|
version: string;
|
|
111
|
+
/** Non-fatal config-file issues (unknown top-level keys, invalid enum values). Empty when no config file or none found. */
|
|
112
|
+
warnings: string[];
|
|
81
113
|
}
|
|
82
114
|
/**
|
|
83
115
|
* Run static-mode analysis and return the structured findings + resolved config.
|
|
84
|
-
* Throws ProjectError when `cwd` is not a SvelteKit project.
|
|
85
|
-
*
|
|
116
|
+
* Throws ProjectError when `cwd` is not a SvelteKit project. Also throws when a
|
|
117
|
+
* `svelte-vitals.config.{mjs,js,ts}` file in `cwd` fails to load or fails
|
|
118
|
+
* validation (unknown rule ids in `rules`, invalid `weights` entries) — see
|
|
119
|
+
* `loadConfigFile`. Shared by the CLI's run() and by @svelte-vitals/mcp (issue #24).
|
|
120
|
+
*
|
|
121
|
+
* Config precedence is per field: an explicit option here wins, otherwise the
|
|
122
|
+
* config file's value is used, otherwise the built-in default (design doc
|
|
123
|
+
* 2026-07-05-config-file-design.md §3).
|
|
86
124
|
*/
|
|
87
125
|
declare function analyzeProject(opts?: AnalyzeOptions): Promise<AnalyzeResult>;
|
|
88
126
|
/**
|
|
@@ -91,4 +129,4 @@ declare function analyzeProject(opts?: AnalyzeOptions): Promise<AnalyzeResult>;
|
|
|
91
129
|
*/
|
|
92
130
|
declare function run(opts?: RunOptions): Promise<number>;
|
|
93
131
|
|
|
94
|
-
export { type AnalyzeOptions, type AnalyzeResult, ProjectError, type RunOptions, analyzeProject, buildRulesConfig, findUnknownRuleIds, knownRuleIds, routeMatcher, run, spinnerEnabled };
|
|
132
|
+
export { type AnalyzeOptions, type AnalyzeResult, type LoadedConfigFile, ProjectError, type RunOptions, analyzeProject, buildRulesConfig, findUnknownRuleIds, knownRuleIds, loadConfigFile, routeMatcher, run, spinnerEnabled };
|
package/dist/index.js
CHANGED
|
@@ -2,18 +2,22 @@ import {
|
|
|
2
2
|
ProjectError,
|
|
3
3
|
analyzeProject,
|
|
4
4
|
buildRulesConfig,
|
|
5
|
+
defineConfig,
|
|
5
6
|
findUnknownRuleIds,
|
|
6
7
|
knownRuleIds,
|
|
8
|
+
loadConfigFile,
|
|
7
9
|
routeMatcher,
|
|
8
10
|
run,
|
|
9
11
|
spinnerEnabled
|
|
10
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-ZE3M3T6U.js";
|
|
11
13
|
export {
|
|
12
14
|
ProjectError,
|
|
13
15
|
analyzeProject,
|
|
14
16
|
buildRulesConfig,
|
|
17
|
+
defineConfig,
|
|
15
18
|
findUnknownRuleIds,
|
|
16
19
|
knownRuleIds,
|
|
20
|
+
loadConfigFile,
|
|
17
21
|
routeMatcher,
|
|
18
22
|
run,
|
|
19
23
|
spinnerEnabled
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelte-vitals",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.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",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"homepage": "https://github.com/oekazuma/svelte-vitals#readme",
|
|
25
25
|
"engines": {
|
|
26
|
-
"node": ">=
|
|
26
|
+
"node": ">=22.13.0"
|
|
27
27
|
},
|
|
28
28
|
"sideEffects": false,
|
|
29
29
|
"bin": {
|
|
@@ -39,13 +39,13 @@
|
|
|
39
39
|
"dist"
|
|
40
40
|
],
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@clack/prompts": "^1.
|
|
42
|
+
"@clack/prompts": "^1.7.0",
|
|
43
43
|
"magicast": "^0.5.3",
|
|
44
44
|
"mri": "^1.2.0",
|
|
45
45
|
"smol-toml": "^1.7.0",
|
|
46
46
|
"svelte": "^5.56.4",
|
|
47
47
|
"tinyglobby": "^0.2.17",
|
|
48
|
-
"@svelte-vitals/core": "0.
|
|
48
|
+
"@svelte-vitals/core": "0.20.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@types/node": "^24.13.2"
|