Files
medusa-store/docs-util/packages/react-docs-generator/src/resolvers/custom-resolver.ts
Shahed Nasser 245e5c9a69 docs: generate documentation for UI components (#5849)
* added tool to generate spec files for React components

* use typedoc for missing descriptions and types

* improvements and fixes

* improvements

* added doc comments for half of the components

* add custom resolver + more doc comments

* added all tsdocs

* general improvements

* add specs to UI docs

* added github action

* remove unnecessary api route

* Added readme for react-docs-generator

* remove comment

* Update packages/design-system/ui/src/components/currency-input/currency-input.tsx

Co-authored-by: Kasper Fabricius Kristensen <45367945+kasperkristensen@users.noreply.github.com>

* remove description of aria fields + add generate script

---------

Co-authored-by: Kasper Fabricius Kristensen <45367945+kasperkristensen@users.noreply.github.com>
2023-12-13 16:02:41 +02:00

106 lines
3.5 KiB
TypeScript

import { visitors } from "@babel/traverse"
import { utils, builtinResolvers, FileState, NodePath } from "react-docgen"
import { ComponentNodePath } from "react-docgen/dist/resolver/index.js"
import TypedocManager from "../classes/typedoc-manager.js"
type State = {
foundDefinitions: Set<ComponentNodePath>
}
/**
* This resolver extends react-docgen's FindAllDefinitionsResolver
* + adds the ability to resolve variable components such as:
*
* ```tsx
* const Value = SelectPrimitive.Value
* Value.displayName = "Select.Value"
* ```
*/
export default class CustomResolver
implements builtinResolvers.FindAllDefinitionsResolver
{
private typedocManager: TypedocManager
constructor(typedocManager: TypedocManager) {
this.typedocManager = typedocManager
}
resolve(file: FileState): ComponentNodePath[] {
const state = {
foundDefinitions: new Set<ComponentNodePath>(),
}
file.traverse(
visitors.explode({
FunctionDeclaration: { enter: this.statelessVisitor },
FunctionExpression: { enter: this.statelessVisitor },
ObjectMethod: { enter: this.statelessVisitor },
ArrowFunctionExpression: { enter: this.statelessVisitor },
ClassExpression: { enter: this.classVisitor },
ClassDeclaration: { enter: this.classVisitor },
VariableDeclaration: {
enter: (path, state: State) => {
const found = path.node.declarations.some((declaration) => {
if (
"name" in declaration.id &&
this.typedocManager.isReactComponent(declaration.id.name) &&
declaration.init
) {
const init = path.get("declarations")[0].get("init")
if (init.isMemberExpression()) {
state.foundDefinitions.add(
path as unknown as ComponentNodePath
)
return true
}
return false
}
})
if (found) {
return path.skip()
}
},
},
CallExpression: {
enter: (path, state: State) => {
const argument = path.get("arguments")[0]
if (!argument) {
return
}
if (utils.isReactForwardRefCall(path)) {
// If the the inner function was previously identified as a component
// replace it with the parent node
const inner = utils.resolveToValue(argument) as ComponentNodePath
state.foundDefinitions.delete(inner)
state.foundDefinitions.add(path)
// Do not traverse into arguments
return path.skip()
} else if (utils.isReactCreateClassCall(path)) {
const resolvedPath = utils.resolveToValue(argument)
if (resolvedPath.isObjectExpression()) {
state.foundDefinitions.add(resolvedPath)
}
// Do not traverse into arguments
return path.skip()
}
},
},
}),
state
)
return Array.from(state.foundDefinitions)
}
classVisitor(path: NodePath, state: State) {
if (utils.isReactComponentClass(path)) {
utils.normalizeClassDefinition(path)
state.foundDefinitions.add(path)
}
path.skip()
}
statelessVisitor(path: NodePath, state: State) {
if (utils.isStatelessComponent(path)) {
state.foundDefinitions.add(path)
}
path.skip()
}
}