Files

118 lines
4.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import fs from 'node:fs';
import path from 'node:path';
const DIST_DIR = path.resolve('dist');
if (!fs.existsSync(DIST_DIR)) {
console.error(`❌ Dist directory not found at ${DIST_DIR}. Please run "pnpm build" first.`);
process.exit(1);
}
function getAllHtmlFiles(dir, fileList = []) {
const files = fs.readdirSync(dir);
for (const file of files) {
const fullPath = path.join(dir, file);
const stat = fs.statSync(fullPath);
if (stat.isDirectory()) {
getAllHtmlFiles(fullPath, fileList);
} else if (file.endsWith('.html')) {
fileList.push(fullPath);
}
}
return fileList;
}
const htmlFiles = getAllHtmlFiles(DIST_DIR);
console.log(`\n======================================================`);
console.log(`🔍 Programmatic SEO Automated Audit`);
console.log(`======================================================`);
console.log(`📁 Scanning static output directory: ${DIST_DIR}`);
console.log(`📄 Total HTML Pages Generated (N): ${htmlFiles.length}\n`);
let passedCount = 0;
let warningCount = 0;
let errorCount = 0;
const issues = [];
for (const filePath of htmlFiles) {
const relPath = path.relative(DIST_DIR, filePath);
const content = fs.readFileSync(filePath, 'utf-8');
const is404 = relPath.includes('404.html');
// 1. Check Title
const titleMatch = content.match(/<title[^>]*>([^<]+)<\/title>/i);
if (!titleMatch && !is404) {
issues.push({ file: relPath, level: 'ERROR', message: 'Missing <title> tag' });
errorCount++;
}
// 2. Check Description
const descMatch = content.match(/<meta[^>]*name=["']description["'][^>]*content=["']([^"']*)["']/i);
if (!descMatch && !is404) {
issues.push({ file: relPath, level: 'WARNING', message: 'Missing meta description' });
warningCount++;
}
// 3. Check Canonical
const canonicalMatch = content.match(/<link[^>]*rel=["']canonical["'][^>]*href=["']([^"']*)["']/i);
if (!canonicalMatch && !is404) {
issues.push({ file: relPath, level: 'ERROR', message: 'Missing canonical URL link tag' });
errorCount++;
} else if (canonicalMatch) {
const canonical = canonicalMatch[1];
if (canonical.includes('?')) {
issues.push({ file: relPath, level: 'ERROR', message: `Canonical contains query parameters: ${canonical}` });
errorCount++;
}
if (!canonical.startsWith('http://') && !canonical.startsWith('https://')) {
issues.push({ file: relPath, level: 'ERROR', message: `Canonical is relative instead of absolute: ${canonical}` });
errorCount++;
}
}
// 4. Check Robots (Accidental noindex detection)
const robotsMatch = content.match(/<meta[^>]*name=["']robots["'][^>]*content=["']([^"']*)["']/i);
if (robotsMatch) {
const robots = robotsMatch[1].toLowerCase();
if (robots.includes('noindex') && !is404) {
issues.push({ file: relPath, level: 'WARNING', message: `Page has noindex tag: "${robots}"` });
warningCount++;
}
}
// 5. Check JSON-LD
const hasJsonLd = content.includes('application/ld+json');
if (!hasJsonLd && !is404) {
issues.push({ file: relPath, level: 'INFO', message: 'Missing Schema.org JSON-LD structured data' });
}
if (issues.filter(i => i.file === relPath).length === 0) {
passedCount++;
}
}
// Print results
console.log(`📊 Audit Summary:`);
console.log(` - Perfect Pages: ${passedCount}`);
console.log(` - Warnings: ${warningCount}`);
console.log(` - Critical Errors: ${errorCount}`);
if (issues.length > 0) {
console.log(`\n📋 Detailed Findings:`);
for (const issue of issues.slice(0, 15)) {
const icon = issue.level === 'ERROR' ? '❌' : issue.level === 'WARNING' ? '⚠️' : 'ℹ️';
console.log(` ${icon} [${issue.level}] ${issue.file}: ${issue.message}`);
}
if (issues.length > 15) {
console.log(` ... and ${issues.length - 15} more findings.`);
}
}
if (errorCount > 0) {
console.log(`\n❌ Audit failed with ${errorCount} critical error(s).`);
process.exit(1);
} else {
console.log(`\n✅ SEO Pre-flight Audit Passed! All pages ready for Googlebot indexing.`);
}