|
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