Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/index-page-from-input.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@doc-kit/generator-react': patch
---

Generate `index.html` from the input `index` document instead of a synthetic page
9 changes: 7 additions & 2 deletions packages/react/src/jsx-ast/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ The `jsx-ast` generator converts MDAST (Markdown Abstract Syntax Tree) to JSX AS
documentation structure.
- `generateAllPage` {boolean} When `true`, creates a synthetic JSX AST entry
for `all.html`. **Default:** `true`.
- `generateIndexPage` {boolean} When `true`, creates a synthetic JSX AST entry
for `index.html`. **Default:** `true`.
- `generateNotFoundPage` {boolean} When `true`, creates a synthetic JSX AST
entry for `404.html`. **Default:** `true`.

## Index page

`index.html` is generated when an `index` document is part of the input, and
is rendered from that document like any other page. A section containing a
`<!-- DOCUMENTATION_INDEX -->` comment additionally receives the Stability
Overview table of all modules.
47 changes: 46 additions & 1 deletion packages/react/src/jsx-ast/__tests__/generate.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ describe('jsx-ast generate', () => {

const jsxAstConfig = getConfig('jsx-ast');
jsxAstConfig.generateAllPage = false;
jsxAstConfig.generateIndexPage = false;
jsxAstConfig.generateNotFoundPage = false;

const seenItems = [];
Expand All @@ -95,4 +94,50 @@ describe('jsx-ast generate', () => {
['index', 'fs']
);
});

it('only generates an index page when an index document is an input', async () => {
await setConfig({ target: ['jsx-ast'] });

const jsxAstConfig = getConfig('jsx-ast');
jsxAstConfig.generateAllPage = false;
jsxAstConfig.generateNotFoundPage = false;

const seenItems = [];
await collect(
generate([createEntry('fs', 'File system')], createWorker(seenItems))
);

assert.deepEqual(
seenItems.map(({ head }) => head.api),
['fs']
);
});

it('places the stability overview at the DOCUMENTATION_INDEX comment', async () => {
await setConfig({ target: ['jsx-ast'] });

const jsxAstConfig = getConfig('jsx-ast');
jsxAstConfig.generateAllPage = false;
jsxAstConfig.generateNotFoundPage = false;

const index = createEntry('index', 'Index', { stabilityIndex: null });
// The metadata parser turns a `<!-- DOCUMENTATION_INDEX -->` comment into
// this tag on the entry of the section containing it.
index.tags = ['DOCUMENTATION_INDEX'];

const seenItems = [];
await collect(
generate(
[index, createEntry('fs', 'File system')],
createWorker(seenItems)
)
);

const [{ entries }] = seenItems;
const table = entries[0].content.children.at(-1);

assert.equal(table.tagName, 'table');
const [row] = table.children.at(-1).children;
assert.equal(row.children[0].children[0].properties.href, 'fs.html');
});
});
11 changes: 8 additions & 3 deletions packages/react/src/jsx-ast/generate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { groupNodesByModule } from '@doc-kit/core/utils/generators.mjs';
import { jsx, toJs } from 'estree-util-to-js';

import buildContent from './utils/buildContent.mjs';
import { injectDocumentationIndex } from './utils/documentationIndex.mjs';
import { getSortedHeadNodes } from './utils/getSortedHeadNodes.mjs';
import { buildNotFoundPage } from './utils/synthetic/404.mjs';
import { buildAllPage } from './utils/synthetic/all.mjs';
import { buildIndexPage } from './utils/synthetic/index.mjs';

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

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

// Sections tagged with a `<!-- DOCUMENTATION_INDEX -->` comment (e.g. in
// the `index` document) receive the Stability Overview of all modules.
injectDocumentationIndex(input, moduleInput);

