Skip to content

Commit ea44283

Browse files
committed
fix: restore JSDoc component detection on ESLint 10
ESLint 10 removed SourceCode#getJSDocComment, which this plugin still relies on for JSDoc-based component detection. Reintroduce the ESLint 9 getJSDocComment logic in the shared eslint util.
1 parent 1cf8915 commit ea44283

2 files changed

Lines changed: 90 additions & 3 deletions

File tree

lib/util/componentUtil.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ const doctrine = require('doctrine');
44
const pragmaUtil = require('./pragma');
55
const eslintUtil = require('./eslint');
66

7+
const getJSDocComment = eslintUtil.getJSDocComment;
78
const getScope = eslintUtil.getScope;
8-
const getSourceCode = eslintUtil.getSourceCode;
99
const getText = eslintUtil.getText;
1010

1111
// eslint-disable-next-line valid-jsdoc
@@ -62,14 +62,13 @@ function isES5Component(node, context) {
6262
* @returns {boolean}
6363
*/
6464
function isExplicitComponent(node, context) {
65-
const sourceCode = getSourceCode(context);
6665
let comment;
6766
// Sometimes the passed node may not have been parsed yet by eslint, and this function call crashes.
6867
// Can be removed when eslint sets "parent" property for all nodes on initial AST traversal: https://github.com/eslint/eslint-scope/issues/27
6968
// eslint-disable-next-line no-warning-comments
7069
// FIXME: Remove try/catch when https://github.com/eslint/eslint-scope/issues/27 is implemented.
7170
try {
72-
comment = sourceCode.getJSDocComment(node);
71+
comment = getJSDocComment(context, node);
7372
} catch (e) {
7473
comment = null;
7574
}

lib/util/eslint.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
'use strict';
22

3+
const semver = require('semver');
4+
const eslintVersion = require('eslint/package.json').version;
5+
36
function getSourceCode(context) {
47
return context.getSourceCode ? context.getSourceCode() : context.sourceCode;
58
}
@@ -40,6 +43,90 @@ function getText(context) {
4043
return sourceCode.getText ? sourceCode.getText.apply(sourceCode, args) : context.getSource.apply(context, args);
4144
}
4245

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+
43130
function isSpaceBetweenTokens(context, leftToken, rightToken) {
44131
const sourceCode = getSourceCode(context);
45132

@@ -71,6 +158,7 @@ module.exports = {
71158
getFilename,
72159
getFirstTokens,
73160
getScope,
161+
getJSDocComment,
74162
getSourceCode,
75163
getText,
76164
isSpaceBetweenTokens,

0 commit comments

Comments
 (0)