docs: place Office warnings within feature sections (#1764)
* docs: place Office warnings within feature sections * test(docs): cover substantive status sections * docs: attach experimental badges to Office features * test(docs): cover callouts without section headings
This commit is contained in:
@@ -29,6 +29,7 @@ export async function validatePublicDocs(docsDir = defaultDocsDir) {
|
||||
|
||||
assertFrontmatter(file, markdown);
|
||||
assertDocumentStructure(file, markdown);
|
||||
assertExperimentalCalloutPlacement(file, markdown);
|
||||
|
||||
const slug = file.replace(/\.mdx?$/, "").replace(/\/index$/, "");
|
||||
const existing = pagesBySlug.get(slug);
|
||||
@@ -835,6 +836,61 @@ function assertDocumentStructure(file, markdown) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep experimental callouts attached to an explicit feature section.
|
||||
* A section-level indicator is the first content under its heading, followed
|
||||
* by the documentation it qualifies.
|
||||
*
|
||||
* @param {string} file Relative page path used in validation errors.
|
||||
* @param {string} markdown Page source.
|
||||
* @returns {void}
|
||||
*/
|
||||
function assertExperimentalCalloutPlacement(file, markdown) {
|
||||
const source = stripMarkdownCode(markdown);
|
||||
const headings = [...source.matchAll(/^(#{2,6})\s+([^\n]+?)\s*$/gm)];
|
||||
const callouts = [...source.matchAll(/^>\s*\[!EXPERIMENTAL\]\s*$/gim)];
|
||||
|
||||
for (const callout of callouts) {
|
||||
const headingIndex = headings.findLastIndex(
|
||||
(candidate) => candidate.index < callout.index,
|
||||
);
|
||||
const heading = headings[headingIndex];
|
||||
if (
|
||||
!heading ||
|
||||
source
|
||||
.slice(heading.index + heading[0].length, callout.index)
|
||||
.trim()
|
||||
) {
|
||||
throw new Error(
|
||||
`${file} experimental callouts must immediately follow a descriptive heading`,
|
||||
);
|
||||
}
|
||||
|
||||
const title = heading[2].trim();
|
||||
const level = heading[1].length;
|
||||
const nextHeading = headings
|
||||
.slice(headingIndex + 1)
|
||||
.find((candidate) => candidate[1].length <= level);
|
||||
const sectionEnd = nextHeading?.index ?? source.length;
|
||||
const calloutBlock = source
|
||||
.slice(callout.index)
|
||||
.match(
|
||||
/^>\s*\[!EXPERIMENTAL\]\s*\r?\n(?:^>.*(?:\r?\n|$))*/im,
|
||||
)?.[0];
|
||||
const sectionContent = source
|
||||
.slice(
|
||||
callout.index + (calloutBlock?.length ?? callout[0].length),
|
||||
sectionEnd,
|
||||
)
|
||||
.trim();
|
||||
if (!sectionContent) {
|
||||
throw new Error(
|
||||
`${file} has an experimental callout without substantive section content: ${title}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Require every relative Markdown link or image to resolve on disk.
|
||||
*
|
||||
|
||||
@@ -322,6 +322,59 @@ test("accepts experimental page status frontmatter", async () => {
|
||||
await assert.doesNotReject(validatePublicDocs(dir));
|
||||
});
|
||||
|
||||
test("rejects an experimental callout with no substantive section content", async () => {
|
||||
const dir = await createDocs(
|
||||
{
|
||||
"index.md": `${validPage}\n## Office dependencies\n\n> [!EXPERIMENTAL]\n> Office is disabled by default.\n`,
|
||||
},
|
||||
{ pages: ["index"] },
|
||||
);
|
||||
|
||||
await assert.rejects(
|
||||
validatePublicDocs(dir),
|
||||
/index.md has an experimental callout without substantive section content: Office dependencies/,
|
||||
);
|
||||
});
|
||||
|
||||
test("accepts a feature-specific experimental section with substantive content", async () => {
|
||||
const dir = await createDocs(
|
||||
{
|
||||
"index.md": `${validPage}\n## Office dependencies\n\n> [!EXPERIMENTAL]\n> Office is disabled by default.\n\nUse workflow approval steps for the supported human-gated path.\n`,
|
||||
},
|
||||
{ pages: ["index"] },
|
||||
);
|
||||
|
||||
await assert.doesNotReject(validatePublicDocs(dir));
|
||||
});
|
||||
|
||||
test("rejects an experimental callout dropped after unrelated section content", async () => {
|
||||
const dir = await createDocs(
|
||||
{
|
||||
"index.md": `${validPage}\n## Configure workflows\n\nRegular workflows support human review gates.\n\n> [!EXPERIMENTAL]\n> Office is disabled by default.\n\nUse workflow approval steps for stable human gates.\n`,
|
||||
},
|
||||
{ pages: ["index"] },
|
||||
);
|
||||
|
||||
await assert.rejects(
|
||||
validatePublicDocs(dir),
|
||||
/index.md experimental callouts must immediately follow a descriptive heading/,
|
||||
);
|
||||
});
|
||||
|
||||
test("rejects an experimental callout not under a descriptive section heading", async () => {
|
||||
const dir = await createDocs(
|
||||
{
|
||||
"index.md": `${validPage}\n> [!EXPERIMENTAL]\n> Office is disabled by default.\n\nSome content.\n`,
|
||||
},
|
||||
{ pages: ["index"] },
|
||||
);
|
||||
|
||||
await assert.rejects(
|
||||
validatePublicDocs(dir),
|
||||
/index.md experimental callouts must immediately follow a descriptive heading/,
|
||||
);
|
||||
});
|
||||
|
||||
test("rejects published pages without title and description frontmatter", async () => {
|
||||
const dir = await createDocs(
|
||||
{ "index.md": "# Kandev\n" },
|
||||
|
||||
Reference in New Issue
Block a user