55 lines
2.2 KiB
JavaScript
55 lines
2.2 KiB
JavaScript
// lib/util/doors.mjs
|
|
// The `### Doors` list of a CHANGELOG release section: every delivery says
|
|
// whether it is a one-way door (cannot be walked back without a cost to users)
|
|
// or a two-way door (can). Moved here from scripts/release-check.mjs, which
|
|
// re-exports these names, because lib never imports scripts and the review
|
|
// evidence writer reads the door too. Door types are credited to
|
|
// mattpocock/skills@c55ee46 (idea only, no text copied).
|
|
|
|
export const DOOR_LINE_RE = /^- (one-way|two-way): \S/;
|
|
|
|
/**
|
|
* The CHANGELOG section for `version`, heading line excluded, or null. Matches
|
|
* `## v1.2.3 …`, `## [1.2.3] …` and `## 1.2.3 …` exactly: 1.1.0 is not 1.10.0
|
|
* and not 1.1.0-pre.
|
|
*/
|
|
export function releaseSection(text, version) {
|
|
if (typeof text !== 'string' || !version) return null;
|
|
const lines = text.split('\n');
|
|
const headingVersion = (line) => {
|
|
const m = line.match(/^##\s+\[?v?([^\]\s(]+)\]?/);
|
|
return m ? m[1] : null;
|
|
};
|
|
let start = -1;
|
|
for (let i = 0; i < lines.length; i++) {
|
|
if (/^##\s/.test(lines[i]) && headingVersion(lines[i]) === version) { start = i + 1; break; }
|
|
}
|
|
if (start === -1) return null;
|
|
let end = lines.length;
|
|
for (let i = start; i < lines.length; i++) {
|
|
if (/^##\s/.test(lines[i])) { end = i; break; }
|
|
}
|
|
return lines.slice(start, end).join('\n');
|
|
}
|
|
|
|
/**
|
|
* The `### Doors` list of a release section. Top-level `- ` lines must read
|
|
* `- one-way: …` or `- two-way: …`; indented lines continue the entry above.
|
|
* @returns {{ found: boolean, entries: Array<{door: string, text: string}>, bad: string[] }}
|
|
*/
|
|
export function parseDoors(section) {
|
|
const lines = String(section ?? '').split('\n');
|
|
const start = lines.findIndex((l) => /^###\s+Doors\s*$/.test(l));
|
|
if (start === -1) return { found: false, entries: [], bad: [] };
|
|
const entries = [];
|
|
const bad = [];
|
|
for (let i = start + 1; i < lines.length; i++) {
|
|
const line = lines[i];
|
|
if (/^#{2,3}\s/.test(line)) break;
|
|
if (!line.startsWith('- ')) continue;
|
|
const m = line.match(DOOR_LINE_RE);
|
|
if (m) entries.push({ door: m[1], text: line.slice(m[0].length - 1).trim() });
|
|
else bad.push(line);
|
|
}
|
|
return { found: true, entries, bad };
|
|
}
|