Files
invoke-ai--invokeai/docs/scripts/validate-redirect-targets.mjs
T
wehub-resource-sync cddb07a176
build container image / cpu (push) Waiting to run
build container image / cuda (push) Waiting to run
build container image / rocm (push) Waiting to run
docs / deploy (push) Blocked by required conditions
docs / changes (push) Waiting to run
docs / check-and-build (push) Blocked by required conditions
frontend tests / frontend-tests (push) Waiting to run
openapi checks / openapi-checks (push) Waiting to run
python tests / py3.12: macos-default (push) Waiting to run
python tests / py3.11: windows-cpu (push) Waiting to run
python tests / py3.12: windows-cpu (push) Waiting to run
python tests / py3.11: linux-cpu (push) Waiting to run
python tests / py3.12: linux-cpu (push) Waiting to run
typegen checks / typegen-checks (push) Waiting to run
uv lock checks / uv-lock-checks (push) Waiting to run
frontend checks / frontend-checks (push) Waiting to run
lfs checks / lfs-check (push) Waiting to run
python checks / python-checks (push) Waiting to run
python tests / py3.11: macos-default (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:22:06 +08:00

66 lines
2.2 KiB
JavaScript

import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
import { dirname, join, relative } from 'node:path';
import { fileURLToPath } from 'node:url';
const docsRoot = join(dirname(fileURLToPath(import.meta.url)), '..');
const contentRoot = join(docsRoot, 'src', 'content', 'docs');
const redirectsFile = join(docsRoot, 'src', 'config', 'redirects.ts');
const normalizeRoute = (route) => {
const normalized = route
.replace(/^\/+|\/+$/g, '')
.split('/')
.filter(Boolean)
.map((segment) => segment.toLowerCase().replaceAll(' ', '-'))
.join('/');
return normalized ? `/${normalized}` : '/';
};
const collectDocsRoutes = (dir, routes = new Set()) => {
for (const entry of readdirSync(dir)) {
const entryPath = join(dir, entry);
const stats = statSync(entryPath);
if (stats.isDirectory()) {
collectDocsRoutes(entryPath, routes);
continue;
}
if (!entry.endsWith('.md') && !entry.endsWith('.mdx')) {
continue;
}
const relativePath = relative(contentRoot, entryPath).replace(/\\/g, '/').replace(/\.mdx?$/, '');
const route = relativePath.endsWith('/index') ? relativePath.slice(0, -'/index'.length) : relativePath;
routes.add(normalizeRoute(route));
const segments = route.split('/').filter(Boolean);
for (let index = 1; index < segments.length; index++) {
routes.add(normalizeRoute(segments.slice(0, index).join('/')));
}
}
return routes;
};
if (!existsSync(contentRoot)) {
throw new Error(`Docs content directory not found: ${contentRoot}`);
}
const redirectsSource = readFileSync(redirectsFile, 'utf8');
const redirectMatches = redirectsSource.matchAll(/^\s*['"]([^'"]+)['"]:\s*['"]([^'"]+)['"]/gm);
const redirectTargets = Array.from(redirectMatches, ([, from, to]) => ({ from, to }));
const docsRoutes = collectDocsRoutes(contentRoot);
const missingTargets = redirectTargets.filter(({ to }) => !docsRoutes.has(normalizeRoute(to)));
if (missingTargets.length > 0) {
console.error('Redirect targets must resolve to generated docs routes:');
for (const { from, to } of missingTargets) {
console.error(` ${from} -> ${to}`);
}
process.exit(1);
}
console.log(`Validated ${redirectTargets.length} redirect targets.`);