Skip to content

Commit 7670a78

Browse files
authored
feat(react): add stability index (#1036)
* feat(react): add stability index * fixup!
1 parent 31333d8 commit 7670a78

11 files changed

Lines changed: 136 additions & 48 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@doc-kit/generator-react': patch
3+
---
4+
5+
Generate `index.html` from the input `index` document instead of a synthetic page

packages/react/src/jsx-ast/README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@ The `jsx-ast` generator converts MDAST (Markdown Abstract Syntax Tree) to JSX AS
1010
documentation structure.
1111
- `generateAllPage` {boolean} When `true`, creates a synthetic JSX AST entry
1212
for `all.html`. **Default:** `true`.
13-
- `generateIndexPage` {boolean} When `true`, creates a synthetic JSX AST entry
14-
for `index.html`. **Default:** `true`.
1513
- `generateNotFoundPage` {boolean} When `true`, creates a synthetic JSX AST
1614
entry for `404.html`. **Default:** `true`.
15+
16+
## Index page
17+
18+
`index.html` is generated when an `index` document is part of the input, and
19+
is rendered from that document like any other page. A section containing a
20+
`<!-- DOCUMENTATION_INDEX -->` comment additionally receives the Stability
21+
Overview table of all modules.

packages/react/src/jsx-ast/__tests__/generate.test.mjs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ describe('jsx-ast generate', () => {
7575

7676
const jsxAstConfig = getConfig('jsx-ast');
7777
jsxAstConfig.generateAllPage = false;
78-
jsxAstConfig.generateIndexPage = false;
7978
jsxAstConfig.generateNotFoundPage = false;
8079

8180
const seenItems = [];
@@ -95,4 +94,50 @@ describe('jsx-ast generate', () => {
9594
['index', 'fs']
9695
);
9796
});
97+
98+
it('only generates an index page when an index document is an input', async () => {
99+
await setConfig({ target: ['jsx-ast'] });
100+
101+
const jsxAstConfig = getConfig('jsx-ast');
102+
jsxAstConfig.generateAllPage = false;
103+
jsxAstConfig.generateNotFoundPage = false;
104+
105+
const seenItems = [];
106+
await collect(
107+
generate([createEntry('fs', 'File system')], createWorker(seenItems))
108+
);
109+
110+
assert.deepEqual(
111+
seenItems.map(({ head }) => head.api),
112+
['fs']
113+
);
114+
});
115+
116+
it('places the stability overview at the DOCUMENTATION_INDEX comment', async () => {
117+
await setConfig({ target: ['jsx-ast'] });
118+
119+
const jsxAstConfig = getConfig('jsx-ast');
120+
jsxAstConfig.generateAllPage = false;
121+
jsxAstConfig.generateNotFoundPage = false;
122+
123+
const index = createEntry('index', 'Index', { stabilityIndex: null });
124+
// The metadata parser turns a `<!-- DOCUMENTATION_INDEX -->` comment into
125+
// this tag on the entry of the section containing it.
126+
index.tags = ['DOCUMENTATION_INDEX'];
127+
128+
const seenItems = [];
129+
await collect(
130+
generate(
131+
[index, createEntry('fs', 'File system')],
132+
createWorker(seenItems)
133+
)
134+
);
135+
136+
const [{ entries }] = seenItems;
137+
const table = entries[0].content.children.at(-1);
138+
139+
assert.equal(table.tagName, 'table');
140+
const [row] = table.children.at(-1).children;
141+
assert.equal(row.children[0].children[0].properties.href, 'fs.html');
142+
});
98143
});

packages/react/src/jsx-ast/generate.mjs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import { groupNodesByModule } from '@doc-kit/core/utils/generators.mjs';
33
import { jsx, toJs } from 'estree-util-to-js';
44

55
import buildContent from './utils/buildContent.mjs';
6+
import { injectDocumentationIndex } from './utils/documentationIndex.mjs';
67
import { getSortedHeadNodes } from './utils/getSortedHeadNodes.mjs';
78
import { buildNotFoundPage } from './utils/synthetic/404.mjs';
89
import { buildAllPage } from './utils/synthetic/all.mjs';
9-
import { buildIndexPage } from './utils/synthetic/index.mjs';
1010

