Skip to content

Commit

Permalink
fix(require-jsdoc): more accurate and optimized decorator detection;
Browse files Browse the repository at this point in the history
…fixes #697
  • Loading branch information
brettz9 committed Feb 21, 2021
1 parent 27b95b3 commit 88655b3
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 52 deletions.
14 changes: 14 additions & 0 deletions README.md
Expand Up @@ -10755,6 +10755,12 @@ export class MyComponentComponent {
}
// "jsdoc/require-jsdoc": ["error"|"warn", {"contexts":["ClassProperty:has(Decorator[expression.callee.name=\"Input\"])"]}]
// Message: Missing JSDoc comment.

requestAnimationFrame(draw)

function bench() {
}
// Message: Missing JSDoc comment.
````

The following patterns are not considered problems:
Expand Down Expand Up @@ -11516,6 +11522,14 @@ const foo = {
})
export class UserSettingsState { }
// "jsdoc/require-jsdoc": ["error"|"warn", {"require":{"ClassDeclaration":true}}]

/**
* Entity to represent a user in the system.
*/
@Entity('users')
export class User {
}
// "jsdoc/require-jsdoc": ["error"|"warn", {"contexts":["Decorator"],"require":{"FunctionDeclaration":false}}]
````


Expand Down
49 changes: 5 additions & 44 deletions src/eslint/getJSDocComment.js
Expand Up @@ -14,47 +14,9 @@ const isCommentToken = (token) => {
return token.type === 'Line' || token.type === 'Block' || token.type === 'Shebang';
};

const decoratorMetaTokens = new Map([[')', '('], ['>', '<']]);

const getDecorator = (token, sourceCode) => {
if (!token) {
return false;
}
if (token.type === 'Punctuator') {
const tokenClose = token.value;
const tokenOpen = decoratorMetaTokens.get(tokenClose);
if (tokenOpen) {
let nested = 0;
let tokenBefore = token;
do {
tokenBefore = sourceCode.getTokenBefore(tokenBefore, {includeComments: true});
// istanbul ignore if
if (tokenBefore && tokenBefore.type === 'Punctuator') {
if (tokenBefore.value === tokenClose) {
nested++;
}
if (tokenBefore.value === tokenOpen) {
if (nested) {
nested--;
} else {
break;
}
}
}
} while (tokenBefore);

return tokenBefore;
}
}

if (token.type === 'Identifier') {
const tokenBefore = sourceCode.getTokenBefore(token, {includeComments: true});
if (tokenBefore && tokenBefore.type === 'Punctuator' && tokenBefore.value === '@') {
return tokenBefore;
}
}

return false;
const getDecorator = (node) => {
return node?.declaration?.decorators?.[0] || node?.decorators?.[0] ||
node?.parent?.decorators?.[0];
};

/**
Expand Down Expand Up @@ -224,12 +186,11 @@ const findJSDocComment = (astNode, sourceCode, settings) => {
let tokenBefore = null;

while (currentNode) {
tokenBefore = sourceCode.getTokenBefore(currentNode, {includeComments: true});
const decorator = getDecorator(tokenBefore, sourceCode);
const decorator = getDecorator(currentNode);
if (decorator) {
currentNode = decorator;
continue;
}
tokenBefore = sourceCode.getTokenBefore(currentNode, {includeComments: true});
if (!tokenBefore || !isCommentToken(tokenBefore)) {
return null;
}
Expand Down
12 changes: 4 additions & 8 deletions src/rules/requireJsdoc.js
Expand Up @@ -212,14 +212,10 @@ export default {
const lines = settings.minLines === 0 && settings.maxLines >= 1 ? 1 : settings.minLines;
let baseNode = getReducedASTNode(node, sourceCode);

let decorator;
do {
const tokenBefore = sourceCode.getTokenBefore(baseNode, {includeComments: true});
decorator = getDecorator(tokenBefore, sourceCode);
if (decorator) {
baseNode = decorator;
}
} while (decorator);
const decorator = getDecorator(baseNode);
if (decorator) {
baseNode = decorator;
}

const indent = jsdocUtils.getIndent({
text: sourceCode.getText(
Expand Down
42 changes: 42 additions & 0 deletions test/rules/assertions/requireJsdoc.js
Expand Up @@ -2971,6 +2971,26 @@ function quux (foo) {
`,
parser: require.resolve('@typescript-eslint/parser'),
},
{
code: `
requestAnimationFrame(draw)
function bench() {
}
`,
errors: [{
message: 'Missing JSDoc comment.',
}],
output: `
requestAnimationFrame(draw)
/**
*
*/
function bench() {
}
`,
},
],
valid: [{
code: `
Expand Down Expand Up @@ -4609,5 +4629,27 @@ function quux (foo) {
}],
parser: require.resolve('@typescript-eslint/parser'),
},
{
code: `
/**
* Entity to represent a user in the system.
*/
@Entity('users')
export class User {
}
`,
options: [
{
contexts: ['Decorator'],
require: {
FunctionDeclaration: false,
},
},
],
parser: require.resolve('@typescript-eslint/parser'),
parserOptions: {
sourceType: 'module',
},
},
],
};

0 comments on commit 88655b3

Please sign in to comment.