|
1 | 1 | 'use strict'; |
2 | 2 |
|
| 3 | +const semver = require('semver'); |
| 4 | +const eslintVersion = require('eslint/package.json').version; |
| 5 | + |
3 | 6 | function getSourceCode(context) { |
4 | 7 | return context.getSourceCode ? context.getSourceCode() : context.sourceCode; |
5 | 8 | } |
@@ -40,6 +43,90 @@ function getText(context) { |
40 | 43 | return sourceCode.getText ? sourceCode.getText.apply(sourceCode, args) : context.getSource.apply(context, args); |
41 | 44 | } |
42 | 45 |
|
| 46 | +function getJSDocComment(context, node) { |
| 47 | + const sourceCode = getSourceCode(context); |
| 48 | + |
| 49 | + // ESLint 10 removed the deprecated SourceCode#getJSDocComment API, but we |
| 50 | + // still rely on its behavior for JSDoc-based React component detection. |
| 51 | + // Reuse the ESLint 9 SourceCode#getJSDocComment logic as a compatibility |
| 52 | + // fallback when the built-in helper is no longer available. |
| 53 | + if (semver.major(eslintVersion) < 10 && sourceCode.getJSDocComment) { |
| 54 | + return sourceCode.getJSDocComment(node); |
| 55 | + } |
| 56 | + |
| 57 | + function findJSDocComment(astNode) { |
| 58 | + const tokenBefore = sourceCode.getTokenBefore(astNode, { |
| 59 | + includeComments: true, |
| 60 | + }); |
| 61 | + |
| 62 | + if ( |
| 63 | + tokenBefore |
| 64 | + && (tokenBefore.type === 'Block' || tokenBefore.type === 'Line') |
| 65 | + && tokenBefore.type === 'Block' |
| 66 | + && tokenBefore.value.charAt(0) === '*' |
| 67 | + && astNode.loc.start.line - tokenBefore.loc.end.line <= 1 |
| 68 | + ) { |
| 69 | + return tokenBefore; |
| 70 | + } |
| 71 | + |
| 72 | + return null; |
| 73 | + } |
| 74 | + |
| 75 | + function looksLikeExport(astNode) { |
| 76 | + return astNode |
| 77 | + && ( |
| 78 | + astNode.type === 'ExportDefaultDeclaration' |
| 79 | + || astNode.type === 'ExportNamedDeclaration' |
| 80 | + || astNode.type === 'ExportAllDeclaration' |
| 81 | + || astNode.type === 'ExportSpecifier' |
| 82 | + ); |
| 83 | + } |
| 84 | + |
| 85 | + let parent = node.parent; |
| 86 | + |
| 87 | + switch (node.type) { |
| 88 | + case 'ClassDeclaration': |
| 89 | + case 'FunctionDeclaration': |
| 90 | + return findJSDocComment(looksLikeExport(parent) ? parent : node); |
| 91 | + |
| 92 | + case 'ClassExpression': |
| 93 | + return findJSDocComment(parent.parent); |
| 94 | + |
| 95 | + case 'ArrowFunctionExpression': |
| 96 | + case 'FunctionExpression': |
| 97 | + if ( |
| 98 | + parent.type !== 'CallExpression' |
| 99 | + && parent.type !== 'NewExpression' |
| 100 | + ) { |
| 101 | + while ( |
| 102 | + !sourceCode.getCommentsBefore(parent).length |
| 103 | + && !/Function/u.test(parent.type) |
| 104 | + && parent.type !== 'MethodDefinition' |
| 105 | + && parent.type !== 'Property' |
| 106 | + ) { |
| 107 | + parent = parent.parent; |
| 108 | + |
| 109 | + if (!parent) { |
| 110 | + break; |
| 111 | + } |
| 112 | + } |
| 113 | + |
| 114 | + if ( |
| 115 | + parent |
| 116 | + && parent.type !== 'FunctionDeclaration' |
| 117 | + && parent.type !== 'Program' |
| 118 | + ) { |
| 119 | + return findJSDocComment(parent); |
| 120 | + } |
| 121 | + } |
| 122 | + |
| 123 | + return findJSDocComment(node); |
| 124 | + |
| 125 | + default: |
| 126 | + return null; |
| 127 | + } |
| 128 | +} |
| 129 | + |
43 | 130 | function isSpaceBetweenTokens(context, leftToken, rightToken) { |
44 | 131 | const sourceCode = getSourceCode(context); |
45 | 132 |
|
@@ -71,6 +158,7 @@ module.exports = { |
71 | 158 | getFilename, |
72 | 159 | getFirstTokens, |
73 | 160 | getScope, |
| 161 | + getJSDocComment, |
74 | 162 | getSourceCode, |
75 | 163 | getText, |
76 | 164 | isSpaceBetweenTokens, |
|
0 commit comments