@empline/preflight 1.1.56 → 1.1.57
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/checks/accessibility/accessibility-validation.d.ts +10 -0
- package/dist/checks/accessibility/accessibility-validation.d.ts.map +1 -0
- package/dist/checks/accessibility/accessibility-validation.js +472 -0
- package/dist/checks/accessibility/accessibility-validation.js.map +1 -0
- package/dist/checks/seo/seo-validation.d.ts +10 -0
- package/dist/checks/seo/seo-validation.d.ts.map +1 -0
- package/dist/checks/seo/seo-validation.js +497 -0
- package/dist/checks/seo/seo-validation.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { PreflightCheckResult } from "../../core/types";
|
|
3
|
+
export declare const id = "accessibility/accessibility-validation";
|
|
4
|
+
export declare const name = "Accessibility Validation";
|
|
5
|
+
export declare const description = "Validates accessibility best practices (images, forms, ARIA, headings)";
|
|
6
|
+
export declare const category = "accessibility";
|
|
7
|
+
export declare const blocking = true;
|
|
8
|
+
export declare const tags: string[];
|
|
9
|
+
export declare function run(): Promise<PreflightCheckResult>;
|
|
10
|
+
//# sourceMappingURL=accessibility-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessibility-validation.d.ts","sourceRoot":"","sources":["../../../src/checks/accessibility/accessibility-validation.ts"],"names":[],"mappings":";AAiCA,OAAO,EAAE,oBAAoB,EAAoB,MAAM,kBAAkB,CAAC;AAG1E,eAAO,MAAM,EAAE,2CAA2C,CAAC;AAC3D,eAAO,MAAM,IAAI,6BAA6B,CAAC;AAC/C,eAAO,MAAM,WAAW,2EAA2E,CAAC;AACpG,eAAO,MAAM,QAAQ,kBAAkB,CAAC;AACxC,eAAO,MAAM,QAAQ,OAAO,CAAC;AAC7B,eAAO,MAAM,IAAI,UAA+D,CAAC;AAuRjF,wBAAsB,GAAG,IAAI,OAAO,CAAC,oBAAoB,CAAC,CAqEzD"}
|
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.tags = exports.blocking = exports.category = exports.description = exports.name = exports.id = void 0;
|
|
38
|
+
exports.run = run;
|
|
39
|
+
/**
|
|
40
|
+
* Accessibility Validation Preflight (BLOCKING)
|
|
41
|
+
*
|
|
42
|
+
* Validates accessibility best practices in React/Next.js components:
|
|
43
|
+
*
|
|
44
|
+
* 1. Image Accessibility:
|
|
45
|
+
* - All <img> tags must have alt attribute
|
|
46
|
+
* - Next.js <Image> components must have alt attribute
|
|
47
|
+
* - Decorative images should have alt=""
|
|
48
|
+
*
|
|
49
|
+
* 2. Form Accessibility:
|
|
50
|
+
* - Form inputs must have associated labels (htmlFor/id or aria-label)
|
|
51
|
+
* - Buttons must have accessible text (children or aria-label)
|
|
52
|
+
* - Required fields should have aria-required
|
|
53
|
+
*
|
|
54
|
+
* 3. Interactive Element Accessibility:
|
|
55
|
+
* - onClick handlers on non-interactive elements need role and keyboard support
|
|
56
|
+
* - Links must have meaningful text (not just "click here")
|
|
57
|
+
* - Buttons should not be empty
|
|
58
|
+
*
|
|
59
|
+
* 4. ARIA Validation:
|
|
60
|
+
* - aria-* attributes must have valid values
|
|
61
|
+
* - role attributes must be valid ARIA roles
|
|
62
|
+
* - aria-hidden elements should not contain focusable content
|
|
63
|
+
*
|
|
64
|
+
* 5. Heading Structure:
|
|
65
|
+
* - Pages should have h1
|
|
66
|
+
* - Heading levels should not skip (h1 -> h3)
|
|
67
|
+
*/
|
|
68
|
+
const fs = __importStar(require("node:fs"));
|
|
69
|
+
const path = __importStar(require("node:path"));
|
|
70
|
+
const glob_1 = require("glob");
|
|
71
|
+
const console_chars_1 = require("../../utils/console-chars");
|
|
72
|
+
exports.id = "accessibility/accessibility-validation";
|
|
73
|
+
exports.name = "Accessibility Validation";
|
|
74
|
+
exports.description = "Validates accessibility best practices (images, forms, ARIA, headings)";
|
|
75
|
+
exports.category = "accessibility";
|
|
76
|
+
exports.blocking = true;
|
|
77
|
+
exports.tags = ["accessibility", "a11y", "wcag", "aria", "images", "forms"];
|
|
78
|
+
// Valid ARIA roles
|
|
79
|
+
const VALID_ROLES = new Set([
|
|
80
|
+
"alert", "alertdialog", "application", "article", "banner", "button",
|
|
81
|
+
"cell", "checkbox", "columnheader", "combobox", "complementary",
|
|
82
|
+
"contentinfo", "definition", "dialog", "directory", "document",
|
|
83
|
+
"feed", "figure", "form", "grid", "gridcell", "group", "heading",
|
|
84
|
+
"img", "link", "list", "listbox", "listitem", "log", "main",
|
|
85
|
+
"marquee", "math", "menu", "menubar", "menuitem", "menuitemcheckbox",
|
|
86
|
+
"menuitemradio", "navigation", "none", "note", "option", "presentation",
|
|
87
|
+
"progressbar", "radio", "radiogroup", "region", "row", "rowgroup",
|
|
88
|
+
"rowheader", "scrollbar", "search", "searchbox", "separator",
|
|
89
|
+
"slider", "spinbutton", "status", "switch", "tab", "table",
|
|
90
|
+
"tablist", "tabpanel", "term", "textbox", "timer", "toolbar",
|
|
91
|
+
"tooltip", "tree", "treegrid", "treeitem",
|
|
92
|
+
]);
|
|
93
|
+
// Patterns for meaningless link text
|
|
94
|
+
const MEANINGLESS_LINK_TEXT = [
|
|
95
|
+
/^click\s*here$/i,
|
|
96
|
+
/^here$/i,
|
|
97
|
+
/^read\s*more$/i,
|
|
98
|
+
/^learn\s*more$/i,
|
|
99
|
+
/^more$/i,
|
|
100
|
+
/^link$/i,
|
|
101
|
+
];
|
|
102
|
+
function getLineNumber(content, index) {
|
|
103
|
+
return content.substring(0, index).split("\n").length;
|
|
104
|
+
}
|
|
105
|
+
function checkImageAccessibility(content, filePath) {
|
|
106
|
+
const issues = [];
|
|
107
|
+
// Check <img> tags without alt
|
|
108
|
+
const imgRegex = /<img\s+(?![^>]*\balt\s*=)[^>]*>/gi;
|
|
109
|
+
let match;
|
|
110
|
+
while ((match = imgRegex.exec(content)) !== null) {
|
|
111
|
+
issues.push({
|
|
112
|
+
rule: "img-alt-required",
|
|
113
|
+
message: "<img> tag missing alt attribute",
|
|
114
|
+
file: filePath,
|
|
115
|
+
line: getLineNumber(content, match.index),
|
|
116
|
+
severity: "error",
|
|
117
|
+
suggestion: 'Add alt="description" or alt="" for decorative images',
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
// Check Next.js <Image> components without alt
|
|
121
|
+
const nextImageRegex = /<Image\s+(?![^>]*\balt\s*=)[^>]*\/?>/gi;
|
|
122
|
+
while ((match = nextImageRegex.exec(content)) !== null) {
|
|
123
|
+
issues.push({
|
|
124
|
+
rule: "next-image-alt-required",
|
|
125
|
+
message: "Next.js <Image> component missing alt attribute",
|
|
126
|
+
file: filePath,
|
|
127
|
+
line: getLineNumber(content, match.index),
|
|
128
|
+
severity: "error",
|
|
129
|
+
suggestion: 'Add alt="description" or alt="" for decorative images',
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return issues;
|
|
133
|
+
}
|
|
134
|
+
function checkFormAccessibility(content, filePath) {
|
|
135
|
+
const issues = [];
|
|
136
|
+
// Check for inputs without labels or aria-label
|
|
137
|
+
// This is a simplified check - looks for <input> not followed by aria-label or aria-labelledby
|
|
138
|
+
const inputRegex = /<input\s+(?![^>]*(?:aria-label|aria-labelledby)\s*=)[^>]*type\s*=\s*["'](?!hidden|submit|button|reset)[^"']*["'][^>]*>/gi;
|
|
139
|
+
let match;
|
|
140
|
+
while ((match = inputRegex.exec(content)) !== null) {
|
|
141
|
+
// Check if there's an id and if content has a matching htmlFor
|
|
142
|
+
const idMatch = match[0].match(/id\s*=\s*["']([^"']+)["']/);
|
|
143
|
+
if (idMatch) {
|
|
144
|
+
const labelRegex = new RegExp(`htmlFor\\s*=\\s*["']${idMatch[1]}["']`, "i");
|
|
145
|
+
if (!labelRegex.test(content)) {
|
|
146
|
+
issues.push({
|
|
147
|
+
rule: "input-label-required",
|
|
148
|
+
message: `Input with id="${idMatch[1]}" has no associated label`,
|
|
149
|
+
file: filePath,
|
|
150
|
+
line: getLineNumber(content, match.index),
|
|
151
|
+
severity: "warning",
|
|
152
|
+
suggestion: `Add <label htmlFor="${idMatch[1]}"> or aria-label attribute`,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
else if (!match[0].includes("aria-label")) {
|
|
157
|
+
issues.push({
|
|
158
|
+
rule: "input-label-required",
|
|
159
|
+
message: "Input has no id for label association and no aria-label",
|
|
160
|
+
file: filePath,
|
|
161
|
+
line: getLineNumber(content, match.index),
|
|
162
|
+
severity: "warning",
|
|
163
|
+
suggestion: "Add id with matching label htmlFor, or add aria-label",
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// Check for empty buttons
|
|
168
|
+
const emptyButtonRegex = /<button[^>]*>\s*<\/button>/gi;
|
|
169
|
+
while ((match = emptyButtonRegex.exec(content)) !== null) {
|
|
170
|
+
if (!match[0].includes("aria-label")) {
|
|
171
|
+
issues.push({
|
|
172
|
+
rule: "button-text-required",
|
|
173
|
+
message: "Empty button has no accessible text",
|
|
174
|
+
file: filePath,
|
|
175
|
+
line: getLineNumber(content, match.index),
|
|
176
|
+
severity: "error",
|
|
177
|
+
suggestion: "Add button text content or aria-label",
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// Check for icon-only buttons without aria-label
|
|
182
|
+
const iconButtonRegex = /<button[^>]*>\s*<(?:svg|Icon|[A-Z]\w*Icon)[^>]*\/?>\s*<\/button>/gi;
|
|
183
|
+
while ((match = iconButtonRegex.exec(content)) !== null) {
|
|
184
|
+
if (!match[0].includes("aria-label") && !match[0].includes("title")) {
|
|
185
|
+
issues.push({
|
|
186
|
+
rule: "icon-button-label-required",
|
|
187
|
+
message: "Icon-only button has no accessible text",
|
|
188
|
+
file: filePath,
|
|
189
|
+
line: getLineNumber(content, match.index),
|
|
190
|
+
severity: "error",
|
|
191
|
+
suggestion: 'Add aria-label="action description"',
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return issues;
|
|
196
|
+
}
|
|
197
|
+
function checkInteractiveElements(content, filePath) {
|
|
198
|
+
const issues = [];
|
|
199
|
+
// Check for onClick on non-interactive elements without role
|
|
200
|
+
const onClickDivRegex = /<div\s+(?![^>]*role\s*=)[^>]*onClick\s*=/gi;
|
|
201
|
+
let match;
|
|
202
|
+
while ((match = onClickDivRegex.exec(content)) !== null) {
|
|
203
|
+
issues.push({
|
|
204
|
+
rule: "div-onclick-needs-role",
|
|
205
|
+
message: "<div> with onClick needs role attribute for accessibility",
|
|
206
|
+
file: filePath,
|
|
207
|
+
line: getLineNumber(content, match.index),
|
|
208
|
+
severity: "warning",
|
|
209
|
+
suggestion: 'Add role="button" and tabIndex={0} and onKeyDown handler',
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
// Check for spans with onClick
|
|
213
|
+
const onClickSpanRegex = /<span\s+(?![^>]*role\s*=)[^>]*onClick\s*=/gi;
|
|
214
|
+
while ((match = onClickSpanRegex.exec(content)) !== null) {
|
|
215
|
+
issues.push({
|
|
216
|
+
rule: "span-onclick-needs-role",
|
|
217
|
+
message: "<span> with onClick needs role attribute for accessibility",
|
|
218
|
+
file: filePath,
|
|
219
|
+
line: getLineNumber(content, match.index),
|
|
220
|
+
severity: "warning",
|
|
221
|
+
suggestion: 'Add role="button" and tabIndex={0} and onKeyDown handler',
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
// Check for meaningless link text
|
|
225
|
+
for (const pattern of MEANINGLESS_LINK_TEXT) {
|
|
226
|
+
const linkRegex = new RegExp(`<(?:a|Link)[^>]*>\\s*${pattern.source}\\s*</(?:a|Link)>`, "gi");
|
|
227
|
+
while ((match = linkRegex.exec(content)) !== null) {
|
|
228
|
+
issues.push({
|
|
229
|
+
rule: "meaningful-link-text",
|
|
230
|
+
message: "Link has non-descriptive text",
|
|
231
|
+
file: filePath,
|
|
232
|
+
line: getLineNumber(content, match.index),
|
|
233
|
+
severity: "warning",
|
|
234
|
+
suggestion: "Use descriptive link text that explains the destination",
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return issues;
|
|
239
|
+
}
|
|
240
|
+
function checkAriaAttributes(content, filePath) {
|
|
241
|
+
const issues = [];
|
|
242
|
+
// Check for invalid role values
|
|
243
|
+
const roleRegex = /role\s*=\s*["']([^"']+)["']/gi;
|
|
244
|
+
let match;
|
|
245
|
+
while ((match = roleRegex.exec(content)) !== null) {
|
|
246
|
+
const role = match[1].toLowerCase();
|
|
247
|
+
if (!VALID_ROLES.has(role)) {
|
|
248
|
+
issues.push({
|
|
249
|
+
rule: "valid-aria-role",
|
|
250
|
+
message: `Invalid ARIA role: "${role}"`,
|
|
251
|
+
file: filePath,
|
|
252
|
+
line: getLineNumber(content, match.index),
|
|
253
|
+
severity: "error",
|
|
254
|
+
suggestion: `Use a valid ARIA role (e.g., button, link, dialog, etc.)`,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
// Check for aria-hidden on elements with focusable content
|
|
259
|
+
const ariaHiddenRegex = /aria-hidden\s*=\s*["']true["'][^>]*>[\s\S]*?<(?:button|a|input|select|textarea|Link)\s/gi;
|
|
260
|
+
while ((match = ariaHiddenRegex.exec(content)) !== null) {
|
|
261
|
+
issues.push({
|
|
262
|
+
rule: "aria-hidden-focusable",
|
|
263
|
+
message: "aria-hidden element contains focusable content",
|
|
264
|
+
file: filePath,
|
|
265
|
+
line: getLineNumber(content, match.index),
|
|
266
|
+
severity: "error",
|
|
267
|
+
suggestion: "Remove focusable elements from aria-hidden containers or add tabIndex={-1}",
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
// Check for aria-label on elements that don't support it (non-interactive)
|
|
271
|
+
// This is a simplified check
|
|
272
|
+
const ariaLabelOnDivRegex = /<div\s+(?![^>]*role\s*=)[^>]*aria-label\s*=/gi;
|
|
273
|
+
while ((match = ariaLabelOnDivRegex.exec(content)) !== null) {
|
|
274
|
+
issues.push({
|
|
275
|
+
rule: "aria-label-valid-element",
|
|
276
|
+
message: "aria-label on <div> without role may not be announced",
|
|
277
|
+
file: filePath,
|
|
278
|
+
line: getLineNumber(content, match.index),
|
|
279
|
+
severity: "warning",
|
|
280
|
+
suggestion: "Add a role attribute or use aria-labelledby instead",
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
return issues;
|
|
284
|
+
}
|
|
285
|
+
function checkHeadingStructure(content, filePath) {
|
|
286
|
+
const issues = [];
|
|
287
|
+
// Find all heading tags
|
|
288
|
+
const headingRegex = /<h([1-6])[^>]*>/gi;
|
|
289
|
+
const headings = [];
|
|
290
|
+
let match;
|
|
291
|
+
while ((match = headingRegex.exec(content)) !== null) {
|
|
292
|
+
headings.push({
|
|
293
|
+
level: parseInt(match[1], 10),
|
|
294
|
+
index: match.index,
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
if (headings.length === 0) {
|
|
298
|
+
return issues; // No headings to check
|
|
299
|
+
}
|
|
300
|
+
// Check for skipped heading levels
|
|
301
|
+
for (let i = 1; i < headings.length; i++) {
|
|
302
|
+
const prev = headings[i - 1];
|
|
303
|
+
const curr = headings[i];
|
|
304
|
+
// If going deeper, shouldn't skip levels (h1 -> h3 is bad, h1 -> h2 is fine)
|
|
305
|
+
if (curr.level > prev.level && curr.level - prev.level > 1) {
|
|
306
|
+
issues.push({
|
|
307
|
+
rule: "heading-order",
|
|
308
|
+
message: `Heading level skipped: h${prev.level} to h${curr.level}`,
|
|
309
|
+
file: filePath,
|
|
310
|
+
line: getLineNumber(content, curr.index),
|
|
311
|
+
severity: "warning",
|
|
312
|
+
suggestion: `Use h${prev.level + 1} instead of h${curr.level}`,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return issues;
|
|
317
|
+
}
|
|
318
|
+
async function run() {
|
|
319
|
+
const startTime = Date.now();
|
|
320
|
+
const findings = [];
|
|
321
|
+
// Find all TSX files
|
|
322
|
+
const files = await (0, glob_1.glob)([
|
|
323
|
+
"app/**/*.tsx",
|
|
324
|
+
"components/**/*.tsx",
|
|
325
|
+
"src/**/*.tsx",
|
|
326
|
+
], {
|
|
327
|
+
cwd: process.cwd(),
|
|
328
|
+
ignore: [
|
|
329
|
+
"**/node_modules/**",
|
|
330
|
+
"**/*.test.tsx",
|
|
331
|
+
"**/*.spec.tsx",
|
|
332
|
+
"**/*.stories.tsx",
|
|
333
|
+
],
|
|
334
|
+
});
|
|
335
|
+
let filesChecked = 0;
|
|
336
|
+
const allIssues = [];
|
|
337
|
+
for (const file of files) {
|
|
338
|
+
const filePath = path.join(process.cwd(), file);
|
|
339
|
+
if (!fs.existsSync(filePath))
|
|
340
|
+
continue;
|
|
341
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
342
|
+
// Skip files with accessibility ignore comment
|
|
343
|
+
if (content.includes("a11y-ignore") || content.includes("accessibility-ignore")) {
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
filesChecked++;
|
|
347
|
+
// Run all accessibility checks
|
|
348
|
+
allIssues.push(...checkImageAccessibility(content, file));
|
|
349
|
+
allIssues.push(...checkFormAccessibility(content, file));
|
|
350
|
+
allIssues.push(...checkInteractiveElements(content, file));
|
|
351
|
+
allIssues.push(...checkAriaAttributes(content, file));
|
|
352
|
+
allIssues.push(...checkHeadingStructure(content, file));
|
|
353
|
+
}
|
|
354
|
+
// Convert to findings
|
|
355
|
+
for (const issue of allIssues) {
|
|
356
|
+
findings.push({
|
|
357
|
+
level: issue.severity,
|
|
358
|
+
message: issue.message,
|
|
359
|
+
file: issue.file,
|
|
360
|
+
startLine: issue.line,
|
|
361
|
+
ruleId: issue.rule,
|
|
362
|
+
suggestion: issue.suggestion,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
const errors = findings.filter(f => f.level === "error");
|
|
366
|
+
const warnings = findings.filter(f => f.level === "warning");
|
|
367
|
+
return {
|
|
368
|
+
passed: errors.length === 0,
|
|
369
|
+
findings,
|
|
370
|
+
duration: Date.now() - startTime,
|
|
371
|
+
metadata: {
|
|
372
|
+
filesChecked,
|
|
373
|
+
totalIssues: allIssues.length,
|
|
374
|
+
errors: errors.length,
|
|
375
|
+
warnings: warnings.length,
|
|
376
|
+
},
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
async function main() {
|
|
380
|
+
console.log(`\n${console_chars_1.emoji.accessibility} ACCESSIBILITY VALIDATION`);
|
|
381
|
+
console.log((0, console_chars_1.createDivider)(65, "heavy"));
|
|
382
|
+
const result = await run();
|
|
383
|
+
const { filesChecked, totalIssues, errors, warnings } = result.metadata || {};
|
|
384
|
+
console.log(`\n${console_chars_1.emoji.search} Checking accessibility...`);
|
|
385
|
+
console.log(` Files checked: ${filesChecked}`);
|
|
386
|
+
console.log(` Total issues: ${totalIssues}`);
|
|
387
|
+
console.log(`\n${console_chars_1.emoji.chart} Summary:`);
|
|
388
|
+
console.log(` Errors: ${errors}`);
|
|
389
|
+
console.log(` Warnings: ${warnings}`);
|
|
390
|
+
if (result.passed && warnings === 0) {
|
|
391
|
+
console.log(`\n${console_chars_1.emoji.success} ACCESSIBILITY VALIDATION PASSED`);
|
|
392
|
+
console.log(`\nAll components follow accessibility best practices.`);
|
|
393
|
+
process.exit(0);
|
|
394
|
+
}
|
|
395
|
+
// Group findings by rule
|
|
396
|
+
const findingsByRule = new Map();
|
|
397
|
+
for (const finding of result.findings) {
|
|
398
|
+
const ruleId = finding.ruleId || "unknown";
|
|
399
|
+
if (!findingsByRule.has(ruleId)) {
|
|
400
|
+
findingsByRule.set(ruleId, []);
|
|
401
|
+
}
|
|
402
|
+
findingsByRule.get(ruleId).push(finding);
|
|
403
|
+
}
|
|
404
|
+
// Print errors
|
|
405
|
+
const errorFindings = result.findings.filter(f => f.level === "error");
|
|
406
|
+
if (errorFindings.length > 0) {
|
|
407
|
+
console.log(`\n${console_chars_1.emoji.error} Errors (blocking):`);
|
|
408
|
+
const errorsByRule = new Map();
|
|
409
|
+
for (const finding of errorFindings) {
|
|
410
|
+
const ruleId = finding.ruleId || "unknown";
|
|
411
|
+
if (!errorsByRule.has(ruleId)) {
|
|
412
|
+
errorsByRule.set(ruleId, []);
|
|
413
|
+
}
|
|
414
|
+
errorsByRule.get(ruleId).push(finding);
|
|
415
|
+
}
|
|
416
|
+
for (const [ruleId, ruleFindings] of errorsByRule) {
|
|
417
|
+
console.log(`\n ${ruleId} (${ruleFindings.length}):`);
|
|
418
|
+
for (const finding of ruleFindings.slice(0, 5)) {
|
|
419
|
+
console.log(` ${finding.file}:${finding.startLine}`);
|
|
420
|
+
console.log(` ${finding.message}`);
|
|
421
|
+
if (finding.suggestion) {
|
|
422
|
+
console.log(` ${console_chars_1.emoji.hint} ${finding.suggestion}`);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
if (ruleFindings.length > 5) {
|
|
426
|
+
console.log(` ... and ${ruleFindings.length - 5} more`);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
// Print warnings
|
|
431
|
+
const warningFindings = result.findings.filter(f => f.level === "warning");
|
|
432
|
+
if (warningFindings.length > 0) {
|
|
433
|
+
console.log(`\n${console_chars_1.emoji.warning} Warnings:`);
|
|
434
|
+
const warningsByRule = new Map();
|
|
435
|
+
for (const finding of warningFindings) {
|
|
436
|
+
const ruleId = finding.ruleId || "unknown";
|
|
437
|
+
if (!warningsByRule.has(ruleId)) {
|
|
438
|
+
warningsByRule.set(ruleId, []);
|
|
439
|
+
}
|
|
440
|
+
warningsByRule.get(ruleId).push(finding);
|
|
441
|
+
}
|
|
442
|
+
for (const [ruleId, ruleFindings] of warningsByRule) {
|
|
443
|
+
console.log(`\n ${ruleId} (${ruleFindings.length}):`);
|
|
444
|
+
for (const finding of ruleFindings.slice(0, 3)) {
|
|
445
|
+
console.log(` ${finding.file}:${finding.startLine}`);
|
|
446
|
+
console.log(` ${finding.message}`);
|
|
447
|
+
}
|
|
448
|
+
if (ruleFindings.length > 3) {
|
|
449
|
+
console.log(` ... and ${ruleFindings.length - 3} more`);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
console.log(`\n${console_chars_1.emoji.info} Accessibility Rules:`);
|
|
454
|
+
console.log(` - All images must have alt text`);
|
|
455
|
+
console.log(` - Form inputs need labels or aria-label`);
|
|
456
|
+
console.log(` - Buttons must have accessible text`);
|
|
457
|
+
console.log(` - onClick handlers need role & keyboard support`);
|
|
458
|
+
console.log(` - Heading levels should not skip`);
|
|
459
|
+
if (!result.passed) {
|
|
460
|
+
console.log(`\n${console_chars_1.emoji.error} ACCESSIBILITY VALIDATION FAILED`);
|
|
461
|
+
process.exit(1);
|
|
462
|
+
}
|
|
463
|
+
console.log(`\n${console_chars_1.emoji.warning} ACCESSIBILITY VALIDATION PASSED WITH WARNINGS`);
|
|
464
|
+
process.exit(0);
|
|
465
|
+
}
|
|
466
|
+
if (require.main === module) {
|
|
467
|
+
main().catch((err) => {
|
|
468
|
+
console.error(`${console_chars_1.emoji.error} Preflight failed:`, err);
|
|
469
|
+
process.exit(1);
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
//# sourceMappingURL=accessibility-validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessibility-validation.js","sourceRoot":"","sources":["../../../src/checks/accessibility/accessibility-validation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { PreflightCheckResult } from "../../core/types";
|
|
3
|
+
export declare const id = "seo/seo-validation";
|
|
4
|
+
export declare const name = "SEO Validation";
|
|
5
|
+
export declare const description = "Validates SEO best practices (metadata, Open Graph, structured data, technical SEO)";
|
|
6
|
+
export declare const category = "seo";
|
|
7
|
+
export declare const blocking = true;
|
|
8
|
+
export declare const tags: string[];
|
|
9
|
+
export declare function run(): Promise<PreflightCheckResult>;
|
|
10
|
+
//# sourceMappingURL=seo-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seo-validation.d.ts","sourceRoot":"","sources":["../../../src/checks/seo/seo-validation.ts"],"names":[],"mappings":";AAiCA,OAAO,EAAE,oBAAoB,EAAoB,MAAM,kBAAkB,CAAC;AAG1E,eAAO,MAAM,EAAE,uBAAuB,CAAC;AACvC,eAAO,MAAM,IAAI,mBAAmB,CAAC;AACrC,eAAO,MAAM,WAAW,wFAAwF,CAAC;AACjH,eAAO,MAAM,QAAQ,QAAQ,CAAC;AAC9B,eAAO,MAAM,QAAQ,OAAO,CAAC;AAC7B,eAAO,MAAM,IAAI,UAAgE,CAAC;AAwUlF,wBAAsB,GAAG,IAAI,OAAO,CAAC,oBAAoB,CAAC,CA0EzD"}
|
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.tags = exports.blocking = exports.category = exports.description = exports.name = exports.id = void 0;
|
|
38
|
+
exports.run = run;
|
|
39
|
+
/**
|
|
40
|
+
* SEO Validation Preflight (BLOCKING)
|
|
41
|
+
*
|
|
42
|
+
* Validates SEO best practices in Next.js applications:
|
|
43
|
+
*
|
|
44
|
+
* 1. Metadata Validation:
|
|
45
|
+
* - Pages must have title
|
|
46
|
+
* - Pages should have description
|
|
47
|
+
* - Title length (50-60 chars recommended)
|
|
48
|
+
* - Description length (150-160 chars recommended)
|
|
49
|
+
*
|
|
50
|
+
* 2. Open Graph Tags:
|
|
51
|
+
* - og:title, og:description, og:image for shareable pages
|
|
52
|
+
* - Twitter card meta tags
|
|
53
|
+
*
|
|
54
|
+
* 3. Structured Data:
|
|
55
|
+
* - JSON-LD schema.org markup validation
|
|
56
|
+
* - Required fields for common schema types
|
|
57
|
+
*
|
|
58
|
+
* 4. Technical SEO:
|
|
59
|
+
* - Canonical URLs
|
|
60
|
+
* - robots.txt existence
|
|
61
|
+
* - sitemap.xml existence
|
|
62
|
+
* - No duplicate h1 tags
|
|
63
|
+
*
|
|
64
|
+
* 5. Link SEO:
|
|
65
|
+
* - External links have rel="noopener noreferrer"
|
|
66
|
+
* - Internal links use Next.js Link component
|
|
67
|
+
*/
|
|
68
|
+
const fs = __importStar(require("node:fs"));
|
|
69
|
+
const path = __importStar(require("node:path"));
|
|
70
|
+
const glob_1 = require("glob");
|
|
71
|
+
const console_chars_1 = require("../../utils/console-chars");
|
|
72
|
+
exports.id = "seo/seo-validation";
|
|
73
|
+
exports.name = "SEO Validation";
|
|
74
|
+
exports.description = "Validates SEO best practices (metadata, Open Graph, structured data, technical SEO)";
|
|
75
|
+
exports.category = "seo";
|
|
76
|
+
exports.blocking = true;
|
|
77
|
+
exports.tags = ["seo", "metadata", "opengraph", "schema", "structured-data"];
|
|
78
|
+
// Title length recommendations
|
|
79
|
+
const TITLE_MIN_LENGTH = 30;
|
|
80
|
+
const TITLE_MAX_LENGTH = 60;
|
|
81
|
+
// Description length recommendations
|
|
82
|
+
const DESC_MIN_LENGTH = 120;
|
|
83
|
+
const DESC_MAX_LENGTH = 160;
|
|
84
|
+
function getLineNumber(content, index) {
|
|
85
|
+
return content.substring(0, index).split("\n").length;
|
|
86
|
+
}
|
|
87
|
+
function checkMetadataExports(content, filePath) {
|
|
88
|
+
const issues = [];
|
|
89
|
+
// Check for Next.js metadata export
|
|
90
|
+
const hasMetadataExport = /export\s+(?:const|async\s+function)\s+(?:metadata|generateMetadata)/i.test(content);
|
|
91
|
+
// Check for page.tsx files that should have metadata
|
|
92
|
+
const isPageFile = filePath.includes("/page.tsx") || filePath.includes("\\page.tsx");
|
|
93
|
+
const isLayoutFile = filePath.includes("/layout.tsx") || filePath.includes("\\layout.tsx");
|
|
94
|
+
// Skip non-page files and special pages
|
|
95
|
+
if (!isPageFile && !isLayoutFile) {
|
|
96
|
+
return issues;
|
|
97
|
+
}
|
|
98
|
+
// Skip API routes, error pages, loading pages
|
|
99
|
+
if (filePath.includes("/api/") ||
|
|
100
|
+
filePath.includes("error.tsx") ||
|
|
101
|
+
filePath.includes("loading.tsx") ||
|
|
102
|
+
filePath.includes("not-found.tsx")) {
|
|
103
|
+
return issues;
|
|
104
|
+
}
|
|
105
|
+
if (!hasMetadataExport && isPageFile) {
|
|
106
|
+
issues.push({
|
|
107
|
+
rule: "page-metadata-required",
|
|
108
|
+
message: "Page is missing metadata export",
|
|
109
|
+
file: filePath,
|
|
110
|
+
severity: "warning",
|
|
111
|
+
suggestion: "Add: export const metadata = { title: '...', description: '...' }",
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return issues;
|
|
115
|
+
}
|
|
116
|
+
function checkMetadataContent(content, filePath) {
|
|
117
|
+
const issues = [];
|
|
118
|
+
// Find metadata object
|
|
119
|
+
const metadataMatch = content.match(/export\s+const\s+metadata\s*(?::\s*Metadata)?\s*=\s*\{([\s\S]*?)\n\}/);
|
|
120
|
+
if (!metadataMatch) {
|
|
121
|
+
return issues;
|
|
122
|
+
}
|
|
123
|
+
const metadataContent = metadataMatch[1];
|
|
124
|
+
const metadataStart = metadataMatch.index || 0;
|
|
125
|
+
// Check for title
|
|
126
|
+
const titleMatch = metadataContent.match(/title\s*:\s*["'`]([^"'`]+)["'`]/);
|
|
127
|
+
if (!titleMatch) {
|
|
128
|
+
// Check for template-based title
|
|
129
|
+
const hasTitleTemplate = metadataContent.includes("title:") &&
|
|
130
|
+
(metadataContent.includes("template:") || metadataContent.includes("default:"));
|
|
131
|
+
if (!hasTitleTemplate) {
|
|
132
|
+
issues.push({
|
|
133
|
+
rule: "metadata-title-required",
|
|
134
|
+
message: "Metadata missing title",
|
|
135
|
+
file: filePath,
|
|
136
|
+
line: getLineNumber(content, metadataStart),
|
|
137
|
+
severity: "error",
|
|
138
|
+
suggestion: "Add title to metadata",
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
const titleLength = titleMatch[1].length;
|
|
144
|
+
if (titleLength < TITLE_MIN_LENGTH) {
|
|
145
|
+
issues.push({
|
|
146
|
+
rule: "metadata-title-length",
|
|
147
|
+
message: `Title too short (${titleLength} chars, recommend ${TITLE_MIN_LENGTH}-${TITLE_MAX_LENGTH})`,
|
|
148
|
+
file: filePath,
|
|
149
|
+
line: getLineNumber(content, metadataStart),
|
|
150
|
+
severity: "info",
|
|
151
|
+
suggestion: "Consider a more descriptive title",
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
if (titleLength > TITLE_MAX_LENGTH) {
|
|
155
|
+
issues.push({
|
|
156
|
+
rule: "metadata-title-length",
|
|
157
|
+
message: `Title too long (${titleLength} chars, recommend ${TITLE_MIN_LENGTH}-${TITLE_MAX_LENGTH})`,
|
|
158
|
+
file: filePath,
|
|
159
|
+
line: getLineNumber(content, metadataStart),
|
|
160
|
+
severity: "warning",
|
|
161
|
+
suggestion: "Title may be truncated in search results",
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Check for description
|
|
166
|
+
const descMatch = metadataContent.match(/description\s*:\s*["'`]([^"'`]+)["'`]/);
|
|
167
|
+
if (!descMatch) {
|
|
168
|
+
issues.push({
|
|
169
|
+
rule: "metadata-description-required",
|
|
170
|
+
message: "Metadata missing description",
|
|
171
|
+
file: filePath,
|
|
172
|
+
line: getLineNumber(content, metadataStart),
|
|
173
|
+
severity: "warning",
|
|
174
|
+
suggestion: "Add description to metadata for better SEO",
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
const descLength = descMatch[1].length;
|
|
179
|
+
if (descLength < DESC_MIN_LENGTH) {
|
|
180
|
+
issues.push({
|
|
181
|
+
rule: "metadata-description-length",
|
|
182
|
+
message: `Description short (${descLength} chars, recommend ${DESC_MIN_LENGTH}-${DESC_MAX_LENGTH})`,
|
|
183
|
+
file: filePath,
|
|
184
|
+
line: getLineNumber(content, metadataStart),
|
|
185
|
+
severity: "info",
|
|
186
|
+
suggestion: "Consider a more detailed description",
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
if (descLength > DESC_MAX_LENGTH) {
|
|
190
|
+
issues.push({
|
|
191
|
+
rule: "metadata-description-length",
|
|
192
|
+
message: `Description long (${descLength} chars, recommend ${DESC_MIN_LENGTH}-${DESC_MAX_LENGTH})`,
|
|
193
|
+
file: filePath,
|
|
194
|
+
line: getLineNumber(content, metadataStart),
|
|
195
|
+
severity: "info",
|
|
196
|
+
suggestion: "Description may be truncated in search results",
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return issues;
|
|
201
|
+
}
|
|
202
|
+
function checkOpenGraph(content, filePath) {
|
|
203
|
+
const issues = [];
|
|
204
|
+
// Skip non-shareable pages (admin, settings, auth)
|
|
205
|
+
if (filePath.includes("/admin/") ||
|
|
206
|
+
filePath.includes("/settings/") ||
|
|
207
|
+
filePath.includes("/auth/") ||
|
|
208
|
+
filePath.includes("/account/") ||
|
|
209
|
+
filePath.includes("/api/")) {
|
|
210
|
+
return issues;
|
|
211
|
+
}
|
|
212
|
+
// Check if this is a public-facing page that should have OG tags
|
|
213
|
+
const isPublicPage = filePath.includes("/page.tsx") &&
|
|
214
|
+
!filePath.includes("(authenticated)") &&
|
|
215
|
+
!filePath.includes("(seller)") &&
|
|
216
|
+
!filePath.includes("(admin)");
|
|
217
|
+
if (!isPublicPage) {
|
|
218
|
+
return issues;
|
|
219
|
+
}
|
|
220
|
+
// Check for openGraph in metadata
|
|
221
|
+
const hasOpenGraph = /openGraph\s*:/i.test(content);
|
|
222
|
+
if (!hasOpenGraph) {
|
|
223
|
+
issues.push({
|
|
224
|
+
rule: "opengraph-recommended",
|
|
225
|
+
message: "Public page missing Open Graph metadata",
|
|
226
|
+
file: filePath,
|
|
227
|
+
severity: "info",
|
|
228
|
+
suggestion: "Add openGraph: { title, description, images } for better social sharing",
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
return issues;
|
|
232
|
+
}
|
|
233
|
+
function checkStructuredData(content, filePath) {
|
|
234
|
+
const issues = [];
|
|
235
|
+
// Check for JSON-LD script tags
|
|
236
|
+
const jsonLdRegex = /<script\s+type\s*=\s*["']application\/ld\+json["'][^>]*>([\s\S]*?)<\/script>/gi;
|
|
237
|
+
let match;
|
|
238
|
+
while ((match = jsonLdRegex.exec(content)) !== null) {
|
|
239
|
+
const jsonContent = match[1].trim();
|
|
240
|
+
// Try to parse and validate JSON-LD
|
|
241
|
+
try {
|
|
242
|
+
// Handle JSX expressions - skip validation for dynamic content
|
|
243
|
+
if (jsonContent.includes("{") && !jsonContent.startsWith("{")) {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
// Skip if it's clearly a JSX expression
|
|
247
|
+
if (jsonContent.includes("JSON.stringify")) {
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
const parsed = JSON.parse(jsonContent);
|
|
251
|
+
// Check for @context
|
|
252
|
+
if (!parsed["@context"]) {
|
|
253
|
+
issues.push({
|
|
254
|
+
rule: "jsonld-context-required",
|
|
255
|
+
message: "JSON-LD missing @context",
|
|
256
|
+
file: filePath,
|
|
257
|
+
line: getLineNumber(content, match.index),
|
|
258
|
+
severity: "error",
|
|
259
|
+
suggestion: 'Add "@context": "https://schema.org"',
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
// Check for @type
|
|
263
|
+
if (!parsed["@type"]) {
|
|
264
|
+
issues.push({
|
|
265
|
+
rule: "jsonld-type-required",
|
|
266
|
+
message: "JSON-LD missing @type",
|
|
267
|
+
file: filePath,
|
|
268
|
+
line: getLineNumber(content, match.index),
|
|
269
|
+
severity: "error",
|
|
270
|
+
suggestion: "Add @type (e.g., Product, Organization, BreadcrumbList)",
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
// JSON parse error - might be dynamic content, skip
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return issues;
|
|
279
|
+
}
|
|
280
|
+
function checkHeadingStructure(content, filePath) {
|
|
281
|
+
const issues = [];
|
|
282
|
+
// Count h1 tags
|
|
283
|
+
const h1Regex = /<h1[^>]*>/gi;
|
|
284
|
+
const h1Matches = content.match(h1Regex) || [];
|
|
285
|
+
if (h1Matches.length > 1) {
|
|
286
|
+
issues.push({
|
|
287
|
+
rule: "single-h1",
|
|
288
|
+
message: `Multiple h1 tags found (${h1Matches.length})`,
|
|
289
|
+
file: filePath,
|
|
290
|
+
severity: "warning",
|
|
291
|
+
suggestion: "Use only one h1 per page for better SEO",
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
return issues;
|
|
295
|
+
}
|
|
296
|
+
function checkExternalLinks(content, filePath) {
|
|
297
|
+
const issues = [];
|
|
298
|
+
// Check for external links without rel="noopener noreferrer"
|
|
299
|
+
const externalLinkRegex = /<a\s+[^>]*href\s*=\s*["'](https?:\/\/[^"']+)["'][^>]*>/gi;
|
|
300
|
+
let match;
|
|
301
|
+
while ((match = externalLinkRegex.exec(content)) !== null) {
|
|
302
|
+
const linkTag = match[0];
|
|
303
|
+
const hasNoopener = /rel\s*=\s*["'][^"']*noopener[^"']*["']/i.test(linkTag);
|
|
304
|
+
const hasTarget = /target\s*=\s*["']_blank["']/i.test(linkTag);
|
|
305
|
+
if (hasTarget && !hasNoopener) {
|
|
306
|
+
issues.push({
|
|
307
|
+
rule: "external-link-security",
|
|
308
|
+
message: "External link with target=\"_blank\" missing rel=\"noopener noreferrer\"",
|
|
309
|
+
file: filePath,
|
|
310
|
+
line: getLineNumber(content, match.index),
|
|
311
|
+
severity: "warning",
|
|
312
|
+
suggestion: 'Add rel="noopener noreferrer" for security',
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return issues;
|
|
317
|
+
}
|
|
318
|
+
function checkTechnicalSEO() {
|
|
319
|
+
const issues = [];
|
|
320
|
+
const cwd = process.cwd();
|
|
321
|
+
// Check for robots.txt
|
|
322
|
+
const robotsPath = path.join(cwd, "public", "robots.txt");
|
|
323
|
+
const appRobotsPath = path.join(cwd, "app", "robots.ts");
|
|
324
|
+
if (!fs.existsSync(robotsPath) && !fs.existsSync(appRobotsPath)) {
|
|
325
|
+
issues.push({
|
|
326
|
+
rule: "robots-txt-exists",
|
|
327
|
+
message: "No robots.txt found",
|
|
328
|
+
file: "public/robots.txt",
|
|
329
|
+
severity: "warning",
|
|
330
|
+
suggestion: "Create public/robots.txt or app/robots.ts for search engine crawling rules",
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
// Check for sitemap
|
|
334
|
+
const sitemapPath = path.join(cwd, "public", "sitemap.xml");
|
|
335
|
+
const appSitemapPath = path.join(cwd, "app", "sitemap.ts");
|
|
336
|
+
if (!fs.existsSync(sitemapPath) && !fs.existsSync(appSitemapPath)) {
|
|
337
|
+
issues.push({
|
|
338
|
+
rule: "sitemap-exists",
|
|
339
|
+
message: "No sitemap found",
|
|
340
|
+
file: "app/sitemap.ts",
|
|
341
|
+
severity: "info",
|
|
342
|
+
suggestion: "Create app/sitemap.ts for better search engine indexing",
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
return issues;
|
|
346
|
+
}
|
|
347
|
+
async function run() {
|
|
348
|
+
const startTime = Date.now();
|
|
349
|
+
const findings = [];
|
|
350
|
+
// Find all page and layout files
|
|
351
|
+
const files = await (0, glob_1.glob)([
|
|
352
|
+
"app/**/page.tsx",
|
|
353
|
+
"app/**/layout.tsx",
|
|
354
|
+
"components/**/*.tsx",
|
|
355
|
+
], {
|
|
356
|
+
cwd: process.cwd(),
|
|
357
|
+
ignore: [
|
|
358
|
+
"**/node_modules/**",
|
|
359
|
+
"**/*.test.tsx",
|
|
360
|
+
"**/*.spec.tsx",
|
|
361
|
+
],
|
|
362
|
+
});
|
|
363
|
+
let filesChecked = 0;
|
|
364
|
+
const allIssues = [];
|
|
365
|
+
// Check technical SEO first
|
|
366
|
+
allIssues.push(...checkTechnicalSEO());
|
|
367
|
+
for (const file of files) {
|
|
368
|
+
const filePath = path.join(process.cwd(), file);
|
|
369
|
+
if (!fs.existsSync(filePath))
|
|
370
|
+
continue;
|
|
371
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
372
|
+
// Skip files with seo ignore comment
|
|
373
|
+
if (content.includes("seo-ignore")) {
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
filesChecked++;
|
|
377
|
+
// Run all SEO checks
|
|
378
|
+
allIssues.push(...checkMetadataExports(content, file));
|
|
379
|
+
allIssues.push(...checkMetadataContent(content, file));
|
|
380
|
+
allIssues.push(...checkOpenGraph(content, file));
|
|
381
|
+
allIssues.push(...checkStructuredData(content, file));
|
|
382
|
+
allIssues.push(...checkHeadingStructure(content, file));
|
|
383
|
+
allIssues.push(...checkExternalLinks(content, file));
|
|
384
|
+
}
|
|
385
|
+
// Convert to findings
|
|
386
|
+
for (const issue of allIssues) {
|
|
387
|
+
findings.push({
|
|
388
|
+
level: issue.severity,
|
|
389
|
+
message: issue.message,
|
|
390
|
+
file: issue.file,
|
|
391
|
+
startLine: issue.line,
|
|
392
|
+
ruleId: issue.rule,
|
|
393
|
+
suggestion: issue.suggestion,
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
const errors = findings.filter(f => f.level === "error");
|
|
397
|
+
const warnings = findings.filter(f => f.level === "warning");
|
|
398
|
+
const infos = findings.filter(f => f.level === "info");
|
|
399
|
+
return {
|
|
400
|
+
passed: errors.length === 0,
|
|
401
|
+
findings,
|
|
402
|
+
duration: Date.now() - startTime,
|
|
403
|
+
metadata: {
|
|
404
|
+
filesChecked,
|
|
405
|
+
totalIssues: allIssues.length,
|
|
406
|
+
errors: errors.length,
|
|
407
|
+
warnings: warnings.length,
|
|
408
|
+
infos: infos.length,
|
|
409
|
+
},
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
async function main() {
|
|
413
|
+
console.log(`\n${console_chars_1.emoji.search} SEO VALIDATION`);
|
|
414
|
+
console.log((0, console_chars_1.createDivider)(65, "heavy"));
|
|
415
|
+
const result = await run();
|
|
416
|
+
const { filesChecked, totalIssues, errors, warnings, infos } = result.metadata || {};
|
|
417
|
+
console.log(`\n${console_chars_1.emoji.file} Checking SEO...`);
|
|
418
|
+
console.log(` Files checked: ${filesChecked}`);
|
|
419
|
+
console.log(` Total issues: ${totalIssues}`);
|
|
420
|
+
console.log(`\n${console_chars_1.emoji.chart} Summary:`);
|
|
421
|
+
console.log(` Errors: ${errors}`);
|
|
422
|
+
console.log(` Warnings: ${warnings}`);
|
|
423
|
+
console.log(` Info: ${infos}`);
|
|
424
|
+
if (result.passed && warnings === 0 && infos === 0) {
|
|
425
|
+
console.log(`\n${console_chars_1.emoji.success} SEO VALIDATION PASSED`);
|
|
426
|
+
console.log(`\nAll pages follow SEO best practices.`);
|
|
427
|
+
process.exit(0);
|
|
428
|
+
}
|
|
429
|
+
// Group findings by rule
|
|
430
|
+
const findingsByRule = new Map();
|
|
431
|
+
for (const finding of result.findings) {
|
|
432
|
+
const ruleId = finding.ruleId || "unknown";
|
|
433
|
+
if (!findingsByRule.has(ruleId)) {
|
|
434
|
+
findingsByRule.set(ruleId, []);
|
|
435
|
+
}
|
|
436
|
+
findingsByRule.get(ruleId).push(finding);
|
|
437
|
+
}
|
|
438
|
+
// Print errors
|
|
439
|
+
const errorFindings = result.findings.filter(f => f.level === "error");
|
|
440
|
+
if (errorFindings.length > 0) {
|
|
441
|
+
console.log(`\n${console_chars_1.emoji.error} Errors (blocking):`);
|
|
442
|
+
for (const [ruleId, ruleFindings] of findingsByRule) {
|
|
443
|
+
const errors = ruleFindings.filter(f => f.level === "error");
|
|
444
|
+
if (errors.length === 0)
|
|
445
|
+
continue;
|
|
446
|
+
console.log(`\n ${ruleId} (${errors.length}):`);
|
|
447
|
+
for (const finding of errors.slice(0, 5)) {
|
|
448
|
+
console.log(` ${finding.file}${finding.startLine ? `:${finding.startLine}` : ""}`);
|
|
449
|
+
console.log(` ${finding.message}`);
|
|
450
|
+
if (finding.suggestion) {
|
|
451
|
+
console.log(` ${console_chars_1.emoji.hint} ${finding.suggestion}`);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
if (errors.length > 5) {
|
|
455
|
+
console.log(` ... and ${errors.length - 5} more`);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
// Print warnings
|
|
460
|
+
const warningFindings = result.findings.filter(f => f.level === "warning");
|
|
461
|
+
if (warningFindings.length > 0) {
|
|
462
|
+
console.log(`\n${console_chars_1.emoji.warning} Warnings:`);
|
|
463
|
+
for (const [ruleId, ruleFindings] of findingsByRule) {
|
|
464
|
+
const warns = ruleFindings.filter(f => f.level === "warning");
|
|
465
|
+
if (warns.length === 0)
|
|
466
|
+
continue;
|
|
467
|
+
console.log(`\n ${ruleId} (${warns.length}):`);
|
|
468
|
+
for (const finding of warns.slice(0, 3)) {
|
|
469
|
+
console.log(` ${finding.file}${finding.startLine ? `:${finding.startLine}` : ""}`);
|
|
470
|
+
console.log(` ${finding.message}`);
|
|
471
|
+
}
|
|
472
|
+
if (warns.length > 3) {
|
|
473
|
+
console.log(` ... and ${warns.length - 3} more`);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
console.log(`\n${console_chars_1.emoji.info} SEO Best Practices:`);
|
|
478
|
+
console.log(` - Every page should have metadata with title & description`);
|
|
479
|
+
console.log(` - Title: ${TITLE_MIN_LENGTH}-${TITLE_MAX_LENGTH} chars`);
|
|
480
|
+
console.log(` - Description: ${DESC_MIN_LENGTH}-${DESC_MAX_LENGTH} chars`);
|
|
481
|
+
console.log(` - Public pages should have Open Graph tags`);
|
|
482
|
+
console.log(` - One h1 per page`);
|
|
483
|
+
console.log(` - External links need rel="noopener noreferrer"`);
|
|
484
|
+
if (!result.passed) {
|
|
485
|
+
console.log(`\n${console_chars_1.emoji.error} SEO VALIDATION FAILED`);
|
|
486
|
+
process.exit(1);
|
|
487
|
+
}
|
|
488
|
+
console.log(`\n${console_chars_1.emoji.success} SEO VALIDATION PASSED`);
|
|
489
|
+
process.exit(0);
|
|
490
|
+
}
|
|
491
|
+
if (require.main === module) {
|
|
492
|
+
main().catch((err) => {
|
|
493
|
+
console.error(`${console_chars_1.emoji.error} Preflight failed:`, err);
|
|
494
|
+
process.exit(1);
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
//# sourceMappingURL=seo-validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seo-validation.js","sourceRoot":"","sources":["../../../src/checks/seo/seo-validation.ts"],"names":[],"mappings":"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"}
|