|
1 | 1 | require('string.prototype.matchall').shim();
|
2 |
| -const path = require('path'); |
3 | 2 | const fs = require('fs');
|
4 |
| -const env = require('jsdoc/env'); // eslint-disable-line import/no-unresolved |
| 3 | +const path = require('path'); |
5 | 4 | const addInherited = require('jsdoc/augment').addInherited; // eslint-disable-line import/no-unresolved
|
| 5 | +const env = require('jsdoc/env'); // eslint-disable-line import/no-unresolved |
6 | 6 |
|
7 | 7 | const importRegEx =
|
8 | 8 | /import\(["']([^"']*)["']\)(?:\.([^ \.\|\}><,\)=#\n]*))?([ \.\|\}><,\)=#\n])/g;
|
@@ -117,7 +117,7 @@ function getModuleInfo(modulePath, parser) {
|
117 | 117 |
|
118 | 118 | if (!moduleInfos[modulePath]) {
|
119 | 119 | if (!fileNodes[modulePath]) {
|
120 |
| - const file = fs.readFileSync(modulePath, 'UTF-8'); |
| 120 | + const file = fs.readFileSync(modulePath, {encoding: 'utf8'}); |
121 | 121 |
|
122 | 122 | fileNodes[modulePath] = parser.astBuilder.build(file, modulePath);
|
123 | 123 | }
|
@@ -193,162 +193,6 @@ function replaceByIndices(text, replacements) {
|
193 | 193 | return replacedText;
|
194 | 194 | }
|
195 | 195 |
|
196 |
| -exports.defineTags = function (dictionary) { |
197 |
| - const tags = [ |
198 |
| - 'type', |
199 |
| - 'typedef', |
200 |
| - 'property', |
201 |
| - 'return', |
202 |
| - 'param', |
203 |
| - 'template', |
204 |
| - 'default', |
205 |
| - 'member', |
206 |
| - ]; |
207 |
| - |
208 |
| - tags.forEach(function (tagName) { |
209 |
| - const tag = dictionary.lookUp(tagName); |
210 |
| - const oldOnTagText = tag.onTagText; |
211 |
| - |
212 |
| - /** |
213 |
| - * @param {string} tagText The tag text |
214 |
| - * @return {string} The modified tag text |
215 |
| - */ |
216 |
| - tag.onTagText = function (tagText) { |
217 |
| - if (oldOnTagText) { |
218 |
| - tagText = oldOnTagText.apply(this, arguments); |
219 |
| - } |
220 |
| - |
221 |
| - const startIndex = tagText.search('{'); |
222 |
| - if (startIndex === -1) { |
223 |
| - return tagText; |
224 |
| - } |
225 |
| - |
226 |
| - const len = tagText.length; |
227 |
| - |
228 |
| - /** @type {Array<[number, number, string]>} */ |
229 |
| - let replacements = []; |
230 |
| - let openCurly = 0; |
231 |
| - let openSquare = 0; |
232 |
| - let openRound = 0; |
233 |
| - let isWithinString = false; |
234 |
| - let quoteChar = ''; |
235 |
| - let i = startIndex; |
236 |
| - let functionStartIndex; |
237 |
| - |
238 |
| - while (i < len) { |
239 |
| - switch (tagText[i]) { |
240 |
| - case '\\': |
241 |
| - // Skip escaped character |
242 |
| - ++i; |
243 |
| - break; |
244 |
| - case '"': |
245 |
| - case "'": |
246 |
| - if (isWithinString && quoteChar === tagText[i]) { |
247 |
| - isWithinString = false; |
248 |
| - quoteChar = ''; |
249 |
| - } else if (!isWithinString) { |
250 |
| - isWithinString = true; |
251 |
| - quoteChar = tagText[i]; |
252 |
| - } |
253 |
| - |
254 |
| - break; |
255 |
| - case ';': |
256 |
| - // Replace interface-style semi-colon separators with commas |
257 |
| - if (!isWithinString && openCurly > 1) { |
258 |
| - const isTrailingSemiColon = /^\s*}/.test(tagText.slice(i + 1)); |
259 |
| - |
260 |
| - replacements.push([i, i + 1, isTrailingSemiColon ? '' : ',']); |
261 |
| - } |
262 |
| - |
263 |
| - break; |
264 |
| - case '(': |
265 |
| - if (openRound === 0) { |
266 |
| - functionStartIndex = i; |
267 |
| - } |
268 |
| - |
269 |
| - ++openRound; |
270 |
| - |
271 |
| - break; |
272 |
| - case ')': |
273 |
| - if (!--openRound) { |
274 |
| - // If round brackets form a function |
275 |
| - const returnMatch = tagText.slice(i + 1).match(/^\s*(:|=>)/); |
276 |
| - |
277 |
| - // Replace TS inline function syntax with JSDoc |
278 |
| - if (returnMatch) { |
279 |
| - const functionEndIndex = i + returnMatch[0].length + 1; |
280 |
| - const hasFunctionKeyword = /\bfunction\s*$/.test( |
281 |
| - tagText.slice(0, functionStartIndex), |
282 |
| - ); |
283 |
| - |
284 |
| - // Filter out any replacements that are within the function |
285 |
| - replacements = replacements.filter(([startIndex]) => { |
286 |
| - return startIndex < functionStartIndex || startIndex > i; |
287 |
| - }); |
288 |
| - |
289 |
| - replacements.push([ |
290 |
| - functionStartIndex, |
291 |
| - functionEndIndex, |
292 |
| - hasFunctionKeyword ? '():' : 'function():', |
293 |
| - ]); |
294 |
| - } |
295 |
| - |
296 |
| - functionStartIndex = null; |
297 |
| - } |
298 |
| - |
299 |
| - break; |
300 |
| - case '[': |
301 |
| - if ( |
302 |
| - isWithinString || |
303 |
| - variableNameRegEx.test(tagText.charAt(i - 1)) |
304 |
| - ) { |
305 |
| - break; |
306 |
| - } |
307 |
| - ++openSquare; |
308 |
| - break; |
309 |
| - case ']': |
310 |
| - if (isWithinString) { |
311 |
| - break; |
312 |
| - } |
313 |
| - if (!--openSquare) { |
314 |
| - // Replace [type1, type2] tuples with Array |
315 |
| - replacements.push([startIndex + 1, i + 1, 'Array']); |
316 |
| - } |
317 |
| - break; |
318 |
| - case '{': |
319 |
| - ++openCurly; |
320 |
| - break; |
321 |
| - case '}': |
322 |
| - if (!--openCurly) { |
323 |
| - const head = tagText.slice(0, startIndex); |
324 |
| - const tail = tagText.slice(i + 1); |
325 |
| - |
326 |
| - const replaced = replaceByIndices( |
327 |
| - tagText.slice(startIndex, i + 1), |
328 |
| - replacements, |
329 |
| - ) |
330 |
| - // Replace `templateliteral` with 'templateliteral' |
331 |
| - .replace(/`([^`]*)`/g, "'$1'") |
332 |
| - // Bracket notation to dot notation |
333 |
| - .replace( |
334 |
| - /(\w+|>|\)|\])\[(?:'([^']+)'|"([^"]+)")\]/g, |
335 |
| - '$1.$2$3', |
336 |
| - ); |
337 |
| - |
338 |
| - return head + replaced + tail; |
339 |
| - } |
340 |
| - |
341 |
| - break; |
342 |
| - default: |
343 |
| - break; |
344 |
| - } |
345 |
| - ++i; |
346 |
| - } |
347 |
| - throw new Error("Missing closing '}'"); |
348 |
| - }; |
349 |
| - }); |
350 |
| -}; |
351 |
| - |
352 | 196 | exports.astNodeVisitor = {
|
353 | 197 | visitNode: function (node, e, parser, currentSourceName) {
|
354 | 198 | if (node.type === 'File') {
|
@@ -655,6 +499,162 @@ exports.astNodeVisitor = {
|
655 | 499 | },
|
656 | 500 | };
|
657 | 501 |
|
| 502 | +exports.defineTags = function (dictionary) { |
| 503 | + const tags = [ |
| 504 | + 'type', |
| 505 | + 'typedef', |
| 506 | + 'property', |
| 507 | + 'return', |
| 508 | + 'param', |
| 509 | + 'template', |
| 510 | + 'default', |
| 511 | + 'member', |
| 512 | + ]; |
| 513 | + |
| 514 | + tags.forEach(function (tagName) { |
| 515 | + const tag = dictionary.lookUp(tagName); |
| 516 | + const oldOnTagText = tag.onTagText; |
| 517 | + |
| 518 | + /** |
| 519 | + * @param {string} tagText The tag text |
| 520 | + * @return {string} The modified tag text |
| 521 | + */ |
| 522 | + tag.onTagText = function (tagText) { |
| 523 | + if (oldOnTagText) { |
| 524 | + tagText = oldOnTagText.apply(this, arguments); |
| 525 | + } |
| 526 | + |
| 527 | + const startIndex = tagText.search('{'); |
| 528 | + if (startIndex === -1) { |
| 529 | + return tagText; |
| 530 | + } |
| 531 | + |
| 532 | + const len = tagText.length; |
| 533 | + |
| 534 | + /** @type {Array<[number, number, string]>} */ |
| 535 | + let replacements = []; |
| 536 | + let openCurly = 0; |
| 537 | + let openSquare = 0; |
| 538 | + let openRound = 0; |
| 539 | + let isWithinString = false; |
| 540 | + let quoteChar = ''; |
| 541 | + let i = startIndex; |
| 542 | + let functionStartIndex; |
| 543 | + |
| 544 | + while (i < len) { |
| 545 | + switch (tagText[i]) { |
| 546 | + case '\\': |
| 547 | + // Skip escaped character |
| 548 | + ++i; |
| 549 | + break; |
| 550 | + case '"': |
| 551 | + case "'": |
| 552 | + if (isWithinString && quoteChar === tagText[i]) { |
| 553 | + isWithinString = false; |
| 554 | + quoteChar = ''; |
| 555 | + } else if (!isWithinString) { |
| 556 | + isWithinString = true; |
| 557 | + quoteChar = tagText[i]; |
| 558 | + } |
| 559 | + |
| 560 | + break; |
| 561 | + case ';': |
| 562 | + // Replace interface-style semi-colon separators with commas |
| 563 | + if (!isWithinString && openCurly > 1) { |
| 564 | + const isTrailingSemiColon = /^\s*}/.test(tagText.slice(i + 1)); |
| 565 | + |
| 566 | + replacements.push([i, i + 1, isTrailingSemiColon ? '' : ',']); |
| 567 | + } |
| 568 | + |
| 569 | + break; |
| 570 | + case '(': |
| 571 | + if (openRound === 0) { |
| 572 | + functionStartIndex = i; |
| 573 | + } |
| 574 | + |
| 575 | + ++openRound; |
| 576 | + |
| 577 | + break; |
| 578 | + case ')': |
| 579 | + if (!--openRound) { |
| 580 | + // If round brackets form a function |
| 581 | + const returnMatch = tagText.slice(i + 1).match(/^\s*(:|=>)/); |
| 582 | + |
| 583 | + // Replace TS inline function syntax with JSDoc |
| 584 | + if (returnMatch) { |
| 585 | + const functionEndIndex = i + returnMatch[0].length + 1; |
| 586 | + const hasFunctionKeyword = /\bfunction\s*$/.test( |
| 587 | + tagText.slice(0, functionStartIndex), |
| 588 | + ); |
| 589 | + |
| 590 | + // Filter out any replacements that are within the function |
| 591 | + replacements = replacements.filter(([startIndex]) => { |
| 592 | + return startIndex < functionStartIndex || startIndex > i; |
| 593 | + }); |
| 594 | + |
| 595 | + replacements.push([ |
| 596 | + functionStartIndex, |
| 597 | + functionEndIndex, |
| 598 | + hasFunctionKeyword ? '():' : 'function():', |
| 599 | + ]); |
| 600 | + } |
| 601 | + |
| 602 | + functionStartIndex = null; |
| 603 | + } |
| 604 | + |
| 605 | + break; |
| 606 | + case '[': |
| 607 | + if ( |
| 608 | + isWithinString || |
| 609 | + variableNameRegEx.test(tagText.charAt(i - 1)) |
| 610 | + ) { |
| 611 | + break; |
| 612 | + } |
| 613 | + ++openSquare; |
| 614 | + break; |
| 615 | + case ']': |
| 616 | + if (isWithinString) { |
| 617 | + break; |
| 618 | + } |
| 619 | + if (!--openSquare) { |
| 620 | + // Replace [type1, type2] tuples with Array |
| 621 | + replacements.push([startIndex + 1, i + 1, 'Array']); |
| 622 | + } |
| 623 | + break; |
| 624 | + case '{': |
| 625 | + ++openCurly; |
| 626 | + break; |
| 627 | + case '}': |
| 628 | + if (!--openCurly) { |
| 629 | + const head = tagText.slice(0, startIndex); |
| 630 | + const tail = tagText.slice(i + 1); |
| 631 | + |
| 632 | + const replaced = replaceByIndices( |
| 633 | + tagText.slice(startIndex, i + 1), |
| 634 | + replacements, |
| 635 | + ) |
| 636 | + // Replace `templateliteral` with 'templateliteral' |
| 637 | + .replace(/`([^`]*)`/g, "'$1'") |
| 638 | + // Bracket notation to dot notation |
| 639 | + .replace( |
| 640 | + /(\w+|>|\)|\])\[(?:'([^']+)'|"([^"]+)")\]/g, |
| 641 | + '$1.$2$3', |
| 642 | + ); |
| 643 | + |
| 644 | + return head + replaced + tail; |
| 645 | + } |
| 646 | + |
| 647 | + break; |
| 648 | + default: |
| 649 | + break; |
| 650 | + } |
| 651 | + ++i; |
| 652 | + } |
| 653 | + throw new Error("Missing closing '}'"); |
| 654 | + }; |
| 655 | + }); |
| 656 | +}; |
| 657 | + |
658 | 658 | exports.handlers = {
|
659 | 659 | parseComplete: function (e) {
|
660 | 660 | // Build inheritance chain after adding @extends annotations
|
|
0 commit comments