release: WRNexusJS 0.2.75
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
import type { DevToolbarRule } from "./types.ts";
|
||||
import { createIssue } from "./helpers.ts";
|
||||
|
||||
export const seoRules: DevToolbarRule[] = [
|
||||
{
|
||||
id: "seo/document",
|
||||
category: "seo",
|
||||
defaultSeverity: "warning",
|
||||
description: "Validates core document metadata.",
|
||||
run: ({ document }) => {
|
||||
const issues = [];
|
||||
const title = document.title.trim();
|
||||
if (!title)
|
||||
issues.push(
|
||||
createIssue({
|
||||
ruleId: "seo/title-missing",
|
||||
category: "seo",
|
||||
severity: "error",
|
||||
title: "Page title is missing",
|
||||
message: "The document does not have a useful title.",
|
||||
recommendation: "Add a unique title describing this page.",
|
||||
}),
|
||||
);
|
||||
else if (title.length < 20 || title.length > 65)
|
||||
issues.push(
|
||||
createIssue({
|
||||
ruleId: "seo/title-length",
|
||||
category: "seo",
|
||||
severity: "warning",
|
||||
title: "Page title length may be suboptimal",
|
||||
message: `The title contains ${title.length} characters.`,
|
||||
recommendation: "Keep most titles between about 20 and 65 characters.",
|
||||
confidence: "medium",
|
||||
}),
|
||||
);
|
||||
const description = document
|
||||
.querySelector<HTMLMetaElement>('meta[name="description"]')
|
||||
?.content.trim();
|
||||
if (!description)
|
||||
issues.push(
|
||||
createIssue({
|
||||
ruleId: "seo/description-missing",
|
||||
category: "seo",
|
||||
severity: "warning",
|
||||
title: "Meta description is missing",
|
||||
message: "Search and social previews may not have a useful description.",
|
||||
recommendation: "Add a unique meta description for the page.",
|
||||
}),
|
||||
);
|
||||
else if (description.length < 70 || description.length > 170)
|
||||
issues.push(
|
||||
createIssue({
|
||||
ruleId: "seo/description-length",
|
||||
category: "seo",
|
||||
severity: "suggestion",
|
||||
title: "Meta description length may be suboptimal",
|
||||
message: `The description contains ${description.length} characters.`,
|
||||
recommendation: "Aim for a concise description of roughly 70–170 characters.",
|
||||
confidence: "medium",
|
||||
}),
|
||||
);
|
||||
if (!document.querySelector('meta[name="viewport"]'))
|
||||
issues.push(
|
||||
createIssue({
|
||||
ruleId: "seo/viewport-missing",
|
||||
category: "seo",
|
||||
severity: "error",
|
||||
title: "Viewport meta tag is missing",
|
||||
message: "The page may render incorrectly on mobile devices.",
|
||||
recommendation: "Add width=device-width, initial-scale=1.",
|
||||
}),
|
||||
);
|
||||
if (!document.querySelector('link[rel="canonical"]'))
|
||||
issues.push(
|
||||
createIssue({
|
||||
ruleId: "seo/canonical-missing",
|
||||
category: "seo",
|
||||
severity: "suggestion",
|
||||
title: "Canonical URL is missing",
|
||||
message: "Search engines have no explicit preferred URL for this page.",
|
||||
recommendation: "Add a canonical link for public indexable pages.",
|
||||
confidence: "medium",
|
||||
}),
|
||||
);
|
||||
const h1s = document.querySelectorAll("h1");
|
||||
if (h1s.length === 0)
|
||||
issues.push(
|
||||
createIssue({
|
||||
ruleId: "seo/h1-missing",
|
||||
category: "seo",
|
||||
severity: "warning",
|
||||
title: "Page has no h1 heading",
|
||||
message: "The page lacks a clear primary heading.",
|
||||
recommendation: "Add one descriptive h1.",
|
||||
}),
|
||||
);
|
||||
if (h1s.length > 1)
|
||||
issues.push(
|
||||
createIssue({
|
||||
ruleId: "seo/multiple-h1",
|
||||
category: "seo",
|
||||
severity: "suggestion",
|
||||
title: "Page has multiple h1 headings",
|
||||
message: `Found ${h1s.length} h1 elements.`,
|
||||
recommendation:
|
||||
"Use one clear primary page heading unless multiple h1 elements are intentional.",
|
||||
confidence: "medium",
|
||||
}),
|
||||
);
|
||||
return issues;
|
||||
},
|
||||
},
|
||||
];
|
||||
Reference in New Issue
Block a user