bio-dts 0.7.0 → 0.8.0
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.
- package/lib/parsers/jsdoc.js +2 -2
- package/lib/post-transform.js +312 -34
- package/package.json +1 -1
package/lib/parsers/jsdoc.js
CHANGED
|
@@ -86,7 +86,7 @@ export function parse(str) {
|
|
|
86
86
|
i = skipWhitespace(str, j);
|
|
87
87
|
|
|
88
88
|
// parse type
|
|
89
|
-
if ([ 'param', 'return', 'returns', 'typedef' ].includes(tag.name)) {
|
|
89
|
+
if ([ 'param', 'return', 'returns', 'typedef', 'template' ].includes(tag.name)) {
|
|
90
90
|
|
|
91
91
|
let match = findClose(str, i, '{', '}');
|
|
92
92
|
|
|
@@ -98,7 +98,7 @@ export function parse(str) {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
// parse param
|
|
101
|
-
if ([ 'param', 'typedef' ].includes(tag.name)) {
|
|
101
|
+
if ([ 'param', 'typedef', 'template' ].includes(tag.name)) {
|
|
102
102
|
|
|
103
103
|
let match = tag.name === 'param' && findClose(str, i, '[', ']');
|
|
104
104
|
|
package/lib/post-transform.js
CHANGED
|
@@ -82,6 +82,56 @@ export default function transform(src) {
|
|
|
82
82
|
}));
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
function getDocumentedParams(node, params = [], comment = null) {
|
|
86
|
+
|
|
87
|
+
if (!comment) {
|
|
88
|
+
return params;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const doc = comment.value.replace(/\n\s+/g, '\n ');
|
|
92
|
+
|
|
93
|
+
// parse known parameters
|
|
94
|
+
// ignore hierarchical (sub-type) params
|
|
95
|
+
const knownParams =
|
|
96
|
+
parseJSDoc(doc)
|
|
97
|
+
.filter(tag => tag.name === 'param' && !tag.param.name.includes('.'))
|
|
98
|
+
.map(tag => tag.param.name);
|
|
99
|
+
|
|
100
|
+
if (!knownParams.length) {
|
|
101
|
+
return params;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let i = 0;
|
|
105
|
+
let j = 0;
|
|
106
|
+
|
|
107
|
+
const filteredParams = [];
|
|
108
|
+
|
|
109
|
+
while (j < knownParams.length) {
|
|
110
|
+
|
|
111
|
+
let expectedName = knownParams[j];
|
|
112
|
+
let param = params[i++];
|
|
113
|
+
|
|
114
|
+
if (!param) {
|
|
115
|
+
throw error(node, `documented parameter <${ expectedName }> not found`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (param.name === 'this') {
|
|
119
|
+
filteredParams.push(param);
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const actualName = param.argument?.name || param.name;
|
|
124
|
+
|
|
125
|
+
if (actualName !== expectedName) {
|
|
126
|
+
throw error(node, `documented parameter <${ expectedName }> differs from actual parameter <${ actualName }>`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
filteredParams.push(param);
|
|
130
|
+
j++;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return filteredParams;
|
|
134
|
+
}
|
|
85
135
|
|
|
86
136
|
/**
|
|
87
137
|
* Ensure optional args methods are properly escaped
|
|
@@ -96,40 +146,34 @@ export default function transform(src) {
|
|
|
96
146
|
value: node
|
|
97
147
|
} = nodePath;
|
|
98
148
|
|
|
99
|
-
const
|
|
100
|
-
node.type === 'TSDeclareMethod' ||
|
|
101
|
-
node.type === 'TSDeclareFunction'
|
|
102
|
-
);
|
|
149
|
+
const functionKind = getFunctionKind(node);
|
|
103
150
|
|
|
104
|
-
|
|
105
|
-
node?.typeAnnotation?.typeAnnotation?.type === 'TSFunctionType'
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
if (!fnDeclaration && !fnDefinition) {
|
|
151
|
+
if (!functionKind) {
|
|
109
152
|
return;
|
|
110
153
|
}
|
|
111
154
|
|
|
112
|
-
const params =
|
|
113
|
-
? node.
|
|
114
|
-
: node
|
|
155
|
+
const params = functionKind === 'TSFunctionType'
|
|
156
|
+
? node.typeAnnotation?.typeAnnotation?.parameters
|
|
157
|
+
: node.params;
|
|
158
|
+
|
|
159
|
+
const typeParameters = functionKind === 'TSFunctionType'
|
|
160
|
+
? node.typeAnnotation?.typeAnnotation?.typeParameters
|
|
161
|
+
: node.typeParameters;
|
|
115
162
|
|
|
116
|
-
const
|
|
117
|
-
? node.
|
|
118
|
-
: node
|
|
163
|
+
const returnType = functionKind === 'TSFunctionType'
|
|
164
|
+
? node.typeAnnotation?.typeAnnotation?.typeAnnotation
|
|
165
|
+
: node.returnType;
|
|
119
166
|
|
|
120
|
-
const
|
|
121
|
-
? node.returnType
|
|
122
|
-
: node?.typeAnnotation?.typeAnnotation?.typeAnnotation;
|
|
167
|
+
const hostPath = getHostPath(nodePath);
|
|
123
168
|
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
);
|
|
169
|
+
const commentPaths = hostPath.get('comments');
|
|
170
|
+
|
|
171
|
+
// last comment is significant
|
|
172
|
+
const commentPath = commentPaths?.value && commentPaths.get(commentPaths.value.length - 1) || { value: null };
|
|
129
173
|
|
|
130
174
|
const {
|
|
131
175
|
variations
|
|
132
|
-
} = params.slice().reverse().reduce((res, param) => {
|
|
176
|
+
} = getDocumentedParams(hostPath.value, params, commentPath.value).slice().reverse().reduce((res, param) => {
|
|
133
177
|
|
|
134
178
|
let {
|
|
135
179
|
required,
|
|
@@ -183,11 +227,6 @@ export default function transform(src) {
|
|
|
183
227
|
return;
|
|
184
228
|
}
|
|
185
229
|
|
|
186
|
-
const commentPaths = hostPath.get('comments');
|
|
187
|
-
|
|
188
|
-
// last comment is significant
|
|
189
|
-
const commentPath = commentPaths?.value && commentPaths.get(commentPaths.value.length - 1) || { value: null };
|
|
190
|
-
|
|
191
230
|
const replacements = variations.slice().reverse().map(variation => {
|
|
192
231
|
|
|
193
232
|
const builder = {
|
|
@@ -267,6 +306,182 @@ export default function transform(src) {
|
|
|
267
306
|
replace(nodePath, nodePath.get('declaration').value);
|
|
268
307
|
}
|
|
269
308
|
|
|
309
|
+
/**
|
|
310
|
+
* Ensure that only documented method parameters are used.
|
|
311
|
+
*
|
|
312
|
+
* @param {Path} nodePath
|
|
313
|
+
*
|
|
314
|
+
* @return {boolean} true if modified
|
|
315
|
+
*/
|
|
316
|
+
function removeUnknownParams(nodePath) {
|
|
317
|
+
|
|
318
|
+
const {
|
|
319
|
+
value: node
|
|
320
|
+
} = nodePath;
|
|
321
|
+
|
|
322
|
+
const functionKind = getFunctionKind(node);
|
|
323
|
+
|
|
324
|
+
if (!functionKind) {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const params = functionKind === 'TSFunctionType'
|
|
329
|
+
? nodePath.get('typeAnnotation', 'typeAnnotation', 'parameters')
|
|
330
|
+
: nodePath.get('params');
|
|
331
|
+
|
|
332
|
+
const hostPath = getHostPath(nodePath);
|
|
333
|
+
|
|
334
|
+
const commentPaths = hostPath.get('comments');
|
|
335
|
+
|
|
336
|
+
// last comment is significant
|
|
337
|
+
const commentPath = commentPaths?.value && commentPaths.get(commentPaths.value.length - 1) || { value: null };
|
|
338
|
+
|
|
339
|
+
const knownParams = getDocumentedParams(hostPath.value, params.value, commentPath.value);
|
|
340
|
+
|
|
341
|
+
let replaced = false;
|
|
342
|
+
|
|
343
|
+
for (const idx in params.value || []) {
|
|
344
|
+
if (!knownParams[idx]) {
|
|
345
|
+
replace(params.get(idx));
|
|
346
|
+
replaced = true;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return replaced;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Generate method overloads based on `@overlord` annotated JSDoc comments.
|
|
355
|
+
*
|
|
356
|
+
* Our strategy is to parse for separate `@overlord` annotated tags,
|
|
357
|
+
* use the meta-data, and generate a completely new method from it.
|
|
358
|
+
*
|
|
359
|
+
* @param {Path} nodePath
|
|
360
|
+
*
|
|
361
|
+
* @return {boolean} true if modified
|
|
362
|
+
*/
|
|
363
|
+
function generateOverloads(nodePath) {
|
|
364
|
+
|
|
365
|
+
const {
|
|
366
|
+
value: node
|
|
367
|
+
} = nodePath;
|
|
368
|
+
|
|
369
|
+
const functionKind = getFunctionKind(node);
|
|
370
|
+
|
|
371
|
+
if (!functionKind) {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const hostPath = getHostPath(nodePath);
|
|
376
|
+
|
|
377
|
+
const commentPaths = hostPath.get('comments');
|
|
378
|
+
|
|
379
|
+
const comments = (commentPaths?.value || []);
|
|
380
|
+
|
|
381
|
+
// scan for potential overloads
|
|
382
|
+
const overloads = comments.reduce((overloads, comment, idx) => {
|
|
383
|
+
const commentText = comment.value;
|
|
384
|
+
|
|
385
|
+
if (commentText.includes('@overlord') || idx === comments.length - 1) {
|
|
386
|
+
const tags = parseJSDoc(comment.value);
|
|
387
|
+
|
|
388
|
+
const paramTags = tags.filter(t => t.name === 'param');
|
|
389
|
+
const templateTags = tags.filter(t => t.name === 'template');
|
|
390
|
+
const returnTags = tags.filter(t => t.name === 'return' || t.name === 'returns');
|
|
391
|
+
|
|
392
|
+
if (returnTags.length > 1) {
|
|
393
|
+
throw error(hostPath.value, 'must specify zero or one @return(s) type in @overload fn');
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return [
|
|
397
|
+
...overloads,
|
|
398
|
+
{
|
|
399
|
+
comment,
|
|
400
|
+
templateTags,
|
|
401
|
+
paramTags,
|
|
402
|
+
returnTag: returnTags[0]
|
|
403
|
+
}
|
|
404
|
+
];
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return overloads;
|
|
408
|
+
}, []);
|
|
409
|
+
|
|
410
|
+
if (overloads.length < 2) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const replacements = overloads.slice().reverse().map(overload => {
|
|
415
|
+
|
|
416
|
+
const {
|
|
417
|
+
comment,
|
|
418
|
+
templateTags,
|
|
419
|
+
paramTags,
|
|
420
|
+
returnTag
|
|
421
|
+
} = overload;
|
|
422
|
+
|
|
423
|
+
const methodCode = `/*${cleanComment(comment).value}*/
|
|
424
|
+
declare function p${
|
|
425
|
+
templateTags.length ? '<' + templateTags.map(
|
|
426
|
+
t => [
|
|
427
|
+
t.param.name,
|
|
428
|
+
t.type ? 'extends ' + t.type.value.slice(1, -1) : null
|
|
429
|
+
].filter(f => f).join(' ')
|
|
430
|
+
).join(',') + '>' : ''
|
|
431
|
+
}(${
|
|
432
|
+
paramTags.map(p => {
|
|
433
|
+
|
|
434
|
+
return [
|
|
435
|
+
p.param.name,
|
|
436
|
+
p.param.value.startsWith('[') ? '?' : null,
|
|
437
|
+
': ',
|
|
438
|
+
p.type.value.slice(1, -1)
|
|
439
|
+
].filter(f => f).join('');
|
|
440
|
+
}).join(',')
|
|
441
|
+
}) : ${ returnTag ? returnTag.type.value.slice(1, -1) : 'void' };`;
|
|
442
|
+
|
|
443
|
+
const func = parseDts(methodCode).program.body[0];
|
|
444
|
+
|
|
445
|
+
const builder = {
|
|
446
|
+
'ClassProperty': b.tsDeclareMethod,
|
|
447
|
+
'TSDeclareFunction': b.tsDeclareFunction,
|
|
448
|
+
'TSDeclareMethod': b.tsDeclareMethod
|
|
449
|
+
}[node.type];
|
|
450
|
+
|
|
451
|
+
const hostBuilder = {
|
|
452
|
+
'ExportNamedDeclaration': b.exportNamedDeclaration,
|
|
453
|
+
'ExportDefaultDeclaration': b.exportDefaultDeclaration
|
|
454
|
+
}[hostPath.value.type];
|
|
455
|
+
|
|
456
|
+
const newNode = builder.from({
|
|
457
|
+
key: node.key,
|
|
458
|
+
params: func.params,
|
|
459
|
+
id: node.id,
|
|
460
|
+
declare: node.declare || false,
|
|
461
|
+
returnType: func.returnType ? b.tsTypeAnnotation.from({
|
|
462
|
+
...func.returnType
|
|
463
|
+
}) : null,
|
|
464
|
+
comments: hostPath === nodePath ? func.comments : [],
|
|
465
|
+
typeParameters: func.typeParameters ? b.tsTypeParameterDeclaration.from({
|
|
466
|
+
...func.typeParameters
|
|
467
|
+
}) : null
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
return (
|
|
471
|
+
hostBuilder
|
|
472
|
+
? hostBuilder.from({
|
|
473
|
+
...hostPath.value,
|
|
474
|
+
declaration: newNode,
|
|
475
|
+
comments: func.comments
|
|
476
|
+
})
|
|
477
|
+
: newNode
|
|
478
|
+
);
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
replace(hostPath, ...replacements);
|
|
482
|
+
|
|
483
|
+
return replacements;
|
|
484
|
+
}
|
|
270
485
|
|
|
271
486
|
function cleanComment(comment) {
|
|
272
487
|
|
|
@@ -278,7 +493,10 @@ export default function transform(src) {
|
|
|
278
493
|
for (const tag of tags) {
|
|
279
494
|
|
|
280
495
|
// remove full line including the non-TS tag
|
|
281
|
-
if (
|
|
496
|
+
if (
|
|
497
|
+
/class|constructor|template|method|typedef|property|this|overlord/.test(tag.name) ||
|
|
498
|
+
tag.param?.name?.includes('.')
|
|
499
|
+
) {
|
|
282
500
|
replacements.push([ { start: tag.start - 4, end: tag.end } ]);
|
|
283
501
|
|
|
284
502
|
continue;
|
|
@@ -345,24 +563,27 @@ export default function transform(src) {
|
|
|
345
563
|
return replace(path);
|
|
346
564
|
}
|
|
347
565
|
|
|
348
|
-
if (
|
|
566
|
+
if (generateOverloads(path)) {
|
|
349
567
|
return false;
|
|
350
568
|
}
|
|
351
569
|
|
|
352
|
-
if (
|
|
570
|
+
if (fixOptionalArgsMethods(path)) {
|
|
353
571
|
return false;
|
|
354
572
|
}
|
|
355
573
|
|
|
574
|
+
removeUnknownParams(path);
|
|
356
575
|
cleanComments(path);
|
|
576
|
+
|
|
577
|
+
fixTypeExport(path);
|
|
357
578
|
});
|
|
358
579
|
|
|
359
580
|
for (const [ path, args ] of replacements) {
|
|
360
581
|
try {
|
|
361
582
|
path.replace(...args);
|
|
362
583
|
} catch (err) {
|
|
363
|
-
console.error('Failed to replace path', path, ...args);
|
|
584
|
+
console.error('Failed to replace path', path, ...args, err);
|
|
364
585
|
|
|
365
|
-
throw err;
|
|
586
|
+
throw error(path.value, 'failed to replace path: ' + err.message);
|
|
366
587
|
}
|
|
367
588
|
}
|
|
368
589
|
|
|
@@ -387,4 +608,61 @@ function traverse(path, cb) {
|
|
|
387
608
|
|
|
388
609
|
traverse(path.get(key), cb);
|
|
389
610
|
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* @param {any} node
|
|
616
|
+
* @return {'TSDeclareMethod' | 'TSDeclareFunction' | 'TSFunctionType' | null}
|
|
617
|
+
*/
|
|
618
|
+
function getFunctionKind(node) {
|
|
619
|
+
|
|
620
|
+
if (
|
|
621
|
+
node.type === 'TSDeclareMethod'
|
|
622
|
+
) {
|
|
623
|
+
return 'TSDeclareMethod';
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
if (node.type === 'TSDeclareFunction') {
|
|
627
|
+
return 'TSDeclareFunction';
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
if (
|
|
631
|
+
node.typeAnnotation?.typeAnnotation?.type === 'TSFunctionType'
|
|
632
|
+
) {
|
|
633
|
+
return 'TSFunctionType';
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
return null;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Return host path for node (with attached comments).
|
|
641
|
+
*
|
|
642
|
+
* @param {Path} nodePath
|
|
643
|
+
*
|
|
644
|
+
* @return {Path}
|
|
645
|
+
*/
|
|
646
|
+
function getHostPath(nodePath) {
|
|
647
|
+
return [ 'ExportDefaultDeclaration', 'ExportNamedDeclaration' ].includes(nodePath.parentPath.value.type)
|
|
648
|
+
? nodePath.parentPath
|
|
649
|
+
: nodePath;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* @param {Node} node
|
|
654
|
+
*
|
|
655
|
+
* @return {string}
|
|
656
|
+
*/
|
|
657
|
+
function error(node, message) {
|
|
658
|
+
|
|
659
|
+
const {
|
|
660
|
+
loc: {
|
|
661
|
+
start
|
|
662
|
+
}
|
|
663
|
+
} = node;
|
|
664
|
+
|
|
665
|
+
const loc = `[line ${start.line + 1}, column ${start.column + 1}]`;
|
|
666
|
+
|
|
667
|
+
return new Error(`${message} ${loc}`);
|
|
390
668
|
}
|