@svelte-vitals/core 0.10.0 → 0.11.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 +22 -1
- package/dist/index.js +196 -7
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -385,9 +385,30 @@ declare function formatSarifReport(results: Result[], config: Config, meta: {
|
|
|
385
385
|
*/
|
|
386
386
|
declare function formatGithubReport(results: Result[], config: Config): string;
|
|
387
387
|
|
|
388
|
+
type Band = 'good' | 'warn' | 'poor';
|
|
389
|
+
declare const BAND_COLOR: Record<Band, string>;
|
|
390
|
+
declare function scoreBand(score: number): Band;
|
|
391
|
+
declare function escapeHtml(s: string): string;
|
|
392
|
+
/**
|
|
393
|
+
* Return the URL only when it uses a safe http/https scheme, else null.
|
|
394
|
+
* Guards a finding's `docsUrl` against `javascript:`/`data:` hrefs — escapeHtml
|
|
395
|
+
* neutralizes attribute breakout but not a malicious scheme. Browsers strip
|
|
396
|
+
* ASCII whitespace (tab/newline/CR) from a URL before resolving its scheme (so
|
|
397
|
+
* `java\tscript:` runs as `javascript:`), so strip whitespace first; anything not
|
|
398
|
+
* plainly http(s):// afterward is rejected. Pure string work — no `URL` global,
|
|
399
|
+
* keeping core runtime-agnostic and lib-minimal.
|
|
400
|
+
*/
|
|
401
|
+
declare function safeHref(url: string): string | null;
|
|
402
|
+
declare function buildHtmlDocument(report: JsonReport, meta: {
|
|
403
|
+
version: string;
|
|
404
|
+
}): string;
|
|
405
|
+
declare function formatHtmlReport(results: Result[], config: Config, meta: {
|
|
406
|
+
version: string;
|
|
407
|
+
}): string;
|
|
408
|
+
|
|
388
409
|
/** Drop rules disabled via config (design §6). */
|
|
389
410
|
declare function selectRules(rules: Rule[], config: Config): Rule[];
|
|
390
411
|
/** Apply per-rule severity overrides to results (design §6). */
|
|
391
412
|
declare function applyRuleSeverities(results: Result[], config: Config): Result[];
|
|
392
413
|
|
|
393
|
-
export { type Category, type Classification, type Config, type ConsoleReportOptions, type Detection, type Fix, type HeadProvider, type HeadTag, type HealthResult, type ImageInfo, type JsonReport, type Presence, type Project, ROBOTS_SOURCE_PATHS, type ResolvedHead, type ResolvedImages, 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, computeHealth, computeScore, defaultConfig, defaultProject, defineConfig, docsUrlFor, effectiveSeverity, explainRule, formatAgentReport, formatConsoleReport, formatGithubReport, formatJsonReport, formatSarifReport, hasFailureAtOrAbove, headTagRule, imageRule, isPenalized, perf001ImageDimensions, perf002ImageLoading, runRules, scoresByCategory, selectRules, seo001Title, seo002Description, seo003Canonical, seo004OgImage, seo005OgTitle, seo006Robots, seo007Sitemap, seo008JsonLd, seo009HtmlLang, summarize };
|
|
414
|
+
export { BAND_COLOR, type Category, type Classification, type Config, type ConsoleReportOptions, type Detection, type Fix, type HeadProvider, type HeadTag, type HealthResult, type ImageInfo, type JsonReport, type Presence, type Project, ROBOTS_SOURCE_PATHS, type ResolvedHead, type ResolvedImages, 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, buildHtmlDocument, buildJsonReport, classify, computeHealth, computeScore, defaultConfig, defaultProject, defineConfig, docsUrlFor, effectiveSeverity, escapeHtml, explainRule, formatAgentReport, formatConsoleReport, formatGithubReport, formatHtmlReport, formatJsonReport, formatSarifReport, hasFailureAtOrAbove, headTagRule, imageRule, isPenalized, perf001ImageDimensions, perf002ImageLoading, runRules, safeHref, scoreBand, scoresByCategory, selectRules, seo001Title, seo002Description, seo003Canonical, seo004OgImage, seo005OgTitle, seo006Robots, seo007Sitemap, seo008JsonLd, seo009HtmlLang, summarize };
|
package/dist/index.js
CHANGED
|
@@ -28,7 +28,7 @@ var SITEMAP_SOURCE_PATHS = [
|
|
|
28
28
|
|
|
29
29
|
// src/rule.ts
|
|
30
30
|
function docsUrlFor(id) {
|
|
31
|
-
return `https://svelte-vitals
|
|
31
|
+
return `https://oekazuma.github.io/svelte-vitals/rules/${id.toLowerCase()}`;
|
|
32
32
|
}
|
|
33
33
|
function isPenalized(detection, treatDynamicAs) {
|
|
34
34
|
if (detection.presence === "none") return true;
|
|
@@ -679,11 +679,8 @@ function severityToGithubLevel(sev) {
|
|
|
679
679
|
function messageText(result) {
|
|
680
680
|
return result.recommendation ? `${result.message} ${result.recommendation}` : result.message;
|
|
681
681
|
}
|
|
682
|
-
function docsUrlFor2(id) {
|
|
683
|
-
return `https://svelte-vitals.dev/rules/${id}`;
|
|
684
|
-
}
|
|
685
682
|
var RULE_META = new Map(
|
|
686
|
-
allRules.map((r) => [r.id, { title: r.title, severity: r.severity, docsUrl:
|
|
683
|
+
allRules.map((r) => [r.id, { title: r.title, severity: r.severity, docsUrl: docsUrlFor(r.id) }])
|
|
687
684
|
);
|
|
688
685
|
function ruleMetaById(id) {
|
|
689
686
|
return RULE_META.get(id);
|
|
@@ -703,7 +700,7 @@ function formatSarifReport(results, config, meta) {
|
|
|
703
700
|
id: r.id,
|
|
704
701
|
name,
|
|
705
702
|
shortDescription: { text: name },
|
|
706
|
-
helpUri: r.docsUrl ?? m?.docsUrl ??
|
|
703
|
+
helpUri: r.docsUrl ?? m?.docsUrl ?? docsUrlFor(r.id),
|
|
707
704
|
defaultConfiguration: { level: severityToSarifLevel(m?.severity ?? r.severity) }
|
|
708
705
|
});
|
|
709
706
|
}
|
|
@@ -736,7 +733,7 @@ function formatSarifReport(results, config, meta) {
|
|
|
736
733
|
tool: {
|
|
737
734
|
driver: {
|
|
738
735
|
name: "svelte-vitals",
|
|
739
|
-
informationUri: "https://svelte-vitals
|
|
736
|
+
informationUri: "https://oekazuma.github.io/svelte-vitals",
|
|
740
737
|
version: meta.version,
|
|
741
738
|
rules
|
|
742
739
|
}
|
|
@@ -772,6 +769,192 @@ function formatGithubReport(results, config) {
|
|
|
772
769
|
return lines.join("\n");
|
|
773
770
|
}
|
|
774
771
|
|
|
772
|
+
// src/reporter/html.ts
|
|
773
|
+
var BAND_COLOR = {
|
|
774
|
+
good: "#2FA968",
|
|
775
|
+
warn: "#E8A317",
|
|
776
|
+
poor: "#E5484D"
|
|
777
|
+
};
|
|
778
|
+
function scoreBand(score) {
|
|
779
|
+
return score >= 90 ? "good" : score >= 50 ? "warn" : "poor";
|
|
780
|
+
}
|
|
781
|
+
function escapeHtml(s) {
|
|
782
|
+
return s.replace(
|
|
783
|
+
/[&<>"']/g,
|
|
784
|
+
(c) => c === "&" ? "&" : c === "<" ? "<" : c === ">" ? ">" : c === '"' ? """ : "'"
|
|
785
|
+
);
|
|
786
|
+
}
|
|
787
|
+
function safeHref(url) {
|
|
788
|
+
const normalized = url.replace(/\s/g, "").toLowerCase();
|
|
789
|
+
return /^https?:\/\//.test(normalized) ? url : null;
|
|
790
|
+
}
|
|
791
|
+
var slug = (route) => "route-" + route.replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-|-$/g, "").toLowerCase();
|
|
792
|
+
var categoryLabel = (cat) => cat === "seo" ? "SEO" : cat.charAt(0).toUpperCase() + cat.slice(1);
|
|
793
|
+
var SEVERITIES = ["critical", "warning", "info"];
|
|
794
|
+
function renderFinding(issue) {
|
|
795
|
+
const sev = SEVERITIES.includes(issue.severity) ? issue.severity : "info";
|
|
796
|
+
const dyn = issue.detection.value === "dynamic" ? ' <span class="dyn" title="set dynamically (verified at runtime)">\u21AF</span>' : "";
|
|
797
|
+
const line = issue.line !== void 0 ? `:${issue.line}` : "";
|
|
798
|
+
const fix = issue.fix?.snippet !== void 0 ? `<div class="fix"><div class="label">fix</div><pre><code>${escapeHtml(issue.fix.snippet)}</code></pre></div>` : "";
|
|
799
|
+
const href = issue.docsUrl ? safeHref(issue.docsUrl) : null;
|
|
800
|
+
const docs = href ? `<a class="f-link" href="${escapeHtml(href)}">Learn more</a>` : "";
|
|
801
|
+
return `<article class="finding sev-${sev}" data-severity="${sev}" data-category="${escapeHtml(issue.category)}"><div class="f-head"><span class="ruleid">${escapeHtml(issue.id)}</span><span class="f-title">${escapeHtml(issue.title)}</span><span class="sev-tag ${sev}">${sev}</span></div>` + (issue.location ? `<p class="f-loc">${escapeHtml(issue.location)}${line}${dyn}</p>` : "") + (issue.recommendation ? `<p class="f-rec">${escapeHtml(issue.recommendation)}</p>` : "") + fix + docs + `</article>`;
|
|
802
|
+
}
|
|
803
|
+
function renderTopbar(report, meta) {
|
|
804
|
+
const findings = report.routes.reduce((n, r) => n + r.issues.length, 0) + report.siteIssues.length;
|
|
805
|
+
return `<header class="topbar"><div class="brand"><span class="bolt">\u21AF</span>svelte-<span class="v">vitals</span></div><div class="meta"><span>v${escapeHtml(meta.version)}</span><span>${report.routes.length} routes</span><span>${findings} findings</span></div></header>`;
|
|
806
|
+
}
|
|
807
|
+
function renderHero(report) {
|
|
808
|
+
const C = 2 * Math.PI * 58;
|
|
809
|
+
const offset = (C * (1 - report.score / 100)).toFixed(1);
|
|
810
|
+
const hb = scoreBand(report.score);
|
|
811
|
+
const s = report.summary;
|
|
812
|
+
const dynNote = s.dynamic > 0 ? `<span class="tally"><span class="dot dyn-dot">\u21AF</span>Dynamic <span class="n">${s.dynamic}</span></span>` : "";
|
|
813
|
+
const cats = Object.entries(report.categories).map(([cat, { score }]) => {
|
|
814
|
+
const b = scoreBand(score);
|
|
815
|
+
const weight = report.weights[cat];
|
|
816
|
+
const w = weight !== void 0 ? `<span class="w">weight ${weight}</span>` : "";
|
|
817
|
+
const name = categoryLabel(cat);
|
|
818
|
+
return `<div class="cat"><div class="top"><span class="name">${escapeHtml(name)} ${w}</span><span class="sc" style="color:${BAND_COLOR[b]}">${score}</span></div><div class="bar"><i style="width:${score}%;background:${BAND_COLOR[b]}"></i></div></div>`;
|
|
819
|
+
}).join("");
|
|
820
|
+
return `<section class="hero"><div class="gauge"><svg width="132" height="132" viewBox="0 0 132 132" aria-hidden="true"><circle cx="66" cy="66" r="58" fill="none" stroke="#e4e7ec" stroke-width="11"></circle><circle id="arc" cx="66" cy="66" r="58" fill="none" stroke="${BAND_COLOR[hb]}" stroke-width="11" stroke-linecap="round" stroke-dasharray="${C.toFixed(1)}" stroke-dashoffset="${offset}"></circle></svg><div class="num"><strong id="hnum">${report.score}</strong><span>Health</span></div></div><div class="readout"><div class="eyebrow">SvelteKit \xB7 SEO & Performance</div><div class="tallies"><span class="tally"><span class="dot crit"></span>Critical <span class="n">${s.critical}</span></span><span class="tally"><span class="dot warn"></span>Warning <span class="n">${s.warning}</span></span><span class="tally"><span class="dot info"></span>Info <span class="n">${s.info}</span></span><span class="tally"><span class="dot pass"></span>Passed <span class="n">${s.passed}</span></span>` + dynNote + `</div><div class="cats">${cats}</div></div></section>`;
|
|
821
|
+
}
|
|
822
|
+
function renderRoutes(report) {
|
|
823
|
+
if (report.routes.length === 0) return "";
|
|
824
|
+
const rows = report.routes.map((r) => {
|
|
825
|
+
const b = scoreBand(r.score);
|
|
826
|
+
const crit = r.issues.filter((i) => i.severity === "critical").length;
|
|
827
|
+
const warn = r.issues.filter((i) => i.severity === "warning").length;
|
|
828
|
+
const info = r.issues.filter((i) => i.severity === "info").length;
|
|
829
|
+
const parts = [];
|
|
830
|
+
if (crit) parts.push(`${crit} critical`);
|
|
831
|
+
if (warn) parts.push(`${warn} warning${warn > 1 ? "s" : ""}`);
|
|
832
|
+
if (info) parts.push(`${info} info`);
|
|
833
|
+
const sum = parts.length ? parts.join(" \xB7 ") : '<span class="none">no issues</span>';
|
|
834
|
+
const body = r.issues.length ? r.issues.map(renderFinding).join("") : '<p class="empty">No issues found on this route.</p>';
|
|
835
|
+
return `<details class="route" id="${slug(r.route)}" data-score="${r.score}"${r.issues.length ? " open" : ""}><summary><span class="route-name"><span class="path">${escapeHtml(r.route)}</span></span><span class="issue-sum">${sum}</span><span class="score-chip"><span class="ring" style="background:${BAND_COLOR[b]}"></span>${r.score}</span><span class="chev">\u203A</span></summary><div class="route-body">${body}</div></details>`;
|
|
836
|
+
}).join("");
|
|
837
|
+
return `<section class="section"><h2>Routes</h2><div class="routes">${rows}</div></section>`;
|
|
838
|
+
}
|
|
839
|
+
function renderSiteChecks(report) {
|
|
840
|
+
if (report.siteIssues.length === 0) return "";
|
|
841
|
+
const cards = report.siteIssues.map(renderFinding).join("");
|
|
842
|
+
return `<section class="section"><h2>Site checks</h2>${cards}</section>`;
|
|
843
|
+
}
|
|
844
|
+
function renderFilters(report) {
|
|
845
|
+
const chip = (filter, label, pressed = false) => `<button class="chip" type="button" aria-pressed="${pressed}" data-filter="${escapeHtml(filter)}">${escapeHtml(label)}</button>`;
|
|
846
|
+
const catChips = Object.keys(report.categories).map((cat) => chip(cat, categoryLabel(cat))).join("");
|
|
847
|
+
return `<div class="filters" role="group" aria-label="Filter findings">` + chip("all", "All", true) + chip("critical", "Critical") + chip("warning", "Warning") + chip("info", "Info") + catChips + `</div>`;
|
|
848
|
+
}
|
|
849
|
+
var STYLE = `
|
|
850
|
+
:root{--ground: #f6f7f9;--panel: #fff;--ink: #0c1322;--muted: #5a6472;--faint: #8c95a3;--line: #e4e7ec;--line-strong: #d3d8e0;--accent: #ff3e00;--good: #2fa968;--warn: #e8a317;--poor: #e5484d;--code-bg: #0e1525;--code-ink: #e7ecf4;--radius: 12px;--mono: ui-monospace,"SF Mono","JetBrains Mono",Menlo,Consolas,monospace;--sans: system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif}
|
|
851
|
+
*{box-sizing:border-box}
|
|
852
|
+
body{margin:0;background:var(--ground);color:var(--ink);font-family:var(--sans);line-height:1.5;-webkit-font-smoothing:antialiased}
|
|
853
|
+
.wrap{max-width:960px;margin:0 auto;padding:0 20px 96px}
|
|
854
|
+
.topbar{display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;padding:18px 0 16px;border-bottom:1px solid var(--line)}
|
|
855
|
+
.brand{display:flex;align-items:baseline;gap:8px;font-weight:700;font-size:18px;letter-spacing:-.02em}
|
|
856
|
+
.brand .bolt{color:var(--accent);font-size:20px}.brand .v{color:var(--accent)}
|
|
857
|
+
.meta{font-family:var(--mono);font-size:12.5px;color:var(--muted);display:flex;gap:14px;flex-wrap:wrap}
|
|
858
|
+
.hero{display:grid;grid-template-columns:auto 1fr;gap:28px;align-items:center;padding:30px 0 24px}
|
|
859
|
+
.gauge{position:relative;width:132px;height:132px}.gauge svg{transform:rotate(-90deg);display:block}
|
|
860
|
+
.gauge .num{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center}
|
|
861
|
+
.gauge .num strong{font-family:var(--mono);font-size:40px;font-weight:600;letter-spacing:-.03em;line-height:1;font-variant-numeric:tabular-nums}
|
|
862
|
+
.gauge .num span{font-size:11px;text-transform:uppercase;letter-spacing:.14em;color:var(--muted);margin-top:6px}
|
|
863
|
+
.readout{min-width:0}
|
|
864
|
+
.eyebrow{font-size:11px;text-transform:uppercase;letter-spacing:.16em;color:var(--accent);font-weight:700}
|
|
865
|
+
.eyebrow::before{content:"\u21AF "}
|
|
866
|
+
.tallies{display:flex;gap:8px;flex-wrap:wrap;margin:16px 0 18px}
|
|
867
|
+
.tally{display:inline-flex;align-items:center;gap:7px;font-size:13px;font-weight:600;background:var(--panel);border:1px solid var(--line);padding:5px 11px;border-radius:999px}
|
|
868
|
+
.tally .dot{width:9px;height:9px;border-radius:50%}.tally .n{font-family:var(--mono);font-variant-numeric:tabular-nums}
|
|
869
|
+
.dot.crit{background:var(--poor)}.dot.warn{background:var(--warn)}.dot.info{background:var(--faint)}.dot.pass{background:var(--good)}
|
|
870
|
+
.dot.dyn-dot{background:transparent;color:var(--accent);font-weight:700;width:auto;height:auto}
|
|
871
|
+
.cats{display:flex;gap:22px;flex-wrap:wrap}
|
|
872
|
+
.cat{min-width:190px;flex:1}.cat .top{display:flex;align-items:baseline;justify-content:space-between;margin-bottom:6px}
|
|
873
|
+
.cat .name{font-size:13px;font-weight:600}.cat .name .w{color:var(--faint);font-family:var(--mono);font-size:11px;font-weight:500;margin-left:6px}
|
|
874
|
+
.cat .sc{font-family:var(--mono);font-weight:600;font-variant-numeric:tabular-nums}
|
|
875
|
+
.bar{height:7px;border-radius:999px;background:var(--line);overflow:hidden}.bar>i{display:block;height:100%;border-radius:999px}
|
|
876
|
+
.section{margin-top:40px}
|
|
877
|
+
.section>h2{font-size:13px;text-transform:uppercase;letter-spacing:.12em;color:var(--muted);font-weight:700;margin:0 0 14px}
|
|
878
|
+
.filters{display:flex;gap:8px;flex-wrap:wrap;margin-top:28px}
|
|
879
|
+
.chip{font:inherit;font-size:12.5px;font-weight:600;cursor:pointer;background:var(--panel);border:1px solid var(--line-strong);color:var(--muted);padding:5px 12px;border-radius:999px}
|
|
880
|
+
.chip:hover{border-color:var(--faint);color:var(--ink)}
|
|
881
|
+
.chip[aria-pressed="true"]{background:var(--ink);border-color:var(--ink);color:#fff}
|
|
882
|
+
.chip:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
|
|
883
|
+
.routes{border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;background:var(--panel)}
|
|
884
|
+
.route{border-top:1px solid var(--line)}.route:first-child{border-top:0}
|
|
885
|
+
.route>summary{display:grid;grid-template-columns:1fr auto auto auto;gap:16px;align-items:center;padding:13px 16px;cursor:pointer;list-style:none}
|
|
886
|
+
.route>summary::-webkit-details-marker{display:none}
|
|
887
|
+
.route>summary:hover{background:#fbfcfd}
|
|
888
|
+
.route-name{font-family:var(--mono);font-size:13.5px;min-width:0}
|
|
889
|
+
.route-name .path{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
890
|
+
.issue-sum{font-size:12.5px;color:var(--muted);font-family:var(--mono);white-space:nowrap}.issue-sum .none{color:var(--good)}
|
|
891
|
+
.score-chip{display:inline-flex;align-items:center;gap:7px;font-family:var(--mono);font-weight:600;font-variant-numeric:tabular-nums;font-size:14px}
|
|
892
|
+
.score-chip .ring{width:10px;height:10px;border-radius:50%}
|
|
893
|
+
.chev{color:var(--faint);font-family:var(--mono);transition:transform .15s ease}
|
|
894
|
+
.route[open]>summary .chev{transform:rotate(90deg)}
|
|
895
|
+
.route-body{padding:4px 16px 16px}.empty{color:var(--muted);font-size:13px;margin:6px 0}
|
|
896
|
+
.finding{background:var(--panel);border:1px solid var(--line);border-left-width:3px;border-radius:10px;padding:16px 18px;margin:0 0 12px}
|
|
897
|
+
.finding.sev-critical{border-left-color:var(--poor)}.finding.sev-warning{border-left-color:var(--warn)}.finding.sev-info{border-left-color:var(--faint)}
|
|
898
|
+
.f-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
|
|
899
|
+
.ruleid{font-family:var(--mono);font-size:12px;font-weight:600;background:#eef1f5;padding:2px 8px;border-radius:6px}
|
|
900
|
+
.f-title{font-weight:650;font-size:15px}
|
|
901
|
+
.sev-tag{margin-left:auto;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.08em}
|
|
902
|
+
.sev-tag.critical{color:var(--poor)}.sev-tag.warning{color:var(--warn)}.sev-tag.info{color:var(--faint)}
|
|
903
|
+
.f-loc{font-family:var(--mono);font-size:12.5px;color:var(--muted);margin:8px 0 0}.dyn{color:var(--accent);font-weight:700}
|
|
904
|
+
.f-rec{font-size:14px;color:#2b3340;margin:10px 0 0}
|
|
905
|
+
.fix{margin:12px 0 0;background:var(--code-bg);border-radius:8px;overflow:hidden}
|
|
906
|
+
.fix .label{font-family:var(--mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:#8da0bd;padding:8px 14px 0}
|
|
907
|
+
.fix pre{margin:0;padding:8px 14px 14px;overflow-x:auto}
|
|
908
|
+
.fix code{font-family:var(--mono);font-size:12.5px;color:var(--code-ink);line-height:1.65;white-space:pre}
|
|
909
|
+
.f-link{display:inline-block;margin-top:12px;font-size:13px;font-weight:600;color:var(--accent);text-decoration:none}
|
|
910
|
+
.f-link:hover{text-decoration:underline}.f-link::after{content:" \u2192"}
|
|
911
|
+
.foot{margin-top:48px;padding-top:18px;border-top:1px solid var(--line);font-family:var(--mono);font-size:12px;color:var(--faint);display:flex;justify-content:space-between;flex-wrap:wrap;gap:8px}
|
|
912
|
+
.foot a{color:var(--accent);text-decoration:none}
|
|
913
|
+
@media (max-width:640px){.hero{grid-template-columns:1fr;justify-items:start}.route>summary{grid-template-columns:1fr auto}.chev{display:none}}
|
|
914
|
+
@media (prefers-reduced-motion:reduce){*{transition:none!important}}
|
|
915
|
+
`;
|
|
916
|
+
var SCRIPT = `
|
|
917
|
+
(function(){
|
|
918
|
+
var arc=document.getElementById('arc'),num=document.getElementById('hnum');
|
|
919
|
+
if(arc&&num){
|
|
920
|
+
var C=2*Math.PI*58,score=parseInt(num.textContent,10)||0;
|
|
921
|
+
var reduce=window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
922
|
+
if(!reduce){
|
|
923
|
+
arc.style.transition='stroke-dashoffset 1.1s cubic-bezier(.22,.61,.36,1)';
|
|
924
|
+
arc.style.strokeDashoffset=C.toFixed(1);
|
|
925
|
+
var start=null;
|
|
926
|
+
requestAnimationFrame(function step(t){
|
|
927
|
+
if(start===null)start=t;
|
|
928
|
+
var p=Math.min((t-start)/1100,1);
|
|
929
|
+
num.textContent=Math.round(score*(p<1?1-Math.pow(1-p,3):1));
|
|
930
|
+
if(p<1)requestAnimationFrame(step);
|
|
931
|
+
});
|
|
932
|
+
requestAnimationFrame(function(){arc.style.strokeDashoffset=(C*(1-score/100)).toFixed(1);});
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
var chips=document.querySelectorAll('.chip'),findings=document.querySelectorAll('.finding');
|
|
936
|
+
function apply(f){
|
|
937
|
+
findings.forEach(function(el){
|
|
938
|
+
var ok=f==='all'||el.getAttribute('data-severity')===f||el.getAttribute('data-category')===f;
|
|
939
|
+
el.style.display=ok?'':'none';
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
chips.forEach(function(c){
|
|
943
|
+
c.addEventListener('click',function(){
|
|
944
|
+
chips.forEach(function(o){o.setAttribute('aria-pressed','false');});
|
|
945
|
+
c.setAttribute('aria-pressed','true');
|
|
946
|
+
apply(c.getAttribute('data-filter')||'all');
|
|
947
|
+
});
|
|
948
|
+
});
|
|
949
|
+
})();
|
|
950
|
+
`;
|
|
951
|
+
function buildHtmlDocument(report, meta) {
|
|
952
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>svelte-vitals report</title><style>${STYLE}</style></head><body><div class="wrap">` + renderTopbar(report, meta) + renderHero(report) + renderFilters(report) + renderRoutes(report) + renderSiteChecks(report) + `<footer class="foot"><span>Generated by svelte-vitals \xB7 static analysis, no browser</span><span>oekazuma.github.io/svelte-vitals</span></footer></div><script>${SCRIPT}</script></body></html>`;
|
|
953
|
+
}
|
|
954
|
+
function formatHtmlReport(results, config, meta) {
|
|
955
|
+
return buildHtmlDocument(buildJsonReport(results, config, meta), meta);
|
|
956
|
+
}
|
|
957
|
+
|
|
775
958
|
// src/config-apply.ts
|
|
776
959
|
function selectRules(rules, config) {
|
|
777
960
|
return rules.filter((rule) => config.rules[rule.id] !== "off");
|
|
@@ -783,10 +966,12 @@ function applyRuleSeverities(results, config) {
|
|
|
783
966
|
});
|
|
784
967
|
}
|
|
785
968
|
export {
|
|
969
|
+
BAND_COLOR,
|
|
786
970
|
ROBOTS_SOURCE_PATHS,
|
|
787
971
|
SITEMAP_SOURCE_PATHS,
|
|
788
972
|
allRules,
|
|
789
973
|
applyRuleSeverities,
|
|
974
|
+
buildHtmlDocument,
|
|
790
975
|
buildJsonReport,
|
|
791
976
|
classify,
|
|
792
977
|
computeHealth,
|
|
@@ -796,10 +981,12 @@ export {
|
|
|
796
981
|
defineConfig,
|
|
797
982
|
docsUrlFor,
|
|
798
983
|
effectiveSeverity,
|
|
984
|
+
escapeHtml,
|
|
799
985
|
explainRule,
|
|
800
986
|
formatAgentReport,
|
|
801
987
|
formatConsoleReport,
|
|
802
988
|
formatGithubReport,
|
|
989
|
+
formatHtmlReport,
|
|
803
990
|
formatJsonReport,
|
|
804
991
|
formatSarifReport,
|
|
805
992
|
hasFailureAtOrAbove,
|
|
@@ -809,6 +996,8 @@ export {
|
|
|
809
996
|
perf001ImageDimensions,
|
|
810
997
|
perf002ImageLoading,
|
|
811
998
|
runRules,
|
|
999
|
+
safeHref,
|
|
1000
|
+
scoreBand,
|
|
812
1001
|
scoresByCategory,
|
|
813
1002
|
selectRules,
|
|
814
1003
|
seo001Title,
|