← back to Open Seo
src/client/features/audit/results/export.ts
192 lines
import type { AuditResultsData } from "@/client/features/audit/results/types";
import { getIssueDescriptor } from "@/shared/audit-issues";
import { buildCsv, type CsvValue, downloadCsv } from "@/client/lib/csv";
import { downloadFile } from "@/client/lib/download";
import { exportTableToSheets } from "@/client/lib/exportToSheets";
const ISSUES_HEADERS = ["Severity", "Issue", "URL", "Details", "How To Fix"];
function issuesRows(issues: AuditResultsData["issues"]): CsvValue[][] {
return issues.map((issue) => {
const descriptor = getIssueDescriptor(issue.issueType);
return [
issue.severity,
descriptor?.title ?? issue.issueType,
issue.pageUrl,
issue.detailsJson ?? "",
descriptor?.howToFix ?? "",
];
});
}
export function exportIssues(
issues: AuditResultsData["issues"],
format: "csv" | "json" | "sheets",
) {
if (format === "json") {
const rows = issues.map((issue) => {
const descriptor = getIssueDescriptor(issue.issueType);
return {
severity: issue.severity,
issueType: issue.issueType,
issue: descriptor?.title ?? issue.issueType,
url: issue.pageUrl,
details: issue.detailsJson
? (JSON.parse(issue.detailsJson) as unknown)
: null,
howToFix: descriptor?.howToFix ?? null,
};
});
downloadFile(
JSON.stringify(rows, null, 2),
"audit-issues.json",
"application/json",
);
return;
}
if (format === "sheets") {
void exportTableToSheets({
headers: ISSUES_HEADERS,
rows: issuesRows(issues),
feature: "audit_issues",
});
return;
}
downloadCsv("audit-issues.csv", buildCsv(ISSUES_HEADERS, issuesRows(issues)));
}
const PAGES_HEADERS = [
"URL",
"Status",
"Title",
"H1",
"Words",
"Images",
"Missing Alt",
"Response Time (ms)",
];
function pagesRows(pages: AuditResultsData["pages"]): CsvValue[][] {
return pages.map((page) => [
page.url,
page.statusCode,
page.title ?? "",
page.h1Count,
page.wordCount,
page.imagesTotal,
page.imagesMissingAlt,
page.responseTimeMs,
]);
}
const PERFORMANCE_HEADERS = [
"URL",
"Device",
"Performance",
"Accessibility",
"SEO",
"LCP (ms)",
"CLS",
"INP (ms)",
"TTFB (ms)",
];
function performanceRows(
lighthouse: AuditResultsData["lighthouse"],
pages: AuditResultsData["pages"],
): CsvValue[][] {
return lighthouse.map((result) => {
const page = pages.find((candidate) => candidate.id === result.pageId);
return [
page?.url ?? "",
result.strategy,
result.performanceScore,
result.accessibilityScore,
result.seoScore,
result.lcpMs,
result.cls,
result.inpMs,
result.ttfbMs,
];
});
}
export function exportPages(
pages: AuditResultsData["pages"],
format: "csv" | "json" | "sheets",
) {
if (format === "json") {
const rows = pages.map((page) => ({
url: page.url,
statusCode: page.statusCode,
title: page.title ?? "",
h1Count: page.h1Count,
wordCount: page.wordCount,
imagesTotal: page.imagesTotal,
imagesMissingAlt: page.imagesMissingAlt,
responseTimeMs: page.responseTimeMs,
}));
downloadFile(
JSON.stringify(rows, null, 2),
"audit-pages.json",
"application/json",
);
return;
}
if (format === "sheets") {
void exportTableToSheets({
headers: PAGES_HEADERS,
rows: pagesRows(pages),
feature: "audit_pages",
});
return;
}
downloadCsv("audit-pages.csv", buildCsv(PAGES_HEADERS, pagesRows(pages)));
}
export function exportPerformance(
lighthouse: AuditResultsData["lighthouse"],
pages: AuditResultsData["pages"],
format: "csv" | "json" | "sheets",
) {
if (format === "json") {
const rows = lighthouse.map((result) => {
const page = pages.find((candidate) => candidate.id === result.pageId);
return {
url: page?.url ?? "",
strategy: result.strategy,
performance: result.performanceScore,
accessibility: result.accessibilityScore,
seo: result.seoScore,
lcpMs: result.lcpMs,
cls: result.cls,
inpMs: result.inpMs,
ttfbMs: result.ttfbMs,
};
});
downloadFile(
JSON.stringify(rows, null, 2),
"audit-performance.json",
"application/json",
);
return;
}
const rows = performanceRows(lighthouse, pages);
if (format === "sheets") {
void exportTableToSheets({
headers: PERFORMANCE_HEADERS,
rows,
feature: "audit_performance",
});
return;
}
downloadCsv("audit-performance.csv", buildCsv(PERFORMANCE_HEADERS, rows));
}