feat: add react-metadata package #6047
Draft
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Follow up to work in: #5948 and #6048
tl;dr
This work proposes a new way of documenting for our React library. This new approach will:
About
This Pull Request adds an internal package,
@primer/react-metadata
, that helps with getting the TypeScript information from a JavaScript module. The goal is to use this to automate the generation of ourdocs.json
files and corresponding component metadata 🤞 There is an example using this package inpackages/react/script/generate-metadata.mts
This exploration has been incredibly difficult due to the challenge in getting structured type information from a file in a way that works with some of our wishlist items for the docs site. Ideally, we'd be able to capture the prop information for components in order to display them on the site. This information could look like:
And we would like this type to serialize into a format that we could use to generate prop tables on the website. However, component prop types are not always this simple. Some examples include:
Intersection types
Type references
Expanded types
To make matters more difficult, we would also like to provide structured type information about non-component related exports, as well 😰 Hooks are a great example where it can be helpful to automate our
*.docs.json
files through our source code and provide a good experience for these our site.To this end, the
react-metadata
package here attempts to provide a structured representation of the symbols in a file. The hope is to be able to use the typescript compiler in order to collect structured data that can then be consumed to automatically generate great documentation pages for components, hooks, and more 🎉Implementation
The
getMetadataFromSourceFile
function relies on several different TypeScript concepts in order to parse the information that we want into corresponding types. In particular:ts.Node
to understand the AST of the filets.Symbol
to help with things like functions or interfaces that have multiple definitionsts.Type
to get the underlying type information when this is unavailable from ats.Node
. This is helpful for inferred types for functionsWhen going through a file, we get the exported
ts.Node
nodes and get type information from them using eitherts.Node
or drilling down intots.Type
, as needed. We emit a custom format for representing types. This can change over time but is a rough combination ofts.Node
andts.Type
types. The goal is to be able to format things as-authored in our docs site versus offering the underlying type value. For example:When printing this hook, we would most likely want to display it close to how it was authored. Otherwise, if we use the type information exclusively, it would look like:
While this type is strictly true (this is what the
?
token represents) it seemed unlikely that we would want to expose this in all cases. This is something that we can definitely change in the future though.In many cases, we won't be able to rely strictly on
ts.Node
as types like functions and interaces can be merged or have types that are inferred versus explicitly annotated. In these cases, we need to get the type of the underlying symbol and parse our type information from ats.Type
instead of ats.Node
. For example:In these examples, we do not have an explicit return value and cannot use the
ts.Node
for these function declarations to parse our type information. Instead, we need to use the TypeScript compiler to access the inferredts.Type
.Remaining work
components.json
fileTypeReference
nodes to provide a way to "drill down" in to type references in our docs siteNext steps
If folks on the team are encouraged by this approach, we could put together a pitch to begin the work towards automatically generating our
docs.json
files. Most likely we would want to do this gradually, opening this up per-component to make sure that the package supports all the syntax that we need. This work would be completed when all of our docs.json files are automatically generated using this package.