diff --git a/.changeset/index-page-from-input.md b/.changeset/index-page-from-input.md new file mode 100644 index 000000000..4a08607c2 --- /dev/null +++ b/.changeset/index-page-from-input.md @@ -0,0 +1,5 @@ +--- +'@doc-kit/generator-react': patch +--- + +Generate `index.html` from the input `index` document instead of a synthetic page diff --git a/.changeset/one-shot-generator-resolution.md b/.changeset/one-shot-generator-resolution.md new file mode 100644 index 000000000..8182e24a4 --- /dev/null +++ b/.changeset/one-shot-generator-resolution.md @@ -0,0 +1,5 @@ +--- +'@doc-kit/core': patch +--- + +Resolve generator packages from the invoking project's `node_modules` and the npm global root when they are not installed alongside core, so one-shot runs (`npx @doc-kit/cli`) find locally or globally installed generators diff --git a/.changeset/show-reading-time-config.md b/.changeset/show-reading-time-config.md new file mode 100644 index 000000000..7f79693db --- /dev/null +++ b/.changeset/show-reading-time-config.md @@ -0,0 +1,5 @@ +--- +'@doc-kit/generator-react': patch +--- + +Add a `showReadingTime` option to the `jsx-ast` generator (default `true`); when disabled, the estimated reading time is not computed and the `Layout` component does not receive a `readingTime` prop diff --git a/.changeset/silent-log-level.md b/.changeset/silent-log-level.md new file mode 100644 index 000000000..de841ee80 --- /dev/null +++ b/.changeset/silent-log-level.md @@ -0,0 +1,5 @@ +--- +'@doc-kit/core': patch +--- + +Add a `silent` log level, so `--log-level silent` suppresses all output diff --git a/README.md b/README.md index 9e3948492..92e6bf3ca 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ CLI tool to generate the Node.js API documentation Options: --log-level Log level (choices: "debug", "info", "warn", "error", - "fatal", default: "info") + "fatal", "silent", default: "info") -h, --help display help for command Commands: diff --git a/docs/cli.md b/docs/cli.md index e82748e7d..a6ebf2ebe 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -10,8 +10,8 @@ npx @doc-kit/cli [command] [options] One option applies to every command: -- `--log-level ` {string} `debug`, `info`, `warn`, `error`, or - `fatal`. **Default:** `'info'`. +- `--log-level ` {string} `debug`, `info`, `warn`, `error`, `fatal`, + or `silent` (no output at all). **Default:** `'info'`. ## `doc-kit generate` diff --git a/docs/creating-generators.md b/docs/creating-generators.md index 6580fbbc9..470cea4c0 100644 --- a/docs/creating-generators.md +++ b/docs/creating-generators.md @@ -162,6 +162,11 @@ npx @doc-kit/cli generate -t @my-scope/my-package/my-format ... npx @doc-kit/cli generate -t ./generators/my-format/index.mjs ... ``` +Package specifiers are resolved from wherever doc-kit is installed, then from +the invoking project's `node_modules`, then from the npm global root — so +one-shot runs (`npx @doc-kit/cli`) find generator packages installed either +in your project or globally. + Built-in generators additionally get a shorthand alias in `packages/core/src/generators/index.mjs`, which maps the name users type to the import specifier it resolves to: diff --git a/packages/core/src/generators/__tests__/loader.test.mjs b/packages/core/src/generators/__tests__/loader.test.mjs new file mode 100644 index 000000000..fc55de9a2 --- /dev/null +++ b/packages/core/src/generators/__tests__/loader.test.mjs @@ -0,0 +1,67 @@ +import assert from 'node:assert/strict'; +import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import process from 'node:process'; +import { describe, it, before, after } from 'node:test'; + +import { loadGenerator } from '../loader.mjs'; + +// A package that only exists in the fake project's node_modules — never in +// the workspace — so a bare import() from core is guaranteed to miss and +// exercise the cwd fallback used by one-shot (`npx @doc-kit/cli`) runs. +const PACKAGE_NAME = '@doc-kit-test/fake-generator'; + +describe('loadGenerator', () => { + let projectDir; + let originalCwd; + + before(async () => { + projectDir = await mkdtemp(join(tmpdir(), 'doc-kit-loader-')); + + const packageDir = join(projectDir, 'node_modules', PACKAGE_NAME); + await mkdir(packageDir, { recursive: true }); + + await writeFile( + join(packageDir, 'package.json'), + JSON.stringify({ + name: PACKAGE_NAME, + version: '1.0.0', + exports: { './gen': './gen.mjs' }, + }) + ); + + await writeFile( + join(packageDir, 'gen.mjs'), + 'export default { name: "fake", generate: () => {} };\n' + ); + + originalCwd = process.cwd(); + process.chdir(projectDir); + }); + + after(async () => { + process.chdir(originalCwd); + await rm(projectDir, { recursive: true, force: true }); + }); + + it('should resolve packages from the invoking project', async () => { + const generator = await loadGenerator(`${PACKAGE_NAME}/gen`); + + assert.equal(generator.name, 'fake'); + }); + + it('should throw a friendly error when a package is not installed anywhere', async () => { + await assert.rejects( + loadGenerator('@doc-kit-test/does-not-exist'), + /Could not load generator "@doc-kit-test\/does-not-exist"/ + ); + }); + + it('should reject modules that are not generators', async () => { + const notAGenerator = join(projectDir, 'not-a-generator.mjs'); + await writeFile(notAGenerator, 'export default { name: "broken" };\n'); + + await assert.rejects(loadGenerator(notAGenerator), /is not a generator/); + }); +}); diff --git a/packages/core/src/generators/loader.mjs b/packages/core/src/generators/loader.mjs index 653d202bc..faee80a1d 100644 --- a/packages/core/src/generators/loader.mjs +++ b/packages/core/src/generators/loader.mjs @@ -1,6 +1,9 @@ 'use strict'; +import { execSync } from 'node:child_process'; +import { createRequire } from 'node:module'; import { isAbsolute } from 'node:path'; +import process from 'node:process'; import { pathToFileURL } from 'node:url'; import { allGenerators } from './index.mjs'; @@ -27,6 +30,65 @@ export const resolveGeneratorSpecifier = target => { return target; }; +let npmGlobalRoot; + +/** + * Asking npm for its global root spawns a process, so only do it when a + * generator package is neither installed alongside core nor in the invoking + * project, and remember the answer (`''` = npm unavailable). + * + * @returns {string} The npm global `node_modules` directory, or `''` + */ +const getNpmGlobalRoot = () => { + if (npmGlobalRoot === undefined) { + try { + npmGlobalRoot = execSync('npm root -g', { + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'ignore'], + }).trim(); + } catch { + npmGlobalRoot = ''; + } + } + + return npmGlobalRoot; +}; + +/** + * Resolves a specifier starting from the given directory's `node_modules` + * hierarchy instead of core's own location. + * + * @param {string} specifier - Bare package specifier + * @param {string} base - Directory to resolve from + * @returns {string | undefined} File URL of the resolved module, if found + */ +const tryResolveFrom = (specifier, base) => { + if (!base) { + return undefined; + } + + const require = createRequire(import.meta.url); + + try { + return pathToFileURL(require.resolve(specifier, { paths: [base] })).href; + } catch { + return undefined; + } +}; + +/** + * Resolves a generator package from the invoking project or the npm global + * root. One-shot runs (`npx @doc-kit/cli`) install core into the npx cache, + * where a bare import() cannot see generator packages the user has installed + * locally or globally. + * + * @param {string} specifier - Bare package specifier that failed to import + * @returns {string | undefined} File URL of the resolved module, if found + */ +const resolveInstalledPackage = specifier => + tryResolveFrom(specifier, process.cwd()) ?? + tryResolveFrom(specifier, getNpmGlobalRoot()); + /** * Imports a generator by specifier and returns its default export. * @@ -42,15 +104,22 @@ export const loadGenerator = async specifier => { try { module = await import(resolved); } catch (error) { - if (error.code === 'ERR_MODULE_NOT_FOUND') { + if (error.code !== 'ERR_MODULE_NOT_FOUND') { + throw error; + } + + const installed = resolveInstalledPackage(resolved); + + if (!installed) { throw new Error( `Could not load generator "${specifier}" (resolved to "${resolved}"). ` + - 'If it lives in a separate package, make sure that package is installed.', + 'If it lives in a separate package, make sure that package is ' + + 'installed in your project or globally.', { cause: error } ); } - throw error; + module = await import(installed); } const generator = module.default; diff --git a/packages/core/src/logger/__tests__/logger.test.mjs b/packages/core/src/logger/__tests__/logger.test.mjs index 1673b453e..904e14361 100644 --- a/packages/core/src/logger/__tests__/logger.test.mjs +++ b/packages/core/src/logger/__tests__/logger.test.mjs @@ -173,13 +173,26 @@ describe('createLogger', () => { }); }); - it('should filter all messages when minimum level is set above FATAL', t => { + it('should filter all messages when level is SILENT', t => { const transport = t.mock.fn(); - // silent logs - const logger = createLogger(transport, 100); + const logger = createLogger(transport, LogLevel.silent); - Object.keys(LogLevel).forEach(level => { + ['debug', 'info', 'warn', 'error', 'fatal'].forEach(level => { + logger[level]('Hello, World!'); + }); + + strictEqual(transport.mock.callCount(), 0); + }); + + it('should filter all messages when SILENT is set by name at runtime', t => { + const transport = t.mock.fn(); + + const logger = createLogger(transport, LogLevel.info); + + logger.setLogLevel('silent'); + + ['debug', 'info', 'warn', 'error', 'fatal'].forEach(level => { logger[level]('Hello, World!'); }); diff --git a/packages/core/src/logger/constants.mjs b/packages/core/src/logger/constants.mjs index 07683124b..489556dc8 100644 --- a/packages/core/src/logger/constants.mjs +++ b/packages/core/src/logger/constants.mjs @@ -9,6 +9,10 @@ export const LogLevel = { warn: 30, error: 40, fatal: 50, + // Threshold-only level: no message is ever emitted at `silent` (there is no + // logger method for it), so setting it suppresses all output. It has no + // entry in the tag/color maps below for the same reason. + silent: Infinity, }; /** diff --git a/packages/react/package.json b/packages/react/package.json index a60ec0c5c..cdaae06a3 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -28,12 +28,12 @@ ], "dependencies": { "@11ty/is-land": "^5.0.1", + "@doc-kit/core": "workspace:*", "@fontsource-variable/open-sans": "^5.3.0", "@fontsource/ibm-plex-mono": "^5.3.0", "@heroicons/react": "^2.2.0", - "@doc-kit/core": "workspace:*", "@node-core/rehype-shiki": "^1.4.3", - "@node-core/ui-components": "^1.7.4", + "@node-core/ui-components": "^1.7.5", "@orama/orama": "^3.1.18", "@orama/ui": "^1.5.4", "estree-util-to-js": "^2.0.0", diff --git a/packages/react/src/html/README.md b/packages/react/src/html/README.md index 5dddd6529..90a0562c2 100644 --- a/packages/react/src/html/README.md +++ b/packages/react/src/html/README.md @@ -416,7 +416,8 @@ export default ({ metadata }) => ( - `metadata` {Object} Serialized page metadata — all YAML frontmatter properties plus `addedIn`, `basename`, `path`, and any custom user-defined fields. - `headings` {Array} Pre-computed table of contents heading entries. -- `readingTime` {string} Estimated reading time (e.g. `'5 min read'`). +- `readingTime` {string} Estimated reading time (e.g. `'5 min read'`). Only + passed when the `jsx-ast` generator's `showReadingTime` option is enabled. - `children` {ComponentChildren} Processed page content. The `Layout` component receives the props above. Custom Layout components can use diff --git a/packages/react/src/html/ui/index.css b/packages/react/src/html/ui/index.css index 3af0f65cc..6c5e24593 100644 --- a/packages/react/src/html/ui/index.css +++ b/packages/react/src/html/ui/index.css @@ -110,6 +110,7 @@ main { > h5, > h6 { flex: 1; + margin-top: 0; margin-bottom: 8px; overflow-wrap: anywhere; word-break: break-word; diff --git a/packages/react/src/jsx-ast/README.md b/packages/react/src/jsx-ast/README.md index e4f95dba7..ca559d976 100644 --- a/packages/react/src/jsx-ast/README.md +++ b/packages/react/src/jsx-ast/README.md @@ -10,7 +10,15 @@ 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`. +- `showReadingTime` {boolean} When `true`, computes an estimated reading time + for each page and passes it to the `Layout` component as the `readingTime` + prop, shown in the MetaBar. **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 +`` comment additionally receives the Stability +Overview table of all modules. diff --git a/packages/react/src/jsx-ast/__tests__/generate.test.mjs b/packages/react/src/jsx-ast/__tests__/generate.test.mjs index 7a893a8cc..a3050b09e 100644 --- a/packages/react/src/jsx-ast/__tests__/generate.test.mjs +++ b/packages/react/src/jsx-ast/__tests__/generate.test.mjs @@ -75,7 +75,6 @@ describe('jsx-ast generate', () => { const jsxAstConfig = getConfig('jsx-ast'); jsxAstConfig.generateAllPage = false; - jsxAstConfig.generateIndexPage = false; jsxAstConfig.generateNotFoundPage = false; const seenItems = []; @@ -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 `` 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'); + }); }); diff --git a/packages/react/src/jsx-ast/generate.mjs b/packages/react/src/jsx-ast/generate.mjs index ba62a876c..9f6969543 100644 --- a/packages/react/src/jsx-ast/generate.mjs +++ b/packages/react/src/jsx-ast/generate.mjs @@ -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 @@ -21,7 +21,6 @@ const buildSyntheticDescriptors = input => { return [ config.generateAllPage && buildAllPage(input), - config.generateIndexPage && buildIndexPage(input), config.generateNotFoundPage && buildNotFoundPage(), ].filter(Boolean); }; @@ -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 `` 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); diff --git a/packages/react/src/jsx-ast/index.mjs b/packages/react/src/jsx-ast/index.mjs index 54272b2a0..c4f1d216d 100644 --- a/packages/react/src/jsx-ast/index.mjs +++ b/packages/react/src/jsx-ast/index.mjs @@ -17,8 +17,8 @@ export default { defaultConfiguration: { ref: 'main', generateAllPage: true, - generateIndexPage: true, generateNotFoundPage: true, + showReadingTime: true, }, hasParallelProcessor: true, diff --git a/packages/react/src/jsx-ast/types.d.ts b/packages/react/src/jsx-ast/types.d.ts index 2413cd42a..9d630e1a2 100644 --- a/packages/react/src/jsx-ast/types.d.ts +++ b/packages/react/src/jsx-ast/types.d.ts @@ -5,8 +5,8 @@ export type Generator = GeneratorMetadata< { ref: string; generateAllPage: boolean; - generateIndexPage: boolean; generateNotFoundPage: boolean; + showReadingTime: boolean; }, Generate, AsyncGenerator>, ProcessChunk< diff --git a/packages/react/src/jsx-ast/utils/synthetic/__tests__/index.test.mjs b/packages/react/src/jsx-ast/utils/__tests__/documentationIndex.test.mjs similarity index 67% rename from packages/react/src/jsx-ast/utils/synthetic/__tests__/index.test.mjs rename to packages/react/src/jsx-ast/utils/__tests__/documentationIndex.test.mjs index f083e9055..6817f6349 100644 --- a/packages/react/src/jsx-ast/utils/synthetic/__tests__/index.test.mjs +++ b/packages/react/src/jsx-ast/utils/__tests__/documentationIndex.test.mjs @@ -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, @@ -20,25 +23,40 @@ 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 @@ -46,6 +64,18 @@ describe('buildIndexPage', () => { 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', () => { diff --git a/packages/react/src/jsx-ast/utils/buildContent.mjs b/packages/react/src/jsx-ast/utils/buildContent.mjs index bc92b7be3..f9d1cb0b7 100644 --- a/packages/react/src/jsx-ast/utils/buildContent.mjs +++ b/packages/react/src/jsx-ast/utils/buildContent.mjs @@ -310,7 +310,9 @@ export const createDocumentLayout = (entries, metadata) => { createJSXElement(JSX_IMPORTS.Layout.name, { metadata, headings: extractHeadings(entries), - readingTime: readingTime(extractTextContent(entries)).text, + readingTime: getConfig('jsx-ast').showReadingTime + ? readingTime(extractTextContent(entries)).text + : undefined, children: entries.map(processEntry), }), ]); diff --git a/packages/react/src/jsx-ast/utils/synthetic/index.mjs b/packages/react/src/jsx-ast/utils/documentationIndex.mjs similarity index 57% rename from packages/react/src/jsx-ast/utils/synthetic/index.mjs rename to packages/react/src/jsx-ast/utils/documentationIndex.mjs index a32782027..c12ce0bf7 100644 --- a/packages/react/src/jsx-ast/utils/synthetic/index.mjs +++ b/packages/react/src/jsx-ast/utils/documentationIndex.mjs @@ -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 +// `` 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', @@ -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 `` comment. The parser strips the + * comment itself, so the table lands at the end of the tagged section. * - * @param {Array} entries + * @param {Array} entries - Entries to scan for the tag + * @param {Array} 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)); + } + } }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3c6191fc3..9c6cea83e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -236,8 +236,8 @@ importers: specifier: ^1.4.3 version: 1.4.3(supports-color@7.2.0) '@node-core/ui-components': - specifier: ^1.7.4 - version: 1.7.4(@orama/core@1.2.19)(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(supports-color@7.2.0) + specifier: ^1.7.5 + version: 1.7.5(@orama/core@1.2.19)(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(supports-color@7.2.0) '@orama/orama': specifier: ^3.1.18 version: 3.1.18 @@ -707,8 +707,8 @@ packages: resolution: {integrity: sha512-3LnysU0F1MacdHc+1VHswySfD+smk2bHGob2IlqtHc9VrL5yCC+gbIBbALZYtOOSdmY9J65XvJilFm8XbXJZgA==} engines: {node: '>=20'} - '@node-core/ui-components@1.7.4': - resolution: {integrity: sha512-HmSqvXKOk8xBBpFAjWzx6FNCVYmyWSvqwqwfUVaJVAbNGyRNwtziV6HaBjW/ebhD8pqJlhIsrTYO5SFdnQ8WIQ==} + '@node-core/ui-components@1.7.5': + resolution: {integrity: sha512-BVSpgyjl84yLHU+KHrFs5d+zJdlj8cQSGfkzltPmB3W3daDcUdY1CNGwsVbDc60dAvzzHzpKR8ovgVqAA5UIpg==} engines: {node: '>=20'} '@nodelib/fs.scandir@2.1.5': @@ -4044,7 +4044,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@node-core/ui-components@1.7.4(@orama/core@1.2.19)(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(supports-color@7.2.0)': + '@node-core/ui-components@1.7.5(@orama/core@1.2.19)(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(supports-color@7.2.0)': dependencies: '@heroicons/react': 2.2.0(react@19.2.8) '@orama/orama': 3.1.18 diff --git a/scripts/vercel-build.sh b/scripts/vercel-build.sh index b42fa0b6e..62e7f6793 100755 --- a/scripts/vercel-build.sh +++ b/scripts/vercel-build.sh @@ -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 diff --git a/scripts/vercel-prepare.sh b/scripts/vercel-prepare.sh index ecb337599..c536f8c4b 100755 --- a/scripts/vercel-prepare.sh +++ b/scripts/vercel-prepare.sh @@ -26,6 +26,8 @@ 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 + # Move back out cd .. diff --git a/www/doc-kit.config.mjs b/www/doc-kit.config.mjs index 8d86db88e..726d34f0e 100644 --- a/www/doc-kit.config.mjs +++ b/www/doc-kit.config.mjs @@ -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',