Files
awesome-duckdb/.github/scripts/check-readme-order.mjs
T

103 lines
2.8 KiB
JavaScript

#!/usr/bin/env node
import { readFileSync } from "node:fs";
const [basePath, currentPath] = process.argv.slice(2);
if (!basePath || !currentPath) {
console.error("Usage: check-readme-order.mjs BASE_README CURRENT_README");
process.exit(2);
}
const SORTED_SECTION = "Extensions / Community Extensions";
const IGNORED_SECTIONS = new Set(["Contents"]);
function headingName(markdown) {
const linkedHeading = markdown.match(/^\[([^\]]+)\]\([^)]+\)$/);
return linkedHeading ? linkedHeading[1] : markdown;
}
function parseSections(path) {
const sections = new Map();
const headings = [];
let section = null;
for (const line of readFileSync(path, "utf8").split("\n")) {
const heading = line.match(/^(#{2,6})\s+(.+?)\s*$/);
if (heading) {
const level = heading[1].length;
while (headings.length > 0 && headings.at(-1).level >= level) {
headings.pop();
}
headings.push({ level, name: headingName(heading[2]) });
section = headings.map(({ name }) => name).join(" / ");
if (!sections.has(section)) {
sections.set(section, []);
}
continue;
}
if (!section || !line.startsWith("- [")) {
continue;
}
const item = line.match(/^- \[(.*?)\]\(([^)\s]+)(?:\s+[^)]*)?\)/);
if (!item) {
continue;
}
const label = item[1].replaceAll("`", "").trim();
sections.get(section).push({
sortKey: label.toLowerCase(),
url: item[2],
});
}
return sections;
}
const baseSections = parseSections(basePath);
const currentSections = parseSections(currentPath);
let orderingIsValid = true;
const sortedEntries = currentSections.get(SORTED_SECTION) ?? [];
for (let index = 1; index < sortedEntries.length; index += 1) {
if (sortedEntries[index - 1].sortKey > sortedEntries[index].sortKey) {
orderingIsValid = false;
}
}
for (const [section, currentEntries] of currentSections) {
if (section === SORTED_SECTION || IGNORED_SECTIONS.has(section)) {
continue;
}
const baseEntries = baseSections.get(section) ?? [];
if (baseEntries.length === 0) {
continue;
}
const isExisting = (entry) =>
baseEntries.some(
(baseEntry) =>
baseEntry.url === entry.url || baseEntry.sortKey === entry.sortKey,
);
let foundNewEntry = false;
for (const entry of currentEntries) {
const entryAlreadyExists = isExisting(entry);
if (entryAlreadyExists && foundNewEntry) {
orderingIsValid = false;
} else if (!entryAlreadyExists) {
foundNewEntry = true;
}
}
}
if (!orderingIsValid) {
const message = "Thanks for contributing! Please move your new entry to follow the ordering rules in CONTRIBUTING.md, then update this pull request.";
console.error(`::error file=${currentPath},title=README entry order::${message}`);
process.exit(1);
}
console.log("README entries follow the required ordering.");