#!/usr/bin/env node
/**
* Scan HTML pages for mindspace-cover + SEO/GEO compliance.
*
* Usage:
* node scripts/check-mindspace-cover.mjs
* node scripts/check-mindspace-cover.mjs --scope all
* node scripts/check-mindspace-cover.mjs --user
* node scripts/check-mindspace-cover.mjs --file public/thumbnail-demo-samples/vietnam-guide-good.html
*/
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import {
auditHtmlFile,
collectHtmlTargets,
} from '../mindspace-seo-audit.mjs';
const repoRoot = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
const DEFAULT_PUBLISH_ROOT = 'MindSpace';
function parseArgs(argv) {
let scope = 'all';
let root = path.join(repoRoot, DEFAULT_PUBLISH_ROOT);
let publicRoot = path.join(repoRoot, 'public');
let storageRoot = null;
let userId = null;
let file = null;
let seo = true;
for (let i = 2; i < argv.length; i += 1) {
if (argv[i] === '--scope' && argv[i + 1]) {
scope = argv[i + 1];
i += 1;
} else if (argv[i] === '--root' && argv[i + 1]) {
root = path.resolve(argv[i + 1]);
i += 1;
} else if (argv[i] === '--public-root' && argv[i + 1]) {
publicRoot = path.resolve(argv[i + 1]);
i += 1;
} else if (argv[i] === '--storage-root' && argv[i + 1]) {
storageRoot = path.resolve(argv[i + 1]);
i += 1;
} else if (argv[i] === '--user' && argv[i + 1]) {
userId = argv[i + 1];
i += 1;
} else if (argv[i] === '--file' && argv[i + 1]) {
file = path.resolve(argv[i + 1]);
i += 1;
} else if (argv[i] === '--seo') {
seo = true;
} else if (argv[i] === '--skip-seo') {
seo = false;
} else if (argv[i] === '--help' || argv[i] === '-h') {
console.log(`Usage: node scripts/check-mindspace-cover.mjs [--scope mindspace|platform|all] [--root ${DEFAULT_PUBLISH_ROOT}] [--user ] [--file ] [--skip-seo]`);
process.exit(0);
}
}
return { scope, root, publicRoot, storageRoot, userId, file, seo };
}
function isPlatformOnlyPath(htmlPath, publicRoot) {
const resolvedPublicRoot = path.resolve(publicRoot);
const resolved = path.resolve(htmlPath);
return resolved.startsWith(`${resolvedPublicRoot}${path.sep}`);
}
const { scope, root, publicRoot, storageRoot, userId, file, seo } = parseArgs(process.argv);
const targets = collectHtmlTargets({
scope,
mindspaceRoot: root,
publicRoot,
storageRoot,
userId,
file,
});
const results = [];
for (const htmlPath of targets) {
const html = fs.readFileSync(htmlPath, 'utf8');
const platformOnly = isPlatformOnlyPath(htmlPath, publicRoot);
const issues = auditHtmlFile(htmlPath, html, {
seo,
requireCoverMeta: !platformOnly,
});
if (issues.length) {
results.push({ htmlPath, issues });
}
}
if (results.length === 0) {
const scopeLabel = file ?? `${scope} @ ${root}${userId ? ` (user ${userId})` : ''}`;
console.log(`OK: ${targets.length} 个 HTML 页面均符合 mindspace-cover + SEO/GEO 规范 (${scopeLabel})`);
process.exit(0);
}
let errors = 0;
let warns = 0;
console.error(`Found cover/SEO issues in ${results.length} / ${targets.length} HTML files:`);
for (const { htmlPath, issues } of results) {
const rel = file ? htmlPath : path.relative(repoRoot, htmlPath);
console.error(`\n${rel}`);
for (const issue of issues) {
const prefix = issue.level === 'error' ? ' ✗' : ' ⚠';
console.error(`${prefix} [${issue.code}] ${issue.message}`);
if (issue.level === 'error') errors += 1;
else warns += 1;
}
}
console.error(`\n合计: ${errors} 错误, ${warns} 警告`);
process.exit(errors > 0 ? 1 : 0);