1111
/**
1212
* Builds the `{ head, entries }` page descriptors for all configured synthetic
@@ -21,7 +21,6 @@ const buildSyntheticDescriptors = input => {
2121

2222
return [
2323
config.generateAllPage && buildAllPage(input),
24-
config.generateIndexPage && buildIndexPage(input),
2524
config.generateNotFoundPage && buildNotFoundPage(),
2625
].filter(Boolean);
2726
};
@@ -60,9 +59,15 @@ export async function processChunk(slicedInput, itemIndices) {
6059
* @type {import('./types').Generator['generate']}
6160
*/
6261
export async function* generate(input, worker) {
63-
// The synthetic `index` page replaces the Core `index` document.
62+
// The `index` page is only generated when an `index` document is part of
63+
// the input; the module list for the synthetic pages and the stability
64+
// overview excludes it.
6465
const moduleInput = input.filter(entry => entry.api !== 'index');
6566

67+
// Sections tagged with a `<!-- DOCUMENTATION_INDEX -->` comment (e.g. in
68+
// the `index` document) receive the Stability Overview of all modules.
69+
injectDocumentationIndex(input, moduleInput);
70+
6671
// Create sliced input: each item contains head + its module's entries
6772
// This avoids sending all 4700+ entries to every worker
6873
const groupedModules = groupNodesByModule(input);

packages/react/src/jsx-ast/index.mjs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ export default {
1717
defaultConfiguration: {
1818
ref: 'main',
1919
generateAllPage: true,
20-
generateIndexPage: true,
2120
generateNotFoundPage: true,
2221
},
2322

packages/react/src/jsx-ast/types.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ export type Generator = GeneratorMetadata<
55
{
66
ref: string;
77
generateAllPage: boolean;
8-
generateIndexPage: boolean;
98
generateNotFoundPage: boolean;
109
},
1110
Generate<Array<MetadataEntry>, AsyncGenerator<JSXContent>>,

packages/react/src/jsx-ast/utils/synthetic/__tests__/index.test.mjs renamed to packages/react/src/jsx-ast/utils/__tests__/documentationIndex.test.mjs

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import assert from 'node:assert/strict';
22
import { describe, it } from 'node:test';
33

4-
import { buildIndexPage, buildStabilityOverview } from '../index.mjs';
4+
import {
5+
buildStabilityOverview,
6+
injectDocumentationIndex,
7+
} from '../documentationIndex.mjs';
58

69
const fakeHead = (api, name, stabilityIndex, depth = 1) => ({
710
api,
@@ -20,32 +23,59 @@ const fakeHead = (api, name, stabilityIndex, depth = 1) => ({
2023
const findChild = (node, tagName) =>
2124
node.children.find(child => child.tagName === tagName);
2225

23-
describe('buildIndexPage', () => {
24-
it('returns a synthetic `index` head with an "Index" heading', () => {
25-
const { head } = buildIndexPage([]);
26+
describe('injectDocumentationIndex', () => {
27+
const createEntry = tags => ({
28+
...fakeHead('index', 'Index', null),
29+
tags,
30+
content: { type: 'root', children: [] },
31+
});
32+
33+
it('appends the overview to entries tagged DOCUMENTATION_INDEX', () => {
34+
const tagged = createEntry(['DOCUMENTATION_INDEX']);
35+
const untagged = createEntry(undefined);
36+
37+
injectDocumentationIndex(
38+
[tagged, untagged],
39+
[fakeHead('fs', 'fs', 2), fakeHead('assert', 'assert', 2)]
40+
);
2641

27-
assert.equal(head.api, 'index');
28-
assert.equal(head.path, '/index');
29-
assert.equal(head.basename, 'index');
30-
assert.equal(head.heading.data.name, 'Index');
31-
assert.equal(head.synthetic, true);
42+
const table = findChild(tagged.content, 'table');
43+
assert.equal(findChild(table, 'tbody').children.length, 2);
44+
assert.equal(untagged.content.children.length, 0);
3245
});
3346

3447
it('sorts the stability overview rows alphabetically by API name', () => {
35-
const { entries } = buildIndexPage([
36-
fakeHead('fs', 'fs', 2),
37-
fakeHead('assert', 'assert', 2),
38-
fakeHead('crypto', 'crypto', 2),
39-
]);
48+
const entry = createEntry(['DOCUMENTATION_INDEX']);
49+
50+
injectDocumentationIndex(
51+
[entry],
52+
[
53+
fakeHead('fs', 'fs', 2),
54+
fakeHead('assert', 'assert', 2),
55+
fakeHead('crypto', 'crypto', 2),
56+
]
57+
);
4058

41-
const table = findChild(entries[0].content, 'table');
59+
const table = findChild(entry.content, 'table');
4260
const rows = findChild(table, 'tbody').children;
4361
const names = rows.map(
4462
row => row.children[0].children[0].children[0].value
4563
);
4664

4765
assert.deepEqual(names, ['assert', 'crypto', 'fs']);
4866
});
67+
68+
it('excludes module heads without a stability index', () => {
69+
const entry = createEntry(['DOCUMENTATION_INDEX']);
70+
71+
injectDocumentationIndex(
72+
[entry],
73+
[fakeHead('fs', 'fs', 2), fakeHead('synopsis', 'Usage', null)]
74+
);
75+
76+
const table = findChild(entry.content, 'table');
77+
assert.equal(findChild(table, 'tbody').children.length, 1);
78+
});
4979
});
5080

5181
describe('buildStabilityOverview', () => {

packages/react/src/jsx-ast/utils/synthetic/index.mjs renamed to packages/react/src/jsx-ast/utils/documentationIndex.mjs

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22

33
import { h as createElement } from 'hastscript';
44

5-
import { createSyntheticHead, wrapAsEntry } from './synthetic.mjs';
6-
import { JSX_IMPORTS } from '../../../html/constants.mjs';
7-
import { createJSXElement } from '../ast.mjs';
8-
import { getSortedHeadNodes } from '../getSortedHeadNodes.mjs';
5+
import { createJSXElement } from './ast.mjs';
6+
import { getSortedHeadNodes } from './getSortedHeadNodes.mjs';
7+
import { JSX_IMPORTS } from '../../html/constants.mjs';
8+
9+
// The metadata parser turns bare HTML comments into entry tags, so a
10+
// `<!-- DOCUMENTATION_INDEX -->` comment in a source document surfaces as
11+
// this tag on the entry for the section containing it.
12+
export const DOCUMENTATION_INDEX_TAG = 'DOCUMENTATION_INDEX';
913

1014
const STABILITY_BADGE_KINDS = [
1115
'error',
@@ -62,20 +66,21 @@ export const buildStabilityOverview = headEntries =>
6266
]);
6367

6468
/**
65-
* Builds the page descriptor for `index.html`
69+
* Places the Stability Overview into every entry whose source section
70+
* contains a `<!-- DOCUMENTATION_INDEX -->` comment. The parser strips the
71+
* comment itself, so the table lands at the end of the tagged section.
6672
*
67-
* @param {Array<import('@doc-kit/core/generators/metadata/types').MetadataEntry>} entries
73+
* @param {Array<import('@doc-kit/core/generators/metadata/types').MetadataEntry>} entries - Entries to scan for the tag
74+
* @param {Array<import('@doc-kit/core/generators/metadata/types').MetadataEntry>} moduleEntries - Entries providing the module heads for the overview
6875
*/
69-
export const buildIndexPage = entries => {
70-
const head = createSyntheticHead('index', 'Index');
71-
const moduleEntries = getSortedHeadNodes(entries);
76+
export const injectDocumentationIndex = (entries, moduleEntries) => {
77+
const headEntries = getSortedHeadNodes(moduleEntries).filter(
78+
entry => entry.stability
79+
);
7280

73-
return {
74-
head,
75-
entries: [
76-
wrapAsEntry(head, [
77-
buildStabilityOverview(moduleEntries.filter(entry => entry.stability)),
78-
]),
79-
],
80-
};
81+
for (const entry of entries) {
82+
if (entry.tags?.includes(DOCUMENTATION_INDEX_TAG)) {
83+
entry.content.children.push(buildStabilityOverview(headEntries));
84+
}
85+
}
8186
};

scripts/vercel-build.sh

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ node packages/cli/bin/cli.mjs generate \
1616
-c "./node/CHANGELOG.md" \
1717
-v "$NODE_VERSION" \
1818
--type-map "./node/doc/type-map.json" \
19-
--index "./node/doc/api/index.md" \
2019
--config-file "./beta/doc-kit.config.mjs" \
2120
--log-level debug
2221

scripts/vercel-prepare.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ cd node
2626
# Enable sparse checkout and specify the folder
2727
git sparse-checkout set lib doc .
2828

29+
sed 's/STABILITY_OVERVIEW_SLOT_BEGIN/DOCUMENTATION_INDEX/g' ./doc/api/documentation.md > ./doc/api/index.md
30+
rm ./doc/api/documentation.md
31+
2932
# Move back out
3033
cd ..
3134

0 commit comments

Comments
 (0)