// Create sliced input: each item contains head + its module's entries
// This avoids sending all 4700+ entries to every worker
const groupedModules = groupNodesByModule(input);
Expand Down
1 change: 0 additions & 1 deletion packages/react/src/jsx-ast/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ export default {
defaultConfiguration: {
ref: 'main',
generateAllPage: true,
generateIndexPage: true,
generateNotFoundPage: true,
},

Expand Down
1 change: 0 additions & 1 deletion packages/react/src/jsx-ast/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ export type Generator = GeneratorMetadata<
{
ref: string;
generateAllPage: boolean;
generateIndexPage: boolean;
generateNotFoundPage: boolean;
},
Generate<Array<MetadataEntry>, AsyncGenerator<JSXContent>>,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import assert from 'node:assert/strict';
import { describe, it } from 'node:test';

import { buildIndexPage, buildStabilityOverview } from '../index.mjs';
import {
buildStabilityOverview,
injectDocumentationIndex,
} from '../documentationIndex.mjs';

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

describe('buildIndexPage', () => {
it('returns a synthetic `index` head with an "Index" heading', () => {
const { head } = buildIndexPage([]);
describe('injectDocumentationIndex', () => {
const createEntry = tags => ({
...fakeHead('index', 'Index', null),
tags,
content: { type: 'root', children: [] },
});

it('appends the overview to entries tagged DOCUMENTATION_INDEX', () => {
const tagged = createEntry(['DOCUMENTATION_INDEX']);
const untagged = createEntry(undefined);

injectDocumentationIndex(
[tagged, untagged],
[fakeHead('fs', 'fs', 2), fakeHead('assert', 'assert', 2)]
);

assert.equal(head.api, 'index');
assert.equal(head.path, '/index');
assert.equal(head.basename, 'index');
assert.equal(head.heading.data.name, 'Index');
assert.equal(head.synthetic, true);
const table = findChild(tagged.content, 'table');
assert.equal(findChild(table, 'tbody').children.length, 2);
assert.equal(untagged.content.children.length, 0);
});

it('sorts the stability overview rows alphabetically by API name', () => {
const { entries } = buildIndexPage([
fakeHead('fs', 'fs', 2),
fakeHead('assert', 'assert', 2),
fakeHead('crypto', 'crypto', 2),
]);
const entry = createEntry(['DOCUMENTATION_INDEX']);

injectDocumentationIndex(
[entry],
[
fakeHead('fs', 'fs', 2),
fakeHead('assert', 'assert', 2),
fakeHead('crypto', 'crypto', 2),
]
);

const table = findChild(entries[0].content, 'table');
const table = findChild(entry.content, 'table');
const rows = findChild(table, 'tbody').children;
const names = rows.map(
row => row.children[0].children[0].children[0].value
);

assert.deepEqual(names, ['assert', 'crypto', 'fs']);
});

it('excludes module heads without a stability index', () => {
const entry = createEntry(['DOCUMENTATION_INDEX']);

injectDocumentationIndex(
[entry],
[fakeHead('fs', 'fs', 2), fakeHead('synopsis', 'Usage', null)]
);

const table = findChild(entry.content, 'table');
assert.equal(findChild(table, 'tbody').children.length, 1);
});
});

describe('buildStabilityOverview', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@

import { h as createElement } from 'hastscript';

import { createSyntheticHead, wrapAsEntry } from './synthetic.mjs';
import { JSX_IMPORTS } from '../../../html/constants.mjs';
import { createJSXElement } from '../ast.mjs';
import { getSortedHeadNodes } from '../getSortedHeadNodes.mjs';
import { createJSXElement } from './ast.mjs';
import { getSortedHeadNodes } from './getSortedHeadNodes.mjs';
import { JSX_IMPORTS } from '../../html/constants.mjs';

// The metadata parser turns bare HTML comments into entry tags, so a
// `<!-- DOCUMENTATION_INDEX -->` comment in a source document surfaces as
// this tag on the entry for the section containing it.
export const DOCUMENTATION_INDEX_TAG = 'DOCUMENTATION_INDEX';

const STABILITY_BADGE_KINDS = [
'error',
Expand Down Expand Up @@ -62,20 +66,21 @@ export const buildStabilityOverview = headEntries =>
]);

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

return {
head,
entries: [
wrapAsEntry(head, [
buildStabilityOverview(moduleEntries.filter(entry => entry.stability)),
]),
],
};
for (const entry of entries) {
if (entry.tags?.includes(DOCUMENTATION_INDEX_TAG)) {
entry.content.children.push(buildStabilityOverview(headEntries));
}
}
};
1 change: 0 additions & 1 deletion scripts/vercel-build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ node packages/cli/bin/cli.mjs generate \
-c "./node/CHANGELOG.md" \
-v "$NODE_VERSION" \
--type-map "./node/doc/type-map.json" \
--index "./node/doc/api/index.md" \
--config-file "./beta/doc-kit.config.mjs" \
--log-level debug

Expand Down
3 changes: 3 additions & 0 deletions scripts/vercel-prepare.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ cd node
# Enable sparse checkout and specify the folder
git sparse-checkout set lib doc .

sed 's/STABILITY_OVERVIEW_SLOT_BEGIN/DOCUMENTATION_INDEX/g' ./doc/api/documentation.md > ./doc/api/index.md
rm ./doc/api/documentation.md
Comment thread
avivkeller marked this conversation as resolved.

# Move back out
cd ..

Expand Down
7 changes: 0 additions & 7 deletions www/doc-kit.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,6 @@ export default {
minify: true,
},

'jsx-ast': {
// `jsx-ast` otherwise synthesizes an `index.html` holding the Node.js API
// stability overview, and it silently overrides an authored `index.md`.
// This site has no stability metadata, so that page would render empty.
generateIndexPage: false,
},

html: {
title: '{project} documentation',

Expand Down
Loading