@svelte-vitals/core 0.5.0 → 0.6.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/index.d.ts +65 -17
- package/dist/index.js +73 -31
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -151,12 +151,18 @@ interface Rule {
|
|
|
151
151
|
severity: Severity;
|
|
152
152
|
/** 'route' = evaluated per route, 'project' = site-wide (design §10, §12). */
|
|
153
153
|
scope: Scope;
|
|
154
|
+
/** Why this rule matters — one or two sentences, surfaced by explain_rule (issue #24). */
|
|
155
|
+
rationale: string;
|
|
156
|
+
/** Canonical remediation template, shared by findings and explain_rule (issue #24). */
|
|
157
|
+
fix?: Fix;
|
|
154
158
|
/**
|
|
155
159
|
* Evaluate the resolved heads. A single rule may return one Result per route,
|
|
156
160
|
* so it always returns an array. Project-scoped rules return a single element.
|
|
157
161
|
*/
|
|
158
162
|
check(ctx: RuleContext): Promise<Result[]>;
|
|
159
163
|
}
|
|
164
|
+
/** Documentation URL for a rule id. Single source so no per-rule URL can drift (issue #24). */
|
|
165
|
+
declare function docsUrlFor(id: string): string;
|
|
160
166
|
/**
|
|
161
167
|
* Whether a detection should be penalized by scoring (design §12). Shared by the
|
|
162
168
|
* future Scorer and by the Slice 0 reporter so pass/fail is decided in one place.
|
|
@@ -194,6 +200,18 @@ declare const seo009HtmlLang: Rule;
|
|
|
194
200
|
|
|
195
201
|
declare const allRules: Rule[];
|
|
196
202
|
|
|
203
|
+
interface RuleInfo {
|
|
204
|
+
id: string;
|
|
205
|
+
title: string;
|
|
206
|
+
category: Category;
|
|
207
|
+
severity: Severity;
|
|
208
|
+
rationale: string;
|
|
209
|
+
docsUrl: string;
|
|
210
|
+
fix?: Fix;
|
|
211
|
+
}
|
|
212
|
+
/** Look up a rule's static metadata for the MCP explain_rule tool (issue #24). Rule ids are matched case-insensitively. */
|
|
213
|
+
declare function explainRule(id: string): RuleInfo | undefined;
|
|
214
|
+
|
|
197
215
|
interface HeadTagRuleOptions {
|
|
198
216
|
id: string;
|
|
199
217
|
title: string;
|
|
@@ -203,6 +221,8 @@ interface HeadTagRuleOptions {
|
|
|
203
221
|
/** Short human label, e.g. 'description'. */
|
|
204
222
|
label: string;
|
|
205
223
|
recommendation: string;
|
|
224
|
+
/** Why this rule matters — surfaced by explain_rule (issue #24). */
|
|
225
|
+
rationale: string;
|
|
206
226
|
/** Agent-actionable remediation attached to every finding (issue #18). */
|
|
207
227
|
fix?: Fix;
|
|
208
228
|
}
|
|
@@ -239,6 +259,50 @@ interface ConsoleReportOptions {
|
|
|
239
259
|
*/
|
|
240
260
|
declare function formatConsoleReport(results: Result[], config: Config, options?: ConsoleReportOptions): string;
|
|
241
261
|
|
|
262
|
+
interface ScoreModel {
|
|
263
|
+
routeAverage: number;
|
|
264
|
+
sitePenalty: number;
|
|
265
|
+
/** Headline cap value when it actually lowered the score, else null. */
|
|
266
|
+
criticalCap: number | null;
|
|
267
|
+
}
|
|
268
|
+
interface ScoreResult {
|
|
269
|
+
score: number;
|
|
270
|
+
scoreModel: ScoreModel;
|
|
271
|
+
}
|
|
272
|
+
interface ScoreOptions {
|
|
273
|
+
applyCriticalCap?: boolean;
|
|
274
|
+
}
|
|
275
|
+
/** Compute the headline score and its breakdown (design §12). */
|
|
276
|
+
declare function computeScore(results: Result[], config: Config, options?: ScoreOptions): ScoreResult;
|
|
277
|
+
|
|
278
|
+
declare function issueOf(result: Result): {
|
|
279
|
+
fix?: Fix | undefined;
|
|
280
|
+
docsUrl?: string | undefined;
|
|
281
|
+
id: string;
|
|
282
|
+
title: string;
|
|
283
|
+
detection: Detection;
|
|
284
|
+
location: string | undefined;
|
|
285
|
+
recommendation: string | undefined;
|
|
286
|
+
};
|
|
287
|
+
type JsonIssue = ReturnType<typeof issueOf> & {
|
|
288
|
+
severity: ReturnType<typeof effectiveSeverity>;
|
|
289
|
+
};
|
|
290
|
+
interface JsonReport {
|
|
291
|
+
version: string;
|
|
292
|
+
score: number;
|
|
293
|
+
scoreModel: ScoreModel;
|
|
294
|
+
summary: Summary;
|
|
295
|
+
routes: Array<{
|
|
296
|
+
route: string;
|
|
297
|
+
score: number;
|
|
298
|
+
issues: JsonIssue[];
|
|
299
|
+
}>;
|
|
300
|
+
siteIssues: JsonIssue[];
|
|
301
|
+
}
|
|
302
|
+
/** Build the structured JSON report object (design §7). Shared by the json reporter and the MCP `analyze` tool (issue #24). */
|
|
303
|
+
declare function buildJsonReport(results: Result[], config: Config, meta: {
|
|
304
|
+
version: string;
|
|
305
|
+
}): JsonReport;
|
|
242
306
|
/** Render results as the documented JSON report string (design §7). */
|
|
243
307
|
declare function formatJsonReport(results: Result[], config: Config, meta: {
|
|
244
308
|
version: string;
|
|
@@ -263,20 +327,4 @@ declare function selectRules(rules: Rule[], config: Config): Rule[];
|
|
|
263
327
|
/** Apply per-rule severity overrides to results (design §6). */
|
|
264
328
|
declare function applyRuleSeverities(results: Result[], config: Config): Result[];
|
|
265
329
|
|
|
266
|
-
|
|
267
|
-
routeAverage: number;
|
|
268
|
-
sitePenalty: number;
|
|
269
|
-
/** Headline cap value when it actually lowered the score, else null. */
|
|
270
|
-
criticalCap: number | null;
|
|
271
|
-
}
|
|
272
|
-
interface ScoreResult {
|
|
273
|
-
score: number;
|
|
274
|
-
scoreModel: ScoreModel;
|
|
275
|
-
}
|
|
276
|
-
interface ScoreOptions {
|
|
277
|
-
applyCriticalCap?: boolean;
|
|
278
|
-
}
|
|
279
|
-
/** Compute the headline score and its breakdown (design §12). */
|
|
280
|
-
declare function computeScore(results: Result[], config: Config, options?: ScoreOptions): ScoreResult;
|
|
281
|
-
|
|
282
|
-
export { type Category, type Classification, type Config, type ConsoleReportOptions, type Detection, type Fix, type HeadProvider, type HeadTag, type Presence, type Project, ROBOTS_SOURCE_PATHS, type ResolvedHead, type Result, type Rule, type RuleContext, type RuleSetting, type Runtime, SITEMAP_SOURCE_PATHS, type Scope, type ScoreModel, type ScoreOptions, type ScoreResult, type Severity, type Summary, type TreatDynamicAs, type Value, allRules, applyRuleSeverities, classify, computeScore, defaultConfig, defaultProject, defineConfig, effectiveSeverity, formatAgentReport, formatConsoleReport, formatGithubReport, formatJsonReport, formatSarifReport, hasFailureAtOrAbove, headTagRule, isPenalized, runRules, selectRules, seo001Title, seo002Description, seo003Canonical, seo004OgImage, seo005OgTitle, seo006Robots, seo007Sitemap, seo008JsonLd, seo009HtmlLang, summarize };
|
|
330
|
+
export { type Category, type Classification, type Config, type ConsoleReportOptions, type Detection, type Fix, type HeadProvider, type HeadTag, type JsonReport, type Presence, type Project, ROBOTS_SOURCE_PATHS, type ResolvedHead, type Result, type Rule, type RuleContext, type RuleInfo, type RuleSetting, type Runtime, SITEMAP_SOURCE_PATHS, type Scope, type ScoreModel, type ScoreOptions, type ScoreResult, type Severity, type Summary, type TreatDynamicAs, type Value, allRules, applyRuleSeverities, buildJsonReport, classify, computeScore, defaultConfig, defaultProject, defineConfig, docsUrlFor, effectiveSeverity, explainRule, formatAgentReport, formatConsoleReport, formatGithubReport, formatJsonReport, formatSarifReport, hasFailureAtOrAbove, headTagRule, isPenalized, runRules, selectRules, seo001Title, seo002Description, seo003Canonical, seo004OgImage, seo005OgTitle, seo006Robots, seo007Sitemap, seo008JsonLd, seo009HtmlLang, summarize };
|
package/dist/index.js
CHANGED
|
@@ -27,6 +27,9 @@ var SITEMAP_SOURCE_PATHS = [
|
|
|
27
27
|
];
|
|
28
28
|
|
|
29
29
|
// src/rule.ts
|
|
30
|
+
function docsUrlFor(id) {
|
|
31
|
+
return `https://svelte-vitals.dev/rules/${id}`;
|
|
32
|
+
}
|
|
30
33
|
function isPenalized(detection, treatDynamicAs) {
|
|
31
34
|
if (detection.presence === "none") return true;
|
|
32
35
|
if (detection.value === "absent") return true;
|
|
@@ -41,7 +44,11 @@ async function runRules(rules, ctx) {
|
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
// src/rules/seo/seo001-title.ts
|
|
44
|
-
var
|
|
47
|
+
var FIX = {
|
|
48
|
+
description: "Add a <title> inside <svelte:head> (a dynamic title is fine).",
|
|
49
|
+
snippet: "<svelte:head>\n <title>{data.title}</title>\n</svelte:head>",
|
|
50
|
+
lang: "svelte"
|
|
51
|
+
};
|
|
45
52
|
function detectTitle(head) {
|
|
46
53
|
const title = head.tags.find((t) => t.kind === "title");
|
|
47
54
|
if (!title) {
|
|
@@ -60,6 +67,8 @@ var seo001Title = {
|
|
|
60
67
|
category: "seo",
|
|
61
68
|
severity: "critical",
|
|
62
69
|
scope: "route",
|
|
70
|
+
rationale: "A unique, non-empty <title> is the single strongest on-page SEO signal and the text shown in search results and browser tabs.",
|
|
71
|
+
fix: FIX,
|
|
63
72
|
async check(ctx) {
|
|
64
73
|
return ctx.heads.map((head) => {
|
|
65
74
|
const detection = detectTitle(head);
|
|
@@ -71,12 +80,8 @@ var seo001Title = {
|
|
|
71
80
|
location: head.file,
|
|
72
81
|
message: messageFor(detection),
|
|
73
82
|
recommendation: "Add a <title> inside <svelte:head>, e.g. <title>{data.title}</title>, or set it via your meta component.",
|
|
74
|
-
docsUrl:
|
|
75
|
-
fix: {
|
|
76
|
-
description: "Add a <title> inside <svelte:head> (a dynamic title is fine).",
|
|
77
|
-
snippet: "<svelte:head>\n <title>{data.title}</title>\n</svelte:head>",
|
|
78
|
-
lang: "svelte"
|
|
79
|
-
}
|
|
83
|
+
docsUrl: docsUrlFor("SEO001"),
|
|
84
|
+
fix: { ...FIX }
|
|
80
85
|
};
|
|
81
86
|
});
|
|
82
87
|
}
|
|
@@ -88,13 +93,15 @@ function detect(head, match) {
|
|
|
88
93
|
return tag ? { presence: tag.presence, value: tag.value } : { presence: "none", value: "absent" };
|
|
89
94
|
}
|
|
90
95
|
function headTagRule(opts) {
|
|
91
|
-
const docsUrl =
|
|
96
|
+
const docsUrl = docsUrlFor(opts.id);
|
|
92
97
|
return {
|
|
93
98
|
id: opts.id,
|
|
94
99
|
title: opts.title,
|
|
95
100
|
category: "seo",
|
|
96
101
|
severity: opts.severity,
|
|
97
102
|
scope: "route",
|
|
103
|
+
rationale: opts.rationale,
|
|
104
|
+
...opts.fix ? { fix: opts.fix } : {},
|
|
98
105
|
async check(ctx) {
|
|
99
106
|
return ctx.heads.map((head) => {
|
|
100
107
|
const detection = detect(head, opts.match);
|
|
@@ -125,6 +132,7 @@ var seo002Description = headTagRule({
|
|
|
125
132
|
match: (t) => t.kind === "meta" && t.name === "description",
|
|
126
133
|
label: '<meta name="description">',
|
|
127
134
|
recommendation: 'Add a <meta name="description"> in <svelte:head>, or set the description on your meta component.',
|
|
135
|
+
rationale: "A meta description is the snippet search engines show under your title; without one they invent one from page text, often poorly.",
|
|
128
136
|
fix: {
|
|
129
137
|
description: 'Add a <meta name="description"> inside <svelte:head>, or set description on your meta component.',
|
|
130
138
|
snippet: '<svelte:head>\n <meta name="description" content="A concise page summary." />\n</svelte:head>',
|
|
@@ -138,6 +146,7 @@ var seo003Canonical = headTagRule({
|
|
|
138
146
|
match: (t) => t.kind === "link" && t.rel === "canonical",
|
|
139
147
|
label: '<link rel="canonical">',
|
|
140
148
|
recommendation: 'Add <link rel="canonical"> in <svelte:head>, or set the canonical prop on your meta component.',
|
|
149
|
+
rationale: "A canonical URL tells search engines which URL is authoritative, preventing duplicate-content dilution across query strings and trailing-slash variants.",
|
|
141
150
|
fix: {
|
|
142
151
|
description: 'Add <link rel="canonical"> inside <svelte:head>, or set the canonical prop on your meta component.',
|
|
143
152
|
snippet: '<svelte:head>\n <link rel="canonical" href="https://example.com/this-page" />\n</svelte:head>',
|
|
@@ -151,6 +160,7 @@ var seo004OgImage = headTagRule({
|
|
|
151
160
|
match: (t) => t.kind === "meta" && t.property === "og:image",
|
|
152
161
|
label: '<meta property="og:image">',
|
|
153
162
|
recommendation: 'Add <meta property="og:image">, or set openGraph.images on your meta component.',
|
|
163
|
+
rationale: "og:image is the preview thumbnail shown when the page is shared on social platforms; without it links render bare and get fewer clicks.",
|
|
154
164
|
fix: {
|
|
155
165
|
description: 'Add <meta property="og:image">, or set openGraph.images on your meta component.',
|
|
156
166
|
snippet: '<svelte:head>\n <meta property="og:image" content="https://example.com/og.png" />\n</svelte:head>',
|
|
@@ -164,6 +174,7 @@ var seo005OgTitle = headTagRule({
|
|
|
164
174
|
match: (t) => t.kind === "meta" && t.property === "og:title",
|
|
165
175
|
label: '<meta property="og:title">',
|
|
166
176
|
recommendation: 'Add <meta property="og:title">, or set openGraph.title on your meta component.',
|
|
177
|
+
rationale: "og:title controls the headline shown when the page is shared on social platforms, independent of the document <title>.",
|
|
167
178
|
fix: {
|
|
168
179
|
description: 'Add <meta property="og:title">, or set openGraph.title on your meta component.',
|
|
169
180
|
snippet: '<svelte:head>\n <meta property="og:title" content="Page title" />\n</svelte:head>',
|
|
@@ -177,6 +188,7 @@ var seo008JsonLd = headTagRule({
|
|
|
177
188
|
match: (t) => t.kind === "jsonld",
|
|
178
189
|
label: 'JSON-LD (<script type="application/ld+json">)',
|
|
179
190
|
recommendation: "Add JSON-LD structured data, e.g. via <svelte:head> or a JsonLd component.",
|
|
191
|
+
rationale: "JSON-LD structured data lets search engines render rich results (breadcrumbs, articles, products) for the page.",
|
|
180
192
|
fix: {
|
|
181
193
|
// Svelte ships <script> contents verbatim (the body is raw text, not Svelte
|
|
182
194
|
// markup), so use literal JSON here — an interpolation like {JSON.stringify(...)}
|
|
@@ -190,12 +202,19 @@ var seo008JsonLd = headTagRule({
|
|
|
190
202
|
// src/rules/seo/project-rules.ts
|
|
191
203
|
var present = { presence: "own", value: "static" };
|
|
192
204
|
var absent = { presence: "none", value: "absent" };
|
|
205
|
+
var SEO006_FIX = {
|
|
206
|
+
description: "Create static/robots.txt (or a src/routes/robots.txt/+server endpoint).",
|
|
207
|
+
snippet: "User-agent: *\nAllow: /\n\nSitemap: https://example.com/sitemap.xml",
|
|
208
|
+
lang: "text"
|
|
209
|
+
};
|
|
193
210
|
var seo006Robots = {
|
|
194
211
|
id: "SEO006",
|
|
195
212
|
title: "robots.txt",
|
|
196
213
|
category: "seo",
|
|
197
214
|
severity: "warning",
|
|
198
215
|
scope: "project",
|
|
216
|
+
rationale: "robots.txt tells crawlers which paths they may fetch and points them to your sitemap; missing it leaves crawl behaviour to defaults.",
|
|
217
|
+
fix: SEO006_FIX,
|
|
199
218
|
async check(ctx) {
|
|
200
219
|
const detection = ctx.project.hasRobotsTxt ? present : absent;
|
|
201
220
|
return [
|
|
@@ -205,22 +224,25 @@ var seo006Robots = {
|
|
|
205
224
|
detection,
|
|
206
225
|
message: ctx.project.hasRobotsTxt ? "robots.txt" : "Missing robots.txt",
|
|
207
226
|
recommendation: "Add static/robots.txt or a src/routes/robots.txt/+server endpoint.",
|
|
208
|
-
docsUrl: "
|
|
209
|
-
fix: {
|
|
210
|
-
description: "Create static/robots.txt (or a src/routes/robots.txt/+server endpoint).",
|
|
211
|
-
snippet: "User-agent: *\nAllow: /\n\nSitemap: https://example.com/sitemap.xml",
|
|
212
|
-
lang: "text"
|
|
213
|
-
}
|
|
227
|
+
docsUrl: docsUrlFor("SEO006"),
|
|
228
|
+
fix: { ...SEO006_FIX }
|
|
214
229
|
}
|
|
215
230
|
];
|
|
216
231
|
}
|
|
217
232
|
};
|
|
233
|
+
var SEO007_FIX = {
|
|
234
|
+
description: "Create static/sitemap.xml (or a src/routes/sitemap.xml/+server endpoint).",
|
|
235
|
+
snippet: '<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n <url><loc>https://example.com/</loc></url>\n</urlset>',
|
|
236
|
+
lang: "xml"
|
|
237
|
+
};
|
|
218
238
|
var seo007Sitemap = {
|
|
219
239
|
id: "SEO007",
|
|
220
240
|
title: "sitemap.xml",
|
|
221
241
|
category: "seo",
|
|
222
242
|
severity: "warning",
|
|
223
243
|
scope: "project",
|
|
244
|
+
rationale: "A sitemap.xml lists your URLs so search engines can discover and prioritise them, especially pages not well linked internally.",
|
|
245
|
+
fix: SEO007_FIX,
|
|
224
246
|
async check(ctx) {
|
|
225
247
|
const detection = ctx.project.hasSitemap ? present : absent;
|
|
226
248
|
return [
|
|
@@ -230,22 +252,25 @@ var seo007Sitemap = {
|
|
|
230
252
|
detection,
|
|
231
253
|
message: ctx.project.hasSitemap ? "sitemap.xml" : "Missing sitemap.xml",
|
|
232
254
|
recommendation: "Add static/sitemap.xml or a src/routes/sitemap.xml/+server endpoint.",
|
|
233
|
-
docsUrl: "
|
|
234
|
-
fix: {
|
|
235
|
-
description: "Create static/sitemap.xml (or a src/routes/sitemap.xml/+server endpoint).",
|
|
236
|
-
snippet: '<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n <url><loc>https://example.com/</loc></url>\n</urlset>',
|
|
237
|
-
lang: "xml"
|
|
238
|
-
}
|
|
255
|
+
docsUrl: docsUrlFor("SEO007"),
|
|
256
|
+
fix: { ...SEO007_FIX }
|
|
239
257
|
}
|
|
240
258
|
];
|
|
241
259
|
}
|
|
242
260
|
};
|
|
261
|
+
var SEO009_FIX = {
|
|
262
|
+
description: "Set the lang attribute on <html> in src/app.html.",
|
|
263
|
+
snippet: '<html lang="en">',
|
|
264
|
+
lang: "html"
|
|
265
|
+
};
|
|
243
266
|
var seo009HtmlLang = {
|
|
244
267
|
id: "SEO009",
|
|
245
268
|
title: "<html lang>",
|
|
246
269
|
category: "seo",
|
|
247
270
|
severity: "warning",
|
|
248
271
|
scope: "project",
|
|
272
|
+
rationale: "The <html lang> attribute declares the page language for search engines, screen readers, and translation tools.",
|
|
273
|
+
fix: SEO009_FIX,
|
|
249
274
|
async check(ctx) {
|
|
250
275
|
const detection = ctx.project.htmlLang;
|
|
251
276
|
const message = detection.presence === "none" ? "Missing <html lang>" : detection.value === "absent" ? "Empty <html lang>" : "<html lang>";
|
|
@@ -256,12 +281,8 @@ var seo009HtmlLang = {
|
|
|
256
281
|
detection,
|
|
257
282
|
message,
|
|
258
283
|
recommendation: 'Set <html lang="..."> in src/app.html.',
|
|
259
|
-
docsUrl: "
|
|
260
|
-
fix: {
|
|
261
|
-
description: "Set the lang attribute on <html> in src/app.html.",
|
|
262
|
-
snippet: '<html lang="en">',
|
|
263
|
-
lang: "html"
|
|
264
|
-
}
|
|
284
|
+
docsUrl: docsUrlFor("SEO009"),
|
|
285
|
+
fix: { ...SEO009_FIX }
|
|
265
286
|
}
|
|
266
287
|
];
|
|
267
288
|
}
|
|
@@ -279,6 +300,20 @@ var allRules = [
|
|
|
279
300
|
seo008JsonLd,
|
|
280
301
|
seo009HtmlLang
|
|
281
302
|
];
|
|
303
|
+
function explainRule(id) {
|
|
304
|
+
const target = id.toUpperCase();
|
|
305
|
+
const rule = allRules.find((r) => r.id === target);
|
|
306
|
+
if (!rule) return void 0;
|
|
307
|
+
return {
|
|
308
|
+
id: rule.id,
|
|
309
|
+
title: rule.title,
|
|
310
|
+
category: rule.category,
|
|
311
|
+
severity: rule.severity,
|
|
312
|
+
rationale: rule.rationale,
|
|
313
|
+
docsUrl: docsUrlFor(rule.id),
|
|
314
|
+
...rule.fix ? { fix: rule.fix } : {}
|
|
315
|
+
};
|
|
316
|
+
}
|
|
282
317
|
|
|
283
318
|
// src/summary.ts
|
|
284
319
|
function classify(result, config) {
|
|
@@ -432,10 +467,11 @@ function issueOf(result) {
|
|
|
432
467
|
detection: result.detection,
|
|
433
468
|
location: result.location,
|
|
434
469
|
recommendation: result.recommendation,
|
|
470
|
+
...result.docsUrl ? { docsUrl: result.docsUrl } : {},
|
|
435
471
|
...result.fix ? { fix: result.fix } : {}
|
|
436
472
|
};
|
|
437
473
|
}
|
|
438
|
-
function
|
|
474
|
+
function buildJsonReport(results, config, meta) {
|
|
439
475
|
const { score, scoreModel } = computeScore(results, config);
|
|
440
476
|
const summary = summarize(results, config);
|
|
441
477
|
const routeMap = /* @__PURE__ */ new Map();
|
|
@@ -450,7 +486,10 @@ function formatJsonReport(results, config, meta) {
|
|
|
450
486
|
issues: rs.filter((r) => isPenalized(r.detection, config.treatDynamicAs)).map((r) => ({ ...issueOf(r), severity: effectiveSeverity(r, config) }))
|
|
451
487
|
}));
|
|
452
488
|
const siteIssues = results.filter((r) => r.route === void 0 && isPenalized(r.detection, config.treatDynamicAs)).map((r) => ({ ...issueOf(r), severity: effectiveSeverity(r, config) }));
|
|
453
|
-
return
|
|
489
|
+
return { version: meta.version, score, scoreModel, summary, routes, siteIssues };
|
|
490
|
+
}
|
|
491
|
+
function formatJsonReport(results, config, meta) {
|
|
492
|
+
return JSON.stringify(buildJsonReport(results, config, meta), null, 2);
|
|
454
493
|
}
|
|
455
494
|
|
|
456
495
|
// src/reporter/agent.ts
|
|
@@ -509,11 +548,11 @@ function severityToGithubLevel(sev) {
|
|
|
509
548
|
function messageText(result) {
|
|
510
549
|
return result.recommendation ? `${result.message} ${result.recommendation}` : result.message;
|
|
511
550
|
}
|
|
512
|
-
function
|
|
551
|
+
function docsUrlFor2(id) {
|
|
513
552
|
return `https://svelte-vitals.dev/rules/${id}`;
|
|
514
553
|
}
|
|
515
554
|
var RULE_META = new Map(
|
|
516
|
-
allRules.map((r) => [r.id, { title: r.title, severity: r.severity, docsUrl:
|
|
555
|
+
allRules.map((r) => [r.id, { title: r.title, severity: r.severity, docsUrl: docsUrlFor2(r.id) }])
|
|
517
556
|
);
|
|
518
557
|
function ruleMetaById(id) {
|
|
519
558
|
return RULE_META.get(id);
|
|
@@ -533,7 +572,7 @@ function formatSarifReport(results, config, meta) {
|
|
|
533
572
|
id: r.id,
|
|
534
573
|
name,
|
|
535
574
|
shortDescription: { text: name },
|
|
536
|
-
helpUri: r.docsUrl ?? m?.docsUrl ??
|
|
575
|
+
helpUri: r.docsUrl ?? m?.docsUrl ?? docsUrlFor2(r.id),
|
|
537
576
|
defaultConfiguration: { level: severityToSarifLevel(m?.severity ?? r.severity) }
|
|
538
577
|
});
|
|
539
578
|
}
|
|
@@ -605,12 +644,15 @@ export {
|
|
|
605
644
|
SITEMAP_SOURCE_PATHS,
|
|
606
645
|
allRules,
|
|
607
646
|
applyRuleSeverities,
|
|
647
|
+
buildJsonReport,
|
|
608
648
|
classify,
|
|
609
649
|
computeScore,
|
|
610
650
|
defaultConfig,
|
|
611
651
|
defaultProject,
|
|
612
652
|
defineConfig,
|
|
653
|
+
docsUrlFor,
|
|
613
654
|
effectiveSeverity,
|
|
655
|
+
explainRule,
|
|
614
656
|
formatAgentReport,
|
|
615
657
|
formatConsoleReport,
|
|
616
658
|
formatGithubReport,
|