357e26ab49
Finish/交付写盘前自动回填 mindspace-geo,扩展 storage publications 扫描与 geo meta 解析,并增加 143 生产回填脚本。 Co-authored-by: Cursor <cursoragent@cursor.com>
111 lines
3.6 KiB
JavaScript
111 lines
3.6 KiB
JavaScript
#!/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 <uuid>
|
|
* 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 <uuid>] [--file <html>] [--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);
|