Track HiveGrid Cloud planning reference
This commit is contained in:
1 parent
b3d5ea6764
commit
e341c5938a
5784 files changed
+1104929
-3
No files matched your search
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env node
|
||||
/* eslint-disable no-console */
|
||||
import { readFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const repoRoot = path.resolve(__dirname, "..");
|
||||
const guideRelativePath = "docs/porting-guide.md";
|
||||
const guidePath = path.join(repoRoot, guideRelativePath);
|
||||
|
||||
const requiredHeadings = [
|
||||
"# Porting Guide",
|
||||
"## Rename/Replace Map",
|
||||
"## Do-Not-Replace Rules",
|
||||
"## Verification",
|
||||
];
|
||||
const unresolvedMarkerPattern = /^(?!.*(?:inline TODO markers|node -e )).*\b(?:TODO|TBD|FIXME)\b.*$/m;
|
||||
const minimumMapRows = 12;
|
||||
|
||||
function fail(message) {
|
||||
console.error(`Porting Docs porting guide verification failed: ${message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function headingPattern(heading) {
|
||||
const escapedHeading = heading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
if (heading === "# Porting Guide") return /^# .*Porting Guide\s*$/m;
|
||||
return new RegExp(`^${escapedHeading}\\s*$`, "m");
|
||||
}
|
||||
|
||||
function hasHeading(text, heading) {
|
||||
return headingPattern(heading).test(text);
|
||||
}
|
||||
|
||||
function sectionBody(text, heading) {
|
||||
const lines = text.split(/\r?\n/);
|
||||
const startIndex = lines.findIndex((line) => line.trim() === heading);
|
||||
if (startIndex === -1) return "";
|
||||
const currentLevel = heading.match(/^#+/)?.[0].length ?? 1;
|
||||
const endIndex = lines.findIndex((line, index) => {
|
||||
if (index <= startIndex) return false;
|
||||
const match = line.match(/^(#+)\s+/);
|
||||
return Boolean(match && match[1].length <= currentLevel);
|
||||
});
|
||||
return lines.slice(startIndex + 1, endIndex === -1 ? undefined : endIndex).join("\n");
|
||||
}
|
||||
|
||||
function countUsefulTableRows(section) {
|
||||
return section
|
||||
.split(/\r?\n/)
|
||||
.map((line) => line.trim())
|
||||
.filter((line) => {
|
||||
if (!line.startsWith("|") || !line.endsWith("|")) return false;
|
||||
const cells = line
|
||||
.split("|")
|
||||
.slice(1, -1)
|
||||
.map((cell) => cell.trim());
|
||||
if (cells.length < 4) return false;
|
||||
if (cells.every((cell) => /^:?-{3,}:?$/.test(cell))) return false;
|
||||
return cells.every((cell) => cell.length > 0);
|
||||
}).length;
|
||||
}
|
||||
|
||||
let text;
|
||||
try {
|
||||
text = await readFile(guidePath, "utf8");
|
||||
} catch (error) {
|
||||
if (error?.code === "ENOENT") fail(`missing ${guideRelativePath}`);
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!text.trim()) fail(`${guideRelativePath} is empty`);
|
||||
if (unresolvedMarkerPattern.test(text)) fail(`${guideRelativePath} contains unresolved TODO/TBD/FIXME markers`);
|
||||
|
||||
for (const heading of requiredHeadings) {
|
||||
if (!hasHeading(text, heading)) fail(`missing required heading \`${heading}\``);
|
||||
}
|
||||
|
||||
const mapRows = countUsefulTableRows(sectionBody(text, "## Rename/Replace Map"));
|
||||
if (mapRows < minimumMapRows) {
|
||||
fail(`Rename/Replace Map has ${mapRows} useful table rows; expected at least ${minimumMapRows}`);
|
||||
}
|
||||
|
||||
console.log(`Verified ${guideRelativePath}: ${mapRows} Rename/Replace Map table rows.`);
|
||||
Reference in new issue
Block a user