diff --git a/packages/react-docgen-cli/src/commands/parse/command.ts b/packages/react-docgen-cli/src/commands/parse/command.ts index 6a53701d238..be172ee989d 100644 --- a/packages/react-docgen-cli/src/commands/parse/command.ts +++ b/packages/react-docgen-cli/src/commands/parse/command.ts @@ -79,18 +79,18 @@ program '--resolver ', `Built-in resolver config (${Object.values(ResolverConfigs).join( ', ', - )}), package name or path to a module that exports a resolver. Can also be used multiple times. When used, no default handlers will be added.`, + )}), package name or path to a trusted module that exports a resolver. Can also be used multiple times. When used, no default resolvers will be added.`, collect, defaultResolvers, ) .option( '--importer ', - 'Built-in importer name (fsImport, ignoreImporter), package name or path to a module that exports an importer.', + 'Built-in importer name (fsImporter, ignoreImporter), package name or path to a trusted module that exports an importer.', 'fsImporter', ) .option( '--handler ', - 'Comma separated list of handlers to use. Can also be used multiple times. When used, no default handlers will be added.', + 'Comma separated list of trusted handlers to use. Can also be used multiple times. When used, no default handlers will be added.', collect, defaultHandlers, ) @@ -109,7 +109,7 @@ program let finalIgnores = ignore; - // Push the default ignores unless the --no-default-ignore is set + // Push the default ignores unless the --no-default-ignores is set if (defaultIgnores === true && ignore !== defaultIgnoreGlobs) { finalIgnores.push(...defaultIgnoreGlobs); } else if (defaultIgnores === false && ignore === defaultIgnoreGlobs) { diff --git a/packages/website/src/app/docs/extending/architecture/page.mdx b/packages/website/src/app/docs/extending/architecture/page.mdx index 33deda4cec1..cae6c283443 100644 --- a/packages/website/src/app/docs/extending/architecture/page.mdx +++ b/packages/website/src/app/docs/extending/architecture/page.mdx @@ -1,5 +1,54 @@ -import ContentMissing from '@/components/ContentMissing'; - # Architecture - +react-docgen works in four steps: + +1. Parse the source code with Babel. +2. Build a [`FileState`](../reference/file-state) object around the AST. +3. Run a resolver to find React component definitions. +4. Run handlers on each component definition and build the final documentation. + +## Parsing + +`parse()` accepts source code and a config object. The source is parsed with +Babel. If no Babel config file is found, react-docgen applies default parser +plugins for JSX and either TypeScript or Flow. + +TypeScript syntax is enabled by default for filenames ending in `.ts`, `.tsx`, +`.mts`, or `.cts`. Other files use Flow syntax by default. + +## FileState + +`FileState` stores the AST, root program path, source code, parser options, and +the configured importer. It also provides helpers used by resolvers, handlers, +and utilities: + +- `file.traverse()` to walk the AST +- `file.import()` to resolve imported values +- `file.parse()` to parse another file with the same importer + +## Resolvers + +The resolver receives the `FileState` and returns component definition paths. + +The default parser config uses a chain of built-in resolvers to find exported +components and annotated components. Custom resolvers can replace that behavior. + +## Handlers + +Handlers receive a `DocumentationBuilder` and one component definition. Each +handler adds one kind of information, such as prop types, default props, display +name, or component methods. + +When all handlers have run, the builder produces a `Documentation` object. + +## Importers + +Importers are used when react-docgen needs to follow imports. The default +filesystem importer resolves imported source files from disk. The ignore +importer disables cross-file resolution. + +## Output + +The JavaScript API returns an array of `Documentation` objects. The CLI returns +a JSON object keyed by file path, where each value is an array of +`Documentation` objects for that file. diff --git a/packages/website/src/app/docs/extending/handler/page.mdx b/packages/website/src/app/docs/extending/handler/page.mdx index 94df3360b20..cd62a307fac 100644 --- a/packages/website/src/app/docs/extending/handler/page.mdx +++ b/packages/website/src/app/docs/extending/handler/page.mdx @@ -1,5 +1,69 @@ -import ContentMissing from '@/components/ContentMissing'; - # Handler - +Handlers extract information from every component found by the resolver. A +handler receives a `DocumentationBuilder` and the Babel `NodePath` for the +component definition. + +```ts +import type { Handler } from 'react-docgen'; + +const handler: Handler = (documentation, componentDefinition) => { + documentation.set('customValue', componentDefinition.node.type); +}; +``` + +## Signature + +```ts +type Handler = ( + documentation: DocumentationBuilder, + componentDefinition: NodePath, +) => void; +``` + +## Writing data + +Use the methods on `DocumentationBuilder` to add data. + +```ts +documentation.set('displayName', 'Button'); +documentation.addComposes('./otherProps'); + +const prop = documentation.getPropDescriptor('label'); +prop.description = 'Text shown inside the button.'; +``` + +For context-related handlers, use: + +```ts +documentation.getContextDescriptor('theme'); +documentation.getChildContextDescriptor('theme'); +``` + +## Using a custom handler + +Pass handlers directly to `parse()`. + +```ts +import { defaultHandlers, parse } from 'react-docgen'; +import myHandler from './myHandler.js'; + +const docs = parse(code, { + filename: 'Button.tsx', + handlers: [...defaultHandlers, myHandler], +}); +``` + +When `handlers` is provided, the default handlers are not added automatically. +Include `defaultHandlers` yourself if you want to extend the default behavior. + +## CLI + +The CLI can load a handler by built-in name, package name, or path. + +```shell filename="Terminal" +react-docgen --handler ./myHandler.js ./src/Button.tsx +``` + +Custom handler modules are executed as JavaScript in the current Node.js +process. Only load handlers you trust. diff --git a/packages/website/src/app/docs/extending/importer/page.mdx b/packages/website/src/app/docs/extending/importer/page.mdx index c594ba5b9f8..237262a6571 100644 --- a/packages/website/src/app/docs/extending/importer/page.mdx +++ b/packages/website/src/app/docs/extending/importer/page.mdx @@ -1,5 +1,65 @@ -import ContentMissing from '@/components/ContentMissing'; - # Importer - +Importers resolve imported values for handlers and utilities that need to follow +references across files. + +## Signature + +```ts +import type { Importer } from 'react-docgen'; + +const importer: Importer = (path, name, file) => { + return null; +}; +``` + +The importer receives: + +- `path`: the import or export declaration being resolved +- `name`: the imported or exported name to find +- `file`: the current [`FileState`](../reference/file-state) + +It returns a Babel `NodePath` for the resolved value, or `null` when the value +cannot be resolved. + +## Built-in importers + +### fsImporter + +The default importer. It resolves modules from disk using Node.js resolution +rules, reads the resolved file, parses it, and looks for the requested exported +value. + +It supports JavaScript and TypeScript source extensions, including `.js`, `.ts`, +`.tsx`, `.mjs`, `.cjs`, `.mts`, `.cts`, and `.jsx`. + +### ignoreImporter + +Always returns `null`. Use this when imported values should not be followed. + +## makeFsImporter + +`makeFsImporter()` creates a new filesystem importer. You can use it to provide +separate caches or a custom module lookup function. + +```ts +import { makeFsImporter, parse } from 'react-docgen'; + +const importer = makeFsImporter(); + +parse(code, { + filename: '/absolute/path/Button.tsx', + importer, +}); +``` + +## CLI + +The CLI can load an importer by built-in name, package name, or path. + +```shell filename="Terminal" +react-docgen --importer ignoreImporter ./src/Button.tsx +``` + +Custom importer modules are executed as JavaScript in the current Node.js +process. Only load importers you trust. diff --git a/packages/website/src/app/docs/extending/resolver/page.mdx b/packages/website/src/app/docs/extending/resolver/page.mdx index c3f67954fef..0bc478f7510 100644 --- a/packages/website/src/app/docs/extending/resolver/page.mdx +++ b/packages/website/src/app/docs/extending/resolver/page.mdx @@ -1,5 +1,68 @@ -import ContentMissing from '@/components/ContentMissing'; - # Resolver - +Resolvers decide which AST nodes are React component definitions. Handlers are +then run for each node returned by the resolver. + +## Signature + +A resolver can be a function: + +```ts +import type { ResolverFunction } from 'react-docgen'; + +const resolver: ResolverFunction = (file) => { + return []; +}; +``` + +Or an object with a `resolve()` method: + +```ts +import type { ResolverClass } from 'react-docgen'; + +const resolver: ResolverClass = { + resolve(file) { + return []; + }, +}; +``` + +Both forms return an array of `NodePath`. + +## FileState + +Resolvers receive a [`FileState`](../reference/file-state) object. The resolver +can inspect `file.path`, traverse the AST with `file.traverse()`, or parse and +import related files through the configured importer. + +## Built-in resolvers + +react-docgen exports these resolver classes through `builtinResolvers`: + +- `FindExportedDefinitionsResolver` +- `FindAllDefinitionsResolver` +- `FindAnnotatedDefinitionsResolver` +- `ChainResolver` + +## Using a custom resolver + +```ts +import { parse } from 'react-docgen'; +import myResolver from './myResolver.js'; + +const docs = parse(code, { + filename: 'Button.tsx', + resolver: myResolver, +}); +``` + +## CLI + +The CLI can load a resolver by built-in config name, package name, or path. + +```shell filename="Terminal" +react-docgen --resolver ./myResolver.js ./src/Button.tsx +``` + +Custom resolver modules are executed as JavaScript in the current Node.js +process. Only load resolvers you trust. diff --git a/packages/website/src/app/docs/getting-started/cli/page.mdx b/packages/website/src/app/docs/getting-started/cli/page.mdx index 7e6971c6151..f3f342279b4 100644 --- a/packages/website/src/app/docs/getting-started/cli/page.mdx +++ b/packages/website/src/app/docs/getting-started/cli/page.mdx @@ -1,4 +1,5 @@ import { Tabs } from 'nextra/components'; +import { Callout } from 'nextra/components'; # Getting started with the Command Line Interface (CLI) @@ -70,8 +71,9 @@ react component. The result will be printed to the `stdout` and in case there are errors or warnings while analyzing the file, these will be printed to `stderr`. -The result will be an array of [Documentation](../reference/documentation/basic.mdx) -objects, stringified to JSON. +The result will be an object keyed by file path. Each value will be an array of +[Documentation](../reference/documentation/basic.mdx) objects, stringified to +JSON. ```shell filename="Terminal" copy react-docgen ./src/components/Button.tsx @@ -98,3 +100,10 @@ react-docgen -o output.json ./src/components/Button.tsx The CLI supports a lot more advanced options and for a full list checkout the [reference page](../reference/cli) + + + Options that load custom modules, such as `--handler`, `--resolver`, and + `--importer`, execute trusted JavaScript code in the current Node.js process. + Do not pass values from untrusted input, user-provided configuration, or + unreviewed shared configuration to these options. + diff --git a/packages/website/src/app/docs/getting-started/nodejs/page.mdx b/packages/website/src/app/docs/getting-started/nodejs/page.mdx index a1a818a1aaf..7f40316585a 100644 --- a/packages/website/src/app/docs/getting-started/nodejs/page.mdx +++ b/packages/website/src/app/docs/getting-started/nodejs/page.mdx @@ -39,10 +39,10 @@ import { parse } from 'react-docgen'; const code = ` /** My first component */ -export default ({ name }: { name: string }) =>
{{name}}
; +export default ({ name }: { name: string }) =>
{name}
; `; -const documentation = parse(code); +const documentation = parse(code, { filename: 'index.tsx' }); console.log(documentation); ``` diff --git a/packages/website/src/app/docs/reference/_meta.js b/packages/website/src/app/docs/reference/_meta.js index 56e48080933..44b4d05160b 100644 --- a/packages/website/src/app/docs/reference/_meta.js +++ b/packages/website/src/app/docs/reference/_meta.js @@ -1,7 +1,9 @@ export default { + cli: 'Command Line Interface (CLI)', config: 'Config', api: 'API', documentation: 'Documentation', 'file-state': 'FileState', handlers: 'Handlers', + resolvers: 'Resolvers', }; diff --git a/packages/website/src/app/docs/reference/api/page.mdx b/packages/website/src/app/docs/reference/api/page.mdx index 778b85c92da..c97be78b51c 100644 --- a/packages/website/src/app/docs/reference/api/page.mdx +++ b/packages/website/src/app/docs/reference/api/page.mdx @@ -1,5 +1,102 @@ -import ContentMissing from '@/components/ContentMissing'; - # API - +The public API is exported from the `react-docgen` package. + +## parse + +```ts +function parse(src: Buffer | string, config?: Config): Documentation[]; +``` + +Parses JavaScript, TypeScript, JSX, or TSX source code and returns one +documentation object for each component found by the configured resolver. + +```ts filename="parse.ts" +import { parse } from 'react-docgen'; + +const docs = parse( + ` + /** Button component */ + export function Button({ label }: { label: string }) { + return ; + } +`, + { filename: 'Button.tsx' }, +); +``` + +The `filename` option is recommended. It lets react-docgen choose the right +parser mode for TypeScript files and lets the default importer resolve relative +imports. + +## builtinHandlers + +Contains all built-in handlers keyed by their export name. + +```ts +import { builtinHandlers } from 'react-docgen'; +``` + +These handlers can be passed to `parse()` with the `handlers` config option. + +## builtinImporters + +Contains the built-in importers: + +- `fsImporter` +- `ignoreImporter` + +```ts +import { builtinImporters } from 'react-docgen'; +``` + +## builtinResolvers + +Contains the built-in resolver classes: + +- `ChainResolver` +- `FindAllDefinitionsResolver` +- `FindAnnotatedDefinitionsResolver` +- `FindExportedDefinitionsResolver` + +```ts +import { builtinResolvers } from 'react-docgen'; +``` + +## defaultHandlers + +The list of handlers used when no `handlers` config option is provided. + +```ts +import { defaultHandlers } from 'react-docgen'; +``` + +## makeFsImporter + +Creates a filesystem importer. This is useful when you want separate importer +caches, or when you want to provide a custom module lookup function. + +```ts +import { makeFsImporter } from 'react-docgen'; + +const importer = makeFsImporter(); +``` + +## ERROR_CODES + +Contains the error code constants used by react-docgen errors: + +- `ERR_REACTDOCGEN_MISSING_DEFINITION` +- `ERR_REACTDOCGEN_MULTIPLE_DEFINITIONS` + +## utils + +Exports internal utility functions used by the built-in handlers and resolvers. +These are useful for custom extensions, but they are lower-level APIs than +handlers, importers, and resolvers. + +## Types + +The package also exports the TypeScript types used by the public API, including +`Config`, `Documentation`, `DocumentationBuilder`, `FileState`, `Handler`, +`Importer`, `Resolver`, `ResolverClass`, and `ResolverFunction`. diff --git a/packages/website/src/app/docs/reference/cli/page.mdx b/packages/website/src/app/docs/reference/cli/page.mdx index 75cad1ffb1d..deffca44b34 100644 --- a/packages/website/src/app/docs/reference/cli/page.mdx +++ b/packages/website/src/app/docs/reference/cli/page.mdx @@ -1,3 +1,5 @@ +import { Callout } from 'nextra/components'; + # Command Line Interface (CLI) Reference This is the complete reference of all available options for the CLI. A basic @@ -21,6 +23,14 @@ If you want to know more about glob patterns, check the documentation of ## Options + + The `--handler`, `--resolver`, and `--importer` options can load JavaScript + modules from paths or package names. These modules are executed as trusted + code with the same privileges as the current Node.js process. Do not populate + these options from untrusted input, user-provided configuration, or unreviewed + shared configuration. + + ### `--failOnWarning` **Default**: `false` @@ -65,12 +75,12 @@ The name of one of the A path can be specified to your handler. The path can be absolute (recommended) or relative and in the later case the path will be resolved from the current -working directory. +working directory. The handler module is executed as trusted JavaScript code. #### Module name of handler An npm module name can be specified. The default export of this module needs to -be the handler function. +be the handler function. Only use modules you trust. ### `--help` @@ -92,7 +102,7 @@ files. Possible values are: -#### Built-in resolvers +#### Built-in importers - `ignoreImporter` (disable importing) - `fsImporter` @@ -101,14 +111,14 @@ Possible values are: A path can be specified to your importer. The path can be absolute (recommended) or relative and in the later case the path will be resolved from the current -working directory. +working directory. The importer module is executed as trusted JavaScript code. #### Module name of importer An npm module name can be specified. The default export of this module needs to -be the importer. +be the importer. Only use modules you trust. -### `--no-default-ignore` +### `--no-default-ignores` **Default**: `false` @@ -122,7 +132,9 @@ Store extracted information in the specified file instead of printing to **Examples** -**TODO** +```shell filename="Terminal" +react-docgen -o output.json ./src/components/Button.tsx +``` ### `--pretty` @@ -133,7 +145,7 @@ newlines. ### `--resolver ` -**Default**: `["find-exported-component", "find-all-annotated-components"]` +**Default**: `["find-exported-component"]` Allows to specify the resolvers that find react components in the code. When multiple resolvers are specified they are all executed in order and all found @@ -160,9 +172,9 @@ Possible values are: A path can be specified to your resolver. The path can be absolute (recommended) or relative and in the later case the path will be resolved from the current -working directory. +working directory. The resolver module is executed as trusted JavaScript code. #### Module name of resolver An npm module name can be specified. The default export of this module needs to -be an instance of the resolver. +be an instance of the resolver. Only use modules you trust. diff --git a/packages/website/src/app/docs/reference/config/page.mdx b/packages/website/src/app/docs/reference/config/page.mdx index 78005f1491a..272e2bb7fbb 100644 --- a/packages/website/src/app/docs/reference/config/page.mdx +++ b/packages/website/src/app/docs/reference/config/page.mdx @@ -1,5 +1,72 @@ -import ContentMissing from '@/components/ContentMissing'; - # Config - +The `Config` object controls how `parse()` finds components, resolves imports, +and extracts documentation. + +```ts +interface Config { + handlers?: Handler[]; + importer?: Importer; + resolver?: Resolver; + filename?: string; + babelOptions?: TransformOptions; +} +``` + +## handlers + +**Default**: all built-in handlers + +An array of handlers that extract information from every component found by the +resolver. If this option is provided, only the handlers in the array are used. + +```ts +import { builtinHandlers, parse } from 'react-docgen'; + +parse(code, { + handlers: [builtinHandlers.componentDocblockHandler], +}); +``` + +## importer + +**Default**: `fsImporter` + +The importer used when react-docgen needs to follow imports while resolving prop +types or values. The default importer reads files from disk using Node.js +resolution rules. + +Use `builtinImporters.ignoreImporter` when imported values should not be +resolved. + +## resolver + +**Default**: exported components and annotated components + +The resolver decides which AST nodes are React component definitions. The +default resolver combines: + +- `FindExportedDefinitionsResolver` with `limit: 1` +- `FindAnnotatedDefinitionsResolver` + +This means `parse()` finds the exported component in a file and components +marked with the default `@component` annotation. + +## filename + +Shortcut for `babelOptions.filename`. + +Set this to the absolute path of the file being parsed when possible. A relative +path is resolved by Babel relative to `babelOptions.cwd`. + +The filename affects parser behavior. Files ending in `.ts`, `.tsx`, `.mts`, or +`.cts` use the TypeScript parser plugin by default. Other files use the Flow +parser plugin by default. + +## babelOptions + +Options passed to Babel while parsing. If Babel finds a filesystem config file, +react-docgen uses that config instead of applying its default parser plugins. + +The `estree` parser plugin is always removed because react-docgen expects Babel +AST nodes. diff --git a/packages/website/src/app/docs/reference/documentation/basic/page.mdx b/packages/website/src/app/docs/reference/documentation/basic/page.mdx index 9350d53d78e..783a65584c3 100644 --- a/packages/website/src/app/docs/reference/documentation/basic/page.mdx +++ b/packages/website/src/app/docs/reference/documentation/basic/page.mdx @@ -1,5 +1,3 @@ -import { Callout } from 'nextra/components'; - # Basic This describes the return value of react-docgen either from the `parse()` method @@ -24,6 +22,11 @@ interface Documentation { ### childContext +**Type**: `Record | undefined` + +If the [`childContextTypeHandler`](../handlers/child-context-type-handler) is +active this property will contain legacy React child context descriptors. + ### composes **Type**: `string[] | undefined` @@ -31,12 +34,19 @@ interface Documentation { If the props of the React component are composed of props which react-docgen was unable to resolve, the name of types will be listed here. - - Create examples - +```json filename="JSON" +{ + "composes": ["./sharedProps"] +} +``` ### context +**Type**: `Record | undefined` + +If the [`contextTypeHandler`](../handlers/context-type-handler) is active this +property will contain legacy React context descriptors. + ### description **Type**: `string | undefined` @@ -53,4 +63,71 @@ property will be set to the name of the React component. ### methods +**Type**: `MethodDescriptor[] | undefined` + +If the [`componentMethodsHandler`](../handlers/component-methods-handler) or +`componentMethodsJsDocHandler` is active this property will contain documented +component methods. + ### props + +**Type**: `Record | undefined` + +Contains prop descriptors collected by handlers such as +[`propTypeHandler`](../handlers/prop-type-handler), +[`codeTypeHandler`](../handlers/code-type-handler), +[`propDocblockHandler`](../handlers/prop-docblock-handler), and +[`defaultPropsHandler`](../handlers/default-props-handler). + +## PropDescriptor + +```ts +interface PropDescriptor { + type?: PropTypeDescriptor; + flowType?: TypeDescriptor; + tsType?: TypeDescriptor; + required?: boolean; + defaultValue?: DefaultValueDescriptor; + description?: string; +} +``` + +### type + +**Type**: `PropTypeDescriptor | undefined` + +Set by PropTypes-based handlers. See the [PropTypes documentation](./prop-types) +for details. + +### flowType + +**Type**: `TypeDescriptor | undefined` + +Set by [`codeTypeHandler`](../handlers/code-type-handler) when Flow props are +found. + +### tsType + +**Type**: `TypeDescriptor | undefined` + +Set by [`codeTypeHandler`](../handlers/code-type-handler) when TypeScript props +are found. + +### required + +**Type**: `boolean | undefined` + +Indicates whether the prop is required according to the handler that found it. + +### defaultValue + +**Type**: `DefaultValueDescriptor | undefined` + +Set by [`defaultPropsHandler`](../handlers/default-props-handler) when a default +value is found. + +### description + +**Type**: `string | undefined` + +Set by docblock handlers or by comments in Flow and TypeScript prop definitions. diff --git a/packages/website/src/app/docs/reference/documentation/flow/page.mdx b/packages/website/src/app/docs/reference/documentation/flow/page.mdx index a28de0100db..24c99a5a28f 100644 --- a/packages/website/src/app/docs/reference/documentation/flow/page.mdx +++ b/packages/website/src/app/docs/reference/documentation/flow/page.mdx @@ -1,5 +1,69 @@ -import ContentMissing from '@/components/ContentMissing'; - # Flow - +When [`codeTypeHandler`](../handlers/code-type-handler) extracts Flow props, the +type information is written to `flowType`. + +```ts +interface PropDescriptor { + flowType?: TypeDescriptor; + required?: boolean; + description?: string; + defaultValue?: DefaultValueDescriptor; +} +``` + +## Example + +```js filename="component.js" +type Props = { + label: string, + disabled?: boolean, + size: 'small' | 'large', +}; + +export function Button(props: Props) { + return ; +} +``` + +```json filename="JSON" +{ + "label": { + "required": true, + "flowType": { + "name": "string" + } + }, + "disabled": { + "required": false, + "flowType": { + "name": "boolean" + } + }, + "size": { + "required": true, + "flowType": { + "name": "union", + "raw": "'small' | 'large'", + "elements": [ + { "name": "literal", "value": "'small'" }, + { "name": "literal", "value": "'large'" } + ] + } + } +} +``` + +## Type descriptors + +Most Flow values are represented with the shared +`TypeDescriptor` shape. Common descriptors are: + +- Simple descriptors, such as `{ "name": "string" }` +- Literal descriptors, such as `{ "name": "literal", "value": "'small'" }` +- Element descriptors, such as unions, intersections, arrays, and tuples +- Function signatures with `type: "function"` +- Object signatures with `type: "object"` + +Files that are not parsed as TypeScript use Flow syntax by default unless a +Babel configuration changes the parser plugins. diff --git a/packages/website/src/app/docs/reference/documentation/prop-types/page.mdx b/packages/website/src/app/docs/reference/documentation/prop-types/page.mdx index 35aa67af956..1404db187c6 100644 --- a/packages/website/src/app/docs/reference/documentation/prop-types/page.mdx +++ b/packages/website/src/app/docs/reference/documentation/prop-types/page.mdx @@ -1,5 +1,107 @@ -import ContentMissing from '@/components/ContentMissing'; - # PropTypes - +When [`propTypeHandler`](../handlers/prop-type-handler) is active, PropTypes +declarations are written to the `type` field of a prop, context, or child +context descriptor. + +```ts +interface PropTypeDescriptor { + name: + | 'any' + | 'array' + | 'arrayOf' + | 'bool' + | 'custom' + | 'element' + | 'elementType' + | 'enum' + | 'exact' + | 'func' + | 'instanceOf' + | 'node' + | 'number' + | 'object' + | 'objectOf' + | 'shape' + | 'string' + | 'symbol' + | 'union'; + value?: unknown; + raw?: string; + computed?: boolean; + description?: string; + required?: boolean; +} +``` + +## Simple types + +```ts filename="component.tsx" +Button.propTypes = { + label: PropTypes.string, + disabled: PropTypes.bool.isRequired, +}; +``` + +```json filename="JSON" +{ + "label": { + "type": { + "name": "string" + }, + "required": false + }, + "disabled": { + "type": { + "name": "bool" + }, + "required": true + } +} +``` + +## Complex types + +Some PropTypes include nested values. For example, `oneOf()` produces an `enum`, +`oneOfType()` produces a `union`, and `shape()` or `exact()` produce nested +descriptors in `value`. + +```ts filename="component.tsx" +Button.propTypes = { + size: PropTypes.oneOf(['small', 'large']), + icon: PropTypes.shape({ + name: PropTypes.string.isRequired, + }), +}; +``` + +```json filename="JSON" +{ + "size": { + "type": { + "name": "enum", + "value": [ + { "value": "'small'", "computed": false }, + { "value": "'large'", "computed": false } + ] + }, + "required": false + }, + "icon": { + "type": { + "name": "shape", + "value": { + "name": { + "name": "string", + "required": true + } + } + }, + "required": false + } +} +``` + +If a PropTypes expression cannot be recognized as a built-in PropType, the +handler records it as a `custom` type and stores the printed expression in +`raw`. diff --git a/packages/website/src/app/docs/reference/documentation/typescript/page.mdx b/packages/website/src/app/docs/reference/documentation/typescript/page.mdx index 4c119172ca2..4c35af1057d 100644 --- a/packages/website/src/app/docs/reference/documentation/typescript/page.mdx +++ b/packages/website/src/app/docs/reference/documentation/typescript/page.mdx @@ -1,5 +1,69 @@ -import ContentMissing from '@/components/ContentMissing'; - # TypeScript - +When [`codeTypeHandler`](../handlers/code-type-handler) extracts TypeScript +props, the type information is written to `tsType`. + +```ts +interface PropDescriptor { + tsType?: TypeDescriptor; + required?: boolean; + description?: string; + defaultValue?: DefaultValueDescriptor; +} +``` + +## Example + +```ts filename="component.tsx" +interface Props { + label: string; + disabled?: boolean; + size: 'small' | 'large'; +} + +export function Button(props: Props) { + return ; +} +``` + +```json filename="JSON" +{ + "label": { + "required": true, + "tsType": { + "name": "string" + } + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + } + }, + "size": { + "required": true, + "tsType": { + "name": "union", + "raw": "'small' | 'large'", + "elements": [ + { "name": "literal", "value": "'small'" }, + { "name": "literal", "value": "'large'" } + ] + } + } +} +``` + +## Type descriptors + +Most TypeScript values are represented with the shared +`TypeDescriptor` shape. Common descriptors are: + +- Simple descriptors, such as `{ "name": "string" }` +- Literal descriptors, such as `{ "name": "literal", "value": "'small'" }` +- Element descriptors, such as unions, intersections, arrays, and tuples +- Function signatures with `type: "function"` +- Object signatures with `type: "object"` + +Set `filename` to a TypeScript extension, such as `.ts` or `.tsx`, when calling +`parse()` so the default parser enables TypeScript syntax. diff --git a/packages/website/src/app/docs/reference/handlers/child-context-type-handler/page.mdx b/packages/website/src/app/docs/reference/handlers/child-context-type-handler/page.mdx index 8ce4e4e4648..2fec6ab31c2 100644 --- a/packages/website/src/app/docs/reference/handlers/child-context-type-handler/page.mdx +++ b/packages/website/src/app/docs/reference/handlers/child-context-type-handler/page.mdx @@ -1,5 +1,68 @@ -import ContentMissing from '@/components/ContentMissing'; - # childContextTypeHandler - +Finds legacy React `childContextTypes` declarations and adds them to the +documentation under the `childContext` field. + +It uses the same PropTypes parsing logic as +[`propTypeHandler`](./prop-type-handler.mdx), but writes descriptors to +`childContext` instead of `props`. + +## Examples + +When the `childContextTypeHandler` is active either of these components will +result in the output below. + +```ts {1,4-7} filename="component.tsx" +import PropTypes from 'prop-types'; + +class MyComponent extends React.Component { + static childContextTypes = { + theme: PropTypes.string, + disabled: PropTypes.bool.isRequired, + }; + render() { + return
; + } +} +``` + +```ts {1,7-10} filename="component.tsx" +import PropTypes from 'prop-types'; + +function MyComponent() { + return
; +} + +MyComponent.childContextTypes = { + theme: PropTypes.string, + disabled: PropTypes.bool.isRequired, +}; +``` + +## Output + +```json {3-16} filename="JSON" +[ + { + "childContext": { + "theme": { + "type": { + "name": "string" + }, + "required": false + }, + "disabled": { + "type": { + "name": "bool" + }, + "required": true + } + } + } +] +``` + +## Source + +- [Implementation](https://github.com/reactjs/react-docgen/blob/main/packages/react-docgen/src/handlers/propTypeHandler.ts) +- [Tests](https://github.com/reactjs/react-docgen/blob/main/packages/react-docgen/src/handlers/__tests__/propTypeHandler-test.ts) diff --git a/packages/website/src/app/docs/reference/handlers/code-type-handler/page.mdx b/packages/website/src/app/docs/reference/handlers/code-type-handler/page.mdx index 61f59a88cfb..4d9452f7739 100644 --- a/packages/website/src/app/docs/reference/handlers/code-type-handler/page.mdx +++ b/packages/website/src/app/docs/reference/handlers/code-type-handler/page.mdx @@ -1,5 +1,71 @@ -import ContentMissing from '@/components/ContentMissing'; - # codeTypeHandler - +Extracts Flow and TypeScript prop types from component type annotations. + +The handler writes Flow types to `flowType` and TypeScript types to `tsType`. It +also sets `required` and extracts prop descriptions from comments inside the +type definition. + +Supported component shapes include: + +- Class components with generic props, such as `React.Component` +- Class components with a typed `props` member +- Function components with a typed first argument +- `React.FC`, `React.FunctionComponent`, `React.VFC`, and + `React.VoidFunctionComponent` variable annotations +- `forwardRef()` calls with typed props + +## Examples + +When the `codeTypeHandler` is active this TypeScript component will result in +the output below. + +```ts {3-7,9} filename="component.tsx" +import React from 'react'; + +interface Props { + /** Text shown inside the button. */ + label: string; + disabled?: boolean; +} + +export function Button(props: Props) { + return ; +} +``` + +## Output + +```json {5-9,12-16} filename="JSON" +[ + { + "props": { + "label": { + "required": true, + "tsType": { + "name": "string" + }, + "description": "Text shown inside the button." + }, + "disabled": { + "required": false, + "tsType": { + "name": "boolean" + }, + "description": "" + } + } + } +] +``` + +## Notes + +Intersection types are expanded when their members can be resolved. Union types +at the props-object level are not expanded because the documentation format does +not describe alternate prop object shapes. + +## Source + +- [Implementation](https://github.com/reactjs/react-docgen/blob/main/packages/react-docgen/src/handlers/codeTypeHandler.ts) +- [Tests](https://github.com/reactjs/react-docgen/blob/main/packages/react-docgen/src/handlers/__tests__/codeTypeHandler-test.ts) diff --git a/packages/website/src/app/docs/reference/handlers/component-docblock-handler/page.mdx b/packages/website/src/app/docs/reference/handlers/component-docblock-handler/page.mdx index b22d14a3f42..b818ea42005 100644 --- a/packages/website/src/app/docs/reference/handlers/component-docblock-handler/page.mdx +++ b/packages/website/src/app/docs/reference/handlers/component-docblock-handler/page.mdx @@ -24,3 +24,8 @@ result in the output below } ] ``` + +## Source + +- [Implementation](https://github.com/reactjs/react-docgen/blob/main/packages/react-docgen/src/handlers/componentDocblockHandler.ts) +- [Tests](https://github.com/reactjs/react-docgen/blob/main/packages/react-docgen/src/handlers/__tests__/componentDocblockHandler-test.ts) diff --git a/packages/website/src/app/docs/reference/handlers/component-methods-handler/page.mdx b/packages/website/src/app/docs/reference/handlers/component-methods-handler/page.mdx index 08c1ade98ad..9c0bdeed023 100644 --- a/packages/website/src/app/docs/reference/handlers/component-methods-handler/page.mdx +++ b/packages/website/src/app/docs/reference/handlers/component-methods-handler/page.mdx @@ -91,3 +91,8 @@ forwardRef(function MyComponent(_, ref) { } ] ``` + +## Source + +- [Implementation](https://github.com/reactjs/react-docgen/blob/main/packages/react-docgen/src/handlers/componentMethodsHandler.ts) +- [Tests](https://github.com/reactjs/react-docgen/blob/main/packages/react-docgen/src/handlers/__tests__/componentMethodsHandler-test.ts) diff --git a/packages/website/src/app/docs/reference/handlers/context-type-handler/page.mdx b/packages/website/src/app/docs/reference/handlers/context-type-handler/page.mdx index 3956425b740..e37cca7030a 100644 --- a/packages/website/src/app/docs/reference/handlers/context-type-handler/page.mdx +++ b/packages/website/src/app/docs/reference/handlers/context-type-handler/page.mdx @@ -1,5 +1,68 @@ -import ContentMissing from '@/components/ContentMissing'; - # contextTypeHandler - +Finds legacy React `contextTypes` declarations and adds them to the +documentation under the `context` field. + +It uses the same PropTypes parsing logic as +[`propTypeHandler`](./prop-type-handler.mdx), but writes descriptors to +`context` instead of `props`. + +## Examples + +When the `contextTypeHandler` is active either of these components will result +in the output below. + +```ts {1,4-7} filename="component.tsx" +import PropTypes from 'prop-types'; + +class MyComponent extends React.Component { + static contextTypes = { + theme: PropTypes.string, + disabled: PropTypes.bool.isRequired, + }; + render() { + return
; + } +} +``` + +```ts {1,7-10} filename="component.tsx" +import PropTypes from 'prop-types'; + +function MyComponent() { + return
; +} + +MyComponent.contextTypes = { + theme: PropTypes.string, + disabled: PropTypes.bool.isRequired, +}; +``` + +## Output + +```json {3-16} filename="JSON" +[ + { + "context": { + "theme": { + "type": { + "name": "string" + }, + "required": false + }, + "disabled": { + "type": { + "name": "bool" + }, + "required": true + } + } + } +] +``` + +## Source + +- [Implementation](https://github.com/reactjs/react-docgen/blob/main/packages/react-docgen/src/handlers/propTypeHandler.ts) +- [Tests](https://github.com/reactjs/react-docgen/blob/main/packages/react-docgen/src/handlers/__tests__/propTypeHandler-test.ts) diff --git a/packages/website/src/app/docs/reference/handlers/default-props-handler/page.mdx b/packages/website/src/app/docs/reference/handlers/default-props-handler/page.mdx index f083a9bc283..a13e85b1dad 100644 --- a/packages/website/src/app/docs/reference/handlers/default-props-handler/page.mdx +++ b/packages/website/src/app/docs/reference/handlers/default-props-handler/page.mdx @@ -65,3 +65,8 @@ MyComponent.defaultProps = { } ] ``` + +## Source + +- [Implementation](https://github.com/reactjs/react-docgen/blob/main/packages/react-docgen/src/handlers/defaultPropsHandler.ts) +- [Tests](https://github.com/reactjs/react-docgen/blob/main/packages/react-docgen/src/handlers/__tests__/defaultPropsHandler-test.ts) diff --git a/packages/website/src/app/docs/reference/handlers/display-name-handler/page.mdx b/packages/website/src/app/docs/reference/handlers/display-name-handler/page.mdx index 59280d97225..ca02ef097b5 100644 --- a/packages/website/src/app/docs/reference/handlers/display-name-handler/page.mdx +++ b/packages/website/src/app/docs/reference/handlers/display-name-handler/page.mdx @@ -68,3 +68,8 @@ const DisplayName = () =>
; } ] ``` + +## Source + +- [Implementation](https://github.com/reactjs/react-docgen/blob/main/packages/react-docgen/src/handlers/displayNameHandler.ts) +- [Tests](https://github.com/reactjs/react-docgen/blob/main/packages/react-docgen/src/handlers/__tests__/displayNameHandler-test.ts) diff --git a/packages/website/src/app/docs/reference/handlers/prop-docblock-handler/page.mdx b/packages/website/src/app/docs/reference/handlers/prop-docblock-handler/page.mdx index dd64bfa5aad..9f22c7c47b6 100644 --- a/packages/website/src/app/docs/reference/handlers/prop-docblock-handler/page.mdx +++ b/packages/website/src/app/docs/reference/handlers/prop-docblock-handler/page.mdx @@ -68,3 +68,8 @@ MyComponent.propTypes = { } ] ``` + +## Source + +- [Implementation](https://github.com/reactjs/react-docgen/blob/main/packages/react-docgen/src/handlers/propDocblockHandler.ts) +- [Tests](https://github.com/reactjs/react-docgen/blob/main/packages/react-docgen/src/handlers/__tests__/propDocblockHandler-test.ts) diff --git a/packages/website/src/app/docs/reference/handlers/prop-type-composition-handler/page.mdx b/packages/website/src/app/docs/reference/handlers/prop-type-composition-handler/page.mdx index 4773c98f33e..c6831dd9bfc 100644 --- a/packages/website/src/app/docs/reference/handlers/prop-type-composition-handler/page.mdx +++ b/packages/website/src/app/docs/reference/handlers/prop-type-composition-handler/page.mdx @@ -59,3 +59,8 @@ Button.propTypes = { } ] ``` + +## Source + +- [Implementation](https://github.com/reactjs/react-docgen/blob/main/packages/react-docgen/src/handlers/propTypeCompositionHandler.ts) +- [Tests](https://github.com/reactjs/react-docgen/blob/main/packages/react-docgen/src/handlers/__tests__/propTypeCompositionHandler-test.ts) diff --git a/packages/website/src/app/docs/reference/handlers/prop-type-handler/page.mdx b/packages/website/src/app/docs/reference/handlers/prop-type-handler/page.mdx index c5184f6a35c..ff45c5d6578 100644 --- a/packages/website/src/app/docs/reference/handlers/prop-type-handler/page.mdx +++ b/packages/website/src/app/docs/reference/handlers/prop-type-handler/page.mdx @@ -75,3 +75,8 @@ MyComponent.propTypes = { } ] ``` + +## Source + +- [Implementation](https://github.com/reactjs/react-docgen/blob/main/packages/react-docgen/src/handlers/propTypeHandler.ts) +- [Tests](https://github.com/reactjs/react-docgen/blob/main/packages/react-docgen/src/handlers/__tests__/propTypeHandler-test.ts) diff --git a/packages/website/src/app/docs/reference/resolvers/_meta.js b/packages/website/src/app/docs/reference/resolvers/_meta.js new file mode 100644 index 00000000000..71b9b98b693 --- /dev/null +++ b/packages/website/src/app/docs/reference/resolvers/_meta.js @@ -0,0 +1,6 @@ +export default { + 'chain-resolver': 'Chain', + 'find-all-definitions-resolver': 'FindAllDefinitions', + 'find-annotated-definitions-resolver': 'FindAnnotatedDefinitions', + 'find-exported-definitions-resolver': 'FindExportedDefinitions', +}; diff --git a/packages/website/src/app/docs/reference/resolvers/chain-resolver/page.mdx b/packages/website/src/app/docs/reference/resolvers/chain-resolver/page.mdx new file mode 100644 index 00000000000..d4da4200fa8 --- /dev/null +++ b/packages/website/src/app/docs/reference/resolvers/chain-resolver/page.mdx @@ -0,0 +1,56 @@ +# ChainResolver + +Runs multiple resolvers as one resolver. + +This is useful when more than one strategy should be used to find components in +the same file. + +## Options + +```ts +new ChainResolver(resolvers, { + chainingLogic?: ChainResolver.Logic; +}); +``` + +### resolvers + +**Type**: `Resolver[]` + +The resolvers to run. + +### chainingLogic + +**Default**: `ChainResolver.Logic.ALL` + +Controls how resolver results are combined. + +- `ChainResolver.Logic.ALL` runs every resolver and returns all unique + components. +- `ChainResolver.Logic.FIRST_FOUND` runs resolvers in order and returns the + first non-empty result. + +## Example + +```ts filename="resolver.ts" +import { builtinResolvers } from 'react-docgen'; + +const { + ChainResolver, + FindAnnotatedDefinitionsResolver, + FindExportedDefinitionsResolver, +} = builtinResolvers; + +const resolver = new ChainResolver( + [ + new FindExportedDefinitionsResolver({ limit: 1 }), + new FindAnnotatedDefinitionsResolver(), + ], + { chainingLogic: ChainResolver.Logic.ALL }, +); +``` + +## Source + +- [Implementation](https://github.com/reactjs/react-docgen/blob/main/packages/react-docgen/src/resolver/ChainResolver.ts) +- [Tests](https://github.com/reactjs/react-docgen/blob/main/packages/react-docgen/src/resolver/__tests__/ChainResolver-test.ts) diff --git a/packages/website/src/app/docs/reference/resolvers/find-all-definitions-resolver/page.mdx b/packages/website/src/app/docs/reference/resolvers/find-all-definitions-resolver/page.mdx new file mode 100644 index 00000000000..c4e158ee3b4 --- /dev/null +++ b/packages/website/src/app/docs/reference/resolvers/find-all-definitions-resolver/page.mdx @@ -0,0 +1,38 @@ +# FindAllDefinitionsResolver + +Finds all React component definitions in a file, regardless of whether they are +exported. + +It detects: + +- `React.createClass()` calls +- Class components extending `React.Component` or `React.PureComponent` +- Classes with a `render()` method +- Function components that return JSX or `React.createElement()` +- `React.forwardRef()` components + +The resolver can follow imported references with the configured importer. + +## Examples + +When the `FindAllDefinitionsResolver` is active, both components in this file +are returned. + +```ts {3-5,7-9} filename="component.tsx" +import React from 'react'; + +function Button() { + return