Skip to content

Commit 0fa285d

Browse files
committed
parseSignature impl
Signed-off-by: flakey5 <[email protected]>
1 parent b95dafb commit 0fa285d

File tree

5 files changed

+158
-29
lines changed

5 files changed

+158
-29
lines changed

src/generators/legacy-json-all/types.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ export interface Output {
44
miscs: MiscSection[];
55
modules: Section[];
66
classes: SignatureSection[];
7-
globals: object[]; // TODO
7+
globals: (ModuleSection | { type: 'global' })[];
88
methods: SignatureSection[];
99
}

src/generators/legacy-json/constants.mjs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,9 @@ export const LEADING_HYPHEN = /^-\s*/;
2222
* Denotes a default value
2323
*/
2424
export const DEFAULT_EXPRESSION = /\s*\*\*Default:\*\*\s*([^]+)$/i;
25+
26+
/**
27+
* Grabs the parameters from a method's signature
28+
* @example 'new buffer.Blob([sources[, options]])'.match(PARAM_EXPRESSION) = ['([sources[, options]])', '[sources[, options]]']
29+
*/
30+
export const PARAM_EXPRESSION = /\((.+)\);?$/;

src/generators/legacy-json/types.d.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,8 @@ export interface ModuleSection extends SectionBase {
3838
classes?: SignatureSection[];
3939
methods?: MethodSignature[];
4040
properties?: PropertySection[];
41-
globals?: object[]; // TODO
42-
examples?: object[]; // TODO
41+
globals?: ModuleSection | { type: 'global' };
4342
signatures?: SignatureSection[];
44-
// TODO the rest
4543
}
4644

