Closed Boshen closed 7 months ago
Prioritizing because @thepassle need this. See https://twitter.com/passle_/status/1637446645104164865
Update:
The original intent was to come up with a generic comment attachment algorithm, where we attach comments to some AST node driven by a set of rules. After some research and reading about it in the ESLint and Babel codebases, I conclude that it is incomprehensible for me right now.
So instead, let's reduce the problem space down to "attach jsdoc comments to specific AST node". This will be much more approachable.
We will build the jsdoc data structure inside the semantic analyzer. When a jsdoc targeting AST node such as a Statement or Declaration is visited, we will ask Trivias
to get us a leading comment. The jsdoc data structure will save the comment span and mark the Statement (SemanticNode) to indicate it has a jsdoc. A getter will be provided to parse the jsdoc lazily in a OnceCell
.
I want to help somehow. What do you need or what can I do? :)
I want to help somehow. What do you need or what can I do? :)
Hi Ema! I've forgotten everything we wrote about jsdocs, but would you be interested in getting a https://github.com/gajus/eslint-plugin-jsdoc rule to work? I can guide you on the missing pieces in the discord channel. I think we had some of the infra working, targeting a lint rule would be the easiest to fill in the gaps.
Summary as a pointer to someone in the future...
There are 2 main parts to do to implement #1170 .
@type
,@returns
and their contentsdeclaration
kindstrivias.comment().filter(isBlock && startsWith("*"))
eslint-plugin-jsdoc
depends on
I'd love to be able to just see & access comment values instead of skipping them outright.
I'd definitely consider it a bonus step for oxc to parse any JSDocs and translate its meaning(s) to the AST, but there are lots of instances where users either invent directives or just use a standard comment to transfer additional information
Quick examples:
/** @table "users" */
type User = { ... }
import(/* webpackChunkName: "my-chunk" */ "foobar");
// comptime
const DAY = ms(1, "day")
We talked a bit about this in #2437 ...,
eslint-plugin-jsdoc
eslint-plugin-jsdoc
firstSo, I spent these days reading through the code.
eslint-plugin-jsdoc
itselfjsdoccomment
, comment-parser
and jsdoc-type-pratt-parser
, which are heavily dependent(There are 3 articles on my blog if you're interested. Sorry it's in Japanese.)
As a result, although IMO, I'm not sure we should aim for 100% compatibility for this.
For a number of reasons,
esquery
(+estraverse
), as the ability to refine the execution context with their originally invented AST seems particularly hard(but widely integrated)
@Boshen Sorry for the long lines, then I'd like to confirm,
eslint-plugin-jsdoc
with compatibility, no matter how hard it was?rules/oxc_jsdoc
like rules/oxc
?eslint-plugin-jsdoc
?getLeadingComments(node)
and leave it at allWhat do you think? 👀
@leaysgur Oh wow I didn't expect 3 blog posts on this topic, I thought jsdoc is a solved problem 😰
So in summary, it seems like the hardest part about jsdoc is comment attachment to AST nodes.
We can leave this part out and focus our task on just jsdoc content rules, which is quiet easy as all we need to do is finish the jsdoc parser and run rules against these parsed jsdocs.
then I'd like to confirm
The intention was to pass all the tests, but we don't really need to do it if it's not a fun task.
And if you're sick of jsdoc after looking at it for 3 days ... you may also join me on the eslint-plugin-import task.
For future reference, let me elaborate.
the hardest part about jsdoc is comment attachment to AST nodes.
This wasn't particularly difficult, at least if we trust the logic of eslint-plugin-jsdoc
, or rather, jsdoccomment
.
To put it simply, it was just this:
// findJSDocComment(node, sourceCode): Comment | null;
const beforeTokens = sourceCode.getTokensBefore(node, { includeComments: true });
while (let token = beforeTokens.pop()) {
if (token.type === 'Block' && token.value.startsWith('*')) return token;
}
return null;
And I believe this behavior was already implemented in #2437 .
The part I found to be the hardest was that some(about half of) rules can:
astNode
to execute the above logicif (
esquery.matches(
astNode,
// from rule config
`MethodDefinition:not([accessibility="public"]):has(JsdocBlock)`
)
) {
const jsdocComment = findJSDocComment(astNode, sourceCode);
}
Comment
obtained, freely determine whether to execute rule handlerconst jsdocAstNode = toESTreeLikeAST(jsdocComment);
if (
esquery.matches(
jsdocAstNode,
// from rule config
`JsdocBlock[postDelimiter=""]:has(JsdocTypeUnion > JsdocTypeName[value="Bar"]:nth-child(1))`
)
) {
ruleHandler({ astNode, jsdocAstNode });
}
https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/advanced.md
(Actually, they seemed to have a bit more complicated logic...)
About rest half of the rules seem to simply check "only the text of all JSDoc comments in the source".
However, their implementation was like
for (const { astNode, jsdocAstNode } of jsdocNodesWithAttachedNode)
// Why astNode required...??
ruleHandler({ astNode, jsdocAstNode });
// ...
for (const { jsdocAstNode } of jsdocNodesWithoutAttachedNode)
ruleHandler({ astNode: null, jsdocAstNode });
they are called differently for some reason.
And when I tried to replace astNode
of the former to null
, the tests started to FAIL...! 😇
💡 After additional research, I finally solved this mystery.
It seems that these 3 rules perform extra linting if node
exists. Other all rule's tests pass without node
!
you may also join me on the eslint-plugin-import task.
That sounds interesting as well.
Either way, I'll think a bit more about how to conclude #2437 .
@leaysgur is carrying this task 👍 . Future issues can be created separately now.
I see there are some demand for reading jsdoc information, let's do this in two steps:
Note: This is for parsing jsdoc comments to structured data. This is not about wiring up all the jsdoc comments into a document tree (which is the main purpose for jsdoc).
To parse jsdoc comments, we need to:
References: