@rivi-software/agentic-seo-check 0.3.0 → 0.5.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/README.md +29 -5
- package/dist/cli.js +20 -0
- package/dist/cli.js.map +1 -1
- package/dist/core/aeo/audit.d.ts +8 -0
- package/dist/core/aeo/audit.js +101 -17
- package/dist/core/aeo/audit.js.map +1 -1
- package/dist/core/aeo/bots.d.ts +48 -0
- package/dist/core/aeo/bots.js +151 -5
- package/dist/core/aeo/bots.js.map +1 -1
- package/dist/core/aeo/fixes.d.ts +38 -0
- package/dist/core/aeo/fixes.js +111 -0
- package/dist/core/aeo/fixes.js.map +1 -0
- package/dist/core/aeo/site-rules.d.ts +5 -0
- package/dist/core/aeo/site-rules.js +293 -10
- package/dist/core/aeo/site-rules.js.map +1 -1
- package/dist/core/audit.d.ts +8 -0
- package/dist/core/audit.js +144 -25
- package/dist/core/audit.js.map +1 -1
- package/dist/core/crawl.js +43 -4
- package/dist/core/crawl.js.map +1 -1
- package/dist/core/html/answer.d.ts +5 -2
- package/dist/core/html/answer.js +25 -7
- package/dist/core/html/answer.js.map +1 -1
- package/dist/core/html/dom.d.ts +13 -1
- package/dist/core/html/dom.js +30 -2
- package/dist/core/html/dom.js.map +1 -1
- package/dist/core/html/extract.js +122 -30
- package/dist/core/html/extract.js.map +1 -1
- package/dist/core/html/head.js +114 -19
- package/dist/core/html/head.js.map +1 -1
- package/dist/core/html/resources.js +15 -7
- package/dist/core/html/resources.js.map +1 -1
- package/dist/core/html/structured-data.js +21 -5
- package/dist/core/html/structured-data.js.map +1 -1
- package/dist/core/lighthouse.d.ts +2 -5
- package/dist/core/lighthouse.js +290 -5
- package/dist/core/lighthouse.js.map +1 -1
- package/dist/core/performance-fixes.d.ts +25 -0
- package/dist/core/performance-fixes.js +446 -0
- package/dist/core/performance-fixes.js.map +1 -0
- package/dist/core/performance-rules.js +56 -8
- package/dist/core/performance-rules.js.map +1 -1
- package/dist/core/recheck.d.ts +68 -0
- package/dist/core/recheck.js +247 -0
- package/dist/core/recheck.js.map +1 -0
- package/dist/core/render-fixes.d.ts +27 -0
- package/dist/core/render-fixes.js +164 -0
- package/dist/core/render-fixes.js.map +1 -0
- package/dist/core/render-rules.js +60 -21
- package/dist/core/render-rules.js.map +1 -1
- package/dist/core/report/markdown.js +68 -1
- package/dist/core/report/markdown.js.map +1 -1
- package/dist/core/robots.d.ts +5 -0
- package/dist/core/robots.js +8 -0
- package/dist/core/robots.js.map +1 -1
- package/dist/core/rules/aeo.js +339 -25
- package/dist/core/rules/aeo.js.map +1 -1
- package/dist/core/rules/canonical.js +107 -11
- package/dist/core/rules/canonical.js.map +1 -1
- package/dist/core/rules/content.js +65 -7
- package/dist/core/rules/content.js.map +1 -1
- package/dist/core/rules/document.js +73 -8
- package/dist/core/rules/document.js.map +1 -1
- package/dist/core/rules/fix.d.ts +55 -0
- package/dist/core/rules/fix.js +159 -0
- package/dist/core/rules/fix.js.map +1 -0
- package/dist/core/rules/head.js +81 -4
- package/dist/core/rules/head.js.map +1 -1
- package/dist/core/rules/headings.js +92 -9
- package/dist/core/rules/headings.js.map +1 -1
- package/dist/core/rules/hreflang.js +79 -9
- package/dist/core/rules/hreflang.js.map +1 -1
- package/dist/core/rules/images.js +49 -0
- package/dist/core/rules/images.js.map +1 -1
- package/dist/core/rules/index.d.ts +11 -0
- package/dist/core/rules/index.js +21 -1
- package/dist/core/rules/index.js.map +1 -1
- package/dist/core/rules/indexing.js +108 -1
- package/dist/core/rules/indexing.js.map +1 -1
- package/dist/core/rules/keyword.js +92 -2
- package/dist/core/rules/keyword.js.map +1 -1
- package/dist/core/rules/links.js +42 -0
- package/dist/core/rules/links.js.map +1 -1
- package/dist/core/rules/loading.js +54 -1
- package/dist/core/rules/loading.js.map +1 -1
- package/dist/core/rules/meta.js +40 -6
- package/dist/core/rules/meta.js.map +1 -1
- package/dist/core/rules/page-fixes.d.ts +39 -0
- package/dist/core/rules/page-fixes.js +107 -0
- package/dist/core/rules/page-fixes.js.map +1 -0
- package/dist/core/rules/quoted-tags.d.ts +23 -0
- package/dist/core/rules/quoted-tags.js +79 -0
- package/dist/core/rules/quoted-tags.js.map +1 -0
- package/dist/core/rules/response.js +71 -7
- package/dist/core/rules/response.js.map +1 -1
- package/dist/core/rules/rule.d.ts +17 -4
- package/dist/core/rules/rule.js +11 -2
- package/dist/core/rules/rule.js.map +1 -1
- package/dist/core/rules/social.js +101 -10
- package/dist/core/rules/social.js.map +1 -1
- package/dist/core/rules/structured-data-fixes.d.ts +33 -0
- package/dist/core/rules/structured-data-fixes.js +469 -0
- package/dist/core/rules/structured-data-fixes.js.map +1 -0
- package/dist/core/rules/structured-data-values.js +242 -31
- package/dist/core/rules/structured-data-values.js.map +1 -1
- package/dist/core/rules/structured-data.js +171 -4
- package/dist/core/rules/structured-data.js.map +1 -1
- package/dist/core/rules/title.js +39 -5
- package/dist/core/rules/title.js.map +1 -1
- package/dist/core/rules/urls.js +166 -0
- package/dist/core/rules/urls.js.map +1 -1
- package/dist/core/site-fixes.d.ts +111 -0
- package/dist/core/site-fixes.js +650 -0
- package/dist/core/site-fixes.js.map +1 -0
- package/dist/core/site-rules.js +481 -54
- package/dist/core/site-rules.js.map +1 -1
- package/dist/core/sitemap.d.ts +16 -0
- package/dist/core/sitemap.js +34 -0
- package/dist/core/sitemap.js.map +1 -1
- package/dist/core/types.d.ts +223 -3
- package/dist/core/types.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +16 -2
- package/dist/mcp.js.map +1 -1
- package/package.json +1 -1
- package/skills/seo-audit/SKILL.md +35 -5
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { quote } from "../html/dom.js";
|
|
1
2
|
import { hostOf, normalizeUrl, resolveUrl, samePath } from "../url.js";
|
|
3
|
+
import { addHtml, escapeAttribute, removeEdit, replaceEdit, setAttributeEdit, } from "./fix.js";
|
|
4
|
+
import { liveUrl, ORIGIN_WRITE } from "./quoted-tags.js";
|
|
2
5
|
import { cap, defineRule, GOOGLE_DOCS } from "./rule.js";
|
|
3
6
|
const SELECTOR = 'head > link[rel="canonical"]';
|
|
4
7
|
const LOCAL_HOSTS = new Set([
|
|
@@ -47,6 +50,52 @@ export const headerCanonicals = (response, base) => {
|
|
|
47
50
|
return out;
|
|
48
51
|
};
|
|
49
52
|
const stripTrailingSlash = (url) => url.endsWith("/") && new URL(url).pathname !== "/" ? url.slice(0, -1) : url;
|
|
53
|
+
/** `url` for an href on the live site, and what the agent writes when its origin is a slot. */
|
|
54
|
+
const hrefFor = (page, url) => {
|
|
55
|
+
const local = isLocalHost(page.url) || page.url.startsWith("file:");
|
|
56
|
+
return local
|
|
57
|
+
? { href: liveUrl(url, true), write: ORIGIN_WRITE }
|
|
58
|
+
: { href: escapeAttribute(url) };
|
|
59
|
+
};
|
|
60
|
+
/** This page's own URL, for a self-referencing canonical: no fragment, no tracking parameters. */
|
|
61
|
+
const selfUrl = (page) => normalizeUrl(page.url) ?? page.url;
|
|
62
|
+
/** The edit that points the canonical <link> the page reads at `url`. */
|
|
63
|
+
const pointCanonical = (page, url, write) => {
|
|
64
|
+
const { href, write: originWrite } = hrefFor(page, url);
|
|
65
|
+
const tag = page.markup.canonicals[0] ?? null;
|
|
66
|
+
const text = [originWrite, write].filter(Boolean).join(" ") || undefined;
|
|
67
|
+
return tag === null
|
|
68
|
+
? addHtml("head", `<link rel="canonical" href="${href}">`, text)
|
|
69
|
+
: setAttributeEdit(tag, SELECTOR, "href", href, text);
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* The edit of the Link header that leaves only the canonical entries `keep` keeps, and every entry
|
|
73
|
+
* that is not a canonical (preload, preconnect) as it was.
|
|
74
|
+
*/
|
|
75
|
+
const linkHeaderEdit = (header, base, keep, write) => {
|
|
76
|
+
const kept = [];
|
|
77
|
+
for (const match of header.slice(0, 16_384).matchAll(LINK_ENTRY)) {
|
|
78
|
+
const entry = match[0].trim().replace(/,$/u, "").trim();
|
|
79
|
+
const [canonical] = headerCanonicals({
|
|
80
|
+
status: null,
|
|
81
|
+
finalUrl: base,
|
|
82
|
+
redirectChain: [],
|
|
83
|
+
timeMs: null,
|
|
84
|
+
contentType: null,
|
|
85
|
+
headers: { link: entry },
|
|
86
|
+
}, base);
|
|
87
|
+
if (canonical === undefined || keep(canonical)) {
|
|
88
|
+
kept.push(entry);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const current = quote(`Link: ${header}`);
|
|
92
|
+
return kept.length === 0
|
|
93
|
+
? removeEdit("http", current, { at: "Link", write })
|
|
94
|
+
: replaceEdit("http", current, `Link: ${kept.join(", ")}`, {
|
|
95
|
+
at: "Link",
|
|
96
|
+
write,
|
|
97
|
+
});
|
|
98
|
+
};
|
|
50
99
|
export const canonicalRules = [
|
|
51
100
|
defineRule({
|
|
52
101
|
id: "canonical.missing",
|
|
@@ -57,7 +106,12 @@ export const canonicalRules = [
|
|
|
57
106
|
docs: GOOGLE_DOCS.canonical,
|
|
58
107
|
}, ({ page, response }, make) => page.canonicals.length === 0 &&
|
|
59
108
|
headerCanonicals(response, page.url).length === 0
|
|
60
|
-
? make("Page has no canonical link.", {
|
|
109
|
+
? make("Page has no canonical link.", {
|
|
110
|
+
fix: [
|
|
111
|
+
pointCanonical(page, selfUrl(page), "If this page is a copy of another one, name that page's URL instead."),
|
|
112
|
+
],
|
|
113
|
+
selector: SELECTOR,
|
|
114
|
+
})
|
|
61
115
|
: null),
|
|
62
116
|
defineRule({
|
|
63
117
|
id: "canonical.header-conflict",
|
|
@@ -71,16 +125,30 @@ export const canonicalRules = [
|
|
|
71
125
|
const targets = new Set(
|
|
72
126
|
// Google treats /page and /page/ as two URLs, so a trailing slash is a real difference.
|
|
73
127
|
[...header, ...(page.canonical ? [page.canonical] : [])].map((url) => normalizeUrl(url) ?? url));
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
128
|
+
if (header.length === 0 || targets.size <= 1) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
// Keep the <link> in <head> the page's author wrote; with none, keep the header's first URL.
|
|
132
|
+
const keepUrl = normalizeUrl(page.canonical ?? header[0] ?? "");
|
|
133
|
+
let firstKept = false;
|
|
134
|
+
const edit = linkHeaderEdit(response?.headers["link"] ?? "", page.url, (target) => {
|
|
135
|
+
if (page.canonical || firstKept) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
firstKept = (normalizeUrl(target) ?? target) === keepUrl;
|
|
139
|
+
return firstKept;
|
|
140
|
+
}, page.canonical
|
|
141
|
+
? `Name the canonical once, in the <link> in <head>. If the header's URL is the right one, point the <link> at it and remove the header entry all the same. A CDN, plugin or platform rule often adds the header.`
|
|
142
|
+
: "Keep one canonical URL in the header: the URL this page should rank under.");
|
|
143
|
+
return make(page.canonical
|
|
144
|
+
? `The Link header names ${header.join(" and ")} as canonical, but <link rel="canonical"> names ${page.canonical}.`
|
|
145
|
+
: `The Link header names ${header.length} different canonical URLs.`, {
|
|
146
|
+
fix: [edit],
|
|
147
|
+
evidence: {
|
|
148
|
+
linkHeader: cap(header),
|
|
149
|
+
htmlCanonical: page.canonical,
|
|
150
|
+
},
|
|
151
|
+
});
|
|
84
152
|
}),
|
|
85
153
|
defineRule({
|
|
86
154
|
id: "canonical.multiple",
|
|
@@ -94,7 +162,25 @@ export const canonicalRules = [
|
|
|
94
162
|
return null;
|
|
95
163
|
}
|
|
96
164
|
const distinct = new Set(page.canonicals.map((c) => c.trim()));
|
|
165
|
+
// Keep the canonical that names this page, if one does, else the first; remove the others.
|
|
166
|
+
const self = normalizeUrl(page.url);
|
|
167
|
+
const selfIndex = page.canonicals.findIndex((c) => {
|
|
168
|
+
const url = resolveUrl(c, page.url);
|
|
169
|
+
return url !== null && normalizeUrl(url) === self;
|
|
170
|
+
});
|
|
171
|
+
const keep = Math.max(selfIndex, 0);
|
|
172
|
+
const removals = page.markup.canonicals.flatMap((tag, i) => i === keep
|
|
173
|
+
? []
|
|
174
|
+
: [
|
|
175
|
+
removeEdit("html", tag, {
|
|
176
|
+
at: SELECTOR,
|
|
177
|
+
write: distinct.size > 1 && selfIndex === -1
|
|
178
|
+
? `This keeps the first canonical, ${page.canonicals[keep] ?? ""}. If another one names the URL this page should rank under, keep that one instead.`
|
|
179
|
+
: undefined,
|
|
180
|
+
}),
|
|
181
|
+
]);
|
|
97
182
|
return make(`Page has ${page.canonicals.length} canonical links${distinct.size > 1 ? " with different targets" : " (identical)"}.`, {
|
|
183
|
+
fix: removals,
|
|
98
184
|
selector: SELECTOR,
|
|
99
185
|
severity: distinct.size > 1 ? "error" : "warning",
|
|
100
186
|
evidence: { canonicals: cap(page.canonicals) },
|
|
@@ -115,6 +201,9 @@ export const canonicalRules = [
|
|
|
115
201
|
return page.canonical
|
|
116
202
|
? null
|
|
117
203
|
: make(`Canonical href "${first}" is not a valid URL.`, {
|
|
204
|
+
fix: [
|
|
205
|
+
pointCanonical(page, selfUrl(page), "This points the canonical at this page; if it was meant for another page, write that page's absolute URL."),
|
|
206
|
+
],
|
|
118
207
|
selector: SELECTOR,
|
|
119
208
|
evidence: { href: first },
|
|
120
209
|
});
|
|
@@ -134,6 +223,7 @@ export const canonicalRules = [
|
|
|
134
223
|
return /^https?:\/\//iu.test(first)
|
|
135
224
|
? null
|
|
136
225
|
: make(`Canonical href "${first}" is relative.`, {
|
|
226
|
+
fix: [pointCanonical(page, page.canonical)],
|
|
137
227
|
selector: SELECTOR,
|
|
138
228
|
evidence: { href: first, resolved: page.canonical },
|
|
139
229
|
});
|
|
@@ -152,6 +242,9 @@ export const canonicalRules = [
|
|
|
152
242
|
return page.url.startsWith("https://") &&
|
|
153
243
|
page.canonical.startsWith("http://")
|
|
154
244
|
? make("Canonical uses http:// while the page is served over https://.", {
|
|
245
|
+
fix: [
|
|
246
|
+
pointCanonical(page, page.canonical.replace(/^http:/iu, "https:")),
|
|
247
|
+
],
|
|
155
248
|
selector: SELECTOR,
|
|
156
249
|
evidence: { canonical: page.canonical, page: page.url },
|
|
157
250
|
})
|
|
@@ -181,6 +274,9 @@ export const canonicalRules = [
|
|
|
181
274
|
return same
|
|
182
275
|
? null
|
|
183
276
|
: make(`Canonical points to ${page.canonical}, not to this page.`, {
|
|
277
|
+
fix: [
|
|
278
|
+
pointCanonical(page, selfUrl(page), `Only if this page should rank on its own. If it is a copy of ${page.canonical} on purpose (a print view, a filtered list, a tracking URL), keep the canonical as it is.`),
|
|
279
|
+
],
|
|
184
280
|
selector: SELECTOR,
|
|
185
281
|
evidence: {
|
|
186
282
|
canonical: page.canonical,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canonical.js","sourceRoot":"","sources":["../../../src/core/rules/canonical.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"canonical.js","sourceRoot":"","sources":["../../../src/core/rules/canonical.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
import { fleschReadingEase, hasPlaceholderText } from "../html/text.js";
|
|
2
|
+
import { addHtml, replaceHtml, slot } from "./fix.js";
|
|
3
|
+
import { bestBlock, rewriteBlock, sentences } from "./page-fixes.js";
|
|
2
4
|
import { defineRule, GOOGLE_DOCS } from "./rule.js";
|
|
5
|
+
/** Where new body copy goes: the page's main content, or the body when it marks none. */
|
|
6
|
+
const CONTENT_AT = "main, article, body";
|
|
7
|
+
/** Words in a sentence, for picking the hardest one to read. */
|
|
8
|
+
const wordsIn = (sentence) => sentence.split(/\s+/u).filter(Boolean).length;
|
|
9
|
+
/** Words in the longest sentence of a block; 0 for one with no full sentence, such as a menu item. */
|
|
10
|
+
const longestIn = (text) => {
|
|
11
|
+
// A loop, not Math.max(...): a long block can hold more sentences than a call takes arguments.
|
|
12
|
+
let most = 0;
|
|
13
|
+
for (const sentence of sentences(text)) {
|
|
14
|
+
most = Math.max(most, wordsIn(sentence));
|
|
15
|
+
}
|
|
16
|
+
return most;
|
|
17
|
+
};
|
|
18
|
+
const PLACEHOLDER_WRITE = "The text this part of the page was designed for (remove the element if it has none yet). Search the templates for other placeholder passages too.";
|
|
19
|
+
const READABILITY_WRITE = "This paragraph, which holds the page's longest sentence, rewritten in sentences of about 15-20 words with common words. Rewrite the page's other long sentences the same way, aiming for a Flesch score of 50-70.";
|
|
20
|
+
/** "Lorem ipsum" as the page writes it, with its capitals. */
|
|
21
|
+
const placeholderQuote = (text) => /lorem\s+ipsum/iu.exec(text)?.[0] ?? "Lorem ipsum";
|
|
22
|
+
/**
|
|
23
|
+
* Each block that holds placeholder text, rewritten whole; when none does (the text sits straight
|
|
24
|
+
* in a <div>), the words "Lorem ipsum" themselves.
|
|
25
|
+
*/
|
|
26
|
+
const placeholderFix = (blocks, text) => {
|
|
27
|
+
const found = blocks.filter((b) => hasPlaceholderText(b.text));
|
|
28
|
+
return found.length
|
|
29
|
+
? found.map((b) => rewriteBlock(b, "real copy", PLACEHOLDER_WRITE))
|
|
30
|
+
: [
|
|
31
|
+
replaceHtml(placeholderQuote(text), slot("real copy"), {
|
|
32
|
+
at: "body",
|
|
33
|
+
write: PLACEHOLDER_WRITE,
|
|
34
|
+
}),
|
|
35
|
+
];
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* The paragraph with the page's longest sentence, rewritten whole. A page whose blocks hold no full
|
|
39
|
+
* sentence (its text is menus and labels) gets new copy instead: words cut from the joined page
|
|
40
|
+
* text would not be in the source as written.
|
|
41
|
+
*/
|
|
42
|
+
const readabilityFix = (page) => {
|
|
43
|
+
const block = bestBlock(page.markup.blocks, (b) => longestIn(b.text));
|
|
44
|
+
return block
|
|
45
|
+
? rewriteBlock(block, "the same text in shorter sentences", READABILITY_WRITE)
|
|
46
|
+
: addHtml(CONTENT_AT, slot("shorter sentences"), "Rewrite the copy in shorter sentences with common words, aiming for a Flesch score of 50-70.");
|
|
47
|
+
};
|
|
3
48
|
export const contentRules = [
|
|
4
49
|
defineRule({
|
|
5
50
|
id: "content.empty",
|
|
@@ -9,7 +54,11 @@ export const contentRules = [
|
|
|
9
54
|
recommendation: "If the page is rendered client-side, use server-side rendering or static generation so the HTML carries the content. Re-run with render enabled to compare the rendered DOM.",
|
|
10
55
|
docs: GOOGLE_DOCS.javascript,
|
|
11
56
|
}, ({ page }, make) => page.wordCount === 0
|
|
12
|
-
? make("Page body has no visible text in the HTML response."
|
|
57
|
+
? make("Page body has no visible text in the HTML response.", {
|
|
58
|
+
fix: [
|
|
59
|
+
addHtml("body", `<main>\n <h1>${slot("page heading")}</h1>\n <p>${slot("page content")}</p>\n</main>`, "The page's heading and content, rendered on the server: with server-side rendering or static generation (or prerendering this route) the HTML response carries the text the JavaScript now builds in the browser."),
|
|
60
|
+
],
|
|
61
|
+
})
|
|
13
62
|
: null),
|
|
14
63
|
defineRule({
|
|
15
64
|
id: "content.thin",
|
|
@@ -19,6 +68,11 @@ export const contentRules = [
|
|
|
19
68
|
recommendation: "Add substantive, useful content, or set noindex if the page is a utility page (login, cart, thank-you). Thin pages rarely rank and can drag down site quality signals.",
|
|
20
69
|
}, ({ page, thresholds }, make) => page.wordCount > 0 && page.wordCount < thresholds.thinContentWords
|
|
21
70
|
? make(`Page has only ${page.wordCount} words of visible text (threshold ${thresholds.thinContentWords}).`, {
|
|
71
|
+
// Adding useful text cannot hurt; noindex would drop the page from search, so it is
|
|
72
|
+
// left to the author for utility pages.
|
|
73
|
+
fix: [
|
|
74
|
+
addHtml(CONTENT_AT, `<p>${slot("more content")}</p>`, `About ${thresholds.thinContentWords - page.wordCount} more words that answer what a visitor comes to this page for: details, examples, answers to common questions. If this is a utility page (login, cart, thank-you), add <meta name="robots" content="noindex"> instead.`),
|
|
75
|
+
],
|
|
22
76
|
evidence: {
|
|
23
77
|
wordCount: page.wordCount,
|
|
24
78
|
threshold: thresholds.thinContentWords,
|
|
@@ -32,7 +86,9 @@ export const contentRules = [
|
|
|
32
86
|
description: 'Placeholder text ("lorem ipsum") is present in the page.',
|
|
33
87
|
recommendation: "Replace the placeholder copy with real content before publishing.",
|
|
34
88
|
}, ({ page }, make) => hasPlaceholderText(page.text)
|
|
35
|
-
? make('Page contains "lorem ipsum" placeholder text.'
|
|
89
|
+
? make('Page contains "lorem ipsum" placeholder text.', {
|
|
90
|
+
fix: placeholderFix(page.markup.blocks, page.text),
|
|
91
|
+
})
|
|
36
92
|
: null),
|
|
37
93
|
defineRule({
|
|
38
94
|
id: "content.readability-low",
|
|
@@ -45,11 +101,13 @@ export const contentRules = [
|
|
|
45
101
|
return null;
|
|
46
102
|
}
|
|
47
103
|
const score = fleschReadingEase(page.text);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
:
|
|
104
|
+
if (score === null || score >= 30) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
return make(`Flesch Reading Ease is ${score} (very difficult).`, {
|
|
108
|
+
fix: [readabilityFix(page)],
|
|
109
|
+
evidence: { fleschReadingEase: score, wordCount: page.wordCount },
|
|
110
|
+
});
|
|
53
111
|
}),
|
|
54
112
|
];
|
|
55
113
|
//# sourceMappingURL=content.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content.js","sourceRoot":"","sources":["../../../src/core/rules/content.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"content.js","sourceRoot":"","sources":["../../../src/core/rules/content.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,31 @@
|
|
|
1
|
+
import { addHtml, escapeAttribute, setAttributeEdit, slot } from "./fix.js";
|
|
1
2
|
import { defineRule, GOOGLE_DOCS } from "./rule.js";
|
|
2
3
|
const BCP47 = /^[a-zA-Z]{2,3}(?:-[a-zA-Z0-9]{2,8})*$/u;
|
|
4
|
+
const VIEWPORT = 'head > meta[name="viewport"]';
|
|
5
|
+
const VIEWPORT_CODE = '<meta name="viewport" content="width=device-width, initial-scale=1">';
|
|
6
|
+
/** What the page itself says about its language, for the agent to confirm. */
|
|
7
|
+
const languageHint = (page) => {
|
|
8
|
+
const { locale } = page.og;
|
|
9
|
+
return locale ? ` The page's og:locale is "${locale.slice(0, 20)}".` : "";
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* The viewport content without what blocks zooming (user-scalable=no or 0, maximum-scale=1), or
|
|
13
|
+
* the standard viewport when nothing else is left.
|
|
14
|
+
*/
|
|
15
|
+
const zoomableViewport = (content) => {
|
|
16
|
+
const kept = content
|
|
17
|
+
.split(/[,;]/u)
|
|
18
|
+
.map((part) => part.trim())
|
|
19
|
+
.filter((part) => {
|
|
20
|
+
const v = part.toLowerCase().replaceAll(/\s/gu, "");
|
|
21
|
+
return (part !== "" &&
|
|
22
|
+
!/^user-scalable=(?:no|0)$/u.test(v) &&
|
|
23
|
+
!/^maximum-scale=1(?:\.0*)?$/u.test(v));
|
|
24
|
+
});
|
|
25
|
+
return kept.length > 0
|
|
26
|
+
? kept.join(", ")
|
|
27
|
+
: "width=device-width, initial-scale=1";
|
|
28
|
+
};
|
|
3
29
|
export const documentRules = [
|
|
4
30
|
defineRule({
|
|
5
31
|
id: "html.lang-missing",
|
|
@@ -9,19 +35,35 @@ export const documentRules = [
|
|
|
9
35
|
recommendation: 'Set <html lang="xx"> (for example "en", "nl", "en-GB") so search engines and screen readers know the content language.',
|
|
10
36
|
}, ({ page }, make) => page.lang
|
|
11
37
|
? null
|
|
12
|
-
: make("<html> has no lang attribute.", {
|
|
38
|
+
: make("<html> has no lang attribute.", {
|
|
39
|
+
fix: [
|
|
40
|
+
setAttributeEdit(page.markup.html, "html", "lang", slot("language code"), `The language the page is written in, as a BCP 47 tag: en, nl, en-GB.${languageHint(page)}`),
|
|
41
|
+
],
|
|
42
|
+
selector: "html",
|
|
43
|
+
})),
|
|
13
44
|
defineRule({
|
|
14
45
|
id: "html.lang-invalid",
|
|
15
46
|
category: "document",
|
|
16
47
|
severity: "warning",
|
|
17
48
|
description: "The <html lang> value is not a valid BCP 47 language tag.",
|
|
18
49
|
recommendation: 'Use a BCP 47 tag such as "en", "en-US" or "pt-BR".',
|
|
19
|
-
}, ({ page }, make) =>
|
|
20
|
-
|
|
50
|
+
}, ({ page }, make) => {
|
|
51
|
+
if (!page.lang || BCP47.test(page.lang)) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
// "en_US" is a locale written with an underscore; the tag is the same with a hyphen.
|
|
55
|
+
const hyphenated = page.lang.replaceAll("_", "-");
|
|
56
|
+
const exact = BCP47.test(hyphenated);
|
|
57
|
+
return make(`<html lang="${page.lang}"> is not a valid language tag.`, {
|
|
58
|
+
fix: [
|
|
59
|
+
setAttributeEdit(page.markup.html, "html", "lang", exact ? escapeAttribute(hyphenated) : slot("language code"), exact
|
|
60
|
+
? undefined
|
|
61
|
+
: `The language the page is written in, as a BCP 47 tag such as en, en-US or pt-BR, instead of "${page.lang.slice(0, 40)}".${languageHint(page)}`),
|
|
62
|
+
],
|
|
21
63
|
selector: "html",
|
|
22
64
|
evidence: { lang: page.lang },
|
|
23
|
-
})
|
|
24
|
-
|
|
65
|
+
});
|
|
66
|
+
}),
|
|
25
67
|
defineRule({
|
|
26
68
|
id: "viewport.missing",
|
|
27
69
|
category: "document",
|
|
@@ -32,7 +74,13 @@ export const documentRules = [
|
|
|
32
74
|
}, ({ page }, make) => page.viewport
|
|
33
75
|
? null
|
|
34
76
|
: make("Page has no viewport meta tag.", {
|
|
35
|
-
|
|
77
|
+
// An empty viewport tag gets its content; with none, the tag goes into <head>.
|
|
78
|
+
fix: [
|
|
79
|
+
page.markup.viewport
|
|
80
|
+
? setAttributeEdit(page.markup.viewport, VIEWPORT, "content", "width=device-width, initial-scale=1")
|
|
81
|
+
: addHtml("head", VIEWPORT_CODE),
|
|
82
|
+
],
|
|
83
|
+
selector: VIEWPORT,
|
|
36
84
|
})),
|
|
37
85
|
defineRule({
|
|
38
86
|
id: "viewport.not-scalable",
|
|
@@ -45,7 +93,12 @@ export const documentRules = [
|
|
|
45
93
|
return /user-scalable=(?:no|0)/u.test(v) ||
|
|
46
94
|
/maximum-scale=1(?:\.0*)?(?:,|$)/u.test(v)
|
|
47
95
|
? make("Viewport prevents users from zooming.", {
|
|
48
|
-
|
|
96
|
+
fix: [
|
|
97
|
+
page.markup.viewport
|
|
98
|
+
? setAttributeEdit(page.markup.viewport, VIEWPORT, "content", escapeAttribute(zoomableViewport(page.viewport ?? "")))
|
|
99
|
+
: addHtml("head", VIEWPORT_CODE),
|
|
100
|
+
],
|
|
101
|
+
selector: VIEWPORT,
|
|
49
102
|
evidence: { viewport: page.viewport },
|
|
50
103
|
})
|
|
51
104
|
: null;
|
|
@@ -61,6 +114,9 @@ export const documentRules = [
|
|
|
61
114
|
({ page, response }, make) => page.charset || /charset=/iu.test(response?.contentType ?? "")
|
|
62
115
|
? null
|
|
63
116
|
: make("No <meta charset> declaration found.", {
|
|
117
|
+
fix: [
|
|
118
|
+
addHtml("head", '<meta charset="utf-8">', "First in <head>, before any script, style or comment. If the file is saved in another encoding, save it as UTF-8 (or name that encoding)."),
|
|
119
|
+
],
|
|
64
120
|
selector: "head > meta[charset]",
|
|
65
121
|
})),
|
|
66
122
|
defineRule({
|
|
@@ -69,7 +125,13 @@ export const documentRules = [
|
|
|
69
125
|
severity: "info",
|
|
70
126
|
description: "The document does not start with <!DOCTYPE html>.",
|
|
71
127
|
recommendation: "Start the document with <!DOCTYPE html> to avoid quirks-mode rendering.",
|
|
72
|
-
}, ({ page }, make) => page.hasDoctype
|
|
128
|
+
}, ({ page }, make) => page.hasDoctype
|
|
129
|
+
? null
|
|
130
|
+
: make("Document has no HTML5 doctype.", {
|
|
131
|
+
fix: [
|
|
132
|
+
addHtml("document start", "<!doctype html>", "As the very first line of the document, before <html>."),
|
|
133
|
+
],
|
|
134
|
+
})),
|
|
73
135
|
defineRule({
|
|
74
136
|
id: "document.html-too-large",
|
|
75
137
|
category: "document",
|
|
@@ -78,6 +140,9 @@ export const documentRules = [
|
|
|
78
140
|
recommendation: "Reduce inline scripts/styles/data (large JSON state, inline SVG sprites). Very large documents slow parsing and may be truncated by crawlers.",
|
|
79
141
|
}, ({ page }, make) => page.htmlBytes > 1_500_000
|
|
80
142
|
? make(`HTML is ${(page.htmlBytes / 1_000_000).toFixed(1)} MB.`, {
|
|
143
|
+
fix: [
|
|
144
|
+
addHtml("body", `<script src="${slot("/assets/moved-inline-script.js")}" defer></script>`, "Move the largest inline block out of the HTML into a file the page loads: inline JavaScript or JSON state into a script file (or fetch the data), an inline SVG sprite into an .svg file used with <use href>, inline CSS into a stylesheet. Write that file's path in the slot, and remove the inline block."),
|
|
145
|
+
],
|
|
81
146
|
evidence: { bytes: page.htmlBytes },
|
|
82
147
|
})
|
|
83
148
|
: null),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document.js","sourceRoot":"","sources":["../../../src/core/rules/document.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"document.js","sourceRoot":"","sources":["../../../src/core/rules/document.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builders for the `fix` of a finding: the exact edits that resolve it. Rules quote what the page
|
|
3
|
+
* has (`PageMarkup`, `ImageInfo.tag`) and say what to write instead; `{{…}}` marks words only the
|
|
4
|
+
* author can write.
|
|
5
|
+
*/
|
|
6
|
+
import type { FixEdit, FixTarget } from "../types.js";
|
|
7
|
+
/** A slot in `code` for words the author writes, such as a title: `{{hint}}`. */
|
|
8
|
+
export declare const slot: (hint: string) => string;
|
|
9
|
+
/** A value for a double-quoted HTML attribute. Text that would read as a character reference keeps its `&` escaped. */
|
|
10
|
+
export declare const escapeAttribute: (value: string) => string;
|
|
11
|
+
/** Text for an HTML element's content. */
|
|
12
|
+
export declare const escapeText: (value: string) => string;
|
|
13
|
+
interface EditOptions {
|
|
14
|
+
url?: string;
|
|
15
|
+
at?: string;
|
|
16
|
+
write?: string;
|
|
17
|
+
}
|
|
18
|
+
/** Write `code` at `at` (a CSS selector: "head" adds to <head>) in `target`. */
|
|
19
|
+
export declare const addEdit: (target: FixTarget, at: string, code: string, options?: Omit<EditOptions, "at">) => FixEdit;
|
|
20
|
+
/** Swap `current` for `code` in `target`. */
|
|
21
|
+
export declare const replaceEdit: (target: FixTarget, current: string, code: string, options?: EditOptions) => FixEdit;
|
|
22
|
+
/** Delete `current` from `target`. */
|
|
23
|
+
export declare const removeEdit: (target: FixTarget, current: string, options?: Omit<EditOptions, "write"> & {
|
|
24
|
+
write?: string;
|
|
25
|
+
}) => FixEdit;
|
|
26
|
+
export declare const addHtml: (at: string, code: string, write?: string) => FixEdit;
|
|
27
|
+
export declare const replaceHtml: (current: string, code: string, options?: Omit<EditOptions, "url">) => FixEdit;
|
|
28
|
+
export declare const removeHtml: (current: string, at?: string) => FixEdit;
|
|
29
|
+
/**
|
|
30
|
+
* A change to a whole file the site serves (a script split, an image re-encoded) that no code can
|
|
31
|
+
* show: a replace at "whole file" with no `current`. `code` is a slot, and `write` says what to
|
|
32
|
+
* change and by how much, since a measurement is not text an agent can find in the file.
|
|
33
|
+
*/
|
|
34
|
+
export declare const rebuildEdit: (url: string | undefined, hint: string, write: string) => FixEdit;
|
|
35
|
+
/** A quote the extractor cut short, which no longer matches the source as a whole. */
|
|
36
|
+
export declare const isCut: (quoted: string) => boolean;
|
|
37
|
+
/**
|
|
38
|
+
* `tag` with attribute `name` set to `value` (replaced where the tag has it, else added before the
|
|
39
|
+
* `>`), or null when `tag` is not one whole start tag. `value` is written as given: escape it with
|
|
40
|
+
* `escapeAttribute` unless it holds a `{{…}}` slot.
|
|
41
|
+
*/
|
|
42
|
+
export declare const withAttribute: (tag: string, name: string, value: string | true) => string | null;
|
|
43
|
+
/** `tag` without attribute `name`, or null when `tag` is not one whole start tag. */
|
|
44
|
+
export declare const withoutAttribute: (tag: string, name: string) => string | null;
|
|
45
|
+
/**
|
|
46
|
+
* The edit that sets attribute `name` on the element `tag` quotes: a replace of the whole tag when
|
|
47
|
+
* it could be rewritten, else an add of the attribute on the element at `at`.
|
|
48
|
+
*/
|
|
49
|
+
export declare const setAttributeEdit: (tag: string | null, at: string, name: string, value: string | true, write?: string) => FixEdit;
|
|
50
|
+
/**
|
|
51
|
+
* `outer`, an element quoted whole (`<title>Old</title>`), with `content` between its tags; null
|
|
52
|
+
* when the quote was cut short.
|
|
53
|
+
*/
|
|
54
|
+
export declare const withContent: (outer: string, content: string) => string | null;
|
|
55
|
+
export {};
|