4745
export interface SignatureSection extends SectionBase {

src/generators/legacy-json/utils/buildHierarchy.mjs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* So we need the files to be in a hierarchy based off of depth, but they're
2+
* We need the files to be in a hierarchy based off of depth, but they're
33
* given to us flattened. So, let's fix that.
44
*
55
* Assuming that {@link entries} is in the same order as the elements are in
@@ -24,8 +24,8 @@ export function buildHierarchy(entries) {
2424
const entry = entries[i];
2525
const currentDepth = entry.heading.depth;
2626

27-
// We're a top-level entry
2827
if (currentDepth <= 1) {
28+
// We're a top-level entry
2929
roots.push(entry);
3030
continue;
3131
}
@@ -38,12 +38,14 @@ export function buildHierarchy(entries) {
3838
if (previousEntry.hierarchyChildren === undefined) {
3939
previousEntry.hierarchyChildren = [];
4040
}
41+
4142
previousEntry.hierarchyChildren.push(entry);
4243
} else {
4344
// Loop to find the entry we're a child of
4445
for (let j = i - 2; j >= 0; j--) {
4546
const jEntry = entries[j];
4647
const jDepth = jEntry.heading.depth;
48+
4749
if (currentDepth > jDepth) {
4850
// Found it
4951
jEntry.hierarchyChildren.push(entry);

src/generators/legacy-json/utils/buildSection.mjs

Lines changed: 146 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
DEFAULT_EXPRESSION,
33
LEADING_HYPHEN,
44
NAME_EXPRESSION,
5+
PARAM_EXPRESSION,
56
RETURN_EXPRESSION,
67
TYPE_EXPRESSION,
78
} from '../constants.mjs';
@@ -33,10 +34,14 @@ function createMeta(entry) {
3334
if (added_in || n_api_version || deprecated_in || removed_in) {
3435
return {
3536
changes,
36-
added: makeArrayIfNotAlready(added_in),
37-
napiVersion: makeArrayIfNotAlready(n_api_version),
38-
deprecated: makeArrayIfNotAlready(deprecated_in),
39-
removed: makeArrayIfNotAlready(removed_in),
37+
added: added_in ? makeArrayIfNotAlready(added_in) : undefined,
38+
napiVersion: n_api_version
39+
? makeArrayIfNotAlready(n_api_version)
40+
: undefined,
41+
deprecated: deprecated_in
42+
? makeArrayIfNotAlready(deprecated_in)
43+
: undefined,
44+
removed: removed_in ? makeArrayIfNotAlready(removed_in) : undefined,
4045
};
4146
}
4247

@@ -51,27 +56,30 @@ function createMeta(entry) {
5156
function createSection(entry, head) {
5257
const text = textJoin(head.children);
5358

54-
// TODO check if type or name can be undefined
5559
return {
5660
textRaw: text,
5761
type: head.data.type,
58-
name: head.data.name,
62+
name: text.toLowerCase().replaceAll(' ', '_'),
63+
displayName: head.data.name,
5964
meta: createMeta(entry),
65+
introduced_in: entry.introduced_in,
6066
};
6167
}
6268

6369
/**
64-
*
65-
* @param {Array<import('../types.d.ts').List} values TODO type
70+
* @param {string} textRaw Something like `new buffer.Blob([sources[, options]])`
71+
* @param {Array<import('../types.d.ts').List} values
6672
* @returns {import('../types.d.ts').MethodSignature}
6773
*/
68-
function parseSignature(values) {
74+
function parseSignature(textRaw, values) {
6975
/**
7076
* @type {import('../types.d.ts').MethodSignature}
7177
*/
72-
const signature = {};
78+
const signature = {
79+
params: [],
80+
};
7381

74-
signature.params = values.filter(value => {
82+
const rawParameters = values.filter(value => {
7583
if (value.name === 'return') {
7684
signature.return = value;
7785
return false;
@@ -80,7 +88,127 @@ function parseSignature(values) {
8088
return true;
8189
});
8290

83-
// TODO the unfortunate logic
91+
/**
92+
* Extract a list of the signatures from the method's declaration
93+
* @example `[sources[, options]]`
94+
*/
95+
let [, declaredParameters] = textRaw.match(PARAM_EXPRESSION) || [];
96+
97+
if (!declaredParameters) {
98+
return;
99+
}
100+
101+
/**
102+
* @type {string[]}
103+
* @example ['sources[,', 'options]]']
104+
*/
105+
declaredParameters = declaredParameters.split(',');
106+
107+
let optionalDepth = 0;
108+
const optionalCharDict = { '[': 1, ' ': 0, ']': -1 };
109+
110+
declaredParameters.forEach((declaredParameter, i) => {
111+
/**
112+
* @example 'length]]'
113+
* @example 'arrayBuffer['
114+
* @example '[sources['
115+
* @example 'end'
116+
*/
117+
declaredParameter = declaredParameter.trim();
118+
119+
if (!declaredParameter) {
120+
throw new Error(`Empty parameter slot: ${textRaw}`);
121+
}
122+
123+
// We need to find out if this parameter is optional or not. We can tell this
124+
// if we're wrapped in brackets, so let's look for them.
125+
126+
let pos;
127+
for (pos = 0; pos < declaredParameter.length; pos++) {
128+
const levelChange = optionalCharDict[declaredParameter[pos]];
129+
130+
if (levelChange === undefined) {
131+
break;
132+
}
133+
134+
optionalDepth += levelChange;
135+
}
136+
137+
// Cut off any trailing brackets
138+
declaredParameter = declaredParameter.substring(pos);
139+
140+
const isParameterOptional = optionalDepth > 0;
141+
142+
for (pos = declaredParameter.length - 1; pos >= 0; pos--) {
143+
const levelChange = optionalCharDict[declaredParameter[pos]];
144+
145+
if (levelChange === undefined) {
146+
break;
147+
}
148+
149+
optionalDepth += levelChange;
150+
}
151+
152+
// Cut off any leading brackets
153+
declaredParameter = declaredParameter.substring(0, pos + 1);
154+
155+
// Default value of this parameter in the method's declaration
156+
let defaultValue;
157+
158+
const equalSignPos = declaredParameter.indexOf('=');
159+
if (equalSignPos !== -1) {
160+
// We have a default value, save it and then cut it off of the signature
161+
defaultValue = declaredParameter.substring(equalSignPos, 1).trim();
162+
declaredParameter = declaredParameter.substring(0, equalSignPos);
163+
console.log('eq', declaredParameter);
164+
}
165+
166+
let parameter = rawParameters[i];
167+
if (!parameter || declaredParameter !== parameter.name) {
168+
// If we're here then the method likely has shared signatures
169+
// Something like, `new Console(stdout[, stderr][, ignoreErrors])` and
170+
// `new Console(options)`
171+
parameter = undefined;
172+
173+
// Try finding a parameter this is being shared with
174+
for (const otherParam of rawParameters) {
175+
if (declaredParameter === otherParam.name) {
176+
// Found a matching one
177+
// TODO break?
178+
parameter = otherParam;
179+
} else if (otherParam.options) {
180+
// Found a matching one in the parameter's options
181+
for (const option of otherParam.options) {
182+
if (declaredParameter === option.name) {
183+
parameter = Object.assign({}, option);
184+
break;
185+
}
186+
}
187+
}
188+
}
189+
190+
if (!parameter) {
191+
// Couldn't find the shared one, I have no idea what this case is but we'll see
192+
if (declaredParameter.startsWith('...')) {
193+
parameter = { name: declaredParameter };
194+
} else {
195+
throw new Error(
196+
`Invalid param "${declaredParameter}"\n` + ` > ${textRaw}`
197+
);
198+
}
199+
}
200+
}
201+
202+
if (isParameterOptional) {
203+
parameter.optional = true;
204+
}
205+
206+
if (defaultValue) {
207+
parameter.default = defaultValue;
208+
}
209+
210+
signature.params.push(parameter);
211+
});
84212

85213
return signature;
86214
}
@@ -93,9 +221,6 @@ function textJoin(nodes) {
93221
return nodes
94222
.map(node => {
95223
switch (node.type) {
96-
case 'linkReference':
97-
console.error(`todo link reference`);
98-
return `TODO`;
99224
case `strong`:
100225
return `**${textJoin(node.children)}**`;
101226
case `emphasis`:
@@ -122,10 +247,9 @@ function parseListItem(child) {
122247
*/
123248
const current = {};
124249

125-
// TODO this
126-
// current.textRaw = child.children
127-
// .filter(node => node.type !== 'list')
128-
// .map(node => node.)
250+
current.textRaw = textJoin(
251+
child.children.filter(node => node.type !== 'list')
252+
);
129253

130254
if (!current.textRaw) {
131255
throw new Error(`empty list item: ${JSON.stringify(child)}`);
@@ -159,7 +283,7 @@ function parseListItem(child) {
159283
const [, defaultValue] = text.match(DEFAULT_EXPRESSION) || [];
160284
if (defaultValue) {
161285
current.default = defaultValue.replace(/\.$/, '');
162-
text = text.parseListItem(DEFAULT_EXPRESSION, '');
286+
text = text.replace(DEFAULT_EXPRESSION, '');
163287
}
164288

165289
// Add remaining text to the desc
@@ -257,8 +381,7 @@ function handleEntry(entry, parentSection) {
257381
case 'ctor':
258382
case 'classMethod':
259383
case 'method': {
260-
const signature = parseSignature(values);
261-
section.signatures = [signature];
384+
section.signatures = [parseSignature(section.textRaw, values)];
262385

263386
break;
264387
}
@@ -330,7 +453,7 @@ function handleEntry(entry, parentSection) {
330453
* @param {import('../types.d.ts').Section} parentSection
331454
*/
332455
const makeChildrenTopLevelIfMisc = (section, parentSection) => {
333-
if (parentSection.type === 'misc') {
456+
if (parentSection.type !== 'misc') {
334457
return;
335458
}
336459

0 commit comments

Comments
 (0)