-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathbuildSection.mjs
175 lines (156 loc) · 6.15 KB
/
buildSection.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
import { buildHierarchy } from '../../../utils/buildHierarchy.mjs';
import { getRemarkRehype } from '../../../utils/remark.mjs';
import { transformNodesToString } from '../../../utils/unist.mjs';
import { parseList } from './parseList.mjs';
import { SECTION_TYPE_PLURALS, UNPROMOTED_KEYS } from '../constants.mjs';
/**
* Converts a value to an array.
* @template T
* @param {T | T[]} val - The value to convert.
* @returns {T[]} The value as an array.
*/
const enforceArray = val => (Array.isArray(val) ? val : [val]);
/**
*
*/
export const createSectionBuilder = () => {
const html = getRemarkRehype();
/**
* Creates metadata from a hierarchized entry.
* @param {import('../types.d.ts').HierarchizedEntry} entry - The entry to create metadata from.
* @returns {import('../types.d.ts').Meta} The created metadata.
*/
const createMeta = ({
added_in = [],
n_api_version = [],
deprecated_in = [],
removed_in = [],
changes,
}) => ({
changes,
added: enforceArray(added_in),
napiVersion: enforceArray(n_api_version),
deprecated: enforceArray(deprecated_in),
removed: enforceArray(removed_in),
});
/**
* Creates a section from an entry and its heading.
* @param {import('../types.d.ts').HierarchizedEntry} entry - The AST entry.
* @param {HeadingMetadataParent} head - The head node of the entry.
* @returns {import('../types.d.ts').Section} The created section.
*/
const createSection = (entry, head) => ({
textRaw: transformNodesToString(head.children),
name: head.data.name,
type: head.data.type,
meta: createMeta(entry),
introduced_in: entry.introduced_in,
});
/**
* Parses stability metadata and adds it to the section.
* @param {import('../types.d.ts').Section} section - The section to update.
* @param {Array} nodes - The remaining AST nodes.
* @param {import('../types.d.ts').HierarchizedEntry} entry - The entry providing stability information.
*/
const parseStability = (section, nodes, { stability }) => {
const stabilityInfo = stability.toJSON()?.[0];
if (stabilityInfo) {
section.stability = stabilityInfo.index;
section.stabilityText = stabilityInfo.description;
nodes.shift(); // Remove stability node from processing
}
};
/**
* Adds a description to the section.
* @param {import('../types.d.ts').Section} section - The section to update.
* @param {Array} nodes - The remaining AST nodes.
*/
const addDescription = (section, nodes) => {
if (!nodes.length) {
return;
}
const rendered = html.stringify(
html.runSync({ type: 'root', children: nodes })
);
section.shortDesc = section.desc || undefined;
section.desc = rendered || undefined;
};
/**
* Adds additional metadata to the section based on its type.
* @param {import('../types.d.ts').Section} section - The section to update.
* @param {import('../types.d.ts').Section} parent - The parent section.
* @param {import('../../types.d.ts').NodeWithData} heading - The heading node of the section.
*/
const addAdditionalMetadata = (section, parent, heading) => {
if (!section.type) {
section.name = section.textRaw.toLowerCase().trim().replace(/\s+/g, '_');
section.displayName = heading.data.name;
section.type = parent.type === 'misc' ? 'misc' : 'module';
}
};
/**
* Adds the section to its parent section.
* @param {import('../types.d.ts').Section} section - The section to add.
* @param {import('../types.d.ts').Section} parent - The parent section.
*/
const addToParent = (section, parent) => {
const key = SECTION_TYPE_PLURALS[section.type] || 'miscs';
parent[key] ??= [];
parent[key].push(section);
};
/**
* Promotes children properties to the parent level if the section type is 'misc'.
* @param {import('../types.d.ts').Section} section - The section to promote.
* @param {import('../types.d.ts').Section} parent - The parent section.
*/
const promoteMiscChildren = (section, parent) => {
// Only promote if the current section is of type 'misc' and the parent is not 'misc'
if (section.type === 'misc' && parent.type !== 'misc') {
Object.entries(section).forEach(([key, value]) => {
// Only promote certain keys
if (!UNPROMOTED_KEYS.includes(key)) {
// Merge the section's properties into the parent section
parent[key] = parent[key]
? // If the parent already has this key, concatenate the values
[].concat(parent[key], value)
: // Otherwise, directly assign the section's value to the parent
[];
}
});
}
};
/**
* Processes children of a given entry and updates the section.
* @param {import('../types.d.ts').HierarchizedEntry} entry - The current entry.
* @param {import('../types.d.ts').Section} section - The current section.
*/
const handleChildren = ({ hierarchyChildren }, section) =>
hierarchyChildren?.forEach(child => handleEntry(child, section));
/**
* Handles an entry and updates the parent section.
* @param {import('../types.d.ts').HierarchizedEntry} entry - The entry to process.
* @param {import('../types.d.ts').Section} parent - The parent section.
*/
const handleEntry = (entry, parent) => {
const [headingNode, ...nodes] = structuredClone(entry.content.children);
const section = createSection(entry, headingNode);
parseStability(section, nodes, entry);
parseList(section, nodes);
addDescription(section, nodes);
handleChildren(entry, section);
addAdditionalMetadata(section, parent, headingNode);
addToParent(section, parent);
promoteMiscChildren(section, parent);
};
/**
* Builds the module section from head metadata and entries.
* @param {ApiDocMetadataEntry} head - The head metadata entry.
* @param {Array<ApiDocMetadataEntry>} entries - The list of metadata entries.
* @returns {import('../types.d.ts').ModuleSection} The constructed module section.
*/
return (head, entries) => {
const rootModule = { type: 'module', source: head.api_doc_source };
buildHierarchy(entries).forEach(entry => handleEntry(entry, rootModule));
return rootModule;
};
};