├── .changeset ├── README.md ├── config.json └── plenty-coins-compare.md ├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ ├── build.yml │ ├── changesets.yml │ ├── codeql-analysis.yml │ ├── lock.yml │ └── ossar-analysis.yml ├── .gitignore ├── .gitpod.yml ├── .npmrc ├── .prettierignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── benchmark ├── index.mjs └── package.json ├── eslint.config.mjs ├── nx.json ├── package.json ├── packages ├── react-docgen-cli │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── cli.ts │ │ ├── commands │ │ │ └── parse │ │ │ │ ├── command.ts │ │ │ │ ├── options │ │ │ │ ├── loadOptions.ts │ │ │ │ ├── loadReactDocgenPlugin.ts │ │ │ │ └── loadResolvers.ts │ │ │ │ └── output │ │ │ │ ├── outputError.ts │ │ │ │ └── outputResult.ts │ │ └── utils │ │ │ └── importFile.ts │ ├── tests │ │ └── integration │ │ │ ├── __fixtures__ │ │ │ ├── basic │ │ │ │ ├── Component.js │ │ │ │ └── NoComponent.js │ │ │ ├── custom-handler-cjs │ │ │ │ ├── Component.js │ │ │ │ └── handler.cjs │ │ │ ├── custom-handler-esm │ │ │ │ ├── Component.js │ │ │ │ └── handler.mjs │ │ │ ├── custom-handler-npm │ │ │ │ ├── Component.js │ │ │ │ └── node_modules │ │ │ │ │ └── test-react-docgen-handler │ │ │ │ │ ├── index.js │ │ │ │ │ └── package.json │ │ │ ├── custom-importer-cjs │ │ │ │ ├── Component.js │ │ │ │ ├── custom-importer-esm │ │ │ │ │ ├── Component.js │ │ │ │ │ └── importer.mjs │ │ │ │ ├── custom-importer-npm │ │ │ │ │ ├── Component.js │ │ │ │ │ └── node_modules │ │ │ │ │ │ └── test-react-docgen-importer │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ └── package.json │ │ │ │ └── importer.cjs │ │ │ ├── custom-importer-esm │ │ │ │ ├── Component.js │ │ │ │ └── importer.mjs │ │ │ ├── custom-importer-npm │ │ │ │ ├── Component.js │ │ │ │ └── node_modules │ │ │ │ │ └── test-react-docgen-importer │ │ │ │ │ ├── index.js │ │ │ │ │ └── package.json │ │ │ ├── custom-resolver-class │ │ │ │ ├── Component.js │ │ │ │ ├── node_modules │ │ │ │ │ ├── test-react-docgen-resolver-class │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ └── package.json │ │ │ │ │ └── test-react-docgen-resolver │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ └── package.json │ │ │ │ ├── resolver.cjs │ │ │ │ └── resolver.mjs │ │ │ ├── custom-resolver-function │ │ │ │ ├── Component.js │ │ │ │ ├── node_modules │ │ │ │ │ └── test-react-docgen-resolver │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ └── package.json │ │ │ │ ├── resolver.cjs │ │ │ │ └── resolver.mjs │ │ │ ├── ignore │ │ │ │ ├── __mocks__ │ │ │ │ │ └── MockComponent.js │ │ │ │ ├── __tests__ │ │ │ │ │ └── TestComponent.js │ │ │ │ ├── foo │ │ │ │ │ └── FooComponent.js │ │ │ │ └── node_modules │ │ │ │ │ └── NodeModulesComponent.js │ │ │ ├── multiple │ │ │ │ └── MultipleComponents.js │ │ │ └── syntax-error │ │ │ │ └── SyntaxError.js │ │ │ ├── cli-test.ts │ │ │ ├── handler-test.ts │ │ │ ├── importer-test.ts │ │ │ ├── resolver-test.ts │ │ │ └── utils │ │ │ └── withFixture.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── react-docgen │ ├── CHANGELOG.md │ ├── package.json │ ├── src │ │ ├── Documentation.ts │ │ ├── FileState.ts │ │ ├── __mocks__ │ │ │ ├── Documentation.ts │ │ │ └── FileState.ts │ │ ├── __tests__ │ │ │ ├── __snapshots__ │ │ │ │ └── main-test.ts.snap │ │ │ ├── main-test.ts │ │ │ └── parse-test.ts │ │ ├── babelParser.ts │ │ ├── config.ts │ │ ├── error.ts │ │ ├── handlers │ │ │ ├── __tests__ │ │ │ │ ├── __snapshots__ │ │ │ │ │ ├── codeTypeHandler-test.ts.snap │ │ │ │ │ ├── componentMethodsHandler-test.ts.snap │ │ │ │ │ ├── componentMethodsJsDocHandler-test.ts.snap │ │ │ │ │ ├── defaultPropsHandler-test.ts.snap │ │ │ │ │ └── propTypeHandler-test.ts.snap │ │ │ │ ├── codeTypeHandler-test.ts │ │ │ │ ├── componentDocblockHandler-test.ts │ │ │ │ ├── componentMethodsHandler-test.ts │ │ │ │ ├── componentMethodsJsDocHandler-test.ts │ │ │ │ ├── defaultPropsHandler-test.ts │ │ │ │ ├── displayNameHandler-test.ts │ │ │ │ ├── propDocblockHandler-test.ts │ │ │ │ ├── propTypeCompositionHandler-test.ts │ │ │ │ └── propTypeHandler-test.ts │ │ │ ├── codeTypeHandler.ts │ │ │ ├── componentDocblockHandler.ts │ │ │ ├── componentMethodsHandler.ts │ │ │ ├── componentMethodsJsDocHandler.ts │ │ │ ├── defaultPropsHandler.ts │ │ │ ├── displayNameHandler.ts │ │ │ ├── index.ts │ │ │ ├── propDocblockHandler.ts │ │ │ ├── propTypeCompositionHandler.ts │ │ │ └── propTypeHandler.ts │ │ ├── importer │ │ │ ├── fsImporter.ts │ │ │ ├── ignoreImporter.ts │ │ │ ├── index.ts │ │ │ ├── makeFsImporter.ts │ │ │ └── makeIgnoreImporter.ts │ │ ├── main.ts │ │ ├── parse.ts │ │ ├── resolver │ │ │ ├── ChainResolver.ts │ │ │ ├── FindAllDefinitionsResolver.ts │ │ │ ├── FindAnnotatedDefinitionsResolver.ts │ │ │ ├── FindExportedDefinitionsResolver.ts │ │ │ ├── __tests__ │ │ │ │ ├── ChainResolver-test.ts │ │ │ │ ├── FindAllDefinitionsResolver-test.ts │ │ │ │ ├── FindAnnotatedDefinitionsResolver-test.ts │ │ │ │ ├── FindExportedDefinitionsResolver-test.ts │ │ │ │ └── __snapshots__ │ │ │ │ │ ├── FindAnnotatedDefinitionsResolver-test.ts.snap │ │ │ │ │ └── FindExportedDefinitionsResolver-test.ts.snap │ │ │ ├── index.ts │ │ │ └── utils │ │ │ │ ├── __tests__ │ │ │ │ └── runResolver-test.ts │ │ │ │ └── runResolver.ts │ │ └── utils │ │ │ ├── __tests__ │ │ │ ├── __snapshots__ │ │ │ │ ├── getClassMemberValuePath-test.ts.snap │ │ │ │ ├── getFlowType-test.ts.snap │ │ │ │ ├── getMemberExpressionRoot-test.ts.snap │ │ │ │ ├── getMembers-test.ts.snap │ │ │ │ ├── getMethodDocumentation-test.ts.snap │ │ │ │ ├── getNameOrValue-test.ts.snap │ │ │ │ ├── getPropType-test.ts.snap │ │ │ │ ├── getTSType-test.ts.snap │ │ │ │ ├── getTypeFromReactComponent-test.ts.snap │ │ │ │ ├── getTypeParameters-test.ts.snap │ │ │ │ ├── parseJsDoc-test.ts.snap │ │ │ │ ├── printValue-test.ts.snap │ │ │ │ ├── resolveFunctionDefinitionToReturnValue-test.ts.snap │ │ │ │ ├── resolveGenericTypeAnnotations-test.ts.snap │ │ │ │ ├── resolveHOC-test.ts.snap │ │ │ │ ├── resolveObjectKeysToArray-test.ts.snap │ │ │ │ ├── resolveObjectPatternPropertyToValue-test.ts.snap │ │ │ │ ├── resolveObjectValuesToArray-test.ts.snap │ │ │ │ ├── resolveToValue-test.ts.snap │ │ │ │ └── unwrapBuiltinTSPropTypes-test.ts.snap │ │ │ ├── docblock-test.ts │ │ │ ├── expressionTo-test.ts │ │ │ ├── findFunctionReturn-test.ts │ │ │ ├── flowUtilityTypes-test.ts │ │ │ ├── getClassMemberValuePath-test.ts │ │ │ ├── getFlowType-test.ts │ │ │ ├── getMemberExpressionRoot-test.ts │ │ │ ├── getMemberExpressionValuePath-test.ts │ │ │ ├── getMemberValuePath-test.ts │ │ │ ├── getMembers-test.ts │ │ │ ├── getMethodDocumentation-test.ts │ │ │ ├── getNameOrValue-test.ts │ │ │ ├── getParameterName-test.ts │ │ │ ├── getPropType-test.ts │ │ │ ├── getPropertyName-test.ts │ │ │ ├── getPropertyValuePath-test.ts │ │ │ ├── getTSType-test.ts │ │ │ ├── getTypeAnnotation-test.ts │ │ │ ├── getTypeFromReactComponent-test.ts │ │ │ ├── getTypeParameters-test.ts │ │ │ ├── isDestructuringAssignment-test.ts │ │ │ ├── isExportsOrModuleAssignment-test.ts │ │ │ ├── isReactBuiltinReference-test.ts │ │ │ ├── isReactChildrenElementCall-test.ts │ │ │ ├── isReactCloneElementCall-test.ts │ │ │ ├── isReactComponentClass-test.ts │ │ │ ├── isReactComponentMethod-test.ts │ │ │ ├── isReactCreateClassCall-test.ts │ │ │ ├── isReactCreateElementCall-test.ts │ │ │ ├── isReactForwardRefCall-test.ts │ │ │ ├── isReactModuleName-test.ts │ │ │ ├── isRequiredPropType-test.ts │ │ │ ├── isStatelessComponent-test.ts │ │ │ ├── isUnreachableFlowType-test.ts │ │ │ ├── normalizeClassDefinition-test.ts │ │ │ ├── parseJsDoc-test.ts │ │ │ ├── printValue-test.ts │ │ │ ├── resolveExportDeclaration-test.ts │ │ │ ├── resolveFunctionDefinitionToReturnValue-test.ts │ │ │ ├── resolveGenericTypeAnnotations-test.ts │ │ │ ├── resolveHOC-test.ts │ │ │ ├── resolveObjectKeysToArray-test.ts │ │ │ ├── resolveObjectPatternPropertyToValue-test.ts │ │ │ ├── resolveObjectValuesToArray-test.ts │ │ │ ├── resolveToModule-test.ts │ │ │ ├── resolveToValue-test.ts │ │ │ ├── setPropDescription-test.ts │ │ │ └── unwrapBuiltinTSPropTypes-test.ts │ │ │ ├── docblock.ts │ │ │ ├── expressionTo.ts │ │ │ ├── findComponentDefinition.ts │ │ │ ├── findFunctionReturn.ts │ │ │ ├── flowUtilityTypes.ts │ │ │ ├── getClassMemberValuePath.ts │ │ │ ├── getFlowType.ts │ │ │ ├── getMemberExpressionRoot.ts │ │ │ ├── getMemberExpressionValuePath.ts │ │ │ ├── getMemberValuePath.ts │ │ │ ├── getMembers.ts │ │ │ ├── getMethodDocumentation.ts │ │ │ ├── getNameOrValue.ts │ │ │ ├── getParameterName.ts │ │ │ ├── getPropType.ts │ │ │ ├── getPropertyName.ts │ │ │ ├── getPropertyValuePath.ts │ │ │ ├── getTSType.ts │ │ │ ├── getTypeAnnotation.ts │ │ │ ├── getTypeFromReactComponent.ts │ │ │ ├── getTypeIdentifier.ts │ │ │ ├── getTypeParameters.ts │ │ │ ├── index.ts │ │ │ ├── isDestructuringAssignment.ts │ │ │ ├── isExportsOrModuleAssignment.ts │ │ │ ├── isImportSpecifier.ts │ │ │ ├── isReactBuiltinCall.ts │ │ │ ├── isReactBuiltinReference.ts │ │ │ ├── isReactChildrenElementCall.ts │ │ │ ├── isReactCloneElementCall.ts │ │ │ ├── isReactComponentClass.ts │ │ │ ├── isReactComponentMethod.ts │ │ │ ├── isReactCreateClassCall.ts │ │ │ ├── isReactCreateElementCall.ts │ │ │ ├── isReactForwardRefCall.ts │ │ │ ├── isReactModuleName.ts │ │ │ ├── isRequiredPropType.ts │ │ │ ├── isStatelessComponent.ts │ │ │ ├── isUnreachableFlowType.ts │ │ │ ├── normalizeClassDefinition.ts │ │ │ ├── parseJsDoc.ts │ │ │ ├── postProcessDocumentation.ts │ │ │ ├── printValue.ts │ │ │ ├── resolveExportDeclaration.ts │ │ │ ├── resolveFunctionDefinitionToReturnValue.ts │ │ │ ├── resolveGenericTypeAnnotation.ts │ │ │ ├── resolveHOC.ts │ │ │ ├── resolveObjectKeysToArray.ts │ │ │ ├── resolveObjectPatternPropertyToValue.ts │ │ │ ├── resolveObjectValuesToArray.ts │ │ │ ├── resolveToModule.ts │ │ │ ├── resolveToValue.ts │ │ │ ├── setPropDescription.ts │ │ │ ├── traverse.ts │ │ │ ├── ts-types │ │ │ └── index.ts │ │ │ └── unwrapBuiltinTSPropTypes.ts │ ├── tests │ │ ├── NodePathSerializer.ts │ │ ├── integration │ │ │ ├── __fixtures__ │ │ │ │ ├── class-without-id.tsx │ │ │ │ ├── component_1.js │ │ │ │ ├── component_10.js │ │ │ │ ├── component_11.js │ │ │ │ ├── component_12.js │ │ │ │ ├── component_13.js │ │ │ │ ├── component_14.js │ │ │ │ ├── component_15.js │ │ │ │ ├── component_16.js │ │ │ │ ├── component_17.js │ │ │ │ ├── component_18.js │ │ │ │ ├── component_19.js │ │ │ │ ├── component_2.js │ │ │ │ ├── component_20.js │ │ │ │ ├── component_21.tsx │ │ │ │ ├── component_22.tsx │ │ │ │ ├── component_23.tsx │ │ │ │ ├── component_24.js │ │ │ │ ├── component_25.tsx │ │ │ │ ├── component_26.js │ │ │ │ ├── component_27.tsx │ │ │ │ ├── component_28.tsx │ │ │ │ ├── component_29.js │ │ │ │ ├── component_3.js │ │ │ │ ├── component_30.js │ │ │ │ ├── component_31.js │ │ │ │ ├── component_32.js │ │ │ │ ├── component_33.tsx │ │ │ │ ├── component_34.js │ │ │ │ ├── component_35.js │ │ │ │ ├── component_36.js │ │ │ │ ├── component_37.js │ │ │ │ ├── component_38.js │ │ │ │ ├── component_39.tsx │ │ │ │ ├── component_4.js │ │ │ │ ├── component_40.js │ │ │ │ ├── component_41.tsx │ │ │ │ ├── component_42.js │ │ │ │ ├── component_43.tsx │ │ │ │ ├── component_5.js │ │ │ │ ├── component_6.js │ │ │ │ ├── component_7.js │ │ │ │ ├── component_8.js │ │ │ │ ├── component_9.js │ │ │ │ ├── flow-export-type.js │ │ │ │ ├── flow-import-type.js │ │ │ │ ├── flow-spread-import-type.js │ │ │ │ ├── namespace-export.tsx │ │ │ │ ├── support │ │ │ │ │ ├── other-exports.ts │ │ │ │ │ └── some-exports.ts │ │ │ │ └── test-all-imports.tsx │ │ │ ├── __snapshots__ │ │ │ │ └── integration-test.ts.snap │ │ │ └── integration-test.ts │ │ ├── setupTestFramework.ts │ │ └── utils.ts │ ├── tsconfig.json │ └── vitest.config.ts └── website │ ├── .gitignore │ ├── .nvmrc │ ├── README.md │ ├── eslint.config.mjs │ ├── netlify.toml │ ├── next-env.d.ts │ ├── next.config.mjs │ ├── package.json │ ├── postcss.config.js │ ├── public │ ├── .keep │ ├── _redirects │ ├── favicon.ico │ └── robots.txt │ ├── src │ ├── components │ │ ├── ContentMissing.tsx │ │ ├── Select.tsx │ │ ├── playground │ │ │ ├── OptionPanel.tsx │ │ │ ├── Panel.tsx │ │ │ ├── Playground.tsx │ │ │ ├── index.ts │ │ │ └── samples │ │ │ │ ├── flow.ts │ │ │ │ ├── javascript.ts │ │ │ │ └── typescript.ts │ │ └── users │ │ │ ├── User.tsx │ │ │ ├── UserList.tsx │ │ │ ├── logos │ │ │ ├── docz.svg │ │ │ ├── facebook.svg │ │ │ ├── gatsby.svg │ │ │ ├── instructure.svg │ │ │ ├── react-styleguidist.svg │ │ │ ├── researchgate.svg │ │ │ └── storybook.svg │ │ │ └── users.json │ ├── pages │ │ ├── _app.tsx │ │ ├── _meta.js │ │ ├── about.mdx │ │ ├── docs │ │ │ ├── _meta.js │ │ │ ├── extending │ │ │ │ ├── _meta.js │ │ │ │ ├── architecture.mdx │ │ │ │ ├── handler.mdx │ │ │ │ ├── importer.mdx │ │ │ │ └── resolver.mdx │ │ │ ├── getting-started.mdx │ │ │ ├── getting-started │ │ │ │ ├── _meta.js │ │ │ │ ├── cli.mdx │ │ │ │ └── nodejs.mdx │ │ │ ├── migrate │ │ │ │ ├── _meta.js │ │ │ │ └── v6.mdx │ │ │ ├── reference │ │ │ │ ├── _meta.js │ │ │ │ ├── api.mdx │ │ │ │ ├── cli.mdx │ │ │ │ ├── config.mdx │ │ │ │ ├── documentation │ │ │ │ │ ├── _meta.js │ │ │ │ │ ├── basic.mdx │ │ │ │ │ ├── flow.mdx │ │ │ │ │ ├── prop-types.mdx │ │ │ │ │ └── typescript.mdx │ │ │ │ ├── file-state.mdx │ │ │ │ └── handlers │ │ │ │ │ ├── _meta.js │ │ │ │ │ ├── child-context-type-handler.mdx │ │ │ │ │ ├── code-type-handler.mdx │ │ │ │ │ ├── component-docblock-handler.mdx │ │ │ │ │ ├── component-methods-handler.mdx │ │ │ │ │ ├── context-type-handler.mdx │ │ │ │ │ ├── default-props-handler.mdx │ │ │ │ │ ├── display-name-handler.mdx │ │ │ │ │ ├── prop-docblock-handler.mdx │ │ │ │ │ ├── prop-type-composition-handler.mdx │ │ │ │ │ └── prop-type-handler.mdx │ │ │ └── release-notes │ │ │ │ ├── _meta.js │ │ │ │ ├── cli.mdx │ │ │ │ └── react-docgen.mdx │ │ ├── globals.css │ │ ├── index.mdx │ │ ├── playground.mdx │ │ └── users.mdx │ └── theme.config.tsx │ ├── tailwind.config.js │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── prettier.config.mjs ├── renovate.json └── tsconfig.base.json /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@2.2.0/schema.json", 3 | "changelog": [ 4 | "@changesets/changelog-github", 5 | { "repo": "reactjs/react-docgen" } 6 | ], 7 | "commit": false, 8 | "fixed": [], 9 | "linked": [], 10 | "access": "public", 11 | "baseBranch": "main", 12 | "updateInternalDependencies": "patch", 13 | "ignore": [ 14 | "@react-docgen-internal/benchmark", 15 | "@react-docgen-internal/website" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /.changeset/plenty-coins-compare.md: -------------------------------------------------------------------------------- 1 | --- 2 | "@react-docgen/cli": patch 3 | --- 4 | 5 | update dependency debug to v4.4.1 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | 8 | [*.{js,cjs,mjs,ts,tsx,mts,cts,mdx}] 9 | indent_style = space 10 | indent_size = 2 11 | #indent_style = tab 12 | 13 | [**.json] 14 | indent_style = space 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [danez] 2 | -------------------------------------------------------------------------------- /.github/workflows/changesets.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | concurrency: ${{ github.workflow }}-${{ github.ref }} 9 | 10 | permissions: 11 | id-token: write 12 | 13 | jobs: 14 | release: 15 | name: Release 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | with: 21 | # Do not persist credentials so changesets can use the provided token 22 | persist-credentials: false 23 | 24 | - name: Install pnpm 25 | uses: pnpm/action-setup@v4 26 | 27 | - name: Setup Node.js 28 | uses: actions/setup-node@v4 29 | with: 30 | node-version: 'lts/*' 31 | cache: 'pnpm' 32 | 33 | - name: Install dependencies 34 | run: pnpm install --frozen-lockfile 35 | 36 | - name: Run Changesets 37 | id: changesets 38 | uses: changesets/action@v1 39 | with: 40 | publish: pnpm changesets-release 41 | version: pnpm changesets-version 42 | title: Packages ready to publish 43 | commit: Publish new versions 44 | env: 45 | GITHUB_TOKEN: ${{ secrets.CHANGESETS_TOKEN }} 46 | NPM_TOKEN: ${{secrets.NPM_TOKEN}} 47 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: 'CodeQL' 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [main] 9 | schedule: 10 | - cron: '43 16 * * 5' 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | runs-on: ubuntu-latest 16 | permissions: 17 | actions: read 18 | contents: read 19 | security-events: write 20 | 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@v4 24 | 25 | - name: Initialize CodeQL 26 | uses: github/codeql-action/init@v3 27 | with: 28 | languages: 'javascript' 29 | 30 | - name: Perform CodeQL Analysis 31 | uses: github/codeql-action/analyze@v3 32 | with: 33 | category: "/language:javascript" 34 | -------------------------------------------------------------------------------- /.github/workflows/lock.yml: -------------------------------------------------------------------------------- 1 | name: 'Lock Threads' 2 | 3 | on: 4 | schedule: 5 | - cron: '36 4 * * 4' 6 | workflow_dispatch: 7 | 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | discussions: write 12 | 13 | concurrency: 14 | group: lock 15 | 16 | jobs: 17 | action: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: dessant/lock-threads@v5 21 | with: 22 | log-output: true 23 | -------------------------------------------------------------------------------- /.github/workflows/ossar-analysis.yml: -------------------------------------------------------------------------------- 1 | name: OSSAR 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [main] 9 | schedule: 10 | - cron: '38 4 * * 1' 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | OSSAR-Scan: 17 | permissions: 18 | contents: read # for actions/checkout to fetch code 19 | security-events: write # for github/codeql-action/upload-sarif to upload SARIF results 20 | runs-on: windows-latest 21 | 22 | steps: 23 | - name: Checkout repository 24 | uses: actions/checkout@v4 25 | 26 | - name: Run OSSAR 27 | uses: github/ossar-action@v1 28 | id: ossar 29 | 30 | - name: Upload OSSAR results 31 | uses: github/codeql-action/upload-sarif@v3 32 | with: 33 | sarif_file: ${{ steps.ossar.outputs.sarifFile }} 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | benchmark/suites/ 2 | dist/ 3 | **/node_modules/* 4 | !**/__fixtures__/**/node_modules/* 5 | .idea/ 6 | coverage/ 7 | .nx/cache 8 | .nx/workspace-data -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | - init: npm install -g pnpm && pnpm install 3 | vscode: 4 | extensions: 5 | - dbaeumer.vscode-eslint 6 | - esbenp.prettier-vscode 7 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | auto-install-peers=true 2 | link-workspace-packages=false 3 | provenance=true 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | benchmark/suites/ 2 | **/__fixtures__/ 3 | **/dist/ 4 | **/node_modules 5 | /.nx/cache 6 | /.nx/workspace-data -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to react-docgen 2 | We want to make contributing to this project as easy and transparent as 3 | possible. 4 | 5 | ## Our Development Process 6 | The majority of development on react-docgen will occur through GitHub. Accordingly, 7 | the process for contributing will follow standard GitHub protocol. 8 | 9 | ## Pull Requests 10 | We actively welcome your pull requests. 11 | 1. Fork the repo and create your branch from `main`. 12 | 2. If you've added code that should be tested, add tests 13 | 3. If you've changed APIs, update the documentation. 14 | 4. Ensure the test suite passes. 15 | 5. Make sure your code lints and typechecks. 16 | 6. If you haven't already, complete the Contributor License Agreement ("CLA"). 17 | 18 | ## Issues 19 | We use GitHub issues to track public bugs. Please ensure your description is 20 | clear and has sufficient instructions to be able to reproduce the issue. 21 | 22 | Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe 23 | disclosure of security bugs. In those cases, please go through the process 24 | outlined on that page and do not file a public issue. 25 | 26 | ## License 27 | react-docgen is [MIT licensed](https://github.com/reactjs/react-docgen/blob/master/LICENSE). 28 | 29 | By contributing to react-docgen, you agree that your contributions will be licensed 30 | under its MIT license. 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Facebook, Inc. and its affiliates. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-docgen 2 | 3 | [`react-docgen`](./packages/react-docgen/) is a highly customizable library that 4 | extracts information from [React](https://reactjs.org/) components and returns 5 | this information in a structured machine readable format from which 6 | documentations can be generated. 7 | 8 | [`@react-docgen/cli`](./packages/react-docgen-cli/) is a cli wrapper around the 9 | library allowing using `react-docgen` on the command line. 10 | 11 | ## Documentation 12 | 13 | For version 5.x please checkout the 14 | [README.md on the 5.x branch](https://github.com/reactjs/react-docgen/blob/5.x/README.md) 15 | 16 | For version 6.x and newer please visit 17 | [react-docgen.dev](https://react-docgen.dev) 18 | 19 | ## License 20 | 21 | This project is licensed under the MIT License. 22 | -------------------------------------------------------------------------------- /benchmark/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-docgen-internal/benchmark", 3 | "version": "0.0.0", 4 | "private": true, 5 | "description": "Generate benchmarks for react-docgen", 6 | "scripts": { 7 | "setup": "rm -rf ./suites/react-bootstrap/ && git clone --depth=1 --branch=v2.7.0 https://github.com/react-bootstrap/react-bootstrap.git ./suites/react-bootstrap/", 8 | "start": "node --expose-gc ./index.mjs", 9 | "debug": "node --inspect-brk --expose-gc ./index.mjs" 10 | }, 11 | "license": "MIT", 12 | "dependencies": { 13 | "benchmark": "2.1.4", 14 | "cli-table": "0.3.11", 15 | "fast-glob": "3.3.3", 16 | "microtime": "3.1.1", 17 | "react-docgen5": "npm:react-docgen@5.4.3", 18 | "react-docgen6": "npm:react-docgen@6.0.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /nx.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/nx/schemas/nx-schema.json", 3 | "targetDefaults": { 4 | "build": { 5 | "dependsOn": ["^build"], 6 | "outputs": ["{projectRoot}/dist"], 7 | "cache": true 8 | }, 9 | "test": { 10 | "dependsOn": ["build"], 11 | "cache": true 12 | } 13 | }, 14 | "namedInputs": { 15 | "default": ["{projectRoot}/**/*", "sharedGlobals"], 16 | "sharedGlobals": [ 17 | "{workspaceRoot}/nx.json", 18 | "{workspaceRoot}/package.json", 19 | "{workspaceRoot}/pnpm-lock.yaml", 20 | "{workspaceRoot}/tsconfig.base.json" 21 | ], 22 | "production": ["default"] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "engines": { 4 | "node": "^20.9.0 || >=22", 5 | "pnpm": ">=10.6.2" 6 | }, 7 | "scripts": { 8 | "build": "nx run-many --target=build --exclude='@react-docgen-internal/*'", 9 | "lint": "eslint . --flag unstable_config_lookup_from_file --report-unused-disable-directives --max-warnings=0", 10 | "fix": "eslint . --flag unstable_config_lookup_from_file --fix --report-unused-disable-directives --max-warnings=0", 11 | "test": "nx run-many --target=test --exclude='@react-docgen-internal/*' --output-style=stream", 12 | "copy:changelog:react-docgen": "cp ./packages/react-docgen/CHANGELOG.md ./packages/website/src/pages/docs/release-notes/react-docgen.mdx", 13 | "copy:changelog:cli": "cp ./packages/react-docgen-cli/CHANGELOG.md ./packages/website/src/pages/docs/release-notes/cli.mdx", 14 | "changesets-release": "pnpm build && changeset publish", 15 | "changesets-version": "changeset version && pnpm install --lockfile-only && pnpm copy:changelog:react-docgen && pnpm copy:changelog:cli" 16 | }, 17 | "license": "MIT", 18 | "devDependencies": { 19 | "@changesets/changelog-github": "0.5.1", 20 | "@changesets/cli": "2.29.4", 21 | "@eslint/eslintrc": "3.3.1", 22 | "@eslint/js": "9.28.0", 23 | "@types/node": "20.17.57", 24 | "@vitest/coverage-v8": "3.2.1", 25 | "cpy": "11.1.0", 26 | "eslint": "9.28.0", 27 | "eslint-config-next": "15.3.3", 28 | "eslint-config-prettier": "10.1.5", 29 | "eslint-plugin-prettier": "5.4.1", 30 | "execa": "9.6.0", 31 | "globals": "16.2.0", 32 | "nx": "20.8.2", 33 | "prettier": "3.5.3", 34 | "prettier-plugin-tailwindcss": "0.6.12", 35 | "rimraf": "6.0.1", 36 | "tempy": "3.1.0", 37 | "typescript": "5.8.3", 38 | "typescript-eslint": "8.33.1", 39 | "vitest": "3.2.1" 40 | }, 41 | "packageManager": "pnpm@10.11.1" 42 | } 43 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/README.md: -------------------------------------------------------------------------------- 1 | # @react-docgen/cli 2 | 3 | # Documentation 4 | 5 | Please visit https://react-docgen.dev/docs/getting-started/cli/ 6 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-docgen/cli", 3 | "version": "3.0.0", 4 | "description": "A CLI to extract information from React components for documentation generation.", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/reactjs/react-docgen.git", 8 | "directory": "packages/react-docgen-cli" 9 | }, 10 | "type": "module", 11 | "bin": { 12 | "react-docgen": "dist/cli.js" 13 | }, 14 | "files": [ 15 | "dist" 16 | ], 17 | "engines": { 18 | "node": "^20.9.0 || >=22" 19 | }, 20 | "scripts": { 21 | "build": "rimraf dist/ && tsc", 22 | "test": "vitest run" 23 | }, 24 | "keywords": [ 25 | "react", 26 | "cli", 27 | "documentation-generation" 28 | ], 29 | "author": { 30 | "name": "Daniel Tschinder (http://github.com/danez)" 31 | }, 32 | "license": "MIT", 33 | "dependencies": { 34 | "chalk": "5.4.1", 35 | "commander": "13.1.0", 36 | "debug": "4.4.1", 37 | "fast-glob": "3.3.3", 38 | "react-docgen": "workspace:8.0.0", 39 | "slash": "5.1.0" 40 | }, 41 | "devDependencies": { 42 | "@types/debug": "4.1.12" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/src/cli.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { program } from 'commander'; 3 | 4 | program 5 | .name('react-docgen') 6 | .helpOption(false) 7 | .executableDir('./commands/') 8 | .command('parse', 'Extract meta information from React components.', { 9 | isDefault: true, 10 | executableFile: 'parse/command.js', 11 | }); 12 | 13 | program.parse(); 14 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/src/commands/parse/options/loadOptions.ts: -------------------------------------------------------------------------------- 1 | import type { Handler, Importer, Resolver } from 'react-docgen'; 2 | import { builtinHandlers, builtinImporters } from 'react-docgen'; 3 | import loadReactDocgenPlugin from './loadReactDocgenPlugin.js'; 4 | import loadResolvers from './loadResolvers.js'; 5 | 6 | export default async function loadOptions(input: { 7 | handler: string[] | undefined; 8 | importer: string | undefined; 9 | resolver: string[] | undefined; 10 | }): Promise<{ 11 | handlers: Handler[] | undefined; 12 | importer: Importer | undefined; 13 | resolver: Resolver | undefined; 14 | }> { 15 | const importer = 16 | input.importer && input.importer.length !== 0 17 | ? await loadReactDocgenPlugin( 18 | input.importer, 19 | 'importer', 20 | builtinImporters, 21 | ) 22 | : undefined; 23 | 24 | const handlers = input.handler 25 | ? await Promise.all( 26 | input.handler.map(async (handler) => { 27 | return await loadReactDocgenPlugin( 28 | handler, 29 | 'handler', 30 | builtinHandlers, 31 | ); 32 | }), 33 | ) 34 | : undefined; 35 | 36 | return { 37 | handlers, 38 | importer, 39 | resolver: await loadResolvers(input.resolver), 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/src/commands/parse/options/loadReactDocgenPlugin.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'path'; 2 | import importFile from '../../../utils/importFile.js'; 3 | 4 | export default async function loadReactDocgenPlugin( 5 | input: string, 6 | name: string, 7 | builtins?: Record, 8 | ): Promise { 9 | if (builtins?.[input]) { 10 | return builtins[input]!; 11 | } 12 | 13 | const path = resolve(process.cwd(), input); 14 | // Maybe it is local path or a package 15 | const plugin: T | undefined = 16 | (await importFile(path)) ?? (await importFile(input)); 17 | 18 | if (plugin) { 19 | return plugin; 20 | } 21 | 22 | throw new Error( 23 | `Unknown ${name}: "${input}" is not a built-in ${name}, ` + 24 | `not a package, and can not be found locally ("${path}")`, 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/src/commands/parse/output/outputError.ts: -------------------------------------------------------------------------------- 1 | import { relative } from 'path'; 2 | import chalk from 'chalk'; 3 | 4 | function isReactDocgenError(error: NodeJS.ErrnoException): boolean { 5 | return Boolean( 6 | error instanceof Error && error.code?.startsWith('ERR_REACTDOCGEN'), 7 | ); 8 | } 9 | 10 | function outputReactDocgenError( 11 | error: Error, 12 | filePath: string, 13 | { failOnWarning }: { failOnWarning: boolean }, 14 | ): boolean { 15 | let label = 'WARNING'; 16 | let color = chalk.yellow; 17 | let log = console.warn; 18 | let isError = false; 19 | 20 | if (failOnWarning && isReactDocgenError(error)) { 21 | process.exitCode = 2; 22 | isError = true; 23 | label = 'ERROR'; 24 | color = chalk.red; 25 | log = console.error; 26 | } 27 | 28 | log( 29 | color( 30 | `▶ ${label}: ${error.message} 👀\n in ${chalk.underline( 31 | relative(process.cwd(), filePath), 32 | )}\n`, 33 | ), 34 | ); 35 | 36 | return isError; 37 | } 38 | 39 | export default function outputError( 40 | error: Error, 41 | filePath: string, 42 | options: { failOnWarning: boolean }, 43 | ): boolean { 44 | if (isReactDocgenError(error)) { 45 | return outputReactDocgenError(error, filePath, options); 46 | } else { 47 | process.exitCode = 1; 48 | console.error(error); 49 | 50 | return true; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/src/commands/parse/output/outputResult.ts: -------------------------------------------------------------------------------- 1 | import { writeFile } from 'fs/promises'; 2 | import type { Documentation } from 'react-docgen'; 3 | 4 | export default async function outputResult( 5 | documentation: Record, 6 | { pretty = false, output }: { pretty: boolean; output: string | undefined }, 7 | ): Promise { 8 | const result = JSON.stringify( 9 | documentation, 10 | undefined, 11 | pretty ? 2 : undefined, 12 | ); 13 | 14 | if (output) { 15 | await writeFile(output, result, 'utf-8'); 16 | } else { 17 | process.stdout.write(result + '\n'); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/src/utils/importFile.ts: -------------------------------------------------------------------------------- 1 | import { createRequire } from 'module'; 2 | import { pathToFileURL } from 'url'; 3 | 4 | const require = createRequire(import.meta.url); 5 | const resolveOptions = { paths: [process.cwd()] }; 6 | 7 | export default async function importFile( 8 | importSpecifier: string, 9 | ): Promise { 10 | try { 11 | const importedFile = await import( 12 | // need to convert to file:// url as on windows absolute path strings do not work 13 | pathToFileURL(require.resolve(importSpecifier, resolveOptions)).href 14 | ); 15 | 16 | return importedFile.default ? importedFile.default : importedFile; 17 | } catch (error) { 18 | if ( 19 | error instanceof Error && 20 | (error as NodeJS.ErrnoException).code !== 'ERR_MODULE_NOT_FOUND' && 21 | (error as NodeJS.ErrnoException).code !== 'MODULE_NOT_FOUND' 22 | ) { 23 | throw error; 24 | } 25 | 26 | return undefined; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/basic/Component.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | 3 | // @component 4 | module.exports = class Component extends React.Component { 5 | displayName = "Component" 6 | 7 | otherMethod() {} 8 | render() {} 9 | }; 10 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/basic/NoComponent.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactjs/react-docgen/d0a133ec154b63218456f19ed887a8951fa70d17/packages/react-docgen-cli/tests/integration/__fixtures__/basic/NoComponent.js -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-handler-cjs/Component.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | import x from './other.cjs'; 3 | 4 | function Component() { 5 | return
; 6 | }; 7 | 8 | Component.displayName = x 9 | 10 | module.exports = Component 11 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-handler-cjs/handler.cjs: -------------------------------------------------------------------------------- 1 | const testHandler = function (documentation) { 2 | documentation.set('displayName', 'testhandler'); 3 | }; 4 | 5 | module.exports = testHandler; 6 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-handler-esm/Component.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | import x from './other.cjs'; 3 | 4 | function Component() { 5 | return
; 6 | }; 7 | 8 | Component.displayName = x 9 | 10 | module.exports = Component 11 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-handler-esm/handler.mjs: -------------------------------------------------------------------------------- 1 | const testHandler = function (documentation) { 2 | documentation.set('displayName', 'testhandler'); 3 | }; 4 | 5 | export default testHandler; 6 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-handler-npm/Component.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | import x from './other.cjs'; 3 | 4 | function Component() { 5 | return
; 6 | }; 7 | 8 | Component.displayName = x 9 | 10 | module.exports = Component 11 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-handler-npm/node_modules/test-react-docgen-handler/index.js: -------------------------------------------------------------------------------- 1 | const testHandler = function (documentation) { 2 | documentation.set('displayName', 'testhandler'); 3 | }; 4 | 5 | module.exports = testHandler; 6 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-handler-npm/node_modules/test-react-docgen-handler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-react-docgen-handler" 3 | } 4 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-importer-cjs/Component.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | import x from './other.cjs'; 3 | 4 | function Component() { 5 | return
; 6 | }; 7 | 8 | Component.displayName = x 9 | 10 | module.exports = Component 11 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-importer-cjs/custom-importer-esm/Component.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | import x from './other.cjs'; 3 | 4 | function Component() { 5 | return
; 6 | }; 7 | 8 | Component.displayName = x 9 | 10 | module.exports = Component 11 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-importer-cjs/custom-importer-esm/importer.mjs: -------------------------------------------------------------------------------- 1 | const testImporter = function (pa, n, file) { 2 | const newFile = file.parse('("importer")', 'x.js'); 3 | 4 | let path; 5 | newFile.traverse({ 6 | Program(p) { 7 | path = p; 8 | p.stop(); 9 | } 10 | }); 11 | 12 | return path.get('body')[0].get('expression'); 13 | }; 14 | 15 | export default testImporter; 16 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-importer-cjs/custom-importer-npm/Component.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | import x from './other.cjs'; 3 | 4 | function Component() { 5 | return
; 6 | }; 7 | 8 | Component.displayName = x 9 | 10 | module.exports = Component 11 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-importer-cjs/custom-importer-npm/node_modules/test-react-docgen-importer/index.js: -------------------------------------------------------------------------------- 1 | const testImporter = function (pa, n, file) { 2 | const newFile = file.parse('("importer")', 'x.js'); 3 | 4 | let path; 5 | newFile.traverse({ 6 | Program(p) { 7 | path = p; 8 | p.stop(); 9 | } 10 | }); 11 | 12 | return path.get('body')[0].get('expression'); 13 | }; 14 | 15 | module.exports = testImporter; 16 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-importer-cjs/custom-importer-npm/node_modules/test-react-docgen-importer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-react-docgen-importer" 3 | } 4 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-importer-cjs/importer.cjs: -------------------------------------------------------------------------------- 1 | const testImporter = function (pa, n, file) { 2 | const newFile = file.parse('("importer")', 'x.js'); 3 | 4 | let path; 5 | newFile.traverse({ 6 | Program(p) { 7 | path = p; 8 | p.stop(); 9 | } 10 | }); 11 | 12 | return path.get('body')[0].get('expression'); 13 | }; 14 | 15 | module.exports = testImporter; 16 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-importer-esm/Component.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | import x from './other.cjs'; 3 | 4 | function Component() { 5 | return
; 6 | }; 7 | 8 | Component.displayName = x 9 | 10 | module.exports = Component 11 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-importer-esm/importer.mjs: -------------------------------------------------------------------------------- 1 | const testImporter = function (pa, n, file) { 2 | const newFile = file.parse('("importer")', 'x.js'); 3 | 4 | let path; 5 | newFile.traverse({ 6 | Program(p) { 7 | path = p; 8 | p.stop(); 9 | } 10 | }); 11 | 12 | return path.get('body')[0].get('expression'); 13 | }; 14 | 15 | export default testImporter; 16 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-importer-npm/Component.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | import x from './other.cjs'; 3 | 4 | function Component() { 5 | return
; 6 | }; 7 | 8 | Component.displayName = x 9 | 10 | module.exports = Component 11 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-importer-npm/node_modules/test-react-docgen-importer/index.js: -------------------------------------------------------------------------------- 1 | const testImporter = function (pa, n, file) { 2 | const newFile = file.parse('("importer")', 'x.js'); 3 | 4 | let path; 5 | newFile.traverse({ 6 | Program(p) { 7 | path = p; 8 | p.stop(); 9 | } 10 | }); 11 | 12 | return path.get('body')[0].get('expression'); 13 | }; 14 | 15 | module.exports = testImporter; 16 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-importer-npm/node_modules/test-react-docgen-importer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-react-docgen-importer" 3 | } 4 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-resolver-class/Component.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | 3 | const Component = React.createClass({ 4 | displayName: 'Component', 5 | render: function () {}, 6 | }); 7 | 8 | module.exports = Component; 9 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-resolver-class/node_modules/test-react-docgen-resolver-class/index.js: -------------------------------------------------------------------------------- 1 | const code = ` 2 | ({ 3 | displayName: 'Custom', 4 | }) 5 | `; 6 | 7 | const customResolver = class { 8 | resolve(file) { 9 | const newFile = file.parse(code, 'x.js'); 10 | let path; 11 | 12 | newFile.traverse({ 13 | Program(p) { 14 | path = p; 15 | p.stop(); 16 | } 17 | }); 18 | 19 | return [path.get('body')[0].get('expression')]; 20 | } 21 | } 22 | 23 | module.exports = customResolver; 24 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-resolver-class/node_modules/test-react-docgen-resolver-class/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-react-docgen-resolver-class" 3 | } 4 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-resolver-class/node_modules/test-react-docgen-resolver/index.js: -------------------------------------------------------------------------------- 1 | const code = ` 2 | ({ 3 | displayName: 'Custom', 4 | }) 5 | `; 6 | 7 | const customResolver = class { 8 | resolve(file) { 9 | const newFile = file.parse(code, 'x.js'); 10 | let path; 11 | 12 | newFile.traverse({ 13 | Program(p) { 14 | path = p; 15 | p.stop(); 16 | } 17 | }); 18 | 19 | return [path.get('body')[0].get('expression')]; 20 | } 21 | } 22 | 23 | module.exports = new customResolver(); 24 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-resolver-class/node_modules/test-react-docgen-resolver/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-react-docgen-resolver" 3 | } 4 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-resolver-class/resolver.cjs: -------------------------------------------------------------------------------- 1 | const code = ` 2 | ({ 3 | displayName: 'Custom', 4 | }) 5 | `; 6 | 7 | const customResolver = class { 8 | resolve(file) { 9 | const newFile = file.parse(code, 'x.js'); 10 | let path; 11 | 12 | newFile.traverse({ 13 | Program(p) { 14 | path = p; 15 | p.stop(); 16 | } 17 | }); 18 | 19 | return [path.get('body')[0].get('expression')]; 20 | } 21 | } 22 | 23 | module.exports = new customResolver(); 24 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-resolver-class/resolver.mjs: -------------------------------------------------------------------------------- 1 | const code = ` 2 | ({ 3 | displayName: 'Custom', 4 | }) 5 | `; 6 | 7 | const customResolver = class { 8 | resolve(file) { 9 | const newFile = file.parse(code, 'x.js'); 10 | let path; 11 | 12 | newFile.traverse({ 13 | Program(p) { 14 | path = p; 15 | p.stop(); 16 | } 17 | }); 18 | 19 | return [path.get('body')[0].get('expression')]; 20 | } 21 | } 22 | 23 | const resolver = new customResolver(); 24 | 25 | export default resolver; 26 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-resolver-function/Component.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | 3 | const Component = React.createClass({ 4 | displayName: 'Component', 5 | render: function () {}, 6 | }); 7 | 8 | module.exports = Component; 9 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-resolver-function/node_modules/test-react-docgen-resolver/index.js: -------------------------------------------------------------------------------- 1 | const code = ` 2 | ({ 3 | displayName: 'Custom', 4 | }) 5 | `; 6 | 7 | const customResolver = function ( 8 | file, 9 | ) { 10 | const newFile = file.parse(code, 'x.js'); 11 | let path; 12 | 13 | newFile.traverse({ 14 | Program(p) { 15 | path = p; 16 | p.stop(); 17 | } 18 | }); 19 | 20 | return [path.get('body')[0].get('expression')]; 21 | }; 22 | 23 | module.exports = customResolver; 24 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-resolver-function/node_modules/test-react-docgen-resolver/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-react-docgen-resolver" 3 | } 4 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-resolver-function/resolver.cjs: -------------------------------------------------------------------------------- 1 | const code = ` 2 | ({ 3 | displayName: 'Custom', 4 | }) 5 | `; 6 | 7 | const customResolver = function ( 8 | file, 9 | ) { 10 | const newFile = file.parse(code, 'x.js'); 11 | let path; 12 | 13 | newFile.traverse({ 14 | Program(p) { 15 | path = p; 16 | p.stop(); 17 | } 18 | }); 19 | 20 | return [path.get('body')[0].get('expression')]; 21 | }; 22 | 23 | module.exports = customResolver; 24 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/custom-resolver-function/resolver.mjs: -------------------------------------------------------------------------------- 1 | const code = ` 2 | ({ 3 | displayName: 'Custom', 4 | }) 5 | `; 6 | 7 | const customResolver = function ( 8 | file, 9 | ) { 10 | const newFile = file.parse(code, 'x.js'); 11 | let path; 12 | 13 | newFile.traverse({ 14 | Program(p) { 15 | path = p; 16 | p.stop(); 17 | } 18 | }); 19 | 20 | return [path.get('body')[0].get('expression')]; 21 | }; 22 | 23 | export default customResolver; 24 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/ignore/__mocks__/MockComponent.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | 3 | const Component = React.createClass({ 4 | displayName: 'MockComponent', 5 | render: function () {}, 6 | }); 7 | 8 | module.exports = Component; 9 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/ignore/__tests__/TestComponent.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | 3 | const Component = React.createClass({ 4 | displayName: 'TestComponent', 5 | render: function () {}, 6 | }); 7 | 8 | module.exports = Component; 9 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/ignore/foo/FooComponent.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | 3 | const Component = React.createClass({ 4 | displayName: 'FooComponent', 5 | render: function () {}, 6 | }); 7 | 8 | module.exports = Component; 9 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/ignore/node_modules/NodeModulesComponent.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | 3 | const Component = React.createClass({ 4 | displayName: 'NodeModulesComponent', 5 | render: function () {}, 6 | }); 7 | 8 | module.exports = Component; 9 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/multiple/MultipleComponents.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | 3 | exports.ComponentA = React.createClass({ 4 | displayName: 'ComponentA', 5 | render: function () { 6 | // ... 7 | }, 8 | }); 9 | 10 | exports.ComponentB = React.createClass({ 11 | displayName: 'ComponentB', 12 | render: function () { 13 | // ... 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/__fixtures__/syntax-error/SyntaxError.js: -------------------------------------------------------------------------------- 1 | module.exports {} 2 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tests/integration/utils/withFixture.ts: -------------------------------------------------------------------------------- 1 | import { rm, stat } from 'fs/promises'; 2 | import { dirname, join } from 'path'; 3 | import type { ExecaError, ExecaReturnValue } from 'execa'; 4 | import { execaNode } from 'execa'; 5 | import copy from 'cpy'; 6 | import { temporaryDirectory } from 'tempy'; 7 | import { fileURLToPath } from 'url'; 8 | 9 | const __dir = dirname(fileURLToPath(import.meta.url)); 10 | 11 | const fixtureDir = join(__dir, '../__fixtures__'); 12 | const cliBinary = join(__dir, '../../../dist/cli.js'); 13 | 14 | export default async function withFixture( 15 | fixture: string, 16 | callback: (api: { 17 | dir: string; 18 | run: ( 19 | args: readonly string[], 20 | ) => Promise<{ stdout: string; stderr: string; exitCode: number }>; 21 | }) => Promise, 22 | ): Promise { 23 | const tempDir = temporaryDirectory(); 24 | 25 | async function run( 26 | args: readonly string[], 27 | ): Promise> { 28 | try { 29 | return await execaNode(cliBinary, args, { 30 | cwd: tempDir, 31 | }); 32 | } catch (error) { 33 | return error as ExecaError; 34 | } 35 | } 36 | 37 | await stat(join(fixtureDir, fixture)); 38 | 39 | await copy(join(fixtureDir, fixture, '**/*'), tempDir, {}); 40 | await callback({ dir: tempDir, run }); 41 | await rm(tempDir, { force: true, recursive: true }); 42 | } 43 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "declaration": false, 5 | "outDir": "./dist" 6 | }, 7 | "exclude": ["**/__tests__/**/*", "**/__mocks__/**/*"], 8 | "include": ["src/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/react-docgen-cli/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | name: 'cli', 6 | include: ['**/__tests__/**/*-test.ts', '**/tests/integration/**/*-test.ts'], 7 | testTimeout: 30_000, 8 | deps: { 9 | interopDefault: false, 10 | }, 11 | coverage: { 12 | include: ['no-coverage'], 13 | }, 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /packages/react-docgen/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-docgen", 3 | "version": "8.0.0", 4 | "description": "A library to extract information from React components for documentation generation.", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/reactjs/react-docgen.git", 8 | "directory": "packages/react-docgen" 9 | }, 10 | "type": "module", 11 | "browser": { 12 | "./dist/importer/fsImporter.js": "./dist/importer/ignoreImporter.js", 13 | "./src/importer/fsImporter.ts": "./src/importer/ignoreImporter.ts", 14 | "./dist/importer/makeFsImporter.js": "./dist/importer/makeIgnoreImporter.js", 15 | "./src/importer/makeFsImporter.ts": "./src/importer/makeIgnoreImporter.ts" 16 | }, 17 | "files": [ 18 | "dist" 19 | ], 20 | "engines": { 21 | "node": "^20.9.0 || >=22" 22 | }, 23 | "main": "dist/main.js", 24 | "typings": "dist/main.d.ts", 25 | "scripts": { 26 | "build": "rimraf dist/ && tsc", 27 | "test": "vitest run" 28 | }, 29 | "keywords": [ 30 | "react", 31 | "documentation", 32 | "documentation-generation" 33 | ], 34 | "author": { 35 | "name": "Felix Kling (http://github.com/fkling)" 36 | }, 37 | "contributors": [ 38 | "Daniel Tschinder (http://github.com/danez)" 39 | ], 40 | "license": "MIT", 41 | "dependencies": { 42 | "@babel/core": "^7.18.9", 43 | "@babel/traverse": "^7.18.9", 44 | "@babel/types": "^7.18.9", 45 | "@types/babel__core": "^7.18.0", 46 | "@types/babel__traverse": "^7.18.0", 47 | "@types/doctrine": "^0.0.9", 48 | "@types/resolve": "^1.20.2", 49 | "doctrine": "^3.0.0", 50 | "resolve": "^1.22.1", 51 | "strip-indent": "^4.0.0" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /packages/react-docgen/src/__mocks__/Documentation.ts: -------------------------------------------------------------------------------- 1 | export default class Documentation { 2 | [x: string]: unknown; 3 | composes: string[] = []; 4 | descriptors: Record = {}; 5 | 6 | getPropDescriptor(name: string): unknown { 7 | return this.descriptors[name] || (this.descriptors[name] = {}); 8 | } 9 | addComposes(name: string): void { 10 | this.composes.push(name); 11 | } 12 | set(key: string, value: unknown): void { 13 | this[key] = value; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/react-docgen/src/__mocks__/FileState.ts: -------------------------------------------------------------------------------- 1 | import { file, program } from '@babel/types'; 2 | import FileState from '../FileState.js'; 3 | 4 | export default class FileStateMock extends FileState { 5 | constructor() { 6 | super( 7 | {}, 8 | { 9 | code: '', 10 | ast: file(program([])), 11 | importer: () => { 12 | return null; 13 | }, 14 | }, 15 | ); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/react-docgen/src/error.ts: -------------------------------------------------------------------------------- 1 | export enum ERROR_CODES { 2 | MISSING_DEFINITION = 'ERR_REACTDOCGEN_MISSING_DEFINITION', 3 | MULTIPLE_DEFINITIONS = 'ERR_REACTDOCGEN_MULTIPLE_DEFINITIONS', 4 | } 5 | 6 | const messages = new Map([ 7 | [ERROR_CODES.MISSING_DEFINITION, 'No suitable component definition found.'], 8 | [ 9 | ERROR_CODES.MULTIPLE_DEFINITIONS, 10 | 'Multiple exported component definitions found.', 11 | ], 12 | ]); 13 | 14 | export class ReactDocgenError extends Error { 15 | code: string | undefined; 16 | constructor(code: ERROR_CODES) { 17 | super(messages.get(code)); 18 | 19 | this.code = code; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/react-docgen/src/handlers/__tests__/__snapshots__/componentMethodsJsDocHandler-test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`componentMethodsJsDocHandler > adds descriptions 1`] = ` 4 | [ 5 | { 6 | "description": "The foo method.", 7 | "docblock": " 8 | The foo method. 9 | @param test The test 10 | @returns The number 11 | ", 12 | "modifiers": [], 13 | "name": "foo", 14 | "params": [ 15 | { 16 | "description": "The test", 17 | "name": "test", 18 | "optional": false, 19 | }, 20 | ], 21 | "returns": { 22 | "description": "The number", 23 | }, 24 | }, 25 | ] 26 | `; 27 | 28 | exports[`componentMethodsJsDocHandler > adds js doc types when no flow types 1`] = ` 29 | [ 30 | { 31 | "description": null, 32 | "docblock": " 33 | @param {string} test 34 | @returns {string} 35 | ", 36 | "modifiers": [], 37 | "name": "foo", 38 | "params": [ 39 | { 40 | "name": "test", 41 | "optional": false, 42 | "type": { 43 | "name": "string", 44 | }, 45 | }, 46 | ], 47 | "returns": { 48 | "type": { 49 | "name": "string", 50 | }, 51 | }, 52 | }, 53 | ] 54 | `; 55 | 56 | exports[`componentMethodsJsDocHandler > keeps flow types over js doc types 1`] = ` 57 | [ 58 | { 59 | "description": null, 60 | "docblock": " 61 | @param {string} test 62 | @returns {string} 63 | ", 64 | "modifiers": [], 65 | "name": "foo", 66 | "params": [ 67 | { 68 | "name": "test", 69 | "optional": false, 70 | "type": { 71 | "name": "number", 72 | }, 73 | }, 74 | ], 75 | "returns": { 76 | "type": { 77 | "name": "number", 78 | }, 79 | }, 80 | }, 81 | ] 82 | `; 83 | -------------------------------------------------------------------------------- /packages/react-docgen/src/handlers/componentMethodsJsDocHandler.ts: -------------------------------------------------------------------------------- 1 | import parseJsDoc from '../utils/parseJsDoc.js'; 2 | import type { 3 | default as Documentation, 4 | MethodDescriptor, 5 | } from '../Documentation.js'; 6 | import type { Handler } from './index.js'; 7 | 8 | function removeEmpty>(obj: T): T { 9 | return Object.fromEntries( 10 | Object.entries(obj).filter(([, v]) => v != null), 11 | ) as T; 12 | } 13 | 14 | // Merges two objects ignoring null/undefined. 15 | function merge(obj1: null | undefined, obj2: null | undefined): null; 16 | function merge(obj1: T1, obj2: T2): T1 & T2; 17 | function merge( 18 | obj1: Record | null | undefined, 19 | obj2: Record | null | undefined, 20 | ): Record | null { 21 | if (obj1 == null && obj2 == null) { 22 | return null; 23 | } 24 | const merged = { 25 | ...removeEmpty(obj1 ?? {}), 26 | ...removeEmpty(obj2 ?? {}), 27 | }; 28 | 29 | return merged; 30 | } 31 | /** 32 | * Extract info from the methods jsdoc blocks. Must be run after 33 | * componentMethodsHandler. 34 | */ 35 | const componentMethodsJsDocHandler: Handler = function ( 36 | documentation: Documentation, 37 | ): void { 38 | let methods = documentation.get('methods'); 39 | 40 | if (!methods) { 41 | return; 42 | } 43 | 44 | methods = methods.map((method) => { 45 | if (!method.docblock) { 46 | return method; 47 | } 48 | 49 | const jsDoc = parseJsDoc(method.docblock); 50 | 51 | const returns = merge(jsDoc.returns, method.returns); 52 | const params = method.params.map((param) => { 53 | const jsDocParam = jsDoc.params.find((p) => p.name === param.name); 54 | 55 | return merge(jsDocParam, param); 56 | }); 57 | 58 | return { 59 | ...method, 60 | description: jsDoc.description || null, 61 | returns, 62 | params, 63 | }; 64 | }); 65 | 66 | documentation.set('methods', methods); 67 | }; 68 | 69 | export default componentMethodsJsDocHandler; 70 | -------------------------------------------------------------------------------- /packages/react-docgen/src/handlers/index.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import type Documentation from '../Documentation.js'; 3 | import type { ComponentNode } from '../resolver/index.js'; 4 | 5 | export { default as componentDocblockHandler } from './componentDocblockHandler.js'; 6 | export { default as componentMethodsHandler } from './componentMethodsHandler.js'; 7 | export { default as componentMethodsJsDocHandler } from './componentMethodsJsDocHandler.js'; 8 | export { default as defaultPropsHandler } from './defaultPropsHandler.js'; 9 | export { default as displayNameHandler } from './displayNameHandler.js'; 10 | export { default as codeTypeHandler } from './codeTypeHandler.js'; 11 | export { default as propDocblockHandler } from './propDocblockHandler.js'; 12 | export { default as propTypeCompositionHandler } from './propTypeCompositionHandler.js'; 13 | export { 14 | propTypeHandler, 15 | contextTypeHandler, 16 | childContextTypeHandler, 17 | } from './propTypeHandler.js'; 18 | 19 | export type Handler = ( 20 | documentation: Documentation, 21 | componentDefinition: NodePath, 22 | ) => void; 23 | -------------------------------------------------------------------------------- /packages/react-docgen/src/handlers/propDocblockHandler.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import getMemberValuePath from '../utils/getMemberValuePath.js'; 3 | import resolveToValue from '../utils/resolveToValue.js'; 4 | import setPropDescription from '../utils/setPropDescription.js'; 5 | import type Documentation from '../Documentation.js'; 6 | import type { ComponentNode } from '../resolver/index.js'; 7 | import type { Handler } from './index.js'; 8 | 9 | function resolveDocumentation( 10 | documentation: Documentation, 11 | path: NodePath, 12 | ): void { 13 | if (!path.isObjectExpression()) { 14 | return; 15 | } 16 | 17 | path.get('properties').forEach((propertyPath) => { 18 | if (propertyPath.isSpreadElement()) { 19 | const resolvedValuePath = resolveToValue(propertyPath.get('argument')); 20 | 21 | resolveDocumentation(documentation, resolvedValuePath); 22 | } else if ( 23 | propertyPath.isObjectProperty() || 24 | propertyPath.isObjectMethod() 25 | ) { 26 | setPropDescription(documentation, propertyPath); 27 | } 28 | }); 29 | } 30 | 31 | const propDocblockHandler: Handler = function ( 32 | documentation: Documentation, 33 | componentDefinition: NodePath, 34 | ): void { 35 | let propTypesPath: NodePath | null = getMemberValuePath( 36 | componentDefinition, 37 | 'propTypes', 38 | ); 39 | 40 | if (!propTypesPath) { 41 | return; 42 | } 43 | propTypesPath = resolveToValue(propTypesPath); 44 | if (!propTypesPath) { 45 | return; 46 | } 47 | 48 | resolveDocumentation(documentation, propTypesPath); 49 | }; 50 | 51 | export default propDocblockHandler; 52 | -------------------------------------------------------------------------------- /packages/react-docgen/src/handlers/propTypeCompositionHandler.ts: -------------------------------------------------------------------------------- 1 | import getMemberValuePath from '../utils/getMemberValuePath.js'; 2 | import resolveToModule from '../utils/resolveToModule.js'; 3 | import resolveToValue from '../utils/resolveToValue.js'; 4 | import type Documentation from '../Documentation.js'; 5 | import type { NodePath } from '@babel/traverse'; 6 | import type { ObjectExpression } from '@babel/types'; 7 | import type { Handler } from './index.js'; 8 | import type { ComponentNode } from '../resolver/index.js'; 9 | 10 | /** 11 | * It resolves the path to its module name and adds it to the "composes" entry 12 | * in the documentation. 13 | */ 14 | function amendComposes(documentation: Documentation, path: NodePath): void { 15 | const moduleName = resolveToModule(path); 16 | 17 | if (moduleName) { 18 | documentation.addComposes(moduleName); 19 | } 20 | } 21 | 22 | function processObjectExpression( 23 | documentation: Documentation, 24 | path: NodePath, 25 | ): void { 26 | path.get('properties').forEach((propertyPath) => { 27 | if (propertyPath.isSpreadElement()) { 28 | amendComposes( 29 | documentation, 30 | resolveToValue(propertyPath.get('argument')), 31 | ); 32 | } 33 | }); 34 | } 35 | 36 | const propTypeCompositionHandler: Handler = function ( 37 | documentation: Documentation, 38 | componentDefinition: NodePath, 39 | ): void { 40 | let propTypesPath: NodePath | null = getMemberValuePath( 41 | componentDefinition, 42 | 'propTypes', 43 | ); 44 | 45 | if (!propTypesPath) { 46 | return; 47 | } 48 | propTypesPath = resolveToValue(propTypesPath); 49 | if (!propTypesPath) { 50 | return; 51 | } 52 | 53 | if (propTypesPath.isObjectExpression()) { 54 | processObjectExpression(documentation, propTypesPath); 55 | 56 | return; 57 | } 58 | 59 | amendComposes(documentation, propTypesPath); 60 | }; 61 | 62 | export default propTypeCompositionHandler; 63 | -------------------------------------------------------------------------------- /packages/react-docgen/src/importer/fsImporter.ts: -------------------------------------------------------------------------------- 1 | import makeFsImporter from './makeFsImporter.js'; 2 | 3 | const defaultFsImporter = makeFsImporter(); 4 | 5 | export default defaultFsImporter; 6 | -------------------------------------------------------------------------------- /packages/react-docgen/src/importer/ignoreImporter.ts: -------------------------------------------------------------------------------- 1 | import makeIgnoreImporter from './makeIgnoreImporter.js'; 2 | 3 | const ignoreImporter = makeIgnoreImporter(); 4 | 5 | export default ignoreImporter; 6 | -------------------------------------------------------------------------------- /packages/react-docgen/src/importer/index.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import type { 3 | ExportAllDeclaration, 4 | ExportNamedDeclaration, 5 | ImportDeclaration, 6 | } from '@babel/types'; 7 | import type FileState from '../FileState.js'; 8 | import ignoreImporter from './ignoreImporter.js'; 9 | import fsImporter from './fsImporter.js'; 10 | import makeFsImporter from './makeFsImporter.js'; 11 | 12 | export type ImportPath = NodePath< 13 | ExportAllDeclaration | ExportNamedDeclaration | ImportDeclaration 14 | >; 15 | 16 | export type Importer = ( 17 | path: ImportPath, 18 | name: string, 19 | file: FileState, 20 | ) => NodePath | null; 21 | 22 | export { fsImporter, ignoreImporter, makeFsImporter }; 23 | -------------------------------------------------------------------------------- /packages/react-docgen/src/importer/makeIgnoreImporter.ts: -------------------------------------------------------------------------------- 1 | import type { Importer } from './index.js'; 2 | 3 | export default function makeIgnoreImporter(): Importer { 4 | return () => null; 5 | } 6 | -------------------------------------------------------------------------------- /packages/react-docgen/src/resolver/ChainResolver.ts: -------------------------------------------------------------------------------- 1 | import type FileState from '../FileState.js'; 2 | import type { ComponentNodePath, Resolver, ResolverClass } from './index.js'; 3 | import runResolver from './utils/runResolver.js'; 4 | 5 | enum ChainingLogic { 6 | ALL, 7 | FIRST_FOUND, 8 | } 9 | 10 | interface ChainResolverOptions { 11 | chainingLogic?: ChainingLogic; 12 | } 13 | 14 | export default class ChainResolver implements ResolverClass { 15 | resolvers: Resolver[]; 16 | options: ChainResolverOptions; 17 | 18 | static Logic = ChainingLogic; 19 | 20 | constructor(resolvers: Resolver[], options: ChainResolverOptions) { 21 | this.resolvers = resolvers; 22 | this.options = options; 23 | } 24 | 25 | private resolveFirstOnly(file: FileState): ComponentNodePath[] { 26 | for (const resolver of this.resolvers) { 27 | const components = runResolver(resolver, file); 28 | 29 | if (components.length > 0) { 30 | return components; 31 | } 32 | } 33 | 34 | return []; 35 | } 36 | 37 | private resolveAll(file: FileState): ComponentNodePath[] { 38 | const allComponents = new Set(); 39 | 40 | for (const resolver of this.resolvers) { 41 | const components = runResolver(resolver, file); 42 | 43 | components.forEach((component) => { 44 | allComponents.add(component); 45 | }); 46 | } 47 | 48 | return Array.from(allComponents); 49 | } 50 | 51 | resolve(file: FileState): ComponentNodePath[] { 52 | if (this.options.chainingLogic === ChainingLogic.FIRST_FOUND) { 53 | return this.resolveFirstOnly(file); 54 | } 55 | 56 | return this.resolveAll(file); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /packages/react-docgen/src/resolver/index.ts: -------------------------------------------------------------------------------- 1 | import ChainResolver from './ChainResolver.js'; 2 | import FindAllDefinitionsResolver from './FindAllDefinitionsResolver.js'; 3 | import FindAnnotatedDefinitionsResolver from './FindAnnotatedDefinitionsResolver.js'; 4 | import FindExportedDefinitionsResolver from './FindExportedDefinitionsResolver.js'; 5 | import type { NodePath } from '@babel/traverse'; 6 | import type FileState from '../FileState.js'; 7 | import type { 8 | ArrowFunctionExpression, 9 | CallExpression, 10 | ClassDeclaration, 11 | ClassExpression, 12 | FunctionDeclaration, 13 | FunctionExpression, 14 | ObjectExpression, 15 | ObjectMethod, 16 | } from '@babel/types'; 17 | 18 | export type StatelessComponentNode = 19 | | ArrowFunctionExpression 20 | | FunctionDeclaration 21 | | FunctionExpression 22 | | ObjectMethod; 23 | 24 | export type ComponentNode = 25 | | CallExpression 26 | | ClassDeclaration 27 | | ClassExpression 28 | | ObjectExpression 29 | | StatelessComponentNode; 30 | 31 | export type ComponentNodePath = NodePath; 32 | 33 | export type ResolverFunction = (file: FileState) => ComponentNodePath[]; 34 | 35 | export interface ResolverClass { 36 | resolve: ResolverFunction; 37 | } 38 | 39 | export type Resolver = ResolverClass | ResolverFunction; 40 | 41 | export { 42 | FindAllDefinitionsResolver, 43 | FindAnnotatedDefinitionsResolver, 44 | FindExportedDefinitionsResolver, 45 | ChainResolver, 46 | }; 47 | -------------------------------------------------------------------------------- /packages/react-docgen/src/resolver/utils/__tests__/runResolver-test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test, vi } from 'vitest'; 2 | import type { 3 | ComponentNodePath, 4 | ResolverClass, 5 | } from '../../../resolver/index.js'; 6 | import runResolver from '../runResolver'; 7 | import FileStateMock from '../../../__mocks__/FileState.js'; 8 | 9 | const createEmptyClassResolver = (path?: ComponentNodePath) => 10 | new (class implements ResolverClass { 11 | resolve = vi.fn(() => { 12 | return path ? [path] : []; 13 | }); 14 | })(); 15 | 16 | const createEmptyFunctionResolver = (path?: ComponentNodePath) => 17 | vi.fn(() => (path ? [path] : [])); 18 | 19 | describe('runResolver', () => { 20 | const fileStateMock = new FileStateMock(); 21 | 22 | test('does run function resolvers', () => { 23 | const resolver = createEmptyFunctionResolver(); 24 | 25 | runResolver(resolver, fileStateMock); 26 | 27 | expect(resolver).toBeCalled(); 28 | expect(resolver).toHaveBeenCalledWith(fileStateMock); 29 | }); 30 | 31 | test('does run class resolvers', () => { 32 | const resolver = createEmptyClassResolver(); 33 | 34 | runResolver(resolver, fileStateMock); 35 | 36 | expect(resolver.resolve).toBeCalled(); 37 | expect(resolver.resolve).toHaveBeenCalledWith(fileStateMock); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /packages/react-docgen/src/resolver/utils/runResolver.ts: -------------------------------------------------------------------------------- 1 | import type FileState from '../../FileState.js'; 2 | import type { Resolver, ResolverClass, ResolverFunction } from '../index.js'; 3 | 4 | function isResolverClass(resolver: Resolver): resolver is ResolverClass { 5 | return typeof resolver === 'object'; 6 | } 7 | 8 | export default function runResolver( 9 | resolver: Resolver, 10 | file: FileState, 11 | ): ReturnType { 12 | return isResolverClass(resolver) ? resolver.resolve(file) : resolver(file); 13 | } 14 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/__tests__/__snapshots__/getMemberExpressionRoot-test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`getMemberExpressionRoot > returns the root of a member expression 1`] = ` 4 | Node { 5 | "name": "foo", 6 | "type": "Identifier", 7 | } 8 | `; 9 | 10 | exports[`getMemberExpressionRoot > returns the same path if identifier 1`] = ` 11 | Node { 12 | "extra": { 13 | "parenStart": 0, 14 | "parenthesized": true, 15 | }, 16 | "name": "foo", 17 | "type": "Identifier", 18 | } 19 | `; 20 | 21 | exports[`getMemberExpressionRoot > returns the same path if literal 1`] = ` 22 | Node { 23 | "extra": { 24 | "parenStart": 0, 25 | "parenthesized": true, 26 | "raw": "1", 27 | "rawValue": 1, 28 | }, 29 | "type": "NumericLiteral", 30 | "value": 1, 31 | } 32 | `; 33 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/__tests__/__snapshots__/getNameOrValue-test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`getNameOrValue > errors on invalid path 1`] = `[TypeError: Argument must be Identifier, Literal, QualifiedTypeIdentifier or TSQualifiedName. Received 'FunctionDeclaration']`; 4 | 5 | exports[`getNameOrValue > gets Identifier name 1`] = `"foo"`; 6 | 7 | exports[`getNameOrValue > gets QualifiedTypeIdentifier 1`] = `"x.h"`; 8 | 9 | exports[`getNameOrValue > gets TSQualifiedName 1`] = `"x.h"`; 10 | 11 | exports[`getNameOrValue > gets boolean literal value 1`] = `true`; 12 | 13 | exports[`getNameOrValue > gets null RegExp pattern 1`] = `"abc?"`; 14 | 15 | exports[`getNameOrValue > gets null literal value 1`] = `null`; 16 | 17 | exports[`getNameOrValue > gets numeric literal value 1`] = `1`; 18 | 19 | exports[`getNameOrValue > gets string literal value 1`] = `"foo"`; 20 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/__tests__/__snapshots__/getTypeParameters-test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`getTypeParameters > Flow > detects simple type 1`] = ` 4 | { 5 | "T": Node { 6 | "id": Node { 7 | "name": "T", 8 | "type": "Identifier", 9 | }, 10 | "type": "GenericTypeAnnotation", 11 | "typeParameters": null, 12 | }, 13 | } 14 | `; 15 | 16 | exports[`getTypeParameters > TypeScript > detects default 1`] = ` 17 | { 18 | "R": Node { 19 | "type": "TSStringKeyword", 20 | }, 21 | "T": Node { 22 | "type": "TSTypeReference", 23 | "typeName": Node { 24 | "name": "T", 25 | "type": "Identifier", 26 | }, 27 | }, 28 | } 29 | `; 30 | 31 | exports[`getTypeParameters > TypeScript > detects simple type 1`] = ` 32 | { 33 | "T": Node { 34 | "type": "TSTypeReference", 35 | "typeName": Node { 36 | "name": "T", 37 | "type": "Identifier", 38 | }, 39 | }, 40 | } 41 | `; 42 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/__tests__/__snapshots__/printValue-test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`printValue > deindents code 1`] = ` 4 | "function () { 5 | return x; 6 | }" 7 | `; 8 | 9 | exports[`printValue > removes trailing , for TsCallSignatureDeclaration 1`] = `"(): number"`; 10 | 11 | exports[`printValue > removes trailing , for TsConstructSignatureDeclaration 1`] = `"new (x:number)"`; 12 | 13 | exports[`printValue > removes trailing , for TsIndexSignature 1`] = `"[x:string]: number"`; 14 | 15 | exports[`printValue > removes trailing , for TsMethodSignature 1`] = `"x(): number"`; 16 | 17 | exports[`printValue > removes trailing , for TsPropertySignature 1`] = `"x: number"`; 18 | 19 | exports[`printValue > removes trailing ; for TsCallSignatureDeclaration 1`] = `"(): number"`; 20 | 21 | exports[`printValue > removes trailing ; for TsConstructSignatureDeclaration 1`] = `"new (x:number)"`; 22 | 23 | exports[`printValue > removes trailing ; for TsIndexSignature 1`] = `"[x:string]: number"`; 24 | 25 | exports[`printValue > removes trailing ; for TsMethodSignature 1`] = `"x(): number"`; 26 | 27 | exports[`printValue > removes trailing ; for TsPropertySignature 1`] = `"x: number"`; 28 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/__tests__/__snapshots__/resolveFunctionDefinitionToReturnValue-test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`resolveFunctionDefinitionToReturnValue > can resolve easy return statement 1`] = ` 4 | Node { 5 | "extra": { 6 | "raw": ""result"", 7 | "rawValue": "result", 8 | }, 9 | "type": "StringLiteral", 10 | "value": "result", 11 | } 12 | `; 13 | 14 | exports[`resolveFunctionDefinitionToReturnValue > stops after first return 1`] = ` 15 | Node { 16 | "extra": { 17 | "raw": ""first"", 18 | "rawValue": "first", 19 | }, 20 | "type": "StringLiteral", 21 | "value": "first", 22 | } 23 | `; 24 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/__tests__/__snapshots__/resolveGenericTypeAnnotations-test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`resolveGenericTypeAnnotation > resolves type 1`] = ` 4 | Node { 5 | "callProperties": [], 6 | "exact": false, 7 | "indexers": [], 8 | "inexact": false, 9 | "internalSlots": [], 10 | "properties": [ 11 | Node { 12 | "key": Node { 13 | "name": "x", 14 | "type": "Identifier", 15 | }, 16 | "kind": "init", 17 | "method": false, 18 | "optional": false, 19 | "proto": false, 20 | "static": false, 21 | "type": "ObjectTypeProperty", 22 | "value": Node { 23 | "type": "StringTypeAnnotation", 24 | }, 25 | "variance": null, 26 | }, 27 | ], 28 | "type": "ObjectTypeAnnotation", 29 | } 30 | `; 31 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/__tests__/__snapshots__/resolveObjectKeysToArray-test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`resolveObjectKeysToArray > can resolve imported objects passed to Object.keys 1`] = ` 4 | [ 5 | ""bar"", 6 | ""foo"", 7 | ""1"", 8 | ""2"", 9 | ""3"", 10 | ""baz"", 11 | ] 12 | `; 13 | 14 | exports[`resolveObjectKeysToArray > can resolve spreads from imported objects 1`] = ` 15 | [ 16 | ""foo"", 17 | ""baz"", 18 | ""bar"", 19 | ] 20 | `; 21 | 22 | exports[`resolveObjectKeysToArray > resolves Object.keys but ignores duplicates 1`] = ` 23 | [ 24 | ""boo"", 25 | ""foo"", 26 | ""doo"", 27 | ] 28 | `; 29 | 30 | exports[`resolveObjectKeysToArray > resolves Object.keys but ignores duplicates with getter and setter 1`] = ` 31 | [ 32 | ""x"", 33 | ] 34 | `; 35 | 36 | exports[`resolveObjectKeysToArray > resolves Object.keys when using getters 1`] = ` 37 | [ 38 | ""boo"", 39 | ""foo"", 40 | ""bar"", 41 | ] 42 | `; 43 | 44 | exports[`resolveObjectKeysToArray > resolves Object.keys when using resolvable spread 1`] = ` 45 | [ 46 | ""boo"", 47 | ""foo"", 48 | ""doo"", 49 | ] 50 | `; 51 | 52 | exports[`resolveObjectKeysToArray > resolves Object.keys when using setters 1`] = ` 53 | [ 54 | ""boo"", 55 | ""foo"", 56 | ""bar"", 57 | ] 58 | `; 59 | 60 | exports[`resolveObjectKeysToArray > resolves Object.keys with identifiers 1`] = ` 61 | [ 62 | ""bar"", 63 | ""foo"", 64 | ] 65 | `; 66 | 67 | exports[`resolveObjectKeysToArray > resolves Object.keys with literals 1`] = ` 68 | [ 69 | ""bar"", 70 | ""5"", 71 | ] 72 | `; 73 | 74 | exports[`resolveObjectKeysToArray > resolves Object.keys with literals as computed key 1`] = ` 75 | [ 76 | ""bar"", 77 | ""5"", 78 | ] 79 | `; 80 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/__tests__/__snapshots__/resolveObjectPatternPropertyToValue-test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`resolveObjectPatternPropertyToValue > AssignmentExpression > resolved basic case 1`] = ` 4 | Node { 5 | "extra": { 6 | "raw": ""string"", 7 | "rawValue": "string", 8 | }, 9 | "type": "StringLiteral", 10 | "value": "string", 11 | } 12 | `; 13 | 14 | exports[`resolveObjectPatternPropertyToValue > VariableDeclarator > resolved basic case 1`] = ` 15 | Node { 16 | "extra": { 17 | "raw": ""string"", 18 | "rawValue": "string", 19 | }, 20 | "type": "StringLiteral", 21 | "value": "string", 22 | } 23 | `; 24 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/__tests__/__snapshots__/resolveObjectValuesToArray-test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`resolveObjectValuesToArray > can resolve imported objects passed to Object.values 1`] = ` 4 | [ 5 | ""bar"", 6 | ""foo"", 7 | "0", 8 | "5", 9 | "null", 10 | "null", 11 | "7", 12 | ""foo"", 13 | ] 14 | `; 15 | 16 | exports[`resolveObjectValuesToArray > can resolve spreads from imported objects 1`] = ` 17 | [ 18 | ""foo"", 19 | ""baz"", 20 | ""bar"", 21 | ] 22 | `; 23 | 24 | exports[`resolveObjectValuesToArray > resolves Object.values but ignores duplicates 1`] = ` 25 | [ 26 | "1", 27 | "2", 28 | "5", 29 | ] 30 | `; 31 | 32 | exports[`resolveObjectValuesToArray > resolves Object.values but ignores duplicates with getter and setter 1`] = `[]`; 33 | 34 | exports[`resolveObjectValuesToArray > resolves Object.values when using getters 1`] = ` 35 | [ 36 | "1", 37 | "2", 38 | ] 39 | `; 40 | 41 | exports[`resolveObjectValuesToArray > resolves Object.values when using methods 1`] = ` 42 | [ 43 | "1", 44 | "2", 45 | ] 46 | `; 47 | 48 | exports[`resolveObjectValuesToArray > resolves Object.values when using resolvable spread 1`] = ` 49 | [ 50 | "1", 51 | "2", 52 | "4", 53 | ] 54 | `; 55 | 56 | exports[`resolveObjectValuesToArray > resolves Object.values when using setters 1`] = ` 57 | [ 58 | "1", 59 | "2", 60 | ] 61 | `; 62 | 63 | exports[`resolveObjectValuesToArray > resolves Object.values with literals as computed key 1`] = ` 64 | [ 65 | "1", 66 | "2", 67 | ] 68 | `; 69 | 70 | exports[`resolveObjectValuesToArray > resolves Object.values with numbers 1`] = ` 71 | [ 72 | "0", 73 | "5", 74 | ] 75 | `; 76 | 77 | exports[`resolveObjectValuesToArray > resolves Object.values with strings 1`] = ` 78 | [ 79 | ""bar"", 80 | ""foo"", 81 | ] 82 | `; 83 | 84 | exports[`resolveObjectValuesToArray > resolves Object.values with undefined or null 1`] = ` 85 | [ 86 | "null", 87 | "null", 88 | ] 89 | `; 90 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/__tests__/__snapshots__/unwrapBuiltinTSPropTypes-test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`unwrapBuiltinTSPropTypes > React.PropsWithChildren 1`] = ` 4 | Node { 5 | "type": "TSTypeReference", 6 | "typeName": Node { 7 | "name": "Props", 8 | "type": "Identifier", 9 | }, 10 | } 11 | `; 12 | 13 | exports[`unwrapBuiltinTSPropTypes > React.PropsWithRef 1`] = ` 14 | Node { 15 | "type": "TSTypeReference", 16 | "typeName": Node { 17 | "name": "Props", 18 | "type": "Identifier", 19 | }, 20 | } 21 | `; 22 | 23 | exports[`unwrapBuiltinTSPropTypes > React.PropsWithoutRef 1`] = ` 24 | Node { 25 | "type": "TSTypeReference", 26 | "typeName": Node { 27 | "name": "Props", 28 | "type": "Identifier", 29 | }, 30 | } 31 | `; 32 | 33 | exports[`unwrapBuiltinTSPropTypes > does not follow reassignment 1`] = ` 34 | Node { 35 | "type": "TSTypeReference", 36 | "typeName": Node { 37 | "name": "bar", 38 | "type": "Identifier", 39 | }, 40 | } 41 | `; 42 | 43 | exports[`unwrapBuiltinTSPropTypes > multiple 1`] = ` 44 | Node { 45 | "type": "TSTypeReference", 46 | "typeName": Node { 47 | "name": "Props", 48 | "type": "Identifier", 49 | }, 50 | } 51 | `; 52 | 53 | exports[`unwrapBuiltinTSPropTypes > with named import 1`] = ` 54 | Node { 55 | "type": "TSTypeReference", 56 | "typeName": Node { 57 | "name": "Props", 58 | "type": "Identifier", 59 | }, 60 | } 61 | `; 62 | 63 | exports[`unwrapBuiltinTSPropTypes > with require 1`] = ` 64 | Node { 65 | "type": "TSTypeReference", 66 | "typeName": Node { 67 | "name": "Props", 68 | "type": "Identifier", 69 | }, 70 | } 71 | `; 72 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/__tests__/docblock-test.ts: -------------------------------------------------------------------------------- 1 | import { parse } from '../../../tests/utils'; 2 | import { getDoclets, getDocblock } from '../docblock.js'; 3 | import { describe, expect, test } from 'vitest'; 4 | 5 | describe('docblock', () => { 6 | describe('getDoclets', () => { 7 | test('extracts single line doclets', () => { 8 | expect(getDoclets('@foo bar\n@bar baz')).toEqual({ 9 | foo: 'bar', 10 | bar: 'baz', 11 | }); 12 | }); 13 | 14 | test('extracts multi line doclets', () => { 15 | expect(getDoclets('@foo bar\nbaz\n@bar baz')).toEqual({ 16 | foo: 'bar\nbaz', 17 | bar: 'baz', 18 | }); 19 | }); 20 | 21 | test('extracts boolean doclets', () => { 22 | expect(getDoclets('@foo bar\nbaz\n@abc\n@bar baz')).toEqual({ 23 | foo: 'bar\nbaz', 24 | abc: true, 25 | bar: 'baz', 26 | }); 27 | }); 28 | }); 29 | 30 | describe('getDocblock', () => { 31 | const comment = ['This is a docblock.', 'This is the second line.']; 32 | const source = [ 33 | '/**', 34 | ` * ${comment[0]}`, 35 | ` * ${comment[1]}`, 36 | ' */', 37 | 'foo;', 38 | ]; 39 | 40 | test('gets the closest docblock of the given node', () => { 41 | const node = parse.statement(source.join('\n')); 42 | 43 | expect(getDocblock(node)).toEqual(comment.join('\n')); 44 | }); 45 | 46 | const terminators = [ 47 | '\u000A', // \n 48 | '\u000D', // \r 49 | '\u2028', 50 | '\u2029', 51 | '\u000D\u000A', // \r\n 52 | ]; 53 | 54 | terminators.forEach((t) => { 55 | test('can handle ' + escape(t) + ' as line terminator', () => { 56 | const node = parse.statement(source.join(t)); 57 | 58 | expect(getDocblock(node)).toEqual(comment.join(t)); 59 | }); 60 | }); 61 | 62 | test('supports "short" docblocks', () => { 63 | const node = parse.statement('/** bar */\nfoo;'); 64 | 65 | expect(getDocblock(node)).toEqual('bar'); 66 | }); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/__tests__/expressionTo-test.ts: -------------------------------------------------------------------------------- 1 | import { parse, parseTypescript } from '../../../tests/utils'; 2 | import { Array as expressionToArray } from '../expressionTo.js'; 3 | import { describe, expect, test } from 'vitest'; 4 | 5 | describe('expressionTo', () => { 6 | describe('MemberExpression', () => { 7 | test('with only identifiers', () => { 8 | expect(expressionToArray(parse.expression('foo.bar.baz'))).toEqual([ 9 | 'foo', 10 | 'bar', 11 | 'baz', 12 | ]); 13 | }); 14 | 15 | test('with one computed literal', () => { 16 | expect(expressionToArray(parse.expression('foo["bar"].baz'))).toEqual([ 17 | 'foo', 18 | '"bar"', 19 | 'baz', 20 | ]); 21 | }); 22 | 23 | test('with one computed identifier', () => { 24 | expect(expressionToArray(parse.expression('foo[bar].baz'))).toEqual([ 25 | 'foo', 26 | 'bar', 27 | 'baz', 28 | ]); 29 | }); 30 | 31 | test('with one computed object', () => { 32 | expect( 33 | expressionToArray(parse.expression('foo[{ a: "true"}].baz')), 34 | ).toEqual(['foo', '{a: "true"}', 'baz']); 35 | }); 36 | 37 | test('with one computed object with spread', () => { 38 | expect(expressionToArray(parse.expression('foo[{ ...a }].baz'))).toEqual([ 39 | 'foo', 40 | '{...a}', 41 | 'baz', 42 | ]); 43 | }); 44 | 45 | test('with one computed object with method', () => { 46 | expect(expressionToArray(parse.expression('foo[{ a(){} }].baz'))).toEqual( 47 | ['foo', '{a: }', 'baz'], 48 | ); 49 | }); 50 | 51 | test('with TSAsExpression', () => { 52 | expect( 53 | expressionToArray(parseTypescript.expression('(baz as X).prop')), 54 | ).toEqual(['baz', 'prop']); 55 | }); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/__tests__/getMemberExpressionRoot-test.ts: -------------------------------------------------------------------------------- 1 | import type { MemberExpression } from '@babel/types'; 2 | import { parse } from '../../../tests/utils'; 3 | import getMemberExpressionRoot from '../getMemberExpressionRoot.js'; 4 | import { describe, expect, test } from 'vitest'; 5 | 6 | describe('getMemberExpressionRoot', () => { 7 | test('returns the root of a member expression', () => { 8 | const root = getMemberExpressionRoot(parse.expression('foo.bar.baz')); 9 | 10 | expect(root).toMatchSnapshot(); 11 | }); 12 | 13 | test('returns the same path if identifier', () => { 14 | const id = parse.expression('foo'); 15 | const root = getMemberExpressionRoot(id); 16 | 17 | expect(root).toMatchSnapshot(); 18 | }); 19 | 20 | test('returns the same path if literal', () => { 21 | const literal = parse.expression('1'); 22 | const root = getMemberExpressionRoot(literal); 23 | 24 | expect(root).toMatchSnapshot(); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/__tests__/getMembers-test.ts: -------------------------------------------------------------------------------- 1 | import { parse } from '../../../tests/utils'; 2 | import getMembers from '../getMembers.js'; 3 | import { describe, expect, test } from 'vitest'; 4 | 5 | describe('getMembers', () => { 6 | test('finds all "members" "inside" a MemberExpression', () => { 7 | const members = getMembers(parse.expression('foo.bar(123)(456)[baz][42]')); 8 | 9 | expect(members).toMatchSnapshot(); 10 | }); 11 | 12 | test('includes the root if option set to true', () => { 13 | const members = getMembers(parse.expression('foo.bar(123)[baz]'), true); 14 | 15 | expect(members).toMatchSnapshot(); 16 | }); 17 | 18 | test('does work with custom expressions in chain', () => { 19 | const members = getMembers(parse.expression('foo.bar(123)["" + ""]')); 20 | 21 | expect(members).toMatchSnapshot(); 22 | }); 23 | 24 | test('does work with custom expressions in arguments', () => { 25 | const members = getMembers(parse.expression('foo.bar(123 + 123)["baz"]')); 26 | 27 | expect(members).toMatchSnapshot(); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/__tests__/getTypeAnnotation-test.ts: -------------------------------------------------------------------------------- 1 | import type { TypeCastExpression } from '@babel/types'; 2 | import { parse } from '../../../tests/utils'; 3 | import getTypeAnnotation from '../getTypeAnnotation.js'; 4 | import { describe, expect, test } from 'vitest'; 5 | 6 | describe('getTypeAnnotation', () => { 7 | test('detects simple type', () => { 8 | const path = parse.expression('x: xyz'); 9 | 10 | expect(getTypeAnnotation(path)).toEqual( 11 | path.get('typeAnnotation').get('typeAnnotation'), 12 | ); 13 | }); 14 | 15 | test('does not fail if no type', () => { 16 | const path = parse.expression('x = 0'); 17 | 18 | expect(getTypeAnnotation(path)).toEqual(null); 19 | }); 20 | 21 | test('stops at first nested type', () => { 22 | const path = parse.expression('x: ?xyz'); 23 | 24 | expect(getTypeAnnotation(path)).toEqual( 25 | path.get('typeAnnotation').get('typeAnnotation'), 26 | ); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/__tests__/getTypeParameters-test.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | TSTypeAliasDeclaration, 3 | TSTypeParameterDeclaration, 4 | TSTypeParameterInstantiation, 5 | TypeAlias, 6 | TypeParameterDeclaration, 7 | TypeParameterInstantiation, 8 | } from '@babel/types'; 9 | import { parse, parseTypescript } from '../../../tests/utils'; 10 | import getTypeParameters from '../getTypeParameters.js'; 11 | import { describe, expect, test } from 'vitest'; 12 | import type { NodePath } from '@babel/traverse'; 13 | 14 | describe('getTypeParameters', () => { 15 | describe('TypeScript', () => { 16 | test('detects simple type', () => { 17 | const path = 18 | parseTypescript.statement('type x = y'); 19 | 20 | expect( 21 | getTypeParameters( 22 | path.get('typeParameters') as NodePath, 23 | path 24 | .get('typeAnnotation') 25 | .get('typeParameters') as NodePath, 26 | null, 27 | ), 28 | ).toMatchSnapshot(); 29 | }); 30 | test('detects default', () => { 31 | const path = parseTypescript.statement( 32 | 'type x = y;', 33 | ); 34 | 35 | expect( 36 | getTypeParameters( 37 | path.get('typeParameters') as NodePath, 38 | path 39 | .get('typeAnnotation') 40 | .get('typeParameters') as NodePath, 41 | null, 42 | ), 43 | ).toMatchSnapshot(); 44 | }); 45 | }); 46 | describe('Flow', () => { 47 | test('detects simple type', () => { 48 | const path = parse.statement('type x = y'); 49 | 50 | expect( 51 | getTypeParameters( 52 | path.get('typeParameters') as NodePath, 53 | path 54 | .get('right') 55 | .get('typeParameters') as NodePath, 56 | null, 57 | ), 58 | ).toMatchSnapshot(); 59 | }); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/__tests__/isDestructuringAssignment-test.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import type { Node } from '@babel/types'; 3 | import { parse } from '../../../tests/utils'; 4 | import isDestructuringAssignment from '../isDestructuringAssignment.js'; 5 | import { describe, expect, test } from 'vitest'; 6 | 7 | describe('isDestructuringAssignment', () => { 8 | test('detects destructuring', () => { 9 | const def = parse(` 10 | var { Component } = require('react'); 11 | `).get('body.0.declarations.0.id.properties.0') as NodePath; 12 | 13 | expect(isDestructuringAssignment(def, 'Component')).toBe(true); 14 | }); 15 | 16 | test('fails if name does not match', () => { 17 | const def = parse(` 18 | var { Component } = require('react'); 19 | `).get('body.0.declarations.0.id.properties.0') as NodePath; 20 | 21 | expect(isDestructuringAssignment(def, 'Component2')).toBe(false); 22 | }); 23 | 24 | test('detects destructuring with alias', () => { 25 | const def = parse(` 26 | var { Component: C } = require('react'); 27 | `).get('body.0.declarations.0.id.properties.0') as NodePath; 28 | 29 | expect(isDestructuringAssignment(def, 'Component')).toBe(true); 30 | }); 31 | 32 | test('fails if name does not match with alias', () => { 33 | const def = parse(` 34 | var { Component: C } = require('react'); 35 | `).get('body.0.declarations.0.id.properties.0') as NodePath; 36 | 37 | expect(isDestructuringAssignment(def, 'Component2')).toBe(false); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/__tests__/isExportsOrModuleAssignment-test.ts: -------------------------------------------------------------------------------- 1 | import { parse } from '../../../tests/utils'; 2 | import isExportsOrModuleAssignment from '../isExportsOrModuleAssignment.js'; 3 | import { describe, expect, test } from 'vitest'; 4 | 5 | describe('isExportsOrModuleAssignment', () => { 6 | test('detects "module.exports = ...;"', () => { 7 | expect( 8 | isExportsOrModuleAssignment(parse.expression('module.exports = foo')), 9 | ).toBe(true); 10 | }); 11 | 12 | test('detects "exports.foo = ..."', () => { 13 | expect( 14 | isExportsOrModuleAssignment(parse.expression('exports.foo = foo')), 15 | ).toBe(true); 16 | }); 17 | 18 | test('does not accept "exports = foo;"', () => { 19 | // That doesn't actually export anything 20 | expect(isExportsOrModuleAssignment(parse.expression('exports = foo'))).toBe( 21 | false, 22 | ); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/__tests__/isReactModuleName-test.ts: -------------------------------------------------------------------------------- 1 | import isReactModuleName from '../isReactModuleName.js'; 2 | import { describe, expect, test } from 'vitest'; 3 | 4 | describe('isReactModuleName', () => { 5 | const reactModules = [ 6 | 'react', 7 | 'react/addons', 8 | 'react-native', 9 | 'proptypes', 10 | 'prop-types', 11 | ]; 12 | 13 | reactModules.forEach((module) => { 14 | test(`returns true for ${module}`, () => { 15 | expect(isReactModuleName(module)).toBe(true); 16 | }); 17 | }); 18 | 19 | test(`returns false by default`, () => { 20 | expect(isReactModuleName('not-react')).toBe(false); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/__tests__/isRequiredPropType-test.ts: -------------------------------------------------------------------------------- 1 | import { parse } from '../../../tests/utils'; 2 | import isRequiredPropType from '../isRequiredPropType.js'; 3 | import { describe, expect, test } from 'vitest'; 4 | 5 | describe('isRequiredPropType', () => { 6 | test('considers isRequired', () => { 7 | expect(isRequiredPropType(parse.expression('foo.bar.isRequired'))).toEqual( 8 | true, 9 | ); 10 | expect(isRequiredPropType(parse.expression('foo.isRequired.bar'))).toEqual( 11 | true, 12 | ); 13 | }); 14 | 15 | test('considers ["isRequired"]', () => { 16 | expect( 17 | isRequiredPropType(parse.expression('foo.bar["isRequired"]')), 18 | ).toEqual(true); 19 | expect( 20 | isRequiredPropType(parse.expression('foo["isRequired"].bar')), 21 | ).toEqual(true); 22 | }); 23 | 24 | test('ignores variables', () => { 25 | expect(isRequiredPropType(parse.expression('foo.bar[isRequired]'))).toEqual( 26 | false, 27 | ); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/__tests__/isUnreachableFlowType-test.ts: -------------------------------------------------------------------------------- 1 | import type { ImportDeclaration } from '@babel/types'; 2 | import { parse } from '../../../tests/utils'; 3 | import isUnreachableFlowType from '../isUnreachableFlowType.js'; 4 | import { describe, expect, test } from 'vitest'; 5 | 6 | describe('isUnreachableFlowType', () => { 7 | test('considers Identifier as unreachable', () => { 8 | expect(isUnreachableFlowType(parse.expression('foo'))).toBe(true); 9 | }); 10 | 11 | test('considers any ImportSpecifier as unreachable', () => { 12 | expect( 13 | isUnreachableFlowType( 14 | parse 15 | .statement('import x from "";') 16 | .get('specifiers')[0], 17 | ), 18 | ).toBe(true); 19 | }); 20 | 21 | test('considers CallExpression as unreachable', () => { 22 | expect(isUnreachableFlowType(parse.expression('foo()'))).toBe(true); 23 | }); 24 | 25 | test('considers VariableDeclaration not as unreachable', () => { 26 | expect(isUnreachableFlowType(parse.statement('const x = 1;'))).toBe(false); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/__tests__/parseJsDoc-test.ts: -------------------------------------------------------------------------------- 1 | import parseJsDoc from '../parseJsDoc.js'; 2 | import { describe, expect, test } from 'vitest'; 3 | 4 | describe('parseJsDoc', () => { 5 | describe('description', () => { 6 | test('extracts the method description in jsdoc', () => { 7 | const docblock = ` 8 | Don't use this! 9 | `; 10 | 11 | expect(parseJsDoc(docblock)).toMatchSnapshot(); 12 | }); 13 | }); 14 | describe('@param', () => { 15 | const docBlocks = { 16 | 'extracts jsdoc description': '@param bar test', 17 | 'extracts jsdoc empty description': '@param {string} bar', 18 | 'extracts jsdoc union type param': '@param {string|Object|some[]} bar', 19 | 'extracts jsdoc optional': '@param {string=} bar', 20 | 'extracts jsdoc typed array': '@param {[string, number]} bar', 21 | }; 22 | 23 | Object.keys(docBlocks).forEach((name) => { 24 | const docBlock = docBlocks[name]; 25 | 26 | test(name, () => { 27 | expect(parseJsDoc(docBlock)).toMatchSnapshot(); 28 | }); 29 | }); 30 | }); 31 | describe('@returns', () => { 32 | const docBlocks = { 33 | 'extracts jsdoc types': '@returns {string}', 34 | 'extracts jsdoc mixed types': '@returns {*}', 35 | 'extracts description from jsdoc': '@returns The number', 36 | 'works with @return': '@return The number', 37 | 'extracts jsdoc typed array': '@param {[string, number]} bar', 38 | }; 39 | 40 | Object.keys(docBlocks).forEach((name) => { 41 | const docBlock = docBlocks[name]; 42 | 43 | test(name, () => { 44 | expect(parseJsDoc(docBlock)).toMatchSnapshot(); 45 | }); 46 | }); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/__tests__/resolveFunctionDefinitionToReturnValue-test.ts: -------------------------------------------------------------------------------- 1 | import type { FunctionDeclaration } from '@babel/types'; 2 | import { parse } from '../../../tests/utils'; 3 | import resolveFunctionDefinitionToReturnValue from '../resolveFunctionDefinitionToReturnValue.js'; 4 | import { describe, expect, test } from 'vitest'; 5 | 6 | describe('resolveFunctionDefinitionToReturnValue', () => { 7 | test('can resolve easy return statement', () => { 8 | const path = parse.statement(` 9 | function x () { return "result"; } 10 | `); 11 | 12 | expect(resolveFunctionDefinitionToReturnValue(path)).toMatchSnapshot(); 13 | }); 14 | 15 | test('stops after first return', () => { 16 | const path = parse.statement(` 17 | function x () { return "first"; return "second"; } 18 | `); 19 | 20 | expect(resolveFunctionDefinitionToReturnValue(path)).toMatchSnapshot(); 21 | }); 22 | 23 | test('ignores return values in other blocks', () => { 24 | const path = parse.statement(` 25 | function x () { 26 | const a = function () { return "funcexpr"; } 27 | const b = () => { return "arrow"; } 28 | function c () { return "funcdecl"; } 29 | const d = { 30 | d() { return "objmthd"; } 31 | } 32 | 33 | class A { 34 | method() { 35 | return "classmthd"; 36 | } 37 | } 38 | 39 | if (e) { 40 | return "if"; 41 | } else { 42 | return "else"; 43 | } 44 | } 45 | `); 46 | 47 | expect(resolveFunctionDefinitionToReturnValue(path)).toBeNull(); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/__tests__/resolveGenericTypeAnnotations-test.ts: -------------------------------------------------------------------------------- 1 | import { parse } from '../../../tests/utils'; 2 | import resolveGenericTypeAnnotation from '../resolveGenericTypeAnnotation.js'; 3 | import { describe, expect, test } from 'vitest'; 4 | 5 | describe('resolveGenericTypeAnnotation', () => { 6 | test('resolves type', () => { 7 | const code = ` 8 | var x: Props; 9 | type Props = { x: string }; 10 | `; 11 | 12 | expect( 13 | resolveGenericTypeAnnotation( 14 | parse 15 | .statement(code) 16 | .get('declarations')[0] 17 | .get('id') 18 | .get('typeAnnotation') 19 | .get('typeAnnotation'), 20 | ), 21 | ).toMatchSnapshot(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/docblock.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Helper functions to work with docblock comments. 3 | */ 4 | 5 | import type { NodePath } from '@babel/traverse'; 6 | import type { CommentBlock, CommentLine } from '@babel/types'; 7 | 8 | const DOCLET_PATTERN = /^@(\w+)(?:$|\s((?:[^](?!^@\w))*))/gim; 9 | 10 | function parseDocblock(str: string): string { 11 | // Does not use \s in the regex as this would match also \n and conflicts 12 | // with windows line endings. 13 | return str.replace(/^[ \t]*\*[ \t]?/gm, '').trim(); 14 | } 15 | 16 | const DOCBLOCK_HEADER = /^\*\s/; 17 | 18 | /** 19 | * Given a path, this function returns the closest preceding docblock if it 20 | * exists. 21 | */ 22 | export function getDocblock(path: NodePath, trailing = false): string | null { 23 | let comments: Array = []; 24 | 25 | if (trailing && path.node.trailingComments) { 26 | comments = path.node.trailingComments.filter( 27 | (comment) => 28 | comment.type === 'CommentBlock' && DOCBLOCK_HEADER.test(comment.value), 29 | ); 30 | } else if (path.node.leadingComments) { 31 | comments = path.node.leadingComments.filter( 32 | (comment) => 33 | comment.type === 'CommentBlock' && DOCBLOCK_HEADER.test(comment.value), 34 | ); 35 | } 36 | 37 | if (comments.length > 0) { 38 | return parseDocblock(comments[comments.length - 1]!.value); 39 | } 40 | 41 | return null; 42 | } 43 | 44 | /** 45 | * Given a string, this functions returns an object with doclet names as keys 46 | * and their "content" as values. 47 | */ 48 | export function getDoclets(str: string): Record { 49 | const doclets = Object.create(null); 50 | let match: RegExpExecArray | null; 51 | 52 | while ((match = DOCLET_PATTERN.exec(str))) { 53 | doclets[match[1]!] = match[2] || true; 54 | } 55 | 56 | return doclets; 57 | } 58 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/findComponentDefinition.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import type { ComponentNode } from '../resolver/index.js'; 3 | import isReactComponentClass from './isReactComponentClass.js'; 4 | import isReactCreateClassCall from './isReactCreateClassCall.js'; 5 | import isReactForwardRefCall from './isReactForwardRefCall.js'; 6 | import isStatelessComponent from './isStatelessComponent.js'; 7 | import normalizeClassDefinition from './normalizeClassDefinition.js'; 8 | import resolveHOC from './resolveHOC.js'; 9 | import resolveToValue from './resolveToValue.js'; 10 | 11 | function isComponentDefinition( 12 | path: NodePath, 13 | ): path is NodePath { 14 | return ( 15 | isReactCreateClassCall(path) || 16 | isReactComponentClass(path) || 17 | isStatelessComponent(path) || 18 | isReactForwardRefCall(path) 19 | ); 20 | } 21 | 22 | function resolveComponentDefinition( 23 | definition: NodePath, 24 | ): NodePath | null { 25 | if (isReactCreateClassCall(definition)) { 26 | // return argument 27 | const resolvedPath = resolveToValue(definition.get('arguments')[0]!); 28 | 29 | if (resolvedPath.isObjectExpression()) { 30 | return resolvedPath; 31 | } 32 | } else if (isReactComponentClass(definition)) { 33 | normalizeClassDefinition(definition); 34 | 35 | return definition; 36 | } else if ( 37 | isStatelessComponent(definition) || 38 | isReactForwardRefCall(definition) 39 | ) { 40 | return definition; 41 | } 42 | 43 | return null; 44 | } 45 | 46 | export default function findComponentDefinition( 47 | path: NodePath, 48 | ): NodePath | null { 49 | let resolvedPath = path; 50 | 51 | if (!isComponentDefinition(resolvedPath)) { 52 | resolvedPath = resolveToValue(resolveHOC(resolvedPath)); 53 | if (!isComponentDefinition(resolvedPath)) { 54 | return null; 55 | } 56 | } 57 | 58 | return resolveComponentDefinition(resolvedPath); 59 | } 60 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/flowUtilityTypes.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import type { GenericTypeAnnotation } from '@babel/types'; 3 | 4 | /** 5 | * See `supportedUtilityTypes` for which types are supported and 6 | * https://flow.org/en/docs/types/utilities/ for which types are available. 7 | */ 8 | export function isSupportedUtilityType( 9 | path: NodePath, 10 | ): path is NodePath { 11 | if (path.isGenericTypeAnnotation()) { 12 | const idPath = path.get('id'); 13 | 14 | if (idPath.isIdentifier()) { 15 | const name = idPath.node.name; 16 | 17 | return name === '$Exact' || name === '$ReadOnly'; 18 | } 19 | } 20 | 21 | return false; 22 | } 23 | 24 | /** 25 | * Unwraps well known utility types. For example: 26 | * 27 | * $ReadOnly => T 28 | */ 29 | export function unwrapUtilityType(path: NodePath): NodePath { 30 | let resultPath: NodePath = path; 31 | 32 | while (isSupportedUtilityType(resultPath)) { 33 | const typeParameters = resultPath.get('typeParameters'); 34 | 35 | if (!typeParameters.hasNode()) break; 36 | 37 | const firstParam = typeParameters.get('params')[0]; 38 | 39 | if (!firstParam) break; 40 | 41 | resultPath = firstParam; 42 | } 43 | 44 | return resultPath; 45 | } 46 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/getClassMemberValuePath.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import type { 3 | ClassDeclaration, 4 | ClassExpression, 5 | ClassMethod, 6 | ClassProperty, 7 | Expression, 8 | } from '@babel/types'; 9 | import getNameOrValue from './getNameOrValue.js'; 10 | 11 | export default function getClassMemberValuePath( 12 | classDefinition: NodePath, 13 | memberName: string, 14 | ): NodePath | null { 15 | const classMember = classDefinition 16 | .get('body') 17 | .get('body') 18 | .find((memberPath) => { 19 | if ( 20 | (memberPath.isClassMethod() && memberPath.node.kind !== 'set') || 21 | memberPath.isClassProperty() 22 | ) { 23 | const key = (memberPath as NodePath).get( 24 | 'key', 25 | ); 26 | 27 | return ( 28 | (!memberPath.node.computed || key.isLiteral()) && 29 | getNameOrValue(key) === memberName 30 | ); 31 | } 32 | 33 | return false; 34 | }); 35 | 36 | if (classMember) { 37 | // For ClassProperty we return the value and for ClassMethod 38 | // we return itself 39 | return classMember.isClassMethod() 40 | ? classMember 41 | : (classMember.get('value') as NodePath); 42 | } 43 | 44 | return null; 45 | } 46 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/getMemberExpressionRoot.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import type { Expression, MemberExpression } from '@babel/types'; 3 | 4 | /** 5 | * Returns the path to the first part of the MemberExpression. I.e. given a 6 | * path representing 7 | * 8 | * foo.bar.baz 9 | * 10 | * it returns the path of/to `foo`. 11 | */ 12 | export default function getMemberExpressionRoot( 13 | memberExpressionPath: NodePath, 14 | ): NodePath { 15 | let path: NodePath = memberExpressionPath; 16 | 17 | while (path.isMemberExpression()) { 18 | path = path.get('object'); 19 | } 20 | 21 | return path; 22 | } 23 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/getMembers.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import type { Expression, PrivateName } from '@babel/types'; 3 | 4 | interface MemberDescriptor { 5 | path: NodePath; 6 | computed: boolean; 7 | argumentPaths: NodePath[]; 8 | } 9 | 10 | /** 11 | * Given a "nested" Member/CallExpression, e.g. 12 | * 13 | * foo.bar()[baz][42] 14 | * 15 | * this returns a list of "members". In this example it would be something like 16 | * [ 17 | * {path: NodePath, arguments: NodePath, computed: false}, 18 | * {path: NodePath, arguments: null, computed: true}, 19 | * {path: NodePath<42>, arguments: null, computed: false} 20 | * ] 21 | */ 22 | export default function getMembers( 23 | path: NodePath, 24 | includeRoot = false, 25 | ): MemberDescriptor[] { 26 | const result: MemberDescriptor[] = []; 27 | let argumentPaths: NodePath[] = []; 28 | let resultPath: MemberDescriptor['path'] = path as NodePath; 29 | 30 | while (true) { 31 | if (resultPath.isMemberExpression()) { 32 | const property = resultPath.get('property'); 33 | 34 | result.push({ 35 | path: property, 36 | computed: resultPath.node.computed, 37 | argumentPaths, 38 | }); 39 | argumentPaths = []; 40 | resultPath = resultPath.get('object'); 41 | } else if (resultPath.isCallExpression()) { 42 | const callee = resultPath.get('callee'); 43 | 44 | if (callee.isExpression()) { 45 | argumentPaths = resultPath.get('arguments'); 46 | resultPath = callee; 47 | } else { 48 | break; 49 | } 50 | } else { 51 | break; 52 | } 53 | } 54 | if (includeRoot && result.length > 0) { 55 | result.push({ 56 | path: resultPath, 57 | computed: false, 58 | argumentPaths, 59 | }); 60 | } 61 | 62 | return result.reverse(); 63 | } 64 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/getNameOrValue.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import printValue from './printValue.js'; 3 | 4 | /** 5 | * If node is an Identifier, it returns its name. If it is a literal, it returns 6 | * its value. 7 | */ 8 | export default function getNameOrValue( 9 | path: NodePath, 10 | ): boolean | number | string | null { 11 | if (path.isIdentifier()) { 12 | return path.node.name; 13 | } else if (path.isQualifiedTypeIdentifier() || path.isTSQualifiedName()) { 14 | return printValue(path); 15 | } else if ( 16 | path.isStringLiteral() || 17 | path.isNumericLiteral() || 18 | path.isBooleanLiteral() 19 | ) { 20 | return path.node.value; 21 | } else if (path.isRegExpLiteral()) { 22 | return path.node.pattern; 23 | } else if (path.isNullLiteral()) { 24 | return null; 25 | } 26 | 27 | throw new TypeError( 28 | `Argument must be Identifier, Literal, QualifiedTypeIdentifier or TSQualifiedName. Received '${path.node.type}'`, 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/getParameterName.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import type { 3 | ArrayPattern, 4 | AssignmentPattern, 5 | Identifier, 6 | ObjectPattern, 7 | RestElement, 8 | TSParameterProperty, 9 | } from '@babel/types'; 10 | import printValue from './printValue.js'; 11 | 12 | type ParameterNodePath = NodePath< 13 | | ArrayPattern 14 | | AssignmentPattern 15 | | Identifier 16 | | ObjectPattern 17 | | RestElement 18 | | TSParameterProperty 19 | >; 20 | 21 | export default function getParameterName( 22 | parameterPath: ParameterNodePath, 23 | ): string { 24 | if (parameterPath.isIdentifier()) { 25 | return parameterPath.node.name; 26 | } else if (parameterPath.isAssignmentPattern()) { 27 | return getParameterName(parameterPath.get('left') as ParameterNodePath); 28 | } else if ( 29 | parameterPath.isObjectPattern() || 30 | parameterPath.isArrayPattern() 31 | ) { 32 | return printValue(parameterPath); 33 | } else if (parameterPath.isRestElement()) { 34 | return `...${getParameterName( 35 | parameterPath.get('argument') as ParameterNodePath, 36 | )}`; 37 | } else if (parameterPath.isTSParameterProperty()) { 38 | return getParameterName(parameterPath.get('parameter')); 39 | } 40 | 41 | throw new TypeError( 42 | 'Parameter name must be one of Identifier, AssignmentPattern, ArrayPattern, ' + 43 | `ObjectPattern or RestElement, instead got ${parameterPath.node.type}`, 44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/getPropertyValuePath.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import type { Expression, ObjectExpression, ObjectMethod } from '@babel/types'; 3 | import getPropertyName from './getPropertyName.js'; 4 | 5 | /** 6 | * Given an ObjectExpression, this function returns the path of the value of 7 | * the property with name `propertyName`. if the property is an ObjectMethod we 8 | * return the ObjectMethod itself. 9 | */ 10 | export default function getPropertyValuePath( 11 | path: NodePath, 12 | propertyName: string, 13 | ): NodePath | null { 14 | const property = path 15 | .get('properties') 16 | .find( 17 | (propertyPath) => 18 | !propertyPath.isSpreadElement() && 19 | getPropertyName(propertyPath) === propertyName, 20 | ); 21 | 22 | if (property) { 23 | return property.isObjectMethod() 24 | ? property 25 | : (property.get('value') as NodePath); 26 | } 27 | 28 | return null; 29 | } 30 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/getTypeAnnotation.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import type { FlowType, Node, TSType } from '@babel/types'; 3 | 4 | /** 5 | * Gets the most inner valuable TypeAnnotation from path. If no TypeAnnotation 6 | * can be found null is returned 7 | */ 8 | export default function getTypeAnnotation( 9 | path: NodePath, 10 | ): NodePath | null { 11 | if (!path.has('typeAnnotation')) return null; 12 | 13 | let resultPath = path; 14 | 15 | do { 16 | resultPath = resultPath.get('typeAnnotation') as NodePath; 17 | } while ( 18 | resultPath.has('typeAnnotation') && 19 | !resultPath.isFlowType() && 20 | !resultPath.isTSType() 21 | ); 22 | 23 | return resultPath as NodePath; 24 | } 25 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/getTypeIdentifier.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | 3 | export default function getTypeIdentifier(path: NodePath): NodePath | null { 4 | if (path.has('id')) { 5 | return path.get('id') as NodePath; 6 | } else if (path.isTSTypeReference()) { 7 | return path.get('typeName'); 8 | } else if (path.isTSExpressionWithTypeArguments()) { 9 | return path.get('expression'); 10 | } 11 | 12 | return null; 13 | } 14 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/isDestructuringAssignment.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | 3 | /** 4 | * Checks if the input Identifier is part of a destructuring Assignment 5 | * and the name of the property key matches the input name 6 | */ 7 | export default function isDestructuringAssignment( 8 | path: NodePath, 9 | name: string, 10 | ): boolean { 11 | if (!path.isObjectProperty()) { 12 | return false; 13 | } 14 | 15 | const id = path.get('key'); 16 | 17 | return id.isIdentifier({ name }) && path.parentPath.isObjectPattern(); 18 | } 19 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/isExportsOrModuleAssignment.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import * as expressionTo from './expressionTo.js'; 3 | 4 | /** 5 | * Returns true if the expression is of form `exports.foo = ...;` or 6 | * `modules.exports = ...;`. 7 | */ 8 | export default function isExportsOrModuleAssignment(path: NodePath): boolean { 9 | if ( 10 | !path.isAssignmentExpression() || 11 | !path.get('left').isMemberExpression() 12 | ) { 13 | return false; 14 | } 15 | 16 | const exprArr = expressionTo.Array(path.get('left')); 17 | 18 | return ( 19 | (exprArr[0] === 'module' && exprArr[1] === 'exports') || 20 | exprArr[0] === 'exports' 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/isImportSpecifier.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | 3 | /** 4 | * Checks if the path is a ImportSpecifier that imports the given named export 5 | */ 6 | export default function isImportSpecifier( 7 | path: NodePath, 8 | name: string, 9 | ): boolean { 10 | return ( 11 | path.isImportSpecifier() && 12 | (path.get('imported').isIdentifier({ name }) || 13 | path.get('imported').isStringLiteral({ value: name })) 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/isReactBuiltinCall.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import type { CallExpression } from '@babel/types'; 3 | import isReactBuiltinReference from './isReactBuiltinReference.js'; 4 | 5 | /** 6 | * Returns true if the expression is a function call of the form 7 | * `React.foo(...)`. 8 | */ 9 | export default function isReactBuiltinCall( 10 | path: NodePath, 11 | name: string, 12 | ): path is NodePath { 13 | if (!path.isCallExpression()) { 14 | return false; 15 | } 16 | 17 | const callee = path.get('callee'); 18 | 19 | return isReactBuiltinReference(callee, name); 20 | } 21 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/isReactChildrenElementCall.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import type { CallExpression } from '@babel/types'; 3 | import isReactBuiltinReference from './isReactBuiltinReference.js'; 4 | 5 | /** 6 | * Returns true if the expression is a function call of the form 7 | * `React.Children.only(...)` or `React.Children.map(...)`. 8 | */ 9 | export default function isReactChildrenElementCall( 10 | path: NodePath, 11 | ): path is NodePath { 12 | if (!path.isCallExpression()) { 13 | return false; 14 | } 15 | 16 | const callee = path.get('callee'); 17 | 18 | if (callee.isMemberExpression()) { 19 | const calleeProperty = callee.get('property'); 20 | 21 | if ( 22 | calleeProperty.isIdentifier({ name: 'only' }) || 23 | calleeProperty.isIdentifier({ name: 'map' }) 24 | ) { 25 | return isReactBuiltinReference(callee.get('object'), 'Children'); 26 | } 27 | } 28 | 29 | return false; 30 | } 31 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/isReactCloneElementCall.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import type { CallExpression } from '@babel/types'; 3 | import isReactBuiltinCall from './isReactBuiltinCall.js'; 4 | 5 | /** 6 | * Returns true if the expression is a function call of the form 7 | * `React.cloneElement(...)`. 8 | */ 9 | export default function isReactCloneElementCall( 10 | path: NodePath, 11 | ): path is NodePath { 12 | return isReactBuiltinCall(path, 'cloneElement'); 13 | } 14 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/isReactComponentMethod.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import getPropertyName from './getPropertyName.js'; 3 | 4 | const componentMethods = [ 5 | 'componentDidMount', 6 | 'componentDidReceiveProps', 7 | 'componentDidUpdate', 8 | 'componentWillMount', 9 | 'UNSAFE_componentWillMount', 10 | 'componentWillReceiveProps', 11 | 'UNSAFE_componentWillReceiveProps', 12 | 'componentWillUnmount', 13 | 'componentWillUpdate', 14 | 'UNSAFE_componentWillUpdate', 15 | 'getChildContext', 16 | 'getDefaultProps', 17 | 'getInitialState', 18 | 'render', 19 | 'shouldComponentUpdate', 20 | 'getDerivedStateFromProps', 21 | 'getDerivedStateFromError', 22 | 'getSnapshotBeforeUpdate', 23 | 'componentDidCatch', 24 | ]; 25 | 26 | /** 27 | * Returns if the method path is a Component method. 28 | */ 29 | export default function (methodPath: NodePath): boolean { 30 | if ( 31 | !methodPath.isClassMethod() && 32 | !methodPath.isObjectMethod() && 33 | !methodPath.isObjectProperty() 34 | ) { 35 | return false; 36 | } 37 | 38 | const name = getPropertyName(methodPath); 39 | 40 | return Boolean(name && componentMethods.indexOf(name) !== -1); 41 | } 42 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/isReactCreateClassCall.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import resolveToModule from './resolveToModule.js'; 3 | import isReactBuiltinCall from './isReactBuiltinCall.js'; 4 | import type { CallExpression } from '@babel/types'; 5 | 6 | /** 7 | * Returns true if the expression is a function call of the form 8 | * ``` 9 | * import createReactClass from 'create-react-class'; 10 | * createReactClass(...); 11 | * ``` 12 | */ 13 | function isReactCreateClassCallModular(path: NodePath): boolean { 14 | if (!path.isCallExpression()) { 15 | return false; 16 | } 17 | const module = resolveToModule(path); 18 | 19 | return Boolean(module && module === 'create-react-class'); 20 | } 21 | 22 | /** 23 | * Returns true if the expression is a function call of the form 24 | * `React.createClass(...)` or 25 | * ``` 26 | * import createReactClass from 'create-react-class'; 27 | * createReactClass(...); 28 | * ``` 29 | */ 30 | export default function isReactCreateClassCall( 31 | path: NodePath, 32 | ): path is NodePath { 33 | return ( 34 | (isReactBuiltinCall(path, 'createClass') && 35 | path.get('arguments').length === 1) || 36 | isReactCreateClassCallModular(path) 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/isReactCreateElementCall.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import type { CallExpression } from '@babel/types'; 3 | import isReactBuiltinCall from './isReactBuiltinCall.js'; 4 | 5 | /** 6 | * Returns true if the expression is a function call of the form 7 | * `React.createElement(...)`. 8 | */ 9 | export default function isReactCreateElementCall( 10 | path: NodePath, 11 | ): path is NodePath { 12 | return isReactBuiltinCall(path, 'createElement'); 13 | } 14 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/isReactForwardRefCall.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import isReactBuiltinCall from './isReactBuiltinCall.js'; 3 | import type { CallExpression } from '@babel/types'; 4 | 5 | /** 6 | * Returns true if the expression is a function call of the form 7 | * `React.forwardRef(...)`. 8 | */ 9 | export default function isReactForwardRefCall( 10 | path: NodePath, 11 | ): path is NodePath { 12 | return ( 13 | isReactBuiltinCall(path, 'forwardRef') && 14 | (path as NodePath).get('arguments').length === 1 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/isReactModuleName.ts: -------------------------------------------------------------------------------- 1 | const reactModules = [ 2 | 'react', 3 | 'react/addons', 4 | 'react-native', 5 | 'proptypes', 6 | 'prop-types', 7 | ]; 8 | 9 | /** 10 | * Takes a module name (string) and returns true if it refers to a root react 11 | * module name. 12 | */ 13 | export default function isReactModuleName(moduleName: string): boolean { 14 | return reactModules.includes(moduleName.toLowerCase()); 15 | } 16 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/isRequiredPropType.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import getMembers from '../utils/getMembers.js'; 3 | 4 | /** 5 | * Returns true of the prop is required, according to its type definition 6 | */ 7 | export default function isRequiredPropType(path: NodePath): boolean { 8 | return getMembers(path).some( 9 | ({ computed, path: memberPath }) => 10 | (!computed && memberPath.isIdentifier({ name: 'isRequired' })) || 11 | memberPath.isStringLiteral({ value: 'isRequired' }), 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/isStatelessComponent.ts: -------------------------------------------------------------------------------- 1 | import isReactCreateElementCall from './isReactCreateElementCall.js'; 2 | import isReactCloneElementCall from './isReactCloneElementCall.js'; 3 | import isReactChildrenElementCall from './isReactChildrenElementCall.js'; 4 | import type { NodePath } from '@babel/traverse'; 5 | import type { StatelessComponentNode } from '../resolver/index.js'; 6 | import findFunctionReturn from './findFunctionReturn.js'; 7 | import type { CallExpression, JSXElement, JSXFragment } from '@babel/types'; 8 | 9 | const validPossibleStatelessComponentTypes = [ 10 | 'ArrowFunctionExpression', 11 | 'FunctionDeclaration', 12 | 'FunctionExpression', 13 | 'ObjectMethod', 14 | ]; 15 | 16 | function isJSXElementOrReactCall( 17 | path: NodePath, 18 | ): path is NodePath { 19 | return ( 20 | path.isJSXElement() || 21 | path.isJSXFragment() || 22 | (path.isCallExpression() && 23 | (isReactCreateElementCall(path) || 24 | isReactCloneElementCall(path) || 25 | isReactChildrenElementCall(path))) 26 | ); 27 | } 28 | 29 | /** 30 | * Returns `true` if the path represents a function which returns a JSXElement 31 | */ 32 | export default function isStatelessComponent( 33 | path: NodePath, 34 | ): path is NodePath { 35 | if (!path.inType(...validPossibleStatelessComponentTypes)) { 36 | return false; 37 | } 38 | 39 | const foundPath = findFunctionReturn(path, isJSXElementOrReactCall); 40 | 41 | return Boolean(foundPath); 42 | } 43 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/isUnreachableFlowType.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | 3 | /** 4 | * Returns true of the path is an unreachable TypePath 5 | * This evaluates the NodePaths returned from resolveToValue 6 | */ 7 | export default (path: NodePath): boolean => { 8 | return ( 9 | path.isIdentifier() || 10 | path.parentPath?.isImportDeclaration() || 11 | path.isCallExpression() 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/postProcessDocumentation.ts: -------------------------------------------------------------------------------- 1 | import type { Documentation } from '../Documentation.js'; 2 | 3 | export default function (documentation: Documentation): Documentation { 4 | const props = documentation.props; 5 | 6 | if (props) { 7 | Object.values(props).forEach((propInfo) => { 8 | // props with default values should not be required 9 | if (propInfo.defaultValue) { 10 | propInfo.required = false; 11 | } 12 | }); 13 | } 14 | 15 | return documentation; 16 | } 17 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/printValue.ts: -------------------------------------------------------------------------------- 1 | import strip from 'strip-indent'; 2 | import type { NodePath } from '@babel/traverse'; 3 | 4 | function deindent(code: string): string { 5 | const firstNewLine = code.indexOf('\n'); 6 | 7 | return ( 8 | code.slice(0, firstNewLine + 1) + 9 | // remove indentation from all lines except first. 10 | strip(code.slice(firstNewLine + 1)) 11 | ); 12 | } 13 | 14 | /** 15 | * Prints the given path without leading or trailing comments. 16 | */ 17 | export default function printValue(path: NodePath): string { 18 | let source = path.getSource(); 19 | 20 | // variable declarations and interface/type/class members might end with one of these 21 | if (source.endsWith(',') || source.endsWith(';')) { 22 | source = source.slice(0, -1); 23 | } 24 | 25 | return deindent(source); 26 | } 27 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/resolveExportDeclaration.ts: -------------------------------------------------------------------------------- 1 | import resolveToValue from './resolveToValue.js'; 2 | import type { 3 | ExportDefaultDeclaration, 4 | ExportNamedDeclaration, 5 | } from '@babel/types'; 6 | import type { NodePath } from '@babel/traverse'; 7 | 8 | export default function resolveExportDeclaration( 9 | path: NodePath, 10 | ): NodePath[] { 11 | const definitions: NodePath[] = []; 12 | 13 | if (path.isExportDefaultDeclaration()) { 14 | definitions.push(path.get('declaration')); 15 | } else if (path.isExportNamedDeclaration()) { 16 | if (path.has('declaration')) { 17 | const declaration = path.get('declaration'); 18 | 19 | if (declaration.isVariableDeclaration()) { 20 | declaration 21 | .get('declarations') 22 | .forEach((declarator) => definitions.push(declarator)); 23 | } else if (declaration.isDeclaration()) { 24 | definitions.push(declaration); 25 | } 26 | } else if (path.has('specifiers')) { 27 | path.get('specifiers').forEach((specifier) => { 28 | if (specifier.isExportSpecifier()) { 29 | definitions.push(specifier.get('local')); 30 | } 31 | }); 32 | } 33 | } 34 | 35 | return definitions.map((definition) => resolveToValue(definition)); 36 | } 37 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/resolveFunctionDefinitionToReturnValue.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import { visitors } from '@babel/traverse'; 3 | import type { Expression, Function as BabelFunction } from '@babel/types'; 4 | import resolveToValue from './resolveToValue.js'; 5 | import { ignore, shallowIgnoreVisitors } from './traverse.js'; 6 | 7 | interface TraverseState { 8 | returnPath?: NodePath; 9 | } 10 | 11 | const explodedVisitors = visitors.explode({ 12 | ...shallowIgnoreVisitors, 13 | 14 | Function: { enter: ignore }, 15 | ReturnStatement: { 16 | enter: function (nodePath, state) { 17 | const argument = nodePath.get('argument'); 18 | 19 | if (argument.hasNode()) { 20 | state.returnPath = resolveToValue(argument) as NodePath; 21 | 22 | return nodePath.stop(); 23 | } 24 | 25 | nodePath.skip(); 26 | }, 27 | }, 28 | }); 29 | 30 | export default function resolveFunctionDefinitionToReturnValue( 31 | path: NodePath, 32 | ): NodePath | null { 33 | const body = path.get('body'); 34 | const state: TraverseState = {}; 35 | 36 | body.traverse(explodedVisitors, state); 37 | 38 | return state.returnPath || null; 39 | } 40 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/resolveGenericTypeAnnotation.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import isUnreachableFlowType from '../utils/isUnreachableFlowType.js'; 3 | import resolveToValue from '../utils/resolveToValue.js'; 4 | import { unwrapUtilityType } from './flowUtilityTypes.js'; 5 | import getTypeIdentifier from './getTypeIdentifier.js'; 6 | 7 | function tryResolveGenericTypeAnnotation(path: NodePath): NodePath | undefined { 8 | let typePath = unwrapUtilityType(path); 9 | const idPath = getTypeIdentifier(typePath); 10 | 11 | if (idPath) { 12 | typePath = resolveToValue(idPath); 13 | if (isUnreachableFlowType(typePath)) { 14 | return; 15 | } 16 | 17 | if (typePath.isTypeAlias()) { 18 | return tryResolveGenericTypeAnnotation(typePath.get('right')); 19 | } else if (typePath.isTSTypeAliasDeclaration()) { 20 | return tryResolveGenericTypeAnnotation(typePath.get('typeAnnotation')); 21 | } 22 | 23 | return typePath; 24 | } 25 | 26 | return typePath; 27 | } 28 | 29 | /** 30 | * Given an React component (stateless or class) tries to find the 31 | * flow or ts type for the props. If not found or not one of the supported 32 | * component types returns undefined. 33 | */ 34 | export default function resolveGenericTypeAnnotation( 35 | path: NodePath, 36 | ): NodePath | undefined { 37 | const typePath = tryResolveGenericTypeAnnotation(path); 38 | 39 | if (!typePath || typePath === path) return; 40 | 41 | return typePath; 42 | } 43 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/resolveHOC.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import isReactCreateClassCall from './isReactCreateClassCall.js'; 3 | import isReactForwardRefCall from './isReactForwardRefCall.js'; 4 | import resolveToValue from './resolveToValue.js'; 5 | 6 | /** 7 | * If the path is a call expression, it recursively resolves to the 8 | * rightmost argument, stopping if it finds a React.createClass call expression 9 | * 10 | * Else the path itself is returned. 11 | */ 12 | export default function resolveHOC(path: NodePath): NodePath { 13 | if ( 14 | path.isCallExpression() && 15 | !isReactCreateClassCall(path) && 16 | !isReactForwardRefCall(path) 17 | ) { 18 | const node = path.node; 19 | const argumentLength = node.arguments.length; 20 | 21 | if (argumentLength && argumentLength > 0) { 22 | const args = path.get('arguments'); 23 | const firstArg = args[0]!; 24 | 25 | // If the first argument is one of these types then the component might be the last argument 26 | // If there are all identifiers then we cannot figure out exactly and have to assume it is the first 27 | if ( 28 | argumentLength > 1 && 29 | (firstArg.isLiteral() || 30 | firstArg.isObjectExpression() || 31 | firstArg.isArrayExpression() || 32 | firstArg.isSpreadElement()) 33 | ) { 34 | return resolveHOC(resolveToValue(args[argumentLength - 1]!)); 35 | } 36 | 37 | return resolveHOC(resolveToValue(firstArg)); 38 | } 39 | } 40 | 41 | return path; 42 | } 43 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/resolveObjectPatternPropertyToValue.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import type { Identifier, ObjectProperty } from '@babel/types'; 3 | import getPropertyValuePath from './getPropertyValuePath.js'; 4 | import resolveToValue from './resolveToValue.js'; 5 | 6 | function resolveToObjectExpression(path: NodePath): NodePath | null { 7 | if (path.isVariableDeclarator()) { 8 | const init = path.get('init'); 9 | 10 | if (init.hasNode()) { 11 | return resolveToValue(init); 12 | } 13 | } else if (path.isAssignmentExpression()) { 14 | if (path.node.operator === '=') { 15 | return resolveToValue(path.get('right')); 16 | } 17 | } 18 | 19 | return null; 20 | } 21 | 22 | /** 23 | * Resolve and ObjectProperty inside an ObjectPattern to its value if possible 24 | * If not found `null` is returned 25 | */ 26 | export default function resolveObjectPatternPropertyToValue( 27 | path: NodePath, 28 | ): NodePath | null { 29 | if (!path.parentPath.isObjectPattern()) { 30 | return null; 31 | } 32 | 33 | const resolved = resolveToObjectExpression(path.parentPath.parentPath); 34 | 35 | if (resolved && resolved.isObjectExpression()) { 36 | const propertyPath = getPropertyValuePath( 37 | resolved, 38 | // Always id in ObjectPattern 39 | (path.get('key') as NodePath).node.name, 40 | ); 41 | 42 | if (propertyPath) { 43 | return resolveToValue(propertyPath); 44 | } 45 | } 46 | 47 | return null; 48 | } 49 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/resolveToModule.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import type { Expression, StringLiteral } from '@babel/types'; 3 | import getMemberExpressionRoot from './getMemberExpressionRoot.js'; 4 | import resolveToValue from './resolveToValue.js'; 5 | 6 | /** 7 | * Given a path (e.g. call expression, member expression or identifier), 8 | * this function tries to find the name of module from which the "root value" 9 | * was imported. 10 | */ 11 | export default function resolveToModule(path: NodePath): string | null { 12 | if (path.isVariableDeclarator()) { 13 | if (path.node.init) { 14 | return resolveToModule(path.get('init') as NodePath); 15 | } 16 | } else if (path.isCallExpression()) { 17 | const callee = path.get('callee'); 18 | 19 | if (callee.isIdentifier({ name: 'require' })) { 20 | return (path.node.arguments[0] as StringLiteral).value; 21 | } 22 | 23 | return resolveToModule(callee); 24 | } else if (path.isIdentifier() || path.isJSXIdentifier()) { 25 | const valuePath = resolveToValue(path); 26 | 27 | if (valuePath !== path) { 28 | return resolveToModule(valuePath); 29 | } 30 | if (path.parentPath.isObjectProperty()) { 31 | return resolveToModule(path.parentPath); 32 | } 33 | } else if (path.isObjectProperty() || path.isObjectPattern()) { 34 | return resolveToModule(path.parentPath); 35 | } else if (path.parentPath?.isImportDeclaration()) { 36 | return path.parentPath.node.source.value; 37 | } else if (path.isMemberExpression()) { 38 | path = getMemberExpressionRoot(path); 39 | 40 | return resolveToModule(path); 41 | } 42 | 43 | return null; 44 | } 45 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/setPropDescription.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import type Documentation from '../Documentation.js'; 3 | import getPropertyName from './getPropertyName.js'; 4 | import { getDocblock } from './docblock.js'; 5 | import type { 6 | ObjectMethod, 7 | ObjectProperty, 8 | ObjectTypeProperty, 9 | TSPropertySignature, 10 | } from '@babel/types'; 11 | 12 | export default function setPropDescription( 13 | documentation: Documentation, 14 | propertyPath: NodePath< 15 | ObjectMethod | ObjectProperty | ObjectTypeProperty | TSPropertySignature 16 | >, 17 | ): void { 18 | const propName = getPropertyName(propertyPath); 19 | 20 | if (!propName) return; 21 | 22 | const propDescriptor = documentation.getPropDescriptor(propName); 23 | 24 | if (propDescriptor.description) return; 25 | 26 | propDescriptor.description = getDocblock(propertyPath) || ''; 27 | } 28 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/traverse.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | 3 | export function ignore(path: NodePath): void { 4 | path.skip(); 5 | } 6 | 7 | export const shallowIgnoreVisitors = { 8 | FunctionDeclaration: { enter: ignore }, 9 | FunctionExpression: { enter: ignore }, 10 | Class: { enter: ignore }, 11 | IfStatement: { enter: ignore }, 12 | WithStatement: { enter: ignore }, 13 | SwitchStatement: { enter: ignore }, 14 | CatchClause: { enter: ignore }, 15 | Loop: { enter: ignore }, 16 | ExportNamedDeclaration: { enter: ignore }, 17 | ExportDefaultDeclaration: { enter: ignore }, 18 | ConditionalExpression: { enter: ignore }, 19 | }; 20 | -------------------------------------------------------------------------------- /packages/react-docgen/src/utils/unwrapBuiltinTSPropTypes.ts: -------------------------------------------------------------------------------- 1 | import type { NodePath } from '@babel/traverse'; 2 | import isReactBuiltinReference from './isReactBuiltinReference.js'; 3 | 4 | /** 5 | * Unwraps NodePaths from the builtin TS types `PropsWithoutRef`, 6 | * `PropsWithRef` and `PropsWithChildren` and returns the inner type param. 7 | * If none of the builtin types is detected the path is returned as-is 8 | */ 9 | export default function unwrapBuiltinTSPropTypes(typePath: NodePath): NodePath { 10 | if (typePath.isTSTypeReference()) { 11 | const typeName = typePath.get('typeName'); 12 | 13 | if ( 14 | isReactBuiltinReference(typeName, 'PropsWithoutRef') || 15 | isReactBuiltinReference(typeName, 'PropsWithRef') || 16 | isReactBuiltinReference(typeName, 'PropsWithChildren') 17 | ) { 18 | const typeParameters = typePath.get('typeParameters'); 19 | 20 | if (typeParameters.hasNode()) { 21 | const innerType = typeParameters.get('params')[0]; 22 | 23 | if (innerType) { 24 | return unwrapBuiltinTSPropTypes(innerType); 25 | } 26 | } 27 | } 28 | } 29 | 30 | return typePath; 31 | } 32 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/NodePathSerializer.ts: -------------------------------------------------------------------------------- 1 | import { NodePath } from '@babel/traverse'; 2 | import { removePropertiesDeep } from '@babel/types'; 3 | import type { expect } from 'vitest'; 4 | 5 | function removeUndefinedProperties( 6 | node: Record, 7 | ): Record { 8 | for (const key of Object.keys(node)) { 9 | if (node[key] === undefined) { 10 | // eslint-disable-next-line @typescript-eslint/no-dynamic-delete 11 | delete node[key]; 12 | } else if (node[key] === Object(node[key])) { 13 | node[key] = removeUndefinedProperties( 14 | node[key] as Record, 15 | ); 16 | } 17 | } 18 | 19 | return node; 20 | } 21 | 22 | export default { 23 | serialize(val, config, indentation, depth, refs, printer) { 24 | return printer( 25 | removeUndefinedProperties(removePropertiesDeep(val.node)), 26 | config, 27 | indentation, 28 | depth, 29 | refs, 30 | ); 31 | }, 32 | 33 | test(val) { 34 | return val && val instanceof NodePath; 35 | }, 36 | } as Parameters[0]; 37 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/class-without-id.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface IProps { 4 | value: string; 5 | } 6 | 7 | export default class extends React.Component { 8 | render() { 9 | return
; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_1.js: -------------------------------------------------------------------------------- 1 | var Component, React; 2 | 3 | React = require('react'); 4 | 5 | /** 6 | * The is a component to test the document generation 7 | */ 8 | Component = React.createClass({ 9 | displayName: 'Component', 10 | }); 11 | 12 | module.exports = Component; 13 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_11.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Testing component using Flow 3 | */ 4 | 5 | import React from 'react'; 6 | 7 | import { OtherComponentProps as OtherProps } from 'NonExistentFile'; 8 | 9 | type OtherLocalProps = {| 10 | /** 11 | * fooProp is spread in from a locally resolved type 12 | */ 13 | fooProp?: string, 14 | |} 15 | 16 | type Props = {| 17 | /** 18 | * Spread props defined locally 19 | */ 20 | ...OtherLocalProps, 21 | 22 | /** 23 | * Spread props from another file 24 | */ 25 | ...OtherProps, 26 | 27 | /** 28 | * The first prop 29 | */ 30 | prop1: string, 31 | 32 | /** 33 | * The second, covariant prop 34 | */ 35 | +prop2: number 36 | |} 37 | 38 | class MyComponent extends React.Component { 39 | render() { 40 | return null; 41 | } 42 | } 43 | 44 | export default MyComponent; 45 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_13.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Test for documentation of React components with Flow annotations for props. 3 | */ 4 | 5 | import React from 'react'; 6 | 7 | type AlignProps = {| 8 | +align?: "left" | "center" | "right" | "justify", 9 | +left?: boolean, 10 | +center?: boolean, 11 | +right?: boolean, 12 | +justify?: boolean, 13 | |}; 14 | 15 | type TransformProps = {| 16 | +transform?: "lowercase" | "uppercase" | "capitalize", 17 | +lowercase?: boolean, 18 | +uppercase?: boolean, 19 | +capitalize?: boolean, 20 | |}; 21 | 22 | type TrackingProps = {| 23 | +tracking?: "tight" | "normal" | "wide", 24 | +trackingTight?: boolean, 25 | +trackingNormal?: boolean, 26 | +trackingWide?: boolean, 27 | |}; 28 | 29 | type LeadingProps = {| 30 | +leading?: "none" | "tight" | "normal" | "loose", 31 | +leadingNone?: boolean, 32 | +leadingTight?: boolean, 33 | +leadingNormal?: boolean, 34 | +leadingLoose?: boolean, 35 | |}; 36 | 37 | type TextProps = {| 38 | ...AlignProps, 39 | ...TransformProps, 40 | ...TrackingProps, 41 | ...LeadingProps, 42 | +children?: React.Node, 43 | +className?: string, 44 | +RootComponent?: React.ElementType, 45 | +color?: string, 46 | +size?: string, 47 | +wrap?: boolean, 48 | +muted?: boolean, 49 | |}; 50 | 51 | class Foo extends React.Component { 52 | render() { 53 | return
; 54 | } 55 | } 56 | 57 | export default Foo; 58 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_14.js: -------------------------------------------------------------------------------- 1 | type Props = $ReadOnly<{| 2 | color?: ?string, 3 | |}>; 4 | 5 | const ColoredView = React.forwardRef((props: Props, ref) => ( 6 | 7 | )); 8 | 9 | ColoredView.displayName = 'UncoloredView'; 10 | 11 | module.exports = ColoredView; -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_15.js: -------------------------------------------------------------------------------- 1 | import type {Props as BarProps} from 'Bar.react'; 2 | 3 | const Bar = require('Bar.react'); 4 | 5 | type A = { other3: 'c' }; 6 | type B = $Exact<{ other4: 'g' }>; 7 | type C = $Exact<$ReadOnly<{ other5: 'f' }>>; 8 | 9 | type Props = {| 10 | ...$Exact, 11 | ...$Exact<$ReadOnly>, 12 | ...BarProps3, 13 | ...{ other: 'a' }, 14 | ...$Exact<{ other2: 'b' }>, 15 | ...A, 16 | ...B, 17 | ...C, 18 | somePropOverride: 'baz', 19 | |}; 20 | 21 | export default class Foo extends React.Component { 22 | render() { 23 | return ; 24 | } 25 | } -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_16.js: -------------------------------------------------------------------------------- 1 | const propTypes = { 2 | /** This does something. */ 3 | bar: PropTypes.node, 4 | }; 5 | 6 | export default function Foo(props) { 7 | return
; 8 | } 9 | 10 | Foo.propTypes = { 11 | ...propTypes 12 | }; 13 | 14 | Foo.defaultProps = { 15 | bar: null, 16 | }; -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_17.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Testing descriptions of shape and oneOfType values 3 | */ 4 | 5 | import React from 'react'; 6 | import PropTypes from 'prop-types'; 7 | 8 | export default function Foo(props) { 9 | return
; 10 | } 11 | 12 | Foo.propTypes = { 13 | shapeProp: PropTypes.shape({ 14 | /** Comment for property a */ 15 | a: PropTypes.string, 16 | b: PropTypes.number 17 | }), 18 | exactProp: PropTypes.exact({ 19 | /** Comment for property c */ 20 | c: PropTypes.string, 21 | d: PropTypes.number 22 | }), 23 | oneOfTypeProp: PropTypes.oneOfType([ 24 | /** Comment for type string */ 25 | PropTypes.string, 26 | PropTypes.number 27 | ]) 28 | }; 29 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_18.js: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | import PropTypes from 'prop-types'; 4 | import extendStyles from 'enhancers/extendStyles'; 5 | 6 | type Props = $ReadOnly<{| 7 | color?: ?string, 8 | |}>; 9 | 10 | const ColoredView = React.forwardRef((props: Props, ref) => ( 11 |
12 | )); 13 | 14 | ColoredView.displayName = 'UncoloredView'; 15 | ColoredView.propTypes = { 16 | color: PropTypes.string.isRequired, 17 | id: PropTypes.string 18 | } 19 | ColoredView.defaultProps = { 20 | id: 'test-forward-ref-default' 21 | } 22 | 23 | module.exports = extendStyles(ColoredView); 24 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_19.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | type Props = {| 4 | data?: Array, 5 | ...React.ElementConfig, 6 | |}; 7 | 8 | type State = {| 9 | width: number, 10 | |}; 11 | 12 | export default class Component extends React.PureComponent { 13 | UNSAFE_componentWillReceiveProps(nextProps: Props) { 14 | doSomething(); 15 | } 16 | 17 | render() { 18 | return ( 19 |
Hello
20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_2.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export class Button extends React.Component { 4 | static get displayName() { 5 | return "button"; 6 | } 7 | 8 | static get defaultProps() { 9 | return { 10 | type: "primary", 11 | }; 12 | } 13 | } 14 | 15 | export function foo() { 16 | return [].join(); 17 | } 18 | 19 | export function chained() { 20 | return foo.bar().join(); 21 | } 22 | 23 | export function templateLiteral() { 24 | return `foo bar`.split(' '); 25 | } 26 | 27 | export function withThis() { 28 | return this.foo(); 29 | } 30 | 31 | export default function withNestedMemberExpressions() { 32 | return this.blub.blob.foo(); 33 | } 34 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_20.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const Button = () => ( 5 |
6 | ); 7 | 8 | Button.propTypes = { 9 | /** This is a test */ 10 | [children]: PropTypes.string.isRequired, 11 | }; 12 | 13 | Button.defaultProps = { 14 | [children]: "default", 15 | }; 16 | 17 | export default Button; 18 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_21.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | type BaseProps = { 4 | /** Optional prop */ 5 | foo?: string, 6 | /** Required prop */ 7 | bar: number 8 | }; 9 | 10 | type TransitionDuration = number | { enter?: number, exit?: number } | 'auto'; 11 | 12 | type Props = BaseProps & { 13 | /** Complex union prop */ 14 | baz: TransitionDuration 15 | } 16 | 17 | /** 18 | * This is a typescript class component 19 | */ 20 | export default class TSComponent extends Component { 21 | render() { 22 | return

Hello world

; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_22.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | type Props = { 4 | align?: "left" | "center" | "right" | "justify", 5 | left?: boolean, 6 | center?: boolean, 7 | right?: boolean, 8 | justify?: boolean, 9 | /** 10 | * position doc 11 | */ 12 | position: { 13 | /** 14 | * x coordinate doc 15 | */ 16 | x: number, 17 | /** 18 | * y coordinate doc 19 | */ 20 | y: number 21 | } 22 | }; 23 | 24 | /** 25 | * This is a TypeScript function component 26 | */ 27 | export function TSFunctionComponent(props: Props) { 28 | return

Hello world

; 29 | } 30 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_23.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | interface BaseProps { 4 | /** Optional prop */ 5 | foo?: string, 6 | /** Required prop */ 7 | bar: number 8 | } 9 | 10 | type TransitionDuration = number | { enter?: number, exit?: number } | 'auto'; 11 | 12 | interface Props extends BaseProps, OtherProps { 13 | /** Complex union prop */ 14 | baz: TransitionDuration 15 | } 16 | 17 | /** 18 | * This is a typescript class component 19 | */ 20 | export default class TSComponent extends Component { 21 | render() { 22 | return

Hello world

; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_24.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | interface BaseProps { 4 | /** Optional prop */ 5 | foo?: string, 6 | /** Required prop */ 7 | bar: number 8 | } 9 | 10 | type TransitionDuration = number | { enter?: number, exit?: number } | 'auto'; 11 | 12 | interface Props extends BaseProps, OtherProps { 13 | /** Complex union prop */ 14 | baz: TransitionDuration 15 | } 16 | 17 | /** 18 | * This is a flow class component with an interface as props 19 | */ 20 | export default class FlowComponent extends Component { 21 | render() { 22 | return

Hello world

; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_25.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | type Test = Array; 4 | interface BaseProps { 5 | /** Optional prop */ 6 | foo?: T, 7 | /** Required prop */ 8 | bar: Test 9 | } 10 | 11 | interface Child {} 12 | 13 | interface Props extends BaseProps { 14 | /** Complex union prop */ 15 | baz: number 16 | } 17 | 18 | /** 19 | * This is a typescript class component 20 | */ 21 | export default class TSComponent extends Component { 22 | render() { 23 | return

Hello world

; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_26.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | type Test = Array; 4 | interface BaseProps { 5 | /** Optional prop */ 6 | foo?: T, 7 | /** Required prop */ 8 | bar: Test 9 | } 10 | 11 | interface Child {} 12 | 13 | interface Props extends BaseProps { 14 | /** Complex union prop */ 15 | baz: number 16 | } 17 | 18 | /** 19 | * This is a typescript class component 20 | */ 21 | export default class FlowComponent extends Component { 22 | render() { 23 | return

Hello world

; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_27.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | export interface Props { 4 | foo: string 5 | } 6 | 7 | /** 8 | * This is a typescript class component 9 | */ 10 | export default class TSComponent extends Component { 11 | render() { 12 | return

Hello world

; 13 | } 14 | 15 | /** 16 | * This is a method 17 | */ 18 | foo(a: string): string { 19 | return a; 20 | } 21 | 22 | /** 23 | * This is a public method 24 | */ 25 | public bar(a: string): string { 26 | return a; 27 | } 28 | 29 | /** 30 | * This is a private method 31 | */ 32 | private baz(a: string): string { 33 | return a; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_28.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | interface Props { 4 | /** 5 | * Example prop description 6 | */ 7 | foo: boolean; 8 | } 9 | 10 | /** 11 | * Example component description 12 | */ 13 | const Component = React.forwardRef(({ foo = true }: Props, ref: any) => { 14 | return
; 15 | }) 16 | 17 | Component.displayName = 'ABC'; 18 | 19 | export default Component; 20 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_29.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | type Identity = T; 4 | 5 | type Props = { 6 | prop: Identity, 7 | } 8 | 9 | export default function MyComponent(props: Props) { 10 | return
Hello World
11 | } 12 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_3.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | import extendStyles from 'enhancers/extendStyles'; 3 | 4 | const Test = props => 5 |
6 | Hello world! 7 |
; 8 | 9 | Test.propTypes = { 10 | style: PropTypes.object, 11 | }; 12 | 13 | export default extendStyles(Test); 14 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_30.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | type Identity = T; 4 | 5 | type Props = { 6 | prop: Identity, 7 | } 8 | 9 | export default function MyComponent(props: Props) { 10 | return
Hello World
11 | } 12 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_31.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | type Identity = T; 4 | 5 | type Props = { 6 | prop: Identity>, 7 | } 8 | 9 | export default function MyComponent(props: Props) { 10 | return
Hello World
11 | } 12 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_32.js: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | type Props = { 4 | segments: Array, 5 | }; 6 | 7 | export default class Segments extends React.Component> { 8 | render(): React.Node { 9 | return null; 10 | } 11 | 12 | foo(props: Props) {} 13 | } 14 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_33.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import ExtendedProps from './component_39.jsx'; 3 | 4 | /** 5 | * This is a typescript component with imported prop types 6 | */ 7 | export function ImportedExtendedComponent(props: ExtendedProps) { 8 | return

Hello world

; 9 | } 10 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_34.js: -------------------------------------------------------------------------------- 1 | import Button from './component_6.js'; 2 | import PropTypes from 'prop-types'; 3 | 4 | export function CustomButton({color, ...otherProps}) { 5 | return 10 | ); 11 | 12 | Button.propTypes = { 13 | children: React.PropTypes.string.isRequired, 14 | onClick: React.PropTypes.func, 15 | style: React.PropTypes.object, 16 | }; 17 | 18 | Button.childContextTypes = { 19 | color: React.PropTypes.string, 20 | }; 21 | 22 | Button.contextTypes = { 23 | config: React.PropTypes.object, 24 | }; 25 | 26 | export default Button; 27 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_6.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const Button = ({ children, onClick, style = {} }) => ( 5 | 11 | ); 12 | 13 | Button.propTypes = { 14 | children: PropTypes.string.isRequired, 15 | onClick: PropTypes.func, 16 | style: PropTypes.object, 17 | }; 18 | 19 | Button.childContextTypes = { 20 | color: PropTypes.string, 21 | }; 22 | 23 | Button.contextTypes = { 24 | config: PropTypes.object, 25 | }; 26 | export default Button; 27 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_7.js: -------------------------------------------------------------------------------- 1 | var Component, createReactClass; 2 | 3 | createReactClass = require('create-react-class'); 4 | 5 | /** 6 | * The is a component to test the document generation 7 | */ 8 | Component = createReactClass({ 9 | displayName: 'Component', 10 | }); 11 | 12 | module.exports = Component; 13 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_8.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Testing array parameter destructuring. 3 | */ 4 | 5 | import React from 'react'; 6 | 7 | var pt = React.PropTypes; 8 | 9 | class Parent extends React.Component { 10 | onChangeSlider([min, max]) { 11 | this.setState({ min, max }); 12 | } 13 | } 14 | 15 | export default Parent; 16 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/component_9.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Testing render method as public class field. 3 | */ 4 | import view from "./view.jsx"; 5 | /** 6 | * Should be recognized as component. 7 | */ 8 | export default class ExampleComponent extends SomeOtherComponent { 9 | render = view; 10 | } 11 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/flow-export-type.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | import React, { Component } from 'react'; 6 | 7 | export type Props = { 8 | foo: string 9 | } 10 | 11 | /** 12 | * This is a Flow class component 13 | */ 14 | export default class FlowComponent extends Component { 15 | render() { 16 | return

Hello world

; 17 | } 18 | 19 | foo(a: string): string { 20 | return a; 21 | } 22 | 23 | bar(a: string): string { 24 | return a; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/flow-import-type.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | import React, { Component } from 'react'; 6 | import type { Props as ImportedProps } from './flow-export-type.js'; 7 | 8 | export type ExtendedProps = { 9 | ...ImportedProps, 10 | bar: number 11 | } 12 | 13 | /** 14 | * This is a Flow component with imported prop types 15 | */ 16 | export function ImportedComponent(props: ImportedProps) { 17 | return

Hello world

; 18 | } 19 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/flow-spread-import-type.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | import React, { Component } from 'react'; 6 | import type {ExtendedProps} from './flow-import-type.js'; 7 | 8 | /** 9 | * This is a Flow component with imported prop types 10 | */ 11 | export function ImportedExtendedComponent(props: ExtendedProps) { 12 | return

Hello world

; 13 | } 14 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/namespace-export.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface IProps { 4 | value: string; 5 | } 6 | 7 | export default class extends React.Component { 8 | render() { 9 | return
; 10 | } 11 | } 12 | 13 | export * as namespace from "./support/other-exports.js"; 14 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/support/other-exports.ts: -------------------------------------------------------------------------------- 1 | export default 'otherstring' 2 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/support/some-exports.ts: -------------------------------------------------------------------------------- 1 | const obj = { objDestruct: "string" }; 2 | 3 | export const { objDestruct } = obj; 4 | 5 | export defaultFrom from './other-exports.js'; 6 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/__fixtures__/test-all-imports.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { objDestruct, defaultFrom } from './support/some-exports.js'; 3 | 4 | /** 5 | * This is a TS component with imported stuff 6 | */ 7 | export function ImportedExtendedComponent({ x = objDestruct, y = defaultFrom }) { 8 | return

Hello world

; 9 | } 10 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/integration/integration-test.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import { dirname, join } from 'path'; 3 | import { describe, expect, test } from 'vitest'; 4 | import { parse, makeFsImporter } from '../../src/main'; 5 | import { fileURLToPath } from 'url'; 6 | 7 | describe('integration', () => { 8 | describe('fixtures', () => { 9 | const fixturePath = join( 10 | dirname(fileURLToPath(import.meta.url)), 11 | '__fixtures__', 12 | ); 13 | const fileNames = fs.readdirSync(fixturePath, { withFileTypes: true }); 14 | 15 | for (const entry of fileNames) { 16 | if (entry.isDirectory()) { 17 | continue; 18 | } 19 | const name = entry.name; 20 | 21 | const filePath = join(fixturePath, name); 22 | const fileContent = fs.readFileSync(filePath, 'utf8'); 23 | 24 | test(`processes component "${name}" without errors`, () => { 25 | let result; 26 | 27 | expect(() => { 28 | result = parse(fileContent, { 29 | importer: makeFsImporter(), 30 | babelOptions: { 31 | filename: filePath, 32 | babelrc: false, 33 | }, 34 | }); 35 | }).not.toThrowError(); 36 | expect(result).toMatchSnapshot(); 37 | }); 38 | } 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /packages/react-docgen/tests/setupTestFramework.ts: -------------------------------------------------------------------------------- 1 | import NodePathSerializer from './NodePathSerializer.js'; 2 | import { expect } from 'vitest'; 3 | 4 | expect.addSnapshotSerializer(NodePathSerializer); 5 | -------------------------------------------------------------------------------- /packages/react-docgen/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "declaration": true, 5 | "outDir": "./dist" 6 | }, 7 | "exclude": ["**/__tests__/**/*", "**/__mocks__/**/*", "**/__fixtures__/**/*"], 8 | "include": ["src/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/react-docgen/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | name: 'lib', 6 | setupFiles: ['./tests/setupTestFramework.ts'], 7 | include: ['**/__tests__/**/*-test.ts', '**/tests/integration/**/*-test.ts'], 8 | deps: { 9 | interopDefault: false, 10 | }, 11 | coverage: { 12 | all: true, 13 | include: ['src/**'], 14 | provider: 'v8', 15 | reporter: ['text', 'lcov'], 16 | }, 17 | }, 18 | }); 19 | -------------------------------------------------------------------------------- /packages/website/.gitignore: -------------------------------------------------------------------------------- 1 | .next 2 | .netlify 3 | -------------------------------------------------------------------------------- /packages/website/.nvmrc: -------------------------------------------------------------------------------- 1 | 22.16.0 2 | -------------------------------------------------------------------------------- /packages/website/README.md: -------------------------------------------------------------------------------- 1 | # react-docgen website 2 | 3 | 4 | ## Local Development 5 | 6 | First, run `pnpm install` to install the dependencies. 7 | 8 | Then, run `pnpm dev` to start the development server and visit localhost:3000. 9 | 10 | ## License 11 | 12 | This project is licensed under the MIT License. 13 | -------------------------------------------------------------------------------- /packages/website/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import { globalIgnores } from 'eslint/config'; 3 | import { fileURLToPath } from 'node:url'; 4 | import { FlatCompat } from '@eslint/eslintrc'; 5 | import tseslint from 'typescript-eslint'; 6 | import baseConfig from '../../eslint.config.mjs'; 7 | 8 | const __filename = fileURLToPath(import.meta.url); 9 | const __dirname = path.dirname(__filename); 10 | const compat = new FlatCompat({ 11 | baseDirectory: __dirname, 12 | }); 13 | 14 | export default tseslint.config([ 15 | globalIgnores(['**/.next/']), 16 | ...baseConfig, 17 | ...compat.config({ 18 | extends: ['next/core-web-vitals'], 19 | settings: { 20 | next: { 21 | rootDir: __dirname, 22 | }, 23 | }, 24 | rules: { 25 | 'import/no-anonymous-default-export': 'off', 26 | }, 27 | }), 28 | ]); 29 | -------------------------------------------------------------------------------- /packages/website/netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | command = "pnpm build" 3 | publish = ".next" 4 | ignore = "git diff --quiet $COMMIT_REF $CACHED_COMMIT_REF . ../react-docgen ../../pnpm-lock.yaml" 5 | -------------------------------------------------------------------------------- /packages/website/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. 6 | -------------------------------------------------------------------------------- /packages/website/next.config.mjs: -------------------------------------------------------------------------------- 1 | import nextra from 'nextra'; 2 | 3 | const withNextra = nextra({ 4 | theme: 'nextra-theme-docs', 5 | themeConfig: './src/theme.config.tsx', 6 | }); 7 | 8 | export default withNextra({ 9 | webpack: (config) => { 10 | if (!config.resolve.fallback) config.resolve.fallback = {}; 11 | 12 | config.resolve.fallback.fs = false; 13 | config.resolve.aliasFields = ['browser']; 14 | 15 | return config; 16 | }, 17 | }); 18 | -------------------------------------------------------------------------------- /packages/website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-docgen-internal/website", 3 | "private": true, 4 | "version": "0.0.0", 5 | "description": "react-docgen website", 6 | "scripts": { 7 | "dev": "nx exec -- next dev", 8 | "build": "NODE_ENV=production nx exec -- next build", 9 | "start": "nx exec -- next start" 10 | }, 11 | "author": "Daniel Tschinder (http://github.com/danez)", 12 | "license": "MIT", 13 | "dependencies": { 14 | "@codemirror/lang-javascript": "6.2.4", 15 | "@codemirror/lang-json": "6.0.1", 16 | "@codemirror/view": "6.37.1", 17 | "@headlessui/react": "2.2.4", 18 | "@types/react": "19.1.6", 19 | "@types/react-dom": "19.1.6", 20 | "@uiw/react-codemirror": "4.23.12", 21 | "clsx": "2.1.1", 22 | "next": "15.3.3", 23 | "next-themes": "0.4.6", 24 | "nextra": "3.3.1", 25 | "nextra-theme-docs": "3.3.1", 26 | "postcss": "8.5.4", 27 | "postcss-lightningcss": "1.0.1", 28 | "react": "19.1.0", 29 | "react-docgen": "workspace:8.0.0", 30 | "react-dom": "19.1.0", 31 | "tailwindcss": "3.4.17" 32 | }, 33 | "browserslist": [ 34 | "chrome 64", 35 | "edge 79", 36 | "firefox 67", 37 | "opera 51", 38 | "safari 12" 39 | ], 40 | "nx": { 41 | "targets": { 42 | "build": { 43 | "dependsOn": [ 44 | "^build" 45 | ], 46 | "outputs": [ 47 | "{projectRoot}/.next" 48 | ] 49 | }, 50 | "dev": { 51 | "dependsOn": [ 52 | "^build" 53 | ] 54 | }, 55 | "start": { 56 | "dependsOn": [ 57 | "build" 58 | ] 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/website/postcss.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss').Postcss} */ 2 | module.exports = { 3 | plugins: { 4 | tailwindcss: {}, 5 | 'postcss-lightningcss': {}, 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /packages/website/public/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactjs/react-docgen/d0a133ec154b63218456f19ed887a8951fa70d17/packages/website/public/.keep -------------------------------------------------------------------------------- /packages/website/public/_redirects: -------------------------------------------------------------------------------- 1 | /docs/reference/handlers/proptype-handler /docs/reference/handlers/prop-type-handler 2 | /docs/reference/documentation /docs/reference/documentation/basic 3 | -------------------------------------------------------------------------------- /packages/website/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactjs/react-docgen/d0a133ec154b63218456f19ed887a8951fa70d17/packages/website/public/favicon.ico -------------------------------------------------------------------------------- /packages/website/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /packages/website/src/components/ContentMissing.tsx: -------------------------------------------------------------------------------- 1 | import { Callout } from 'nextra/components'; 2 | 3 | export default function ContentMissing() { 4 | return ( 5 | 6 | This content hasn't been created yet. 7 | 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /packages/website/src/components/playground/OptionPanel.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Select } from '../Select'; 3 | 4 | interface OptionPanelProps { 5 | language: Language; 6 | onLanguageChange: (language: Language) => void; 7 | } 8 | 9 | export enum Language { 10 | FLOW = 'flow', 11 | JAVASCRIPT = 'js', 12 | TYPESCRIPT = 'ts', 13 | } 14 | 15 | const options = [ 16 | { key: Language.TYPESCRIPT, name: 'TypeScript' }, 17 | { key: Language.JAVASCRIPT, name: 'JavaScript' }, 18 | { key: Language.FLOW, name: 'Flow' }, 19 | ]; 20 | 21 | export default function OptionPanel({ 22 | language, 23 | onLanguageChange, 24 | }: OptionPanelProps) { 25 | const selectedOption = options.find((option) => option.key === language); 26 | 27 | if (!selectedOption) { 28 | throw new Error(`Could not find language '${language}'`); 29 | } 30 | 31 | return ( 32 | <> 33 |