Closed oandregal closed 3 years ago
Some notes:
The plan would be to update the get-jsdoc-from-token
exported function to use whatever doctrine alternative we settle on.
Relevant to this is the research I did at the time on doctrine alternatives (see comment on Feb 22): https://github.com/WordPress/gutenberg/pull/13329#issuecomment-466389286
I started exploring this, initially aiming to use comment-parser
, seeing as it's the same in use by eslint-plugin-jsdoc
, and on the assumption that we don't really need the type parsing (vs. just the verbatim name). It worked okay, but unlike Doctrine, there's no built-in understanding of "typed" or "named" tags, so these had to be manually adapted from Doctrine. Even then, however, there's some data loss that occurs when trying to reconstruct unnamed tags (like an @example
tag, losing whitespace).
This is about as far as I got with the implementation, but while it technically passes the unit tests, it fails in creating identical documentation for many of the packages for the above reason:
get-jsdoc-from-token.js
source)Curious to align to TypeScript's JSDoc tooling, given that we want to embrace some of this typing syntax (#18838), I discovered theirs is an entirely home-grown solution (some of the source). It makes me wonder though, seeing as we already have to parse the source to find comments and export statements, whether it might be worth using TypeScript both for the parsing of the source and for the parsing of the JSDoc. I haven't yet followed this idea to see how feasible it would be.
It makes me wonder though, seeing as we already have to parse the source to find comments and export statements, whether it might be worth using TypeScript both for the parsing of the source and for the parsing of the JSDoc.
Relevant documentation: https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API
Notable here is to feed a source string into ts.createSourceFile
:
var ts = require( 'typescript' );
ts.createSourceFile( '', '/**\n * Function description.\n *\n * @param {string} myVariable My variable description.\n */', ts.ScriptTarget.ES2015 );
ASTExplorer also has a TypeScript mode for exploring the AST: https://astexplorer.net/#/gist/34e05c635ea8a8904a4d557afc9dccc8/04f2263c5748d52b7a523809ace596e7f6e31e5e
It's includes full parsing of the JSDoc, including types.
Noting: While the TypeScript parser does parse the complex types, we will need to make sure we can print those in a meaningful way. Rolling something custom here could become very hairy very quickly, given the full scope of supported types.
VSCode actually evaluates this very nicely:
Ideally we can reuse this for our own documented output.
I've found a little bit of a lead in the TypeScript Compiler API documentation, though the documentation is far from exhaustive.
Specifically, we may be able to use parts of the "Using the Type Checker" section:
https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API#using-the-type-checker
let program = ts.createProgram(fileNames, options);
// ...
let checker = program.getTypeChecker();
// ...
checker.typeToString( checker.getTypeOfSymbolAtLocation( /* ... */ ) );
Even without a complete substitution of Doctrine, we probably need to find a way to address some of the current problems with the types printing sooner than later. Specifically, we are seeing many instances that docgen
will generate a type of null
for TypeScript-specific complex types. To me, this is worse than if it simply didn't document the type at all, since it is both incorrect and misleading.
For example, see also: #19520
Even without a complete substitution of Doctrine, we probably need to find a way to address some of the current problems with the types printing sooner than later. Specifically, we are seeing many instances that
docgen
will generate a type ofnull
for TypeScript-specific complex types. To me, this is worse than if it simply didn't document the type at all, since it is both incorrect and misleading.
Interim fix proposed at #19571
One other possible advantage of using the TypeScript compiler is more in how we already use a different parser (Espree) than that which runs over our own code (Babel). This hasn't been much of an issue thus far, but I just encountered my first issue where a particularly new syntax that we we support through Babel (ES2019 optional catch binding) will throw an error when running npm run docs:build
:
export function isURL( url ) {
try {
new URL( url );
return true;
} catch {
return false;
}
}
⇒ npm run docs:build
> gutenberg@7.3.0 docs:build /Users/andrew/Documents/Code/gutenberg
> node ./docs/tool/index.js && node ./bin/api-docs/update-readmes.js
url
SyntaxError: Unexpected token {
Using TypeScript doesn't really address the underlying issue that we have different parsers being used, but we could perhaps assume the TypeScript would be more up-to-date. Alternatively (and as an interim solution), it might just be a matter of keeping the espree
dependency up to date, assuming that it's been updated for newer versions of the specification.
Alternatively (and as an interim solution), it might just be a matter of keeping the
espree
dependency up to date, assuming that it's been updated for newer versions of the specification.
This is unfortunately not as simple as it might seem, since one of the breaking changes introduced in espree@5.0.0
is to remove the attachComment
feature, which is pretty critical to how docgen
works (or at least is currently implemented). Thus, upgrading to the latest version would require a pretty significant refactor to the code, one where we'd likely emulate how comment attachment worked previously, using the comment
(and perhaps tokens
) option(s) to infer where a comment occurs in relation to an exported member. I started down this path on Friday, but it seems significant enough to warrant whether it's worth the effort, considering if the alternative to Doctrine would bring with it a different parser implementation as well (e.g. TypeScript).
One thing I noted in the process of this is that we configure the ECMAScript version here:
This is particularly relevant for my previous comment where I mention being unable to use ES2019 features. It's pretty clear by this code why this wouldn't be expected to work 😄 I haven't tested yet, but it's possible that Espree versions prior to the breaking 5.0 version might have support for newer versions of ECMAScript, so that we could still use those language features without bumping to the latest Espree version.
I'm working on this with #19952.
doctrine
is the JSDoc parser we use indocgen
to get structured information out of the JSDoc comments. It was created and maintained by the ESLint people, although it's no longer actively developed. We should migrate out ofdoctrine
for that reason.The suggested replacement (eslint-plugin-jsoc) uses a different JSDoc parser with support for many other syntaxes (typescript, etc). We should look into trying
jsdoctypeparser
or any other that's actively developed.