Skip to content

Commit 0c35801

Browse files
committed
feat: project-neutral defaults + @node-core/doc-kit/config preset
1 parent a13933b commit 0c35801

30 files changed

Lines changed: 384 additions & 167 deletions

.changeset/node-preset.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@node-core/doc-kit': minor
3+
---
4+
5+
Add the `@node-core/doc-kit/config` preset

.changeset/plain-defaults.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@nodejs/doc-kit': patch
3+
'@nodejs/doc-kit-generator-react': minor
4+
---
5+
6+
Defaults are now project-neutral instead of Node.js-specific

beta/doc-kit.config.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export default {
2+
extends: '@node-core/doc-kit/config',
3+
24
html: {
35
remoteConfigUrl:
46
'https://raw.githubusercontent.com/nodejs/doc-kit/main/beta/site.json',

docs/configuration.md

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,50 +17,75 @@ export default {
1717
global: {
1818
input: ['docs/**/*.md'],
1919
output: 'out',
20+
project: 'My Project',
2021
version: '1.2.0',
2122
baseURL: 'https://example.com/docs',
22-
changelog: [],
2323
},
2424

2525
// Generator-specific sections, keyed by generator name
2626
html: {
27-
project: 'My Project',
27+
title: '{project} Documentation',
2828
},
2929
};
3030
```
3131

32+
## Presets (`extends`)
33+
34+
A configuration file may declare `extends`: one or more presets whose values
35+
are merged underneath its own. Each entry is either an import specifier of a
36+
module whose default export is a configuration object, or a path relative to
37+
the configuration file:
38+
39+
```mjs displayName="doc-kit.config.mjs"
40+
export default {
41+
// Build the docs the way nodejs.org does — branding, URL layouts,
42+
// and release history included
43+
extends: '@node-core/doc-kit/config',
44+
45+
html: {
46+
// Your own values still win over the preset
47+
title: '{project} {version} API Reference',
48+
},
49+
};
50+
```
51+
52+
`extends` also accepts an array; later presets take precedence over earlier
53+
ones, and the configuration file itself wins over all of them.
54+
55+
The built-in defaults are deliberately project-neutral: no repository, site
56+
URL, release history, or branding is assumed. The
57+
[`@node-core/doc-kit/config`](./packages/node.md) preset opts back into
58+
everything Node.js-specific.
59+
3260
## How values merge
3361

34-
Three sources, in order of precedence:
62+
Four sources, in order of precedence:
3563

3664
1. **CLI flags** (see the [CLI reference](./cli.md)) override
3765
2. **the configuration file**, which overrides
38-
3. **built-in defaults**.
66+
3. **presets** (listed in the file's `extends`), which override
67+
4. **built-in defaults**.
3968

4069
Each generator's section starts from its own defaults, then inherits every
4170
`global` value it doesn't override. So `global.minify` applies to all
4271
targets, while `'legacy-json': { minify: false }` exempts one.
4372

4473
## Global options
4574

46-
| Property | Type | Description | Default |
47-
| ------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- |
48-
| `input` | `string \| string[]` | Glob patterns for the source Markdown files. Required (with `target`) to run. ||
49-
| `output` | `string` | The directory generated files are written to. ||
50-
| `ignore` | `string \| string[]` | Glob patterns excluded from `input`. ||
51-
| `version` | `string` | The version of the project being documented (coerced to semver). | `process.version` |
52-
| `changelog` | `string \| URL \| Array` | Release history used to build version selectors. A URL or path to a `CHANGELOG.md` to parse, or a pre-parsed array — `[]` disables versioning (and the network fetch). | The Node.js `CHANGELOG.md` |
53-
| `index` | `string \| URL \| Array` | An `index.md` listing section titles, or a pre-parsed array. ||
54-
| `baseURL` | `string \| URL` | The public URL of the published site; used wherever absolute links are needed (sitemaps, `llms.txt`, social metadata). | `'https://nodejs.org/docs'` |
55-
| `repository` | `string` | GitHub repository in `owner/repo` form, used for source and edit links. | `'nodejs/node'` |
56-
| `ref` | `string` | Git ref (branch, tag, or SHA) used in source links. | `'HEAD'` |
57-
| `minify` | `boolean` | Minify the output, in whatever form it takes. | `true` |
58-
| `pathsToCopy` | `Array<string \| Object>` | Extra files or directories copied into the output. A string copies to `output/<basename>`; a `{ source: destination }` object controls the target path. Missing paths are skipped. | `['assets', 'public', 'static']` |
59-
60-
> The defaults still reflect doc-kit's Node.js origins: `changelog`,
61-
> `repository`, and `baseURL` point at the Node.js project unless you set
62-
> them. `doc-kit bootstrap` writes a configuration that overrides the ones
63-
> your project needs.
75+
| Property | Type | Description | Default |
76+
| ------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
77+
| `input` | `string \| string[]` | Glob patterns for the source Markdown files. Required (with `target`) to run. ||
78+
| `output` | `string` | The directory generated files are written to. ||
79+
| `ignore` | `string \| string[]` | Glob patterns excluded from `input`. ||
80+
| `version` | `string` | The version of the project being documented (coerced to semver). | `process.version` |
81+
| `project` | `string` | The name of the project being documented, used in titles, logos, and templated text. | The `name` in your `package.json` |
82+
| `changelog` | `string \| URL \| Array` | Release history used to build version selectors. A URL or path to a `CHANGELOG.md` to parse, or a pre-parsed array. | `[]` (single-version output) |
83+
| `index` | `string \| URL \| Array` | An `index.md` listing section titles, or a pre-parsed array. ||
84+
| `baseURL` | `string \| URL` | The public URL of the published site; used wherever absolute links are needed (sitemaps, `llms.txt`, social metadata). ||
85+
| `repository` | `string` | GitHub repository in `owner/repo` form, used for source and edit links; without one, repository-specific UI (like the GitHub link) is omitted. ||
86+
| `ref` | `string` | Git ref (branch, tag, or SHA) used in source links. | `'HEAD'` |
87+
| `minify` | `boolean` | Minify the output, in whatever form it takes. | `true` |
88+
| `pathsToCopy` | `Array<string \| Object>` | Extra files or directories copied into the output. A string copies to `output/<basename>`; a `{ source: destination }` object controls the target path. Missing paths are skipped. | `['assets', 'public', 'static']` |
6489

6590
## Execution options
6691

@@ -79,7 +104,7 @@ Each generator documents its own options on its reference page — see the
79104
```js
80105
export default {
81106
html: {
82-
project: 'My Project',
107+
title: '{project} Documentation',
83108
},
84109

85110
metadata: {

docs/customization.md

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This page walks the customizations most projects make, in the
44
order they usually make them. The complete option list lives in the
55
[`html` generator reference](./generators/html.md).
66

7-
All of it goes in the `html` section of your configuration file:
7+
Most of it goes in the `html` section of your configuration file:
88

99
```js
1010
// doc-kit.config.mjs
@@ -24,17 +24,18 @@ export default {
2424
## Name and titles
2525

2626
```js
27+
global: {
28+
project: 'My Project', // Used in page titles, the logo, and the version selector
29+
},
30+
2731
html: {
28-
project: 'My Project', // Used in page titles and the version selector
2932
title: '{project} Documentation', // Browser-tab title template
30-
// Suppress the Node.js announcement banner fetched at runtime
31-
remoteConfigUrl: '',
3233
},
3334
```
3435

35-
`title` supports `{project}` and `{version}` placeholders. Projects that
36-
bootstrap with `doc-kit bootstrap` get `project` wired to their
37-
`package.json` automatically.
36+
`project` defaults to the `name` in your `package.json` (and `doc-kit
37+
bootstrap` writes it into the configuration explicitly); `title` supports
38+
`{project}` and `{version}` placeholders.
3839

3940
## The `<head>`
4041

@@ -96,8 +97,9 @@ sidebar also shows a version selector when your configuration provides a
9697

9798
## Logo and theme components
9899

99-
The page chrome is built from swappable components. Override any `#theme/*`
100-
alias with a path to your own:
100+
By default the logo slot renders your project's name as plain text. The page
101+
chrome is built from swappable components — give it a real logo (or replace
102+
any other part) by overriding a `#theme/*` alias with a path to your own:
101103

102104
```js
103105
html: {

docs/troubleshooting.md

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,6 @@ The search box queries an index built by the `orama-db` generator. Generate
2525
it alongside the site — `target: ['html', 'orama-db']` — and make sure
2626
you're serving over HTTP, not `file://`.
2727

28-
## The build fetches from nodejs.org (or fails offline)
29-
30-
Two defaults reach for the network: `changelog` and `index` point at the
31-
Node.js repository unless you say otherwise. A project without a release
32-
history can short-circuit both with empty values:
33-
34-
```js
35-
global: {
36-
changelog: [],
37-
index: [],
38-
},
39-
```
40-
41-
Or point them at your own files — see the
42-
[configuration reference](./configuration.md).
43-
4428
## `bootstrap` or `install` couldn't install packages
4529

4630
Package installation runs through your package manager, so registry issues

packages/core/bin/commands/bootstrap.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ export default new Command('bootstrap')
138138
})
139139
);
140140
bootstrapLogger.info(
141-
'Created doc-kit.config.mjs (wired to your package.json)'
141+
'Created doc-kit.config.mjs (naming and versioning follow your package.json)'
142142
);
143143

144144
// 4. Ignore the output directory

packages/core/src/utils/__tests__/bootstrap.test.mjs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -89,18 +89,6 @@ describe('buildConfigSource', () => {
8989
assert.match(source, /\/\/ baseURL: /);
9090
assert.doesNotMatch(source, /baseURL: packageJson.homepage/);
9191
});
92-
93-
it('should only emit the html block when html is targeted', () => {
94-
const source = buildConfigSource({
95-
targets: ['llms-txt'],
96-
docsDir: 'docs',
97-
output: 'out',
98-
hasHomepage: false,
99-
});
100-
101-
assert.doesNotMatch(source, /html: \{/);
102-
assert.doesNotMatch(source, /project:/);
103-
});
10492
});
10593

10694
describe('addIgnoredOutput', () => {

packages/core/src/utils/bootstrap.mjs

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -112,31 +112,21 @@ export const buildConfigSource = ({
112112
// sitemaps, and llms.txt page URLs
113113
// baseURL: 'https://example.com/docs',`;
114114

115-
// The project name is an html-generator option, so it only belongs in a
116-
// configuration that targets html
117-
const html = targets.includes('html')
118-
? `
119-
120-
html: {
121-
// The name shown in page titles, the logo, and the version picker
122-
project: packageJson.name,
123-
},`
124-
: '';
125-
126115
return `import packageJson from './package.json' with { type: 'json' };
127116
128117
/** @type {import('@nodejs/doc-kit/utils/configuration/types').Configuration} */
129118
export default {
130119
target: ${JSON.stringify(targets)},
131120
132121
global: {
133-
// Versioning follows package.json
122+
// Naming and versioning follow package.json
123+
project: packageJson.name,
134124
version: packageJson.version,
135125
136126
input: [${JSON.stringify(`${docsDir}/**/*.md`)}],
137127
output: ${JSON.stringify(output)},
138128
${baseURL}
139-
},${html}
129+
},
140130
};
141131
`;
142132
};

packages/core/src/utils/configuration/__tests__/index.test.mjs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import assert from 'node:assert';
2+
import { mkdtempSync, writeFileSync } from 'node:fs';
3+
import { tmpdir } from 'node:os';
4+
import { join } from 'node:path';
25
import { describe, it, mock, beforeEach } from 'node:test';
36

47
// Mock dependencies
@@ -96,6 +99,47 @@ describe('config.mjs', () => {
9699
);
97100
assert.strictEqual(mockConfigSearch.mock.calls.length, 0);
98101
});
102+
103+
it('should merge extends presets underneath the config file', async () => {
104+
const dir = mkdtempSync(join(tmpdir(), 'doc-kit-config-'));
105+
106+
writeFileSync(
107+
join(dir, 'base.mjs'),
108+
'export default { global: { project: "Base", ref: "base" }, html: { a: 1 } };'
109+
);
110+
writeFileSync(
111+
join(dir, 'other.mjs'),
112+
'export default { global: { project: "Other" }, html: { b: 2 } };'
113+
);
114+
115+
mockConfigLoad.mock.mockImplementationOnce(async () => ({
116+
config: {
117+
extends: ['./base.mjs', './other.mjs'],
118+
global: { ref: 'own' },
119+
},
120+
filepath: join(dir, 'doc-kit.config.mjs'),
121+
}));
122+
123+
const result = await loadConfigFile('any');
124+
125+
// Later presets win over earlier ones; the file itself wins over all
126+
assert.deepStrictEqual(result, {
127+
global: { project: 'Other', ref: 'own' },
128+
html: { a: 1, b: 2 },
129+
});
130+
});
131+
132+
it('should resolve extends package specifiers from the config file', async () => {
133+
mockConfigLoad.mock.mockImplementationOnce(async () => ({
134+
config: { extends: '@node-core/doc-kit/config' },
135+
filepath: join(process.cwd(), 'doc-kit.config.mjs'),
136+
}));
137+
138+
const result = await loadConfigFile('any');
139+
140+
assert.strictEqual(result.global.project, 'Node.js');
141+
assert.strictEqual(result.global.repository, 'nodejs/node');
142+
});
99143
});
100144

101145
describe('createConfigFromCLIOptions', () => {
@@ -250,6 +294,17 @@ describe('config.mjs', () => {
250294
assert.strictEqual(mockConfigSearch.mock.calls.length, 1);
251295
});
252296

297+
it('should default to project-neutral values', async () => {
298+
const config = await createRunConfiguration({});
299+
300+
// No repository, site, or release history is assumed; presets such as
301+
// @node-core/doc-kit/config opt back into the Node.js values
302+
assert.strictEqual(config.global.repository, undefined);
303+
assert.strictEqual(config.global.baseURL, undefined);
304+
assert.deepStrictEqual(config.global.changelog, []);
305+
assert.strictEqual(typeof config.global.project, 'string');
306+
});
307+
253308
it('should handle generator-specific overrides', async () => {
254309
mockConfigLoad.mock.mockImplementationOnce(async () => ({
255310
config: createMockConfig({

0 commit comments

Comments
 (0)