@doc-kit/core 1.1.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +2 -1
  3. package/package.json +9 -4
  4. package/src/generators/index.mjs +2 -0
  5. package/src/generators/json/README.md +151 -0
  6. package/src/generators/json/constants.mjs +57 -0
  7. package/src/generators/json/generate.mjs +65 -0
  8. package/src/generators/json/generated/schema.d.ts +351 -0
  9. package/src/generators/json/index.mjs +27 -0
  10. package/src/generators/json/schema.json +529 -0
  11. package/src/generators/json/types.d.ts +29 -0
  12. package/src/generators/json/utils/document.mjs +58 -0
  13. package/src/generators/json/utils/entry.mjs +87 -0
  14. package/src/generators/json/utils/lifecycle.mjs +61 -0
  15. package/src/generators/json/utils/markdown.mjs +57 -0
  16. package/src/generators/json/utils/node.mjs +153 -0
  17. package/src/generators/json/utils/schema.mjs +15 -0
  18. package/src/generators/json/utils/signature.mjs +185 -0
  19. package/src/generators/json/utils/types.mjs +28 -0
  20. package/src/generators/json-all/README.md +31 -0
  21. package/src/generators/json-all/constants.mjs +5 -0
  22. package/src/generators/json-all/generate.mjs +35 -0
  23. package/src/generators/json-all/index.mjs +25 -0
  24. package/src/generators/json-all/schema.json +22 -0
  25. package/src/generators/json-all/types.d.ts +20 -0
  26. package/src/generators/json-simple/generate.mjs +2 -7
  27. package/src/generators/metadata/maps/mdn.json +10 -2
  28. package/src/generators/metadata/types.d.ts +5 -0
  29. package/src/generators/metadata/utils/transformers.mjs +1 -1
  30. package/src/logger/logger.mjs +8 -0
  31. package/src/threading/chunk-worker.mjs +7 -0
  32. package/src/threading/index.mjs +1 -0
  33. package/src/utils/file.mjs +10 -0
  34. package/src/utils/hierarchy.mjs +50 -0
  35. package/src/utils/overloads.mjs +54 -0
  36. package/src/utils/queries/index.mjs +2 -0
  37. package/src/utils/queries/utils.mjs +15 -0
  38. package/src/utils/remark.mjs +1 -1
  39. package/src/utils/signature/constants.mjs +3 -0
  40. package/src/utils/signature/extractListItem.mjs +140 -0
  41. package/src/utils/stability.mjs +24 -0
@@ -0,0 +1,87 @@
1
+ 'use strict';
2
+
3
+ import { getEntryDescription } from '#utils/generators.mjs';
4
+ import { splitTypedItems, UNIST } from '#utils/queries/index.mjs';
5
+ import { extractListItem } from '#utils/signature/extractListItem.mjs';
6
+
7
+ import { KINDS_WITH_TYPED_LIST } from '../constants.mjs';
8
+ import { toChanges, toNumbers, toStability, toVersions } from './lifecycle.mjs';
9
+ import { blocksToMarkdown, extractExamples } from './markdown.mjs';
10
+
11
+ /**
12
+ * Whether a node is a stability index the metadata generator recognised.
13
+ *
14
+ * @param {import('mdast').RootContent} node
15
+ */
16
+ const isStabilityIndex = node =>
17
+ node.type === 'blockquote' && node.data?.index !== undefined;
18
+
19
+ /**
20
+ * An entry's body: its content without its heading and, when the entry
21
+ * carries one, its stability index.
22
+ *
23
+ * @param {import('../../metadata/types').MetadataEntry} entry
24
+ * @returns {Array<import('mdast').RootContent>}
25
+ */
26
+ export const entryBody = entry =>
27
+ entry.content.children.filter(
28
+ node =>
29
+ !UNIST.isHeading(node) && !(entry.stability && isStabilityIndex(node))
30
+ );
31
+
32
+ /**
33
+ * Lifts the leading typed items of the body's first typed list out of the
34
+ * body, for the kinds that turn them into data.
35
+ *
36
+ * @param {Array<import('mdast').RootContent>} body
37
+ * @param {string} kind The node's kind
38
+ * @returns {{ body: Array<import('mdast').RootContent>, items: Array<import('mdast').ListItem> }}
39
+ */
40
+ export const takeTypedItems = (body, kind) => {
41
+ const index = KINDS_WITH_TYPED_LIST.has(kind)
42
+ ? body.findIndex(UNIST.isStronglyTypedList)
43
+ : -1;
44
+
45
+ if (index === -1) {
46
+ return { body, items: [] };
47
+ }
48
+
49
+ const list = body[index];
50
+ const { typed: items, rest } = splitTypedItems(list);
51
+
52
+ if (
53
+ kind === 'class' &&
54
+ !items.some(item => extractListItem(item).prefix === 'Extends')
55
+ ) {
56
+ return { body, items: [] };
57
+ }
58
+
59
+ return {
60
+ body: body.toSpliced(
61
+ index,
62
+ 1,
63
+ ...(rest.length ? [{ ...list, children: rest }] : [])
64
+ ),
65
+ items,
66
+ };
67
+ };
68
+
69
+ /**
70
+ * Builds an entry's metadata and body.
71
+ *
72
+ * @param {import('../../metadata/types').MetadataEntry} entry
73
+ * @param {Array<import('mdast').RootContent>} body The entry's body, minus anything lifted out of it
74
+ * @returns {import('../types').Entry}
75
+ */
76
+ export const buildEntry = (entry, body) => ({
77
+ title: entry.heading.data.text,
78
+ stability: toStability(entry.stability, entry.mdx),
79
+ added: toVersions(entry.added),
80
+ deprecated: toVersions(entry.deprecated),
81
+ removed: toVersions(entry.removed),
82
+ napiVersion: toNumbers(entry.napiVersion),
83
+ changes: toChanges(entry.changes),
84
+ description: blocksToMarkdown(body, entry.mdx),
85
+ summary: getEntryDescription(entry),
86
+ examples: extractExamples(body),
87
+ });
@@ -0,0 +1,61 @@
1
+ 'use strict';
2
+
3
+ import { enforceArray } from '#utils/array.mjs';
4
+ import { removeStabilityPrefix } from '#utils/stability.mjs';
5
+
6
+ import { blocksToMarkdown } from './markdown.mjs';
7
+
8
+ /**
9
+ * Normalises a YAML version field
10
+ *
11
+ * @param {unknown} value
12
+ * @returns {Array<string>}
13
+ */
14
+ export const toVersions = value =>
15
+ value == null ? [] : enforceArray(value).map(String);
16
+
17
+ /**
18
+ * Normalises a YAML number field
19
+ *
20
+ * @param {unknown} value
21
+ * @returns {Array<number>}
22
+ */
23
+ export const toNumbers = value =>
24
+ value == null ? [] : enforceArray(value).map(Number);
25
+
26
+ /**
27
+ * Normalises a YAML `changes` list
28
+ *
29
+ * @param {unknown} changes
30
+ * @returns {Array<import('../types').Change>}
31
+ */
32
+ export const toChanges = changes =>
33
+ enforceArray(changes ?? []).map(change => ({
34
+ versions: toVersions(change.version),
35
+ prUrl: change['pr-url'] == null ? null : String(change['pr-url']),
36
+ commit: change.commit == null ? null : String(change.commit),
37
+ description: String(change.description ?? '').trim(),
38
+ }));
39
+
40
+ /**
41
+ * Builds an entry's stability index
42
+ *
43
+ * @param {import('../../metadata/types').StabilityNode | undefined} node
44
+ * @param {boolean} [mdx] Whether the node comes from an MDX document
45
+ * @returns {import('../types').Stability | null}
46
+ */
47
+ export const toStability = (node, mdx) => {
48
+ if (!node) {
49
+ return null;
50
+ }
51
+
52
+ const { index, description } = node.data;
53
+ const body = removeStabilityPrefix(node);
54
+
55
+ return {
56
+ index: String(index),
57
+ description: body?.children.length
58
+ ? blocksToMarkdown(body.children, mdx)
59
+ : description,
60
+ };
61
+ };
@@ -0,0 +1,57 @@
1
+ 'use strict';
2
+
3
+ import { u as createTree } from 'unist-builder';
4
+ import { visit } from 'unist-util-visit';
5
+
6
+ import { getRemark, getRemarkMdx } from '#utils/remark.mjs';
7
+
8
+ import { DISPLAY_NAME } from '../constants.mjs';
9
+
10
+ /**
11
+ * Serialises block nodes back to Markdown.
12
+ *
13
+ * @param {Array<import('mdast').RootContent>} nodes
14
+ * @param {boolean} [mdx] Whether the nodes come from an MDX document
15
+ * @returns {string}
16
+ */
17
+ export const blocksToMarkdown = (nodes, mdx = false) => {
18
+ if (nodes.length === 0) {
19
+ return '';
20
+ }
21
+
22
+ const processor = mdx ? getRemarkMdx() : getRemark();
23
+
24
+ return processor.stringify(createTree('root', nodes)).trim();
25
+ };
26
+
27
+ /**
28
+ * Serialises phrasing (inline) nodes back to Markdown.
29
+ *
30
+ * @param {Array<import('mdast').PhrasingContent>} nodes
31
+ * @param {boolean} [mdx] Whether the nodes come from an MDX document
32
+ * @returns {string}
33
+ */
34
+ export const inlineToMarkdown = (nodes, mdx) =>
35
+ nodes.length === 0
36
+ ? ''
37
+ : blocksToMarkdown([createTree('paragraph', nodes)], mdx);
38
+
39
+ /**
40
+ * Collects the fenced code blocks of a body
41
+ *
42
+ * @param {Array<import('mdast').RootContent>} nodes
43
+ * @returns {Array<import('../types').Example>}
44
+ */
45
+ export const extractExamples = nodes => {
46
+ const examples = [];
47
+
48
+ visit(createTree('root', nodes), 'code', node => {
49
+ examples.push({
50
+ language: node.lang ?? null,
51
+ displayName: DISPLAY_NAME.exec(node.meta ?? '')?.[1] ?? null,
52
+ code: node.value,
53
+ });
54
+ });
55
+
56
+ return examples;
57
+ };
@@ -0,0 +1,153 @@
1
+ 'use strict';
2
+
3
+ import { toString } from 'mdast-util-to-string';
4
+
5
+ import { transformNodeToHeading } from '#generators/metadata/utils/transformers.mjs';
6
+
7
+ import {
8
+ CALLABLE_KINDS,
9
+ EXTENDS_CLAUSE,
10
+ KINDS,
11
+ SECTION_KIND,
12
+ } from '../constants.mjs';
13
+ import { buildEntry, entryBody, takeTypedItems } from './entry.mjs';
14
+ import {
15
+ buildEventParameters,
16
+ buildExtends,
17
+ buildPropertyType,
18
+ buildSignature,
19
+ } from './signature.mjs';
20
+
21
+ /**
22
+ * Classifies an entry: the kind its heading documents and, when a `global`
23
+ * override replaced that classification, the scope it sets instead.
24
+ *
25
+ * @param {import('../../metadata/types').MetadataEntry} entry
26
+ * @returns {{ kind: string, scope?: 'global' }}
27
+ */
28
+ export const classify = entry => {
29
+ const { type } = entry.heading.data;
30
+
31
+ if (type === 'global') {
32
+ const { type: structural } = transformNodeToHeading(entry.heading);
33
+
34
+ return { kind: KINDS[structural] ?? SECTION_KIND, scope: 'global' };
35
+ }
36
+
37
+ return { kind: KINDS[type] ?? SECTION_KIND };
38
+ };
39
+
40
+ /**
41
+ * The bare identifier an entry documents
42
+ *
43
+ * @param {import('../../metadata/types').MetadataEntry} entry
44
+ * @param {string} kind The entry's kind
45
+ * @returns {string}
46
+ */
47
+ export const nodeName = (entry, kind) => {
48
+ if (typeof entry.name === 'string') {
49
+ return entry.name;
50
+ }
51
+
52
+ const { name } = entry.heading.data;
53
+
54
+ switch (kind) {
55
+ case SECTION_KIND:
56
+ return toString(entry.heading.children).trim();
57
+ case 'class':
58
+ case 'constructor':
59
+ // `http.Server`, `buffer.Blob`, `Foo extends Bar`
60
+ return name.split(EXTENDS_CLAUSE)[0].split('.').at(-1);
61
+ default:
62
+ return name;
63
+ }
64
+ };
65
+
66
+ /**
67
+ * The properties a node has on top of its entry, by kind.
68
+ *
69
+ * @param {string} kind
70
+ * @param {import('../../metadata/types').MetadataEntry} entry
71
+ * @param {Array<import('mdast').ListItem>} items The entry's typed list items
72
+ * @returns {{ properties: object, description?: string }}
73
+ */
74
+ const kindProperties = (kind, entry, items) => {
75
+ if (CALLABLE_KINDS.has(kind)) {
76
+ return {
77
+ properties: {
78
+ signature: buildSignature(entry.heading, items, entry.mdx),
79
+ },
80
+ };
81
+ }
82
+
83
+ switch (kind) {
84
+ case 'class':
85
+ return { properties: { extends: buildExtends(entry.heading, items) } };
86
+ case 'property': {
87
+ const {
88
+ type,
89
+ default: value,
90
+ description,
91
+ } = buildPropertyType(items, entry.mdx);
92
+
93
+ return { properties: { type, default: value }, description };
94
+ }
95
+ case 'event':
96
+ return {
97
+ properties: { parameters: buildEventParameters(items, entry.mdx) },
98
+ };
99
+ default:
100
+ return { properties: {} };
101
+ }
102
+ };
103
+
104
+ /**
105
+ * Builds a node and its subtree from a hierarchized entry.
106
+ *
107
+ * @param {import('#utils/hierarchy.mjs').HierarchizedEntry} node
108
+ * @param {'module' | 'global'} scope The document's scope
109
+ * @returns {import('../types').Node}
110
+ */
111
+ export const buildNode = ({ entry, children }, scope) => {
112
+ const { kind, scope: entryScope = scope } = classify(entry);
113
+ const { body, items } = takeTypedItems(entryBody(entry), kind);
114
+ const { properties, description: typeDescription } = kindProperties(
115
+ kind,
116
+ entry,
117
+ items
118
+ );
119
+
120
+ const {
121
+ title,
122
+ stability,
123
+ added,
124
+ deprecated,
125
+ removed,
126
+ napiVersion,
127
+ changes,
128
+ description,
129
+ summary,
130
+ examples,
131
+ } = buildEntry(entry, body);
132
+
133
+ return {
134
+ kind,
135
+ id: entry.heading.data.slug,
136
+ name: nodeName(entry, kind),
137
+ title,
138
+ scope: entryScope,
139
+ overloadOf: entry.heading.data.overloadOf ?? null,
140
+ stability,
141
+ added,
142
+ deprecated,
143
+ removed,
144
+ napiVersion,
145
+ changes,
146
+ ...properties,
147
+ // A property's type item may describe it; that comes first
148
+ description: [typeDescription, description].filter(Boolean).join('\n\n'),
149
+ summary,
150
+ examples,
151
+ children: children.map(child => buildNode(child, scope)),
152
+ };
153
+ };
@@ -0,0 +1,15 @@
1
+ 'use strict';
2
+
3
+ import { populate } from '#utils/configuration/templates.mjs';
4
+
5
+ import { SCHEMA_VERSION } from '../constants.mjs';
6
+
7
+ /**
8
+ * The `$schema` URL a generator's output carries: its `schemaURL` option with
9
+ * the schema version filled in.
10
+ *
11
+ * @param {{ schemaURL: string }} config The generator's configuration
12
+ * @returns {string}
13
+ */
14
+ export const resolveSchemaURL = config =>
15
+ populate(config.schemaURL, { ...config, schemaVersion: SCHEMA_VERSION });
@@ -0,0 +1,185 @@
1
+ 'use strict';
2
+
3
+ import { u as createTree } from 'unist-builder';
4
+
5
+ import {
6
+ extractListItem,
7
+ removeDefault,
8
+ } from '#utils/signature/extractListItem.mjs';
9
+ import parseSignature from '#utils/signature/parseSignature.mjs';
10
+
11
+ import {
12
+ CODE_SPAN,
13
+ DECLARED_DEFAULT,
14
+ EXTENDS_CLAUSE,
15
+ REST_MARKER,
16
+ } from '../constants.mjs';
17
+ import { blocksToMarkdown } from './markdown.mjs';
18
+ import { plainType, toType } from './types.mjs';
19
+
20
+ // What a parameter declared in a heading but absent from the typed list has
21
+ const EMPTY_ITEM = {
22
+ name: undefined,
23
+ prefix: undefined,
24
+ annotation: undefined,
25
+ text: [],
26
+ blocks: [],
27
+ default: undefined,
28
+ items: [],
29
+ };
30
+
31
+ /**
32
+ * The Markdown of a list item's description
33
+ *
34
+ * @param {import('#utils/signature/extractListItem.mjs').ExtractedListItem} item
35
+ * @param {boolean} [mdx] Whether the item comes from an MDX document
36
+ * @returns {string}
37
+ */
38
+ export const itemDescription = ({ text, blocks }, mdx) => {
39
+ const description = removeDefault(text);
40
+
41
+ return blocksToMarkdown(
42
+ [
43
+ ...(description.length ? [createTree('paragraph', description)] : []),
44
+ ...blocks,
45
+ ],
46
+ mdx
47
+ );
48
+ };
49
+
50
+ /**
51
+ * A default value as authored, without the code span around it.
52
+ *
53
+ * @param {string | undefined} value
54
+ * @returns {string | null}
55
+ */
56
+ const toDefault = value =>
57
+ value === undefined
58
+ ? null
59
+ : value.replace(DECLARED_DEFAULT, '').replace(CODE_SPAN, '$1');
60
+
61
+ /**
62
+ * Builds a parameter from its list item and, for a callable, its declaration
63
+ * in the heading.
64
+ *
65
+ * @param {import('#utils/signature/extractListItem.mjs').ExtractedListItem} item
66
+ * @param {import('#utils/signature/types').Parameter} [declared] The heading's declaration
67
+ * @param {boolean} [mdx] Whether the item comes from an MDX document
68
+ * @returns {import('../types').Parameter}
69
+ */
70
+ export const toParameter = (item, declared = {}, mdx) => {
71
+ const name = declared.name ?? item.name ?? '';
72
+ const value = item.default ?? declared.default;
73
+
74
+ return {
75
+ name: name.replace(REST_MARKER, ''),
76
+ type: toType(item.annotation),
77
+ description: itemDescription(item, mdx),
78
+ default: toDefault(value),
79
+ optional: Boolean(declared.optional) || value !== undefined,
80
+ rest: REST_MARKER.test(name),
81
+ properties: item.items.map(nested =>
82
+ toParameter(extractListItem(nested), undefined, mdx)
83
+ ),
84
+ };
85
+ };
86
+
87
+ /**
88
+ * Wraps an extracted item the way `parseSignature` matches parameters
89
+ * (by name, with the nested items as `options`)
90
+ *
91
+ * @param {import('#utils/signature/extractListItem.mjs').ExtractedListItem} item
92
+ */
93
+ const toMarkdownParameter = item => ({
94
+ name: item.prefix
95
+ ? item.prefix === 'Returns'
96
+ ? 'return'
97
+ : item.prefix.toLowerCase()
98
+ : item.name,
99
+ options: item.items.map(nested =>
100
+ toMarkdownParameter(extractListItem(nested))
101
+ ),
102
+ item,
103
+ });
104
+
105
+ /**
106
+ * Builds a return value from its `Returns:` item.
107
+ *
108
+ * @param {import('#utils/signature/extractListItem.mjs').ExtractedListItem} item
109
+ * @param {boolean} [mdx] Whether the item comes from an MDX document
110
+ * @returns {import('../types').Return}
111
+ */
112
+ const toReturn = (item, mdx) => ({
113
+ type: toType(item.annotation),
114
+ description: itemDescription(item, mdx),
115
+ });
116
+
117
+ /**
118
+ * Builds the parameters a callable heading declares
119
+ *
120
+ * @param {import('../../metadata/types').HeadingNode} heading
121
+ * @param {Array<import('mdast').ListItem>} items The typed list's items
122
+ * @param {boolean} [mdx] Whether the items come from an MDX document
123
+ * @returns {import('../types').Signature}
124
+ */
125
+ export const buildSignature = (heading, items, mdx) => {
126
+ const signature = parseSignature(
127
+ heading.data.text,
128
+ items.map(item => toMarkdownParameter(extractListItem(item)))
129
+ );
130
+
131
+ return {
132
+ parameters: signature.params.map(declared =>
133
+ toParameter(declared.item ?? EMPTY_ITEM, declared, mdx)
134
+ ),
135
+ returns: signature.return ? toReturn(signature.return.item, mdx) : null,
136
+ };
137
+ };
138
+
139
+ /**
140
+ * @param {import('../../metadata/types').HeadingNode} heading
141
+ * @param {Array<import('mdast').ListItem>} items The typed list's items
142
+ * @returns {import('../types').Type | null}
143
+ */
144
+ export const buildExtends = (heading, items) => {
145
+ const item = items
146
+ .map(extractListItem)
147
+ .find(({ prefix }) => prefix === 'Extends');
148
+
149
+ if (item) {
150
+ return toType(item.annotation);
151
+ }
152
+
153
+ const [, base] = heading.data.name.split(EXTENDS_CLAUSE);
154
+
155
+ return base ? plainType(base) : null;
156
+ };
157
+
158
+ /**
159
+ * A property's basic metadata
160
+ *
161
+ * @param {Array<import('mdast').ListItem>} items The typed list's items
162
+ * @param {boolean} [mdx] Whether the items come from an MDX document
163
+ * @returns {{ type: import('../types').Type | null, default: string | null, description: string }}
164
+ */
165
+ export const buildPropertyType = (items, mdx) => {
166
+ const [item] = items.map(extractListItem);
167
+
168
+ return item
169
+ ? {
170
+ type: toType(item.annotation),
171
+ default: toDefault(item.default),
172
+ description: itemDescription(item, mdx),
173
+ }
174
+ : { type: null, default: null, description: '' };
175
+ };
176
+
177
+ /**
178
+ * The arguments an event's listeners receive.
179
+ *
180
+ * @param {Array<import('mdast').ListItem>} items The typed list's items
181
+ * @param {boolean} [mdx] Whether the items come from an MDX document
182
+ * @returns {Array<import('../types').Parameter>}
183
+ */
184
+ export const buildEventParameters = (items, mdx) =>
185
+ items.map(item => toParameter(extractListItem(item), undefined, mdx));
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Builds a type from a resolved `typeAnnotation` node.
5
+ *
6
+ * @param {import('mdast').Node | undefined} node
7
+ * @returns {import('../types').Type | null}
8
+ */
9
+ export const toType = node =>
10
+ node
11
+ ? {
12
+ text: node.value,
13
+ links: (node.data?.links ?? []).map(({ text, href, start, end }) => ({
14
+ name: text,
15
+ href,
16
+ start,
17
+ end,
18
+ })),
19
+ }
20
+ : null;
21
+
22
+ /**
23
+ * Builds a type from bare text, with nothing resolved.
24
+ *
25
+ * @param {string} text
26
+ * @returns {import('../types').Type}
27
+ */
28
+ export const plainType = text => ({ text, links: [] });
@@ -0,0 +1,31 @@
1
+ # `json-all` Generator
2
+
3
+ The `json-all` generator bundles the documents of the [`json`](./json.md)
4
+ generator into a single `all.json` file.
5
+
6
+ ```sh
7
+ npx @doc-kit/cli generate -t json-all -i "doc/api/*.md" -o out --index doc/api/index.md
8
+ ```
9
+
10
+ ```json
11
+ {
12
+ "$schema": "https://doc-kit.nodejs.org/schemas/api-doc-all/1.0.0.json",
13
+ "documents": []
14
+ }
15
+ ```
16
+
17
+ `documents` holds every document in the order of the configured `index`,
18
+ then the rest by `id`. The bundle's schema, shipped as
19
+ `@doc-kit/core/generators/json-all/schema.json`, refers to the `json`
20
+ generator's schema for the documents.
21
+
22
+ ## Configuring
23
+
24
+ - `output` {string} The directory where `all.json` will be written.
25
+ - `minify` {boolean} Whether to minify the output. Inherited from `global`.
26
+ **Default:** `true`.
27
+ - `index` {Array} The `{ api }` objects defining the document order. Inherited
28
+ from `global`.
29
+ - `schemaURL` {string} Where the bundle's schema is published.
30
+ `{schemaVersion}` is filled in. **Default:**
31
+ `'https://doc-kit.nodejs.org/schemas/api-doc-all/{schemaVersion}.json'`.
@@ -0,0 +1,5 @@
1
+ 'use strict';
2
+
3
+ // Where a version of the bundle's schema is published.
4
+ export const SCHEMA_URL =
5
+ 'https://doc-kit.nodejs.org/schemas/api-doc-all/{schemaVersion}.json';
@@ -0,0 +1,35 @@
1
+ 'use strict';
2
+
3
+ import { join } from 'node:path';
4
+
5
+ import getConfig from '#utils/configuration/index.mjs';
6
+ import { writeJSON } from '#utils/file.mjs';
7
+
8
+ import { resolveSchemaURL } from '../json/utils/schema.mjs';
9
+
10
+ /**
11
+ * Bundles the `json` generator's documents into one `all.json` file.
12
+ *
13
+ * @type {import('./types').Generator['generate']}
14
+ */
15
+ export async function generate(input) {
16
+ const config = getConfig('json-all');
17
+
18
+ // Documents follow the configured index; the rest go after it, by id
19
+ const order = new Map(config.index?.map(({ api }, i) => [api, i]));
20
+
21
+ const documents = input.toSorted(
22
+ (a, b) =>
23
+ (order.get(a.id) ?? Infinity) - (order.get(b.id) ?? Infinity) ||
24
+ a.id.localeCompare(b.id)
25
+ );
26
+
27
+ /** @type {import('./types').Bundle} */
28
+ const bundle = { $schema: resolveSchemaURL(config), documents };
29
+
30
+ if (config.output) {
31
+ await writeJSON(join(config.output, 'all.json'), bundle, config.minify);
32
+ }
33
+
34
+ return bundle;
35
+ }
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+
3
+ import { SCHEMA_URL } from './constants.mjs';
4
+ import { generate } from './generate.mjs';
5
+
6
+ /**
7
+ * This generator bundles the documents of the `json` generator into a single
8
+ * `all.json` file
9
+ *
10
+ * @type {import('./types').Generator}
11
+ */
12
+ export default {
13
+ name: 'json-all',
14
+
15
+ description:
16
+ 'Bundles the documents of the `json` generator into a single `all.json` file',
17
+
18
+ dependsOn: '@doc-kit/core/json',
19
+
20
+ defaultConfiguration: {
21
+ schemaURL: SCHEMA_URL,
22
+ },
23
+
24
+ generate,
25
+ };