svelte-vitals 0.44.0 → 0.44.2
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/README.md +1 -4
- package/dist/bin.js +18 -6
- package/dist/{chunk-WD5TSADH.js → chunk-EWDGS52A.js} +107 -46
- package/dist/chunk-TFBLQUAC.js +19 -0
- package/dist/{cli-LGD6NDAO.js → cli-L2HHOZDN.js} +15 -2
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -4
- package/package.json +8 -4
package/README.md
CHANGED
|
@@ -31,14 +31,11 @@ Health: 76/100
|
|
|
31
31
|
SEO Score: 51/100 (route avg 61 · site −10)
|
|
32
32
|
Architecture Score: 100/100 (route avg 100)
|
|
33
33
|
|
|
34
|
-
Critical (
|
|
34
|
+
Critical (1)
|
|
35
35
|
────────────────────────
|
|
36
36
|
✗ seo/title-presence Missing <title>
|
|
37
37
|
/
|
|
38
38
|
src/routes/+page.svelte
|
|
39
|
-
✗ seo/description-presence Missing <meta name="description">
|
|
40
|
-
/
|
|
41
|
-
src/routes/+page.svelte
|
|
42
39
|
|
|
43
40
|
Warnings (10)
|
|
44
41
|
────────────────────────
|
package/dist/bin.js
CHANGED
|
@@ -2,20 +2,22 @@
|
|
|
2
2
|
import {
|
|
3
3
|
CONFIG_FILENAMES,
|
|
4
4
|
discoverApps,
|
|
5
|
-
findUnknownRuleIds,
|
|
6
5
|
hasDep,
|
|
7
6
|
isReporterName,
|
|
8
|
-
knownRuleIds,
|
|
9
7
|
readCoreVersion,
|
|
10
8
|
readPackageVersion,
|
|
11
9
|
readPkg,
|
|
12
10
|
run
|
|
13
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-EWDGS52A.js";
|
|
14
12
|
import {
|
|
15
13
|
consoleIO,
|
|
16
14
|
parseCliArgs,
|
|
17
15
|
toList
|
|
18
16
|
} from "./chunk-5Q2PT47V.js";
|
|
17
|
+
import {
|
|
18
|
+
findUnknownRuleIds,
|
|
19
|
+
knownRuleIds
|
|
20
|
+
} from "./chunk-TFBLQUAC.js";
|
|
19
21
|
|
|
20
22
|
// src/resolve-args.ts
|
|
21
23
|
var CATEGORIES = ["seo", "performance", "correctness", "security", "architecture"];
|
|
@@ -132,6 +134,7 @@ function resolveArgs(argv) {
|
|
|
132
134
|
const errors = [];
|
|
133
135
|
for (const flag of VALUE_FLAGS) {
|
|
134
136
|
const v = argv[flag];
|
|
137
|
+
if (flag === "out-file" && v === "-") continue;
|
|
135
138
|
if (v !== void 0 && (typeof v !== "string" || v.trim() === "" || v.startsWith("-"))) {
|
|
136
139
|
errors.push(`svelte-vitals: --${flag} requires a value.`);
|
|
137
140
|
}
|
|
@@ -193,6 +196,15 @@ function resolveArgs(argv) {
|
|
|
193
196
|
const failOn = failOnValid ? failOnRaw : void 0;
|
|
194
197
|
const weights = parseWeights(argv.weights, errors);
|
|
195
198
|
const categories = parseCategories(argv.category, errors);
|
|
199
|
+
if (categories !== void 0 && categories.length > 0 && allow.length > 0) {
|
|
200
|
+
const excluded = allow.filter((id) => !unknown.includes(id)).filter((id) => !categories.includes(id.split("/")[0]));
|
|
201
|
+
if (excluded.length > 0) {
|
|
202
|
+
errors.push(
|
|
203
|
+
`svelte-vitals: --rules id(s) excluded by --category ${categories.join(", ")}: ${excluded.join(", ")}`
|
|
204
|
+
);
|
|
205
|
+
errors.push("Add the rule's category to --category, or drop the rule from --rules.");
|
|
206
|
+
}
|
|
207
|
+
}
|
|
196
208
|
const score = Boolean(argv.score);
|
|
197
209
|
if (score && typeof argv.reporter === "string") {
|
|
198
210
|
warnings.push("svelte-vitals: --score overrides --reporter; reporter output suppressed.");
|
|
@@ -395,7 +407,7 @@ ${lines}`;
|
|
|
395
407
|
}).join("\n\n");
|
|
396
408
|
}
|
|
397
409
|
function sharedBody(version) {
|
|
398
|
-
return `<!-- Generated by \`svelte-vitals install\` (svelte-vitals ${version}). Re-run
|
|
410
|
+
return `<!-- Generated by \`svelte-vitals install\` (svelte-vitals ${version}). Re-run \`svelte-vitals install --refresh\` to regenerate. -->
|
|
399
411
|
|
|
400
412
|
# svelte-vitals
|
|
401
413
|
|
|
@@ -442,7 +454,7 @@ name: improve-svelte
|
|
|
442
454
|
description: Survey a whole SvelteKit codebase as a senior Svelte/SvelteKit engineer, using svelte-vitals' scan as evidence, then produce a prioritized audit and self-contained implementation plans for other agents (or cheaper models) to execute. Read-only on source code \u2014 it plans improvements, it does not apply them. Use when the user asks to "improve this SvelteKit app", "audit this codebase", "make this app more SEO/performance/security solid", or wants a roadmap of fixes rather than a review of a single diff. For routine regression checks while writing code, use the \`svelte-vitals\` skill instead.
|
|
443
455
|
---
|
|
444
456
|
|
|
445
|
-
<!-- Generated by \`svelte-vitals install\` (svelte-vitals ${version}). Re-run
|
|
457
|
+
<!-- Generated by \`svelte-vitals install\` (svelte-vitals ${version}). Re-run \`svelte-vitals install --refresh\` to regenerate. -->
|
|
446
458
|
|
|
447
459
|
# improve-svelte
|
|
448
460
|
|
|
@@ -1775,7 +1787,7 @@ function selectApp(apps) {
|
|
|
1775
1787
|
async function main() {
|
|
1776
1788
|
const rawArgs = process.argv.slice(2);
|
|
1777
1789
|
if (rawArgs[0] === "docs") {
|
|
1778
|
-
const { runDocsCli } = await import("./cli-
|
|
1790
|
+
const { runDocsCli } = await import("./cli-L2HHOZDN.js");
|
|
1779
1791
|
process.exitCode = runDocsCli(rawArgs.slice(1));
|
|
1780
1792
|
return;
|
|
1781
1793
|
}
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
findUnknownRuleIds,
|
|
3
|
+
knownRuleIds,
|
|
4
|
+
ruleOptionsSpec
|
|
5
|
+
} from "./chunk-TFBLQUAC.js";
|
|
6
|
+
|
|
1
7
|
// src/index.ts
|
|
2
8
|
import { mkdirSync, writeFileSync as writeFileSync2 } from "fs";
|
|
3
9
|
import { dirname as dirname2, join as join7 } from "path";
|
|
4
10
|
import {
|
|
5
|
-
allRules as
|
|
11
|
+
allRules as allRules2,
|
|
6
12
|
runRules,
|
|
7
13
|
formatConsoleReport,
|
|
8
14
|
formatJsonReport,
|
|
@@ -17,7 +23,8 @@ import {
|
|
|
17
23
|
defineConfig,
|
|
18
24
|
selectRules,
|
|
19
25
|
applyRuleSeverities,
|
|
20
|
-
applyOverrides
|
|
26
|
+
applyOverrides,
|
|
27
|
+
settingSeverity
|
|
21
28
|
} from "@svelte-vitals/core";
|
|
22
29
|
|
|
23
30
|
// src/runtime/node.ts
|
|
@@ -391,6 +398,29 @@ function collectSvelteHeads(node, acc) {
|
|
|
391
398
|
if (key in node) collectSvelteHeads(childOf(node, key), acc);
|
|
392
399
|
}
|
|
393
400
|
}
|
|
401
|
+
var JS_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
402
|
+
"application/ecmascript",
|
|
403
|
+
"application/javascript",
|
|
404
|
+
"application/x-ecmascript",
|
|
405
|
+
"application/x-javascript",
|
|
406
|
+
"text/ecmascript",
|
|
407
|
+
"text/javascript",
|
|
408
|
+
"text/javascript1.0",
|
|
409
|
+
"text/javascript1.1",
|
|
410
|
+
"text/javascript1.2",
|
|
411
|
+
"text/javascript1.3",
|
|
412
|
+
"text/javascript1.4",
|
|
413
|
+
"text/javascript1.5",
|
|
414
|
+
"text/jscript",
|
|
415
|
+
"text/livescript",
|
|
416
|
+
"text/x-ecmascript",
|
|
417
|
+
"text/x-javascript"
|
|
418
|
+
]);
|
|
419
|
+
function isClassicScriptType(type) {
|
|
420
|
+
if (type === void 0) return true;
|
|
421
|
+
const normalized = type.trim().toLowerCase();
|
|
422
|
+
return normalized === "" || JS_MIME_TYPES.has(normalized);
|
|
423
|
+
}
|
|
394
424
|
function tagsFromHead(head) {
|
|
395
425
|
const tags = [];
|
|
396
426
|
const children = head.fragment.nodes;
|
|
@@ -450,7 +480,7 @@ function tagsFromHead(head) {
|
|
|
450
480
|
} else {
|
|
451
481
|
const src = attrText(attributes, "src");
|
|
452
482
|
if (src) {
|
|
453
|
-
const blocking = findAttr3(attributes, "defer") === void 0 && findAttr3(attributes, "async") === void 0
|
|
483
|
+
const blocking = isClassicScriptType(type) && findAttr3(attributes, "defer") === void 0 && findAttr3(attributes, "async") === void 0;
|
|
454
484
|
tags.push({ kind: "script", value: "static", href: src, ...blocking ? { blocking: true } : {} });
|
|
455
485
|
}
|
|
456
486
|
}
|
|
@@ -592,6 +622,7 @@ function resolveComponentPath(source, fromFileRel) {
|
|
|
592
622
|
}
|
|
593
623
|
async function resolveFileTags(rt, cwd, fileRel, parsed, config, depth, visited, cache = /* @__PURE__ */ new Map()) {
|
|
594
624
|
const tags = [...parsed.headTags];
|
|
625
|
+
const headings = [];
|
|
595
626
|
let broad = false;
|
|
596
627
|
for (const use of parsed.components) {
|
|
597
628
|
const info = parsed.imports.get(use.name);
|
|
@@ -615,10 +646,12 @@ async function resolveFileTags(rt, cwd, fileRel, parsed, config, depth, visited,
|
|
|
615
646
|
const child = await resolveFileTags(rt, cwd, childRel, childParsed, config, depth - 1, childVisited, cache);
|
|
616
647
|
tags.push(...child.tags);
|
|
617
648
|
broad = broad || child.broad;
|
|
649
|
+
for (const h of childParsed.headings) headings.push({ ...h, file: childRel });
|
|
650
|
+
headings.push(...child.headings);
|
|
618
651
|
}
|
|
619
652
|
}
|
|
620
653
|
}
|
|
621
|
-
return { tags, broad };
|
|
654
|
+
return { tags, broad, headings };
|
|
622
655
|
}
|
|
623
656
|
|
|
624
657
|
// src/providers/source/routes.ts
|
|
@@ -694,6 +727,7 @@ async function resolveRoute(rt, cwd, pageRel, config, layouts, cache) {
|
|
|
694
727
|
let broadInherited = false;
|
|
695
728
|
const images = [];
|
|
696
729
|
const headings = [];
|
|
730
|
+
const componentHeadings = [];
|
|
697
731
|
for (const { rel, isPage } of files) {
|
|
698
732
|
const parsed = await readAndParse(rt, cwd, rel, cache);
|
|
699
733
|
for (const img of parsed.images) {
|
|
@@ -710,6 +744,7 @@ async function resolveRoute(rt, cwd, pageRel, config, layouts, cache) {
|
|
|
710
744
|
if (isPage) broadOwn = true;
|
|
711
745
|
else broadInherited = true;
|
|
712
746
|
}
|
|
747
|
+
componentHeadings.push(...resolved.headings);
|
|
713
748
|
}
|
|
714
749
|
if (broadOwn || broadInherited) {
|
|
715
750
|
const presence = broadOwn ? "own" : "inherited";
|
|
@@ -722,7 +757,7 @@ async function resolveRoute(rt, cwd, pageRel, config, layouts, cache) {
|
|
|
722
757
|
return {
|
|
723
758
|
head: { route, source: "static", tags: [...composed.values()], file: pageRel },
|
|
724
759
|
images: { route, images },
|
|
725
|
-
headings: { route, headings }
|
|
760
|
+
headings: { route, headings, componentHeadings }
|
|
726
761
|
};
|
|
727
762
|
}
|
|
728
763
|
async function collectRoutes(rt, cwd, config = defaultConfig, cache = /* @__PURE__ */ new Map()) {
|
|
@@ -863,6 +898,7 @@ function isAutoDetectedGithub(explicit, env = process.env) {
|
|
|
863
898
|
|
|
864
899
|
// src/changed-files.ts
|
|
865
900
|
import { execFileSync } from "child_process";
|
|
901
|
+
import { defaultConfig as defaultConfig2, isPenalized } from "@svelte-vitals/core";
|
|
866
902
|
function git(args, cwd) {
|
|
867
903
|
return execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).split("\0");
|
|
868
904
|
}
|
|
@@ -881,8 +917,10 @@ function getChangedFiles(cwd, opts) {
|
|
|
881
917
|
return void 0;
|
|
882
918
|
}
|
|
883
919
|
}
|
|
884
|
-
function filterToChangedFiles(results, changed) {
|
|
885
|
-
return results.filter(
|
|
920
|
+
function filterToChangedFiles(results, changed, config = defaultConfig2) {
|
|
921
|
+
return results.filter(
|
|
922
|
+
(r) => r.location !== void 0 && changed.has(r.location) && (isPenalized(r.detection, config.treatDynamicAs) || r.route === void 0)
|
|
923
|
+
);
|
|
886
924
|
}
|
|
887
925
|
|
|
888
926
|
// src/baseline.ts
|
|
@@ -890,6 +928,7 @@ import { execFileSync as execFileSync2 } from "child_process";
|
|
|
890
928
|
import { mkdtempSync, rmSync } from "fs";
|
|
891
929
|
import { tmpdir } from "os";
|
|
892
930
|
import { join as join4 } from "path";
|
|
931
|
+
import { defaultConfig as defaultConfig3, isPenalized as isPenalized2 } from "@svelte-vitals/core";
|
|
893
932
|
function git2(args, cwd) {
|
|
894
933
|
return execFileSync2("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
895
934
|
}
|
|
@@ -932,15 +971,16 @@ function checkoutBaseline(cwd, ref) {
|
|
|
932
971
|
return void 0;
|
|
933
972
|
}
|
|
934
973
|
}
|
|
935
|
-
function filterToNewFindings(results, baselineResults) {
|
|
936
|
-
const
|
|
937
|
-
|
|
974
|
+
function filterToNewFindings(results, baselineResults, config = defaultConfig3) {
|
|
975
|
+
const penalized = (rs) => rs.filter((r) => isPenalized2(r.detection, config.treatDynamicAs));
|
|
976
|
+
const baselineKeys = new Set(penalized(baselineResults).map(findingKey));
|
|
977
|
+
return penalized(results).filter((r) => !baselineKeys.has(findingKey(r)));
|
|
938
978
|
}
|
|
939
979
|
|
|
940
980
|
// src/suppressions.ts
|
|
941
981
|
import { readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
942
982
|
import { join as join6 } from "path";
|
|
943
|
-
import { isPenalized } from "@svelte-vitals/core";
|
|
983
|
+
import { isPenalized as isPenalized3 } from "@svelte-vitals/core";
|
|
944
984
|
|
|
945
985
|
// src/config-file.ts
|
|
946
986
|
import { existsSync as existsSync2 } from "fs";
|
|
@@ -948,27 +988,11 @@ import { join as join5 } from "path";
|
|
|
948
988
|
import { pathToFileURL } from "url";
|
|
949
989
|
import {
|
|
950
990
|
CATEGORIES,
|
|
951
|
-
defaultConfig as
|
|
991
|
+
defaultConfig as defaultConfig4,
|
|
952
992
|
resolveRuleOptions,
|
|
953
993
|
shouldSkipRangeCheck,
|
|
954
994
|
validateRuleSetting
|
|
955
995
|
} from "@svelte-vitals/core";
|
|
956
|
-
|
|
957
|
-
// src/rules-config.ts
|
|
958
|
-
import { allRules } from "@svelte-vitals/core";
|
|
959
|
-
var KNOWN_IDS = new Set(allRules.map((r) => r.id));
|
|
960
|
-
var RULE_BY_ID = new Map(allRules.map((r) => [r.id, r]));
|
|
961
|
-
function findUnknownRuleIds(ids) {
|
|
962
|
-
return [...new Set(ids.filter((id) => !KNOWN_IDS.has(id)))];
|
|
963
|
-
}
|
|
964
|
-
function knownRuleIds() {
|
|
965
|
-
return [...KNOWN_IDS].sort();
|
|
966
|
-
}
|
|
967
|
-
function ruleOptionsSpec(id) {
|
|
968
|
-
return RULE_BY_ID.get(id)?.options;
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
// src/config-file.ts
|
|
972
996
|
var CONFIG_FILENAMES = ["svelte-vitals.config.mjs", "svelte-vitals.config.js", "svelte-vitals.config.ts"];
|
|
973
997
|
var TREAT_DYNAMIC_AS_VALUES = ["pass", "warn", "fail"];
|
|
974
998
|
var FAIL_ON_VALUES = ["critical", "warning", "info"];
|
|
@@ -1072,7 +1096,7 @@ function validateConfigFile(raw, path) {
|
|
|
1072
1096
|
}
|
|
1073
1097
|
for (const [key, setting] of Object.entries(entry.rules)) {
|
|
1074
1098
|
const isCategory = CATEGORIES.includes(key);
|
|
1075
|
-
const baseline = isCategory ? void 0 : resolveRuleOptions(key, ruleOptionsSpec(key), { ...
|
|
1099
|
+
const baseline = isCategory ? void 0 : resolveRuleOptions(key, ruleOptionsSpec(key), { ...defaultConfig4, rules: config.rules ?? {} });
|
|
1076
1100
|
const skipRangeCheck = shouldSkipRangeCheck(rawOverrides, i, key, setting);
|
|
1077
1101
|
validateSetting(path, `overrides[${i}].rules`, key, setting, !isCategory, baseline, skipRangeCheck);
|
|
1078
1102
|
}
|
|
@@ -1188,7 +1212,7 @@ function writeSuppressions(cwd, results, config) {
|
|
|
1188
1212
|
const seen = /* @__PURE__ */ new Set();
|
|
1189
1213
|
const entries = [];
|
|
1190
1214
|
for (const r of results) {
|
|
1191
|
-
if (!
|
|
1215
|
+
if (!isPenalized3(r.detection, config.treatDynamicAs)) continue;
|
|
1192
1216
|
const entry = toEntry(r);
|
|
1193
1217
|
const key = findingKey(entry);
|
|
1194
1218
|
if (seen.has(key)) continue;
|
|
@@ -1200,20 +1224,23 @@ function writeSuppressions(cwd, results, config) {
|
|
|
1200
1224
|
writeFileSync(path, JSON.stringify({ version: 1, suppressions: entries }, null, 2) + "\n");
|
|
1201
1225
|
return entries.length;
|
|
1202
1226
|
}
|
|
1203
|
-
function applySuppressions(results, entries, config) {
|
|
1227
|
+
function applySuppressions(results, entries, config, allResults) {
|
|
1204
1228
|
const keys = new Set(entries.map((e) => findingKey(e)));
|
|
1205
|
-
const usedKeys = /* @__PURE__ */ new Set();
|
|
1206
1229
|
const kept = [];
|
|
1207
1230
|
let suppressed = 0;
|
|
1208
1231
|
for (const r of results) {
|
|
1209
1232
|
const key = findingKey(r);
|
|
1210
|
-
if (keys.has(key) &&
|
|
1233
|
+
if (keys.has(key) && isPenalized3(r.detection, config.treatDynamicAs)) {
|
|
1211
1234
|
suppressed++;
|
|
1212
|
-
usedKeys.add(key);
|
|
1213
1235
|
continue;
|
|
1214
1236
|
}
|
|
1215
1237
|
kept.push(r);
|
|
1216
1238
|
}
|
|
1239
|
+
const usedKeys = /* @__PURE__ */ new Set();
|
|
1240
|
+
for (const r of allResults ?? results) {
|
|
1241
|
+
const key = findingKey(r);
|
|
1242
|
+
if (keys.has(key) && isPenalized3(r.detection, config.treatDynamicAs)) usedKeys.add(key);
|
|
1243
|
+
}
|
|
1217
1244
|
const stale = [...keys].filter((k) => !usedKeys.has(k)).length;
|
|
1218
1245
|
return { results: kept, suppressed, stale };
|
|
1219
1246
|
}
|
|
@@ -1463,13 +1490,13 @@ function scoreAnimationEnabled(opts) {
|
|
|
1463
1490
|
}
|
|
1464
1491
|
|
|
1465
1492
|
// src/rule-selection.ts
|
|
1466
|
-
import { allRules
|
|
1493
|
+
import { allRules } from "@svelte-vitals/core";
|
|
1467
1494
|
function resolveRuleSelection(input) {
|
|
1468
1495
|
const out = { ...input.rules ?? input.fileRules };
|
|
1469
1496
|
const allow = input.allowRules ?? [];
|
|
1470
1497
|
if (allow.length > 0) {
|
|
1471
1498
|
const allowed = new Set(allow);
|
|
1472
|
-
for (const rule of
|
|
1499
|
+
for (const rule of allRules) if (!allowed.has(rule.id)) out[rule.id] = "off";
|
|
1473
1500
|
for (const id of allowed) {
|
|
1474
1501
|
const setting = out[id];
|
|
1475
1502
|
if (setting === void 0) continue;
|
|
@@ -1491,6 +1518,28 @@ import { defineConfig as defineConfig2 } from "@svelte-vitals/core";
|
|
|
1491
1518
|
function spinnerEnabled(opts) {
|
|
1492
1519
|
return opts.reporter === "console" && opts.stderrIsTTY && !isAutoDetectedAgent(opts.rawReporter, opts.env) && colorEnabled({ reporter: opts.reporter, isTTY: opts.stderrIsTTY, env: opts.env, noColorFlag: opts.noColorFlag });
|
|
1493
1520
|
}
|
|
1521
|
+
function formatGlob(glob2) {
|
|
1522
|
+
return Array.isArray(glob2) ? `[${glob2.map((g) => `'${g}'`).join(", ")}]` : `'${glob2}'`;
|
|
1523
|
+
}
|
|
1524
|
+
function overridesOffWarnings(allowRules, overrides) {
|
|
1525
|
+
if (!allowRules || allowRules.length === 0 || !overrides || overrides.length === 0) return [];
|
|
1526
|
+
const warnings = [];
|
|
1527
|
+
for (const ruleId of allowRules) {
|
|
1528
|
+
const category = ruleId.split("/")[0] ?? ruleId;
|
|
1529
|
+
for (const entry of overrides) {
|
|
1530
|
+
const severity = settingSeverity(entry.rules[ruleId]) ?? settingSeverity(entry.rules[category]);
|
|
1531
|
+
if (severity !== "off") continue;
|
|
1532
|
+
const scope = [
|
|
1533
|
+
entry.route !== void 0 ? `route: ${formatGlob(entry.route)}` : void 0,
|
|
1534
|
+
entry.files !== void 0 ? `files: ${formatGlob(entry.files)}` : void 0
|
|
1535
|
+
].filter((s) => s !== void 0).join(", ");
|
|
1536
|
+
warnings.push(
|
|
1537
|
+
`--rules '${ruleId}' is scoped 'off' by overrides entry { ${scope} } \u2014 findings there will not be reported. --rules overrides a global 'off' but not a scoped one.`
|
|
1538
|
+
);
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
return warnings;
|
|
1542
|
+
}
|
|
1494
1543
|
async function analyzeProject(opts = {}) {
|
|
1495
1544
|
const cwd = opts.cwd ?? process.cwd();
|
|
1496
1545
|
const rt = createNodeRuntime();
|
|
@@ -1511,12 +1560,16 @@ async function analyzeProject(opts = {}) {
|
|
|
1511
1560
|
...file?.overrides !== void 0 ? { overrides: file.overrides } : {}
|
|
1512
1561
|
});
|
|
1513
1562
|
await detectProject(rt, cwd);
|
|
1514
|
-
const warnings = [
|
|
1563
|
+
const warnings = [
|
|
1564
|
+
...loaded?.warnings ?? [],
|
|
1565
|
+
...await checkVersionFloor(rt, cwd),
|
|
1566
|
+
...overridesOffWarnings(opts.allowRules, config.overrides)
|
|
1567
|
+
];
|
|
1515
1568
|
const { heads, images, headings, project, components, kitModules, sourceFiles } = await collectAll(rt, cwd, config, {
|
|
1516
1569
|
route: opts.route,
|
|
1517
1570
|
parseCache: opts.parseCache
|
|
1518
1571
|
});
|
|
1519
|
-
const selected = selectRules(
|
|
1572
|
+
const selected = selectRules(allRules2, config);
|
|
1520
1573
|
const rules = opts.categories ? selected.filter((r) => opts.categories.includes(r.category)) : selected;
|
|
1521
1574
|
const { results: rawResults, examined } = await runRules(rules, {
|
|
1522
1575
|
heads,
|
|
@@ -1549,7 +1602,7 @@ async function applyScope(results, opts) {
|
|
|
1549
1602
|
"svelte-vitals: could not determine changed files (not a git repo, git unavailable, or bad ref); analyzing all."
|
|
1550
1603
|
);
|
|
1551
1604
|
} else {
|
|
1552
|
-
scoped = filterToChangedFiles(scoped, changed);
|
|
1605
|
+
scoped = filterToChangedFiles(scoped, changed, opts.config);
|
|
1553
1606
|
}
|
|
1554
1607
|
}
|
|
1555
1608
|
if (opts.baseline !== void 0) {
|
|
@@ -1561,7 +1614,7 @@ async function applyScope(results, opts) {
|
|
|
1561
1614
|
} else {
|
|
1562
1615
|
try {
|
|
1563
1616
|
const base = await analyzeProject({ ...opts.analyzeOpts, cwd: checkout.analyzeCwd });
|
|
1564
|
-
scoped = filterToNewFindings(scoped, base.results);
|
|
1617
|
+
scoped = filterToNewFindings(scoped, base.results, opts.config);
|
|
1565
1618
|
} catch {
|
|
1566
1619
|
errorLog(`svelte-vitals: baseline analysis of '${opts.baseline}' failed; reporting all findings.`);
|
|
1567
1620
|
} finally {
|
|
@@ -1572,11 +1625,16 @@ async function applyScope(results, opts) {
|
|
|
1572
1625
|
if (!opts.noSuppressions && opts.config) {
|
|
1573
1626
|
const entries = loadSuppressions(opts.cwd);
|
|
1574
1627
|
if (entries !== void 0) {
|
|
1575
|
-
const {
|
|
1628
|
+
const {
|
|
1629
|
+
results: afterSuppressions,
|
|
1630
|
+
suppressed,
|
|
1631
|
+
stale
|
|
1632
|
+
} = applySuppressions(scoped, entries, opts.config, results);
|
|
1576
1633
|
scoped = afterSuppressions;
|
|
1577
|
-
|
|
1634
|
+
const routeScoped = opts.analyzeOpts?.route !== void 0;
|
|
1635
|
+
if (suppressed > 0 || stale > 0 && !routeScoped) {
|
|
1578
1636
|
errorLog(
|
|
1579
|
-
`svelte-vitals: ${suppressed} finding(s) suppressed by ${SUPPRESSIONS_FILE}` + (stale > 0 ? ` (${stale} stale entr${stale === 1 ? "y" : "ies"} \u2014 re-run --update-suppressions to prune)` : "") + "."
|
|
1637
|
+
`svelte-vitals: ${suppressed} finding(s) suppressed by ${SUPPRESSIONS_FILE}` + (stale > 0 && !routeScoped ? ` (${stale} stale entr${stale === 1 ? "y" : "ies"} \u2014 re-run --update-suppressions to prune)` : "") + "."
|
|
1580
1638
|
);
|
|
1581
1639
|
}
|
|
1582
1640
|
}
|
|
@@ -1675,6 +1733,12 @@ async function run(opts = {}) {
|
|
|
1675
1733
|
try {
|
|
1676
1734
|
const { config, version } = analysis;
|
|
1677
1735
|
if (opts.updateSuppressions) {
|
|
1736
|
+
if (opts.route !== void 0) {
|
|
1737
|
+
errorLog(
|
|
1738
|
+
`svelte-vitals: --update-suppressions cannot be combined with --route ('${opts.route}') \u2014 it would prune every suppression entry outside that route from ${SUPPRESSIONS_FILE}. Re-run --update-suppressions without --route.`
|
|
1739
|
+
);
|
|
1740
|
+
return 2;
|
|
1741
|
+
}
|
|
1678
1742
|
const count = writeSuppressions(cwd, analysis.results, config);
|
|
1679
1743
|
errorLog(`svelte-vitals: wrote ${count} suppression(s) to ${SUPPRESSIONS_FILE}.`);
|
|
1680
1744
|
return 0;
|
|
@@ -1782,9 +1846,6 @@ export {
|
|
|
1782
1846
|
readPackageVersion,
|
|
1783
1847
|
readCoreVersion,
|
|
1784
1848
|
isReporterName,
|
|
1785
|
-
findUnknownRuleIds,
|
|
1786
|
-
knownRuleIds,
|
|
1787
|
-
ruleOptionsSpec,
|
|
1788
1849
|
CONFIG_FILENAMES,
|
|
1789
1850
|
loadConfigFile,
|
|
1790
1851
|
spinnerEnabled,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// src/rules-config.ts
|
|
2
|
+
import { allRules } from "@svelte-vitals/core";
|
|
3
|
+
var KNOWN_IDS = new Set(allRules.map((r) => r.id));
|
|
4
|
+
var RULE_BY_ID = new Map(allRules.map((r) => [r.id, r]));
|
|
5
|
+
function findUnknownRuleIds(ids) {
|
|
6
|
+
return [...new Set(ids.filter((id) => !KNOWN_IDS.has(id)))];
|
|
7
|
+
}
|
|
8
|
+
function knownRuleIds() {
|
|
9
|
+
return [...KNOWN_IDS].sort();
|
|
10
|
+
}
|
|
11
|
+
function ruleOptionsSpec(id) {
|
|
12
|
+
return RULE_BY_ID.get(id)?.options;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
findUnknownRuleIds,
|
|
17
|
+
knownRuleIds,
|
|
18
|
+
ruleOptionsSpec
|
|
19
|
+
};
|
|
@@ -2,6 +2,12 @@ import {
|
|
|
2
2
|
consoleIO,
|
|
3
3
|
parseCliArgs
|
|
4
4
|
} from "./chunk-5Q2PT47V.js";
|
|
5
|
+
import {
|
|
6
|
+
knownRuleIds
|
|
7
|
+
} from "./chunk-TFBLQUAC.js";
|
|
8
|
+
|
|
9
|
+
// src/docs/cli.ts
|
|
10
|
+
import { docsUrlFor } from "@svelte-vitals/core";
|
|
5
11
|
|
|
6
12
|
// src/docs/generated.ts
|
|
7
13
|
var EMBEDDED_DOCS = [
|
|
@@ -100,9 +106,16 @@ function runDocsCli(args, io = consoleIO) {
|
|
|
100
106
|
io.errorLog(`svelte-vitals: known topics: ${knownTopicNames()}.`);
|
|
101
107
|
return 2;
|
|
102
108
|
}
|
|
103
|
-
const
|
|
109
|
+
const name = rest[0];
|
|
110
|
+
const doc = EMBEDDED_DOCS.find((d) => d.name === name);
|
|
104
111
|
if (!doc) {
|
|
105
|
-
|
|
112
|
+
const ruleId = name.startsWith("rules/") ? name.slice("rules/".length) : name;
|
|
113
|
+
if (knownRuleIds().includes(ruleId)) {
|
|
114
|
+
io.errorLog(`svelte-vitals: '${ruleId}' is a rule, not a docs topic.`);
|
|
115
|
+
io.errorLog(`svelte-vitals: rule detail: \`svelte-vitals explain ${ruleId}\`; web: ${docsUrlFor(ruleId)}`);
|
|
116
|
+
return 2;
|
|
117
|
+
}
|
|
118
|
+
io.errorLog(`svelte-vitals: unknown docs topic '${name}'.`);
|
|
106
119
|
io.errorLog(`svelte-vitals: known topics: ${knownTopicNames()}.`);
|
|
107
120
|
return 2;
|
|
108
121
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -268,6 +268,12 @@ interface ApplyScopeOptions {
|
|
|
268
268
|
/** Disable applying svelte-vitals-suppressions.json for this run. */
|
|
269
269
|
noSuppressions?: boolean;
|
|
270
270
|
errorLog?: (line: string) => void;
|
|
271
|
+
/**
|
|
272
|
+
* Also doubles as this call's route-scoping signal: `analyzeOpts.route !== undefined`
|
|
273
|
+
* tells the suppressions block that `results` (this function's first argument) was
|
|
274
|
+
* itself collected route-scoped, not just project-wide-then-narrowed — see the comment
|
|
275
|
+
* at that check.
|
|
276
|
+
*/
|
|
271
277
|
analyzeOpts?: AnalyzeOptions;
|
|
272
278
|
}
|
|
273
279
|
/**
|
package/dist/index.js
CHANGED
|
@@ -3,14 +3,16 @@ import {
|
|
|
3
3
|
analyzeProject,
|
|
4
4
|
applyScope,
|
|
5
5
|
defineConfig,
|
|
6
|
-
findUnknownRuleIds,
|
|
7
|
-
knownRuleIds,
|
|
8
6
|
loadConfigFile,
|
|
9
7
|
routeMatcher,
|
|
10
|
-
ruleOptionsSpec,
|
|
11
8
|
run,
|
|
12
9
|
spinnerEnabled
|
|
13
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-EWDGS52A.js";
|
|
11
|
+
import {
|
|
12
|
+
findUnknownRuleIds,
|
|
13
|
+
knownRuleIds,
|
|
14
|
+
ruleOptionsSpec
|
|
15
|
+
} from "./chunk-TFBLQUAC.js";
|
|
14
16
|
export {
|
|
15
17
|
ProjectError,
|
|
16
18
|
analyzeProject,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelte-vitals",
|
|
3
|
-
"version": "0.44.
|
|
4
|
-
"description": "A SvelteKit SEO
|
|
3
|
+
"version": "0.44.2",
|
|
4
|
+
"description": "A deterministic SvelteKit code-health scanner (SEO, performance, correctness, security, architecture) — not a runtime Web Vitals reporter.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Kazuma Oe (https://github.com/oekazuma)",
|
|
@@ -11,7 +11,11 @@
|
|
|
11
11
|
"seo",
|
|
12
12
|
"meta-tags",
|
|
13
13
|
"cli",
|
|
14
|
-
"svelte-vitals"
|
|
14
|
+
"svelte-vitals",
|
|
15
|
+
"performance",
|
|
16
|
+
"security",
|
|
17
|
+
"code-quality",
|
|
18
|
+
"static-analysis"
|
|
15
19
|
],
|
|
16
20
|
"repository": {
|
|
17
21
|
"type": "git",
|
|
@@ -44,7 +48,7 @@
|
|
|
44
48
|
"magicast": "^0.5.4",
|
|
45
49
|
"svelte": "^5.56.8",
|
|
46
50
|
"tinyglobby": "^0.2.17",
|
|
47
|
-
"@svelte-vitals/core": "0.
|
|
51
|
+
"@svelte-vitals/core": "0.40.0"
|
|
48
52
|
},
|
|
49
53
|
"devDependencies": {
|
|
50
54
|
"@types/estree": "^1.0.9",
|