svelte-vitals 0.44.0 → 0.44.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +13 -3
- package/dist/{chunk-WD5TSADH.js → chunk-4MC7STFI.js} +66 -20
- package/dist/index.d.ts +6 -0
- package/dist/index.js +1 -1
- package/package.json +8 -4
package/dist/bin.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
readPackageVersion,
|
|
11
11
|
readPkg,
|
|
12
12
|
run
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-4MC7STFI.js";
|
|
14
14
|
import {
|
|
15
15
|
consoleIO,
|
|
16
16
|
parseCliArgs,
|
|
@@ -132,6 +132,7 @@ function resolveArgs(argv) {
|
|
|
132
132
|
const errors = [];
|
|
133
133
|
for (const flag of VALUE_FLAGS) {
|
|
134
134
|
const v = argv[flag];
|
|
135
|
+
if (flag === "out-file" && v === "-") continue;
|
|
135
136
|
if (v !== void 0 && (typeof v !== "string" || v.trim() === "" || v.startsWith("-"))) {
|
|
136
137
|
errors.push(`svelte-vitals: --${flag} requires a value.`);
|
|
137
138
|
}
|
|
@@ -193,6 +194,15 @@ function resolveArgs(argv) {
|
|
|
193
194
|
const failOn = failOnValid ? failOnRaw : void 0;
|
|
194
195
|
const weights = parseWeights(argv.weights, errors);
|
|
195
196
|
const categories = parseCategories(argv.category, errors);
|
|
197
|
+
if (categories !== void 0 && categories.length > 0 && allow.length > 0) {
|
|
198
|
+
const excluded = allow.filter((id) => !unknown.includes(id)).filter((id) => !categories.includes(id.split("/")[0]));
|
|
199
|
+
if (excluded.length > 0) {
|
|
200
|
+
errors.push(
|
|
201
|
+
`svelte-vitals: --rules id(s) excluded by --category ${categories.join(", ")}: ${excluded.join(", ")}`
|
|
202
|
+
);
|
|
203
|
+
errors.push("Add the rule's category to --category, or drop the rule from --rules.");
|
|
204
|
+
}
|
|
205
|
+
}
|
|
196
206
|
const score = Boolean(argv.score);
|
|
197
207
|
if (score && typeof argv.reporter === "string") {
|
|
198
208
|
warnings.push("svelte-vitals: --score overrides --reporter; reporter output suppressed.");
|
|
@@ -395,7 +405,7 @@ ${lines}`;
|
|
|
395
405
|
}).join("\n\n");
|
|
396
406
|
}
|
|
397
407
|
function sharedBody(version) {
|
|
398
|
-
return `<!-- Generated by \`svelte-vitals install\` (svelte-vitals ${version}). Re-run
|
|
408
|
+
return `<!-- Generated by \`svelte-vitals install\` (svelte-vitals ${version}). Re-run \`svelte-vitals install --refresh\` to regenerate. -->
|
|
399
409
|
|
|
400
410
|
# svelte-vitals
|
|
401
411
|
|
|
@@ -442,7 +452,7 @@ name: improve-svelte
|
|
|
442
452
|
description: Survey a whole SvelteKit codebase as a senior Svelte/SvelteKit engineer, using svelte-vitals' scan as evidence, then produce a prioritized audit and self-contained implementation plans for other agents (or cheaper models) to execute. Read-only on source code \u2014 it plans improvements, it does not apply them. Use when the user asks to "improve this SvelteKit app", "audit this codebase", "make this app more SEO/performance/security solid", or wants a roadmap of fixes rather than a review of a single diff. For routine regression checks while writing code, use the \`svelte-vitals\` skill instead.
|
|
443
453
|
---
|
|
444
454
|
|
|
445
|
-
<!-- Generated by \`svelte-vitals install\` (svelte-vitals ${version}). Re-run
|
|
455
|
+
<!-- Generated by \`svelte-vitals install\` (svelte-vitals ${version}). Re-run \`svelte-vitals install --refresh\` to regenerate. -->
|
|
446
456
|
|
|
447
457
|
# improve-svelte
|
|
448
458
|
|
|
@@ -17,7 +17,8 @@ import {
|
|
|
17
17
|
defineConfig,
|
|
18
18
|
selectRules,
|
|
19
19
|
applyRuleSeverities,
|
|
20
|
-
applyOverrides
|
|
20
|
+
applyOverrides,
|
|
21
|
+
settingSeverity
|
|
21
22
|
} from "@svelte-vitals/core";
|
|
22
23
|
|
|
23
24
|
// src/runtime/node.ts
|
|
@@ -863,6 +864,7 @@ function isAutoDetectedGithub(explicit, env = process.env) {
|
|
|
863
864
|
|
|
864
865
|
// src/changed-files.ts
|
|
865
866
|
import { execFileSync } from "child_process";
|
|
867
|
+
import { defaultConfig as defaultConfig2, isPenalized } from "@svelte-vitals/core";
|
|
866
868
|
function git(args, cwd) {
|
|
867
869
|
return execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).split("\0");
|
|
868
870
|
}
|
|
@@ -881,8 +883,10 @@ function getChangedFiles(cwd, opts) {
|
|
|
881
883
|
return void 0;
|
|
882
884
|
}
|
|
883
885
|
}
|
|
884
|
-
function filterToChangedFiles(results, changed) {
|
|
885
|
-
return results.filter(
|
|
886
|
+
function filterToChangedFiles(results, changed, config = defaultConfig2) {
|
|
887
|
+
return results.filter(
|
|
888
|
+
(r) => r.location !== void 0 && changed.has(r.location) && (isPenalized(r.detection, config.treatDynamicAs) || r.route === void 0)
|
|
889
|
+
);
|
|
886
890
|
}
|
|
887
891
|
|
|
888
892
|
// src/baseline.ts
|
|
@@ -890,6 +894,7 @@ import { execFileSync as execFileSync2 } from "child_process";
|
|
|
890
894
|
import { mkdtempSync, rmSync } from "fs";
|
|
891
895
|
import { tmpdir } from "os";
|
|
892
896
|
import { join as join4 } from "path";
|
|
897
|
+
import { defaultConfig as defaultConfig3, isPenalized as isPenalized2 } from "@svelte-vitals/core";
|
|
893
898
|
function git2(args, cwd) {
|
|
894
899
|
return execFileSync2("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
895
900
|
}
|
|
@@ -932,15 +937,16 @@ function checkoutBaseline(cwd, ref) {
|
|
|
932
937
|
return void 0;
|
|
933
938
|
}
|
|
934
939
|
}
|
|
935
|
-
function filterToNewFindings(results, baselineResults) {
|
|
936
|
-
const
|
|
937
|
-
|
|
940
|
+
function filterToNewFindings(results, baselineResults, config = defaultConfig3) {
|
|
941
|
+
const penalized = (rs) => rs.filter((r) => isPenalized2(r.detection, config.treatDynamicAs));
|
|
942
|
+
const baselineKeys = new Set(penalized(baselineResults).map(findingKey));
|
|
943
|
+
return penalized(results).filter((r) => !baselineKeys.has(findingKey(r)));
|
|
938
944
|
}
|
|
939
945
|
|
|
940
946
|
// src/suppressions.ts
|
|
941
947
|
import { readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
942
948
|
import { join as join6 } from "path";
|
|
943
|
-
import { isPenalized } from "@svelte-vitals/core";
|
|
949
|
+
import { isPenalized as isPenalized3 } from "@svelte-vitals/core";
|
|
944
950
|
|
|
945
951
|
// src/config-file.ts
|
|
946
952
|
import { existsSync as existsSync2 } from "fs";
|
|
@@ -948,7 +954,7 @@ import { join as join5 } from "path";
|
|
|
948
954
|
import { pathToFileURL } from "url";
|
|
949
955
|
import {
|
|
950
956
|
CATEGORIES,
|
|
951
|
-
defaultConfig as
|
|
957
|
+
defaultConfig as defaultConfig4,
|
|
952
958
|
resolveRuleOptions,
|
|
953
959
|
shouldSkipRangeCheck,
|
|
954
960
|
validateRuleSetting
|
|
@@ -1072,7 +1078,7 @@ function validateConfigFile(raw, path) {
|
|
|
1072
1078
|
}
|
|
1073
1079
|
for (const [key, setting] of Object.entries(entry.rules)) {
|
|
1074
1080
|
const isCategory = CATEGORIES.includes(key);
|
|
1075
|
-
const baseline = isCategory ? void 0 : resolveRuleOptions(key, ruleOptionsSpec(key), { ...
|
|
1081
|
+
const baseline = isCategory ? void 0 : resolveRuleOptions(key, ruleOptionsSpec(key), { ...defaultConfig4, rules: config.rules ?? {} });
|
|
1076
1082
|
const skipRangeCheck = shouldSkipRangeCheck(rawOverrides, i, key, setting);
|
|
1077
1083
|
validateSetting(path, `overrides[${i}].rules`, key, setting, !isCategory, baseline, skipRangeCheck);
|
|
1078
1084
|
}
|
|
@@ -1188,7 +1194,7 @@ function writeSuppressions(cwd, results, config) {
|
|
|
1188
1194
|
const seen = /* @__PURE__ */ new Set();
|
|
1189
1195
|
const entries = [];
|
|
1190
1196
|
for (const r of results) {
|
|
1191
|
-
if (!
|
|
1197
|
+
if (!isPenalized3(r.detection, config.treatDynamicAs)) continue;
|
|
1192
1198
|
const entry = toEntry(r);
|
|
1193
1199
|
const key = findingKey(entry);
|
|
1194
1200
|
if (seen.has(key)) continue;
|
|
@@ -1200,20 +1206,23 @@ function writeSuppressions(cwd, results, config) {
|
|
|
1200
1206
|
writeFileSync(path, JSON.stringify({ version: 1, suppressions: entries }, null, 2) + "\n");
|
|
1201
1207
|
return entries.length;
|
|
1202
1208
|
}
|
|
1203
|
-
function applySuppressions(results, entries, config) {
|
|
1209
|
+
function applySuppressions(results, entries, config, allResults) {
|
|
1204
1210
|
const keys = new Set(entries.map((e) => findingKey(e)));
|
|
1205
|
-
const usedKeys = /* @__PURE__ */ new Set();
|
|
1206
1211
|
const kept = [];
|
|
1207
1212
|
let suppressed = 0;
|
|
1208
1213
|
for (const r of results) {
|
|
1209
1214
|
const key = findingKey(r);
|
|
1210
|
-
if (keys.has(key) &&
|
|
1215
|
+
if (keys.has(key) && isPenalized3(r.detection, config.treatDynamicAs)) {
|
|
1211
1216
|
suppressed++;
|
|
1212
|
-
usedKeys.add(key);
|
|
1213
1217
|
continue;
|
|
1214
1218
|
}
|
|
1215
1219
|
kept.push(r);
|
|
1216
1220
|
}
|
|
1221
|
+
const usedKeys = /* @__PURE__ */ new Set();
|
|
1222
|
+
for (const r of allResults ?? results) {
|
|
1223
|
+
const key = findingKey(r);
|
|
1224
|
+
if (keys.has(key) && isPenalized3(r.detection, config.treatDynamicAs)) usedKeys.add(key);
|
|
1225
|
+
}
|
|
1217
1226
|
const stale = [...keys].filter((k) => !usedKeys.has(k)).length;
|
|
1218
1227
|
return { results: kept, suppressed, stale };
|
|
1219
1228
|
}
|
|
@@ -1491,6 +1500,28 @@ import { defineConfig as defineConfig2 } from "@svelte-vitals/core";
|
|
|
1491
1500
|
function spinnerEnabled(opts) {
|
|
1492
1501
|
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
1502
|
}
|
|
1503
|
+
function formatGlob(glob2) {
|
|
1504
|
+
return Array.isArray(glob2) ? `[${glob2.map((g) => `'${g}'`).join(", ")}]` : `'${glob2}'`;
|
|
1505
|
+
}
|
|
1506
|
+
function overridesOffWarnings(allowRules, overrides) {
|
|
1507
|
+
if (!allowRules || allowRules.length === 0 || !overrides || overrides.length === 0) return [];
|
|
1508
|
+
const warnings = [];
|
|
1509
|
+
for (const ruleId of allowRules) {
|
|
1510
|
+
const category = ruleId.split("/")[0] ?? ruleId;
|
|
1511
|
+
for (const entry of overrides) {
|
|
1512
|
+
const severity = settingSeverity(entry.rules[ruleId]) ?? settingSeverity(entry.rules[category]);
|
|
1513
|
+
if (severity !== "off") continue;
|
|
1514
|
+
const scope = [
|
|
1515
|
+
entry.route !== void 0 ? `route: ${formatGlob(entry.route)}` : void 0,
|
|
1516
|
+
entry.files !== void 0 ? `files: ${formatGlob(entry.files)}` : void 0
|
|
1517
|
+
].filter((s) => s !== void 0).join(", ");
|
|
1518
|
+
warnings.push(
|
|
1519
|
+
`--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.`
|
|
1520
|
+
);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
return warnings;
|
|
1524
|
+
}
|
|
1494
1525
|
async function analyzeProject(opts = {}) {
|
|
1495
1526
|
const cwd = opts.cwd ?? process.cwd();
|
|
1496
1527
|
const rt = createNodeRuntime();
|
|
@@ -1511,7 +1542,11 @@ async function analyzeProject(opts = {}) {
|
|
|
1511
1542
|
...file?.overrides !== void 0 ? { overrides: file.overrides } : {}
|
|
1512
1543
|
});
|
|
1513
1544
|
await detectProject(rt, cwd);
|
|
1514
|
-
const warnings = [
|
|
1545
|
+
const warnings = [
|
|
1546
|
+
...loaded?.warnings ?? [],
|
|
1547
|
+
...await checkVersionFloor(rt, cwd),
|
|
1548
|
+
...overridesOffWarnings(opts.allowRules, config.overrides)
|
|
1549
|
+
];
|
|
1515
1550
|
const { heads, images, headings, project, components, kitModules, sourceFiles } = await collectAll(rt, cwd, config, {
|
|
1516
1551
|
route: opts.route,
|
|
1517
1552
|
parseCache: opts.parseCache
|
|
@@ -1549,7 +1584,7 @@ async function applyScope(results, opts) {
|
|
|
1549
1584
|
"svelte-vitals: could not determine changed files (not a git repo, git unavailable, or bad ref); analyzing all."
|
|
1550
1585
|
);
|
|
1551
1586
|
} else {
|
|
1552
|
-
scoped = filterToChangedFiles(scoped, changed);
|
|
1587
|
+
scoped = filterToChangedFiles(scoped, changed, opts.config);
|
|
1553
1588
|
}
|
|
1554
1589
|
}
|
|
1555
1590
|
if (opts.baseline !== void 0) {
|
|
@@ -1561,7 +1596,7 @@ async function applyScope(results, opts) {
|
|
|
1561
1596
|
} else {
|
|
1562
1597
|
try {
|
|
1563
1598
|
const base = await analyzeProject({ ...opts.analyzeOpts, cwd: checkout.analyzeCwd });
|
|
1564
|
-
scoped = filterToNewFindings(scoped, base.results);
|
|
1599
|
+
scoped = filterToNewFindings(scoped, base.results, opts.config);
|
|
1565
1600
|
} catch {
|
|
1566
1601
|
errorLog(`svelte-vitals: baseline analysis of '${opts.baseline}' failed; reporting all findings.`);
|
|
1567
1602
|
} finally {
|
|
@@ -1572,11 +1607,16 @@ async function applyScope(results, opts) {
|
|
|
1572
1607
|
if (!opts.noSuppressions && opts.config) {
|
|
1573
1608
|
const entries = loadSuppressions(opts.cwd);
|
|
1574
1609
|
if (entries !== void 0) {
|
|
1575
|
-
const {
|
|
1610
|
+
const {
|
|
1611
|
+
results: afterSuppressions,
|
|
1612
|
+
suppressed,
|
|
1613
|
+
stale
|
|
1614
|
+
} = applySuppressions(scoped, entries, opts.config, results);
|
|
1576
1615
|
scoped = afterSuppressions;
|
|
1577
|
-
|
|
1616
|
+
const routeScoped = opts.analyzeOpts?.route !== void 0;
|
|
1617
|
+
if (suppressed > 0 || stale > 0 && !routeScoped) {
|
|
1578
1618
|
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)` : "") + "."
|
|
1619
|
+
`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
1620
|
);
|
|
1581
1621
|
}
|
|
1582
1622
|
}
|
|
@@ -1675,6 +1715,12 @@ async function run(opts = {}) {
|
|
|
1675
1715
|
try {
|
|
1676
1716
|
const { config, version } = analysis;
|
|
1677
1717
|
if (opts.updateSuppressions) {
|
|
1718
|
+
if (opts.route !== void 0) {
|
|
1719
|
+
errorLog(
|
|
1720
|
+
`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.`
|
|
1721
|
+
);
|
|
1722
|
+
return 2;
|
|
1723
|
+
}
|
|
1678
1724
|
const count = writeSuppressions(cwd, analysis.results, config);
|
|
1679
1725
|
errorLog(`svelte-vitals: wrote ${count} suppression(s) to ${SUPPRESSIONS_FILE}.`);
|
|
1680
1726
|
return 0;
|
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
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.1",
|
|
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.39.0"
|
|
48
52
|
},
|
|
49
53
|
"devDependencies": {
|
|
50
54
|
"@types/estree": "^1.0.9",
|