Skip to content

Commit 30e06b7

Browse files
committed
fixup!
1 parent 468b6ce commit 30e06b7

20 files changed

Lines changed: 276 additions & 81 deletions

File tree

src/generators/jsx-ast/README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ The `jsx-ast` generator converts MDAST (Markdown Abstract Syntax Tree) to JSX AS
66

77
The `jsx-ast` generator accepts the following configuration options:
88

9-
| Name | Type | Default | Description |
10-
| ------- | -------- | -------- | ------------------------------------------------------------------------ |
11-
| `ref` | `string` | `'main'` | Git reference/branch for linking to source files |
12-
| `index` | `array` | - | Array of `{ section, api }` objects defining the documentation structure |
9+
| Name | Type | Default | Description |
10+
| ---------------------- | --------- | -------- | ------------------------------------------------------------------------ |
11+
| `ref` | `string` | `'main'` | Git reference/branch for linking to source files |
12+
| `index` | `array` | - | Array of `{ section, api }` objects defining the documentation structure |
13+
| `generateAllPage` | `boolean` | `true` | When `true`, creates a synthetic JSX AST entry for `all.html` |
14+
| `generateIndexPage` | `boolean` | `true` | When `true`, creates a synthetic JSX AST entry for `index.html` |
15+
| `generateNotFoundPage` | `boolean` | `true` | When `true`, creates a synthetic JSX AST entry for `404.html` |
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import assert from 'node:assert/strict';
2+
import { describe, it } from 'node:test';
3+
4+
import getConfig, { setConfig } from '../../../utils/configuration/index.mjs';
5+
import { generate, processChunk } from '../generate.mjs';
6+
7+
const createEntry = (api, name, { stabilityIndex = '2' } = {}) => {
8+
const heading = {
9+
type: 'heading',
10+
depth: 1,
11+
children: [{ type: 'text', value: name }],
12+
data: { name, text: name, slug: api },
13+
};
14+
15+
return {
16+
api,
17+
path: `/${api}`,
18+
basename: api,
19+
heading,
20+
stability:
21+
stabilityIndex == null
22+
? null
23+
: {
24+
data: {
25+
index: stabilityIndex,
26+
description: `${name} stable. Longer description.`,
27+
},
28+
},
29+
content: {
30+
type: 'root',
31+
children: [
32+
heading,
33+
{
34+
type: 'paragraph',
35+
children: [{ type: 'text', value: `${name} body` }],
36+
},
37+
],
38+
},
39+
};
40+
};
41+
42+
const collect = async generator => {
43+
const results = [];
44+
45+
for await (const chunk of generator) {
46+
results.push(...chunk);
47+
}
48+
49+
return results;
50+
};
51+
52+
const createWorker = seenItems => ({
53+
async *stream(items) {
54+
seenItems.push(...items);
55+
yield items.map(({ head }) => ({ type: 'JSXElement', data: head }));
56+
},
57+
});
58+
59+
describe('jsx-ast generate', () => {
60+
it('does not attach raw section entries to regular JSX content', async () => {
61+
await setConfig({});
62+
63+
const fs = createEntry('fs', 'File system');
64+
const [content] = await processChunk([{ head: fs, entries: [fs] }], [0]);
65+
66+
assert.equal(content.data.api, 'fs');
67+
assert.equal('sectionEntries' in content, false);
68+
});
69+
70+
it('omits the core index entry and appends enabled synthetic pages', async () => {
71+
await setConfig({});
72+
73+
const seenItems = [];
74+
const results = await collect(
75+
generate(
76+
[createEntry('index', 'Index'), createEntry('fs', 'File system')],
77+
createWorker(seenItems)
78+
)
79+
);
80+
81+
assert.deepEqual(
82+
seenItems.map(({ head }) => head.api),
83+
['fs']
84+
);
85+
assert.deepEqual(
86+
results.map(({ data }) => data.api),
87+
['fs', 'all', 'index', '404']
88+
);
89+
90+
for (const entry of results.slice(1)) {
91+
assert.equal(entry.data.synthetic, true);
92+
assert.equal(entry.data.hideViewAs, true);
93+
}
94+
});
95+
96+
it('respects jsx-ast synthetic page flags', async () => {
97+
await setConfig({});
98+
99+
const jsxAstConfig = getConfig('jsx-ast');
100+
jsxAstConfig.generateAllPage = false;
101+
jsxAstConfig.generateIndexPage = false;
102+
jsxAstConfig.generateNotFoundPage = false;
103+
104+
const seenItems = [];
105+
const results = await collect(
106+
generate(
107+
[createEntry('index', 'Index'), createEntry('fs', 'File system')],
108+
createWorker(seenItems)
109+
)
110+
);
111+
112+
assert.deepEqual(
113+
seenItems.map(({ head }) => head.api),
114+
['fs']
115+
);
116+
assert.deepEqual(
117+
results.map(({ data }) => data.api),
118+
['fs']
119+
);
120+
});
121+
});

src/generators/jsx-ast/generate.mjs

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,30 @@
11
import buildContent from './utils/buildContent.mjs';
22
import { getSortedHeadNodes } from './utils/getSortedHeadNodes.mjs';
3+
import { buildNotFoundPage } from './utils/synthetic/404.mjs';
4+
import { buildAllPage } from './utils/synthetic/all.mjs';
5+
import { buildIndexPage } from './utils/synthetic/index.mjs';
6+
import getConfig from '../../utils/configuration/index.mjs';
37
import { groupNodesByModule } from '../../utils/generators.mjs';
48

9+
/**
10+
* Builds JSX content for all configured synthetic pages.
11+
*
12+
* @param {Array<import('../metadata/types').MetadataEntry>} input
13+
*/
14+
const buildSyntheticEntries = async input => {
15+
const config = getConfig('jsx-ast');
16+
17+
const descriptors = [
18+
config.generateAllPage && buildAllPage(input),
19+
config.generateIndexPage && buildIndexPage(input),
20+
config.generateNotFoundPage && buildNotFoundPage(),
21+
].filter(Boolean);
22+
23+
return Promise.all(
24+
descriptors.map(({ head, entries }) => buildContent(entries, head))
25+
);
26+
};
27+
528
/**
629
* Process a chunk of items in a worker thread.
730
* Transforms metadata entries into JSX AST nodes.
@@ -19,11 +42,6 @@ export async function processChunk(slicedInput, itemIndices) {
1942

2043
const content = await buildContent(entries, head);
2144

22-
// Preserve the raw section entries so downstream generators (e.g. `web`)
23-
// can build synthetic pages (all.html, index.html) without recomputing
24-
// metadata.
25-
content.sectionEntries = entries;
26-
2745
results.push(content);
2846
}
2947

@@ -36,18 +54,24 @@ export async function processChunk(slicedInput, itemIndices) {
3654
* @type {import('./types').Generator['generate']}
3755
*/
3856
export async function* generate(input, worker) {
39-
const groupedModules = groupNodesByModule(input);
40-
41-
const headNodes = getSortedHeadNodes(input);
57+
// The synthetic `index` page replaces the Core `index` document.
58+
const moduleInput = input.filter(entry => entry.api !== 'index');
4259

4360
// Create sliced input: each item contains head + its module's entries
4461
// This avoids sending all 4700+ entries to every worker
45-
const entries = headNodes.map(head => ({
62+
const groupedModules = groupNodesByModule(input);
63+
const entries = getSortedHeadNodes(input).map(head => ({
4664
head,
4765
entries: groupedModules.get(head.api),
4866
}));
4967

5068
for await (const chunkResult of worker.stream(entries)) {
5169
yield chunkResult;
5270
}
71+
72+
const syntheticEntries = await buildSyntheticEntries(moduleInput);
73+
74+
if (syntheticEntries.length > 0) {
75+
yield syntheticEntries;
76+
}
5377
}

src/generators/jsx-ast/index.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ export default createLazyGenerator({
1818

1919
defaultConfiguration: {
2020
ref: 'main',
21+
generateAllPage: true,
22+
generateIndexPage: true,
23+
generateNotFoundPage: true,
2124
},
2225

2326
hasParallelProcessor: true,

src/generators/jsx-ast/types.d.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ import type { JSXContent } from './utils/buildContent.mjs';
33

44
export type Generator = GeneratorMetadata<
55
{
6-
pageURL: string;
7-
editURL: string;
6+
ref: string;
7+
generateAllPage: boolean;
8+
generateIndexPage: boolean;
9+
generateNotFoundPage: boolean;
810
},
911
Generate<Array<MetadataEntry>, AsyncGenerator<JSXContent>>,
1012
ProcessChunk<
File renamed without changes.

src/generators/web/utils/synthetic/__tests__/404.test.mjs renamed to src/generators/jsx-ast/utils/synthetic/__tests__/404.test.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ describe('buildNotFoundPage', () => {
1111
assert.equal(head.path, '/404');
1212
assert.equal(head.basename, '404');
1313
assert.equal(head.heading.data.name, 'Page Not Found');
14+
assert.equal(head.synthetic, true);
15+
assert.equal(head.hideViewAs, true);
1416
});
1517

1618
it('produces a single synthetic entry with a not-found paragraph', () => {

src/generators/web/utils/synthetic/__tests__/all.test.mjs renamed to src/generators/jsx-ast/utils/synthetic/__tests__/all.test.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ describe('buildAllPage', () => {
1111
assert.equal(head.path, '/all');
1212
assert.equal(head.basename, 'all');
1313
assert.equal(head.heading.data.name, 'All');
14+
assert.equal(head.synthetic, true);
15+
assert.equal(head.hideViewAs, true);
1416
});
1517

1618
it('forwards the input entries as the page entries', () => {

src/generators/web/utils/synthetic/__tests__/index.test.mjs renamed to src/generators/jsx-ast/utils/synthetic/__tests__/index.test.mjs

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

4-
import { buildStabilityOverview } from '../index.mjs';
4+
import { buildIndexPage, buildStabilityOverview } from '../index.mjs';
55

66
const fakeHead = (api, name, stabilityIndex, depth = 1) => ({
77
api,
@@ -20,6 +20,19 @@ const fakeHead = (api, name, stabilityIndex, depth = 1) => ({
2020
const findChild = (node, tagName) =>
2121
node.children.find(child => child.tagName === tagName);
2222

23+
describe('buildIndexPage', () => {
24+
it('returns a synthetic `index` head with an "Index" heading', () => {
25+
const { head } = buildIndexPage([]);
26+
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);
32+
assert.equal(head.hideViewAs, true);
33+
});
34+
});
35+
2336
describe('buildStabilityOverview', () => {
2437
it('renders a header row and one body row per entry', () => {
2538
const table = buildStabilityOverview([

src/generators/web/utils/synthetic/__tests__/synthetic.test.mjs renamed to src/generators/jsx-ast/utils/synthetic/__tests__/synthetic.test.mjs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ describe('createSyntheticHead', () => {
1212
assert.equal(head.basename, 'all');
1313
});
1414

15+
it('marks synthetic heads so web UI can treat them differently', () => {
16+
const head = createSyntheticHead('index', 'Index');
17+
18+
assert.equal(head.synthetic, true);
19+
assert.equal(head.hideViewAs, true);
20+
});
21+
1522
it('produces a depth-1 heading whose data is consistent with the name', () => {
1623
const head = createSyntheticHead('index', 'Index');
1724

@@ -58,5 +65,7 @@ describe('wrapAsEntry', () => {
5865
assert.equal(entry.api, head.api);
5966
assert.equal(entry.path, head.path);
6067
assert.equal(entry.basename, head.basename);
68+
assert.equal(entry.synthetic, true);
69+
assert.equal(entry.hideViewAs, true);
6170
});
6271
});

0 commit comments

Comments
 (0)