Coder Social home page Coder Social logo

atlassian / extract-react-types Goto Github PK

View Code? Open in Web Editor NEW
177.0 8.0 35.0 4.05 MB

One stop shop for documenting your react components.

Home Page: https://atlassian.github.io/extract-react-types/

License: MIT License

JavaScript 98.77% TypeScript 1.23%
babel-plugin prop-types react-components typescript docs-as-code docs docgen docgenerator

extract-react-types's Introduction

Extract React Types

One stop shop to document your react components.

Getting started 🏁

Step 1: Install

npm install --save-dev babel-plugin-extract-react-types pretty-proptypes

Step 2: Annotate your prop types

export interface AvatarPropTypes {
  /** Provides a url for avatars being used as a link. */
  href?: string;
  /** Defines the size of the avatar */
  size?: 'small' | 'medium' | 'large';
  /** Name will be displayed in a tooltip */
  name?: string;
  /** A url to load an image from (this can also be a base64 encoded image). */
  src?: string;
  /** A `testId` prop is provided for specified elements, which is a unique string that appears as a data attribute `data-testid` in the rendered code, serving as a hook for automated tests */
  testId?: string;
}

Step 3: Output prop types

pretty-proptypes can display props from two sources.

Option 1. Using babel-plugin-extract-react-types and passing the component to Props

.babelrc

{
  "plugins": ["babel-plugin-extract-react-types"]
}
import Props from 'pretty-proptypes';
import MyCoolComponent from '../MyCoolComponent';

<Props heading="My Cool Component" component={MyCoolComponent} />;

Option 2. Directly passing a component's props to Props with extract-react-types-loader or getting types from extract-react-types and writing it to a file

import Props from 'pretty-proptypes';

<Props
  heading="My Cool Component"
  props={require('!!extract-react-types-loader!../MyCoolComponent')}
/>;

This analyses prop type definitions, and default props. It creates descriptions from comments before the type definitions, and will render markdown syntax using react-markings.

Packages

  1. extract-react-types Extract Flow & TypeScript types from React Components
  2. extract-react-types-loader Webpack loader for extract-react-types
  3. babel-plugin-extract-react-types A Babel plugin to store the types of React components as a property on the component for documentation
  4. kind2string kind2string is designed to take the data structures output by extract-react-types and convert it down to a (useful) string.
  5. pretty-proptypes PrettyPropTypes is designed to display the output of extract-react-types and display rich prop information for consumers.

Contribute

Pull requests, issues and comments welcome - please read our contributing guidelines and our code of conduct.

Atlassian

extract-react-types's People

Contributors

ajaymathur avatar andrewocc avatar bebraw avatar blasz avatar chasestarr avatar danieldelcore avatar declan-warn avatar dependabot[bot] avatar emmatown avatar github-actions[bot] avatar gwyneplaine avatar jamiebuilds avatar jossmac avatar klauspaiva avatar lukebatchelor avatar madou avatar marionebl avatar marshallofsound avatar mengx666 avatar nathf avatar noviny avatar petegleeson avatar pgmanutd avatar qburst-aparna avatar schnerd avatar soswow avatar superchu avatar thiagodebastos avatar zzarcon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

extract-react-types's Issues

Add converter for TSTypeQuery

I have a React component with a prop with the Typescript type ComponentProps<typeof OtherComponent>['type'], where OtherComponent's type prop is a union of string constants (e.g. type: 'a' | 'b' | 'c'). When I run this component through extract-react-types-loader (via require('!!extract-react-types-loader!./MyComponent.tsx')), I get the error Missing converter for: TSTypeQuery.

Per https://astexplorer.net/#/gist/1c8639f2528c0a3abeffeb6f5b06d074/0e1bfbd296ea053ce20a6829baacb858797fca90, I believe it's the typeof that produces a TSTypeQuery AST node.

FYI, I believe this issue is just a TSTypeQuery version of #59 and #75.

Thanks for a handy tool!

Props not being extracted when using both memo and forward ref

Doesn't work:

import { TextfieldProps } from './types';

const Textfield = forwardRef((props: TextfieldProps, ref) => {
                                     ^^^^^^^^^^^^^^^ not extracted
  return <input />;
});

Textfield.displayName = 'Textfield';

export default memo(Textfield);

Doesn't work:

import { TextfieldProps } from './types';

const Textfield = forwardRef<unknown, TextfieldProps>((props: TextfieldProps, ref) => {
                                      ^^^^^^^^^^^^^^^ not extracted
  return <input />;
});

Textfield.displayName = 'Textfield';

export default memo(Textfield);

Works

import { TextfieldProps } from './types';

const Textfield = forwardRef((props: TextfieldProps, ref) => {
  return <input />;
});

Textfield.displayName = 'Textfield';

export default memo<TextfieldProps>(Textfield);
                    ^^^^^^^^^^^^^^^ extracts, but now missing ref prop in the component types

Feat: Add ability to hide props from the docs

Right now there is no official way to hide props from the docs. Probably we can use something like @internal or @private to hide them.

export interface Props {
  /** Some description */
  id?: string;
  /**
   * @internal
   * Some description
   */
  privateId?: string
}

Make this a mono repository with kind2string and pretty-proptypes

There are two packages that are related to this library:

  1. kind2string: designed to take the data structures outputted by extract-react-types and convert it down to a (useful) string.

  2. pretty-proptypes: PrettyPropTypes is designed to display the output of extract-react-types. It is designed to read the output from extract-react-types, and display rich prop information for consumers.

  3. extract-react-types-loader: Webpack loader for extract-react-types.

Why should we do this?

This will enable us to develop, maintain, test and support the packages in better way as they will live in same place. πŸ™‚

Approvals from creators and maintainers of the above repositories:

Decorator support + Missing defaults

Thanks for the extraction utilities. They are highly useful.

While benchmarking extract-react-types, I noticed a couple of issues:

  • Decorators were disabled (Babel 7 change)?
  • addDefaultProps could crash. If you want, I can figure out a test case for this.

Here are my initial fixes: bebraw@42fbdb6 . I can also PR if you want.

Feat: Add support for forwardRef used within memo using TS type inference

❌ Bad, type inference for ref is lost because memo overrides it

const MyComponent = memo<MyComponentProps>(forwardRef<HTMLElement, MyComponentProps>((props, ref) => {}));

πŸ”₯

<MyComponent ref={foo} />

βœ… Good, ref is inferred

const MyComponent = memo(forwardRef<HTMLElement, MyComponentProps>((props, ref) => {}));

πŸ˜„

<MyComponent ref={foo} />

Can't extract types from namespace type.

I use namespace to define common types like below:

export declare namespace TypeAttributes {
  type Color =
    | "primary"
    | "secondary"
    | "success"
    | "warning"
    | "danger"
    | "info";
}

and use it in type definition.

export type ComponentProps = {
  /**
   * The color to apply to component
   */
  color?: TypeAttributes.Color;
}

it shows: Type: TypeAttributes.Color instead of "union, one of xxxxx".

any idea how to make this work?

Bug: Missing converter for: [path]

Error fires on the below example:

      import React, { FC } from 'react';

      export type Sizes = 'small' | 'medium' | 'large' | 'xlarge';

      const size = Object.keys({}).reduce(
         (p, c) => Object.assign(p, { [c]: c }),
         {},
      );

      export interface IconProps {
        // BAR
        size?: PSizes;
      }

      const Icon: FC<IconProps> = ({
        dangerouslySetGlyph,
        size = 'medium',
        label,
      }) => null;

      export default Icon;

Seems to be related to the fact that the size prop has the same name as the size variable above.
It might just be getting confused by the ambiguity and attempting to convert the variable declaration rather than the props.

Improved support for React.memo & React.forwardRef

ERT currently does support both memo and forwardRef however, for users of @types/react the recommended usage for these methods in typescript are unsupported. It would be nice to add them, similar to React.FC

For example:

React.memo<CheckboxProps>(props) => (null);
React.forwardRef<HTMLInputField, CheckboxProps>(props, ref) => (null);

Babel plugin fails to extract props when project Babel config contains overrides

Because we call babel.parse() without specifying the filename option (code), the plugin is not compatible with projects that have overrides in their Babel config.

E.g. if babel.config.js contains overrides: [{ test: '**/*.ts', plugins: ['@babel/plugin-transform-typescript'] }] then it will silently fail because we ignore errors and Babel cannot determine what config to use for parsing.

The issue explains the problem more thoroughly: babel/babel#11540

Passing { filename: '' } into the babel.parse() options appears to fix the issue.

Issues getting this to work

Hi there,

I've installed the package for my nextjs application we're using to build a documentation app.

I've copied the example from the storybook but when I run my app, I'm getting these errors on the console:

Could not find property to go with default of kind in [object Object] prop types
Could not find property to go with default of value in [object Object] prop types
Could not find property to go with default of name in [object Object] prop types
Could not find property to go with default of kind in [object Object] prop types
Could not find property to go with default of value in [object Object] prop types
Could not find property to go with default of name in [object Object] prop types

The app itself doesn't display anything.
Is there any way to debug & fix that?

Also: Is there a way to literally just get an array of the props rather than having to use pretty-proptypes?

Best

David

Prop opt out

We are finding that our prop types are getting increasingly complex and sophisticated. At some point extract-react-types does not use the TypeScript compiler and it is not reasonable for it to understand the whole breadth of the language.

Can I suggest an opt out?

type Props = {
/* @readonly
….
*/
name: SomeSuperComplexType
}

Perhaps we could put some information in the jsdoc above a prop to tell extract-react-types to not bother trying to understand the type and just print it out as a raw string

Cannot parse anonymous classes

Currently

export default class extends React.PureComponent<Props> {}

fails with a cryptic 'no converter for: null' error. We implicitly expect all classes (and maybe functions) passed to us to be named.

To resolve this we need to either:

a) Make this an explicit rule and add useful error messaging

or

b) Support unnamed classes (and maybe functions)

I am leaning towards b)

Error: Missing converter for X

This is a common issue with extract-react-types (ERT), which essentially means that:

You are using a language feature that ERT is not familiar with

The way ERT evaluates complex types and language specific features is largely a manual process. We have to manually tell ERT how to interpret things like extends and React.FC, so inevitably there will be things that we don't support or haven't considered.

For a list of all supported and unsupported features, see this issue: #149

For example purposes, consider the scenario that the unknown keyword is not known to ERT. When the code below is parsed, it will error stating: Missing converter for: TSUnknownKeyword

interface FooProps {
  // Docs about the bar property here
  bar: unknown; 
}

const Foo = props => null;

If you are experiencing this issue, please leave a comment below or consider raising a pull request.

Add converter for TSTypeOperator

We have a complicated type that looks like:

// HtmlAttributes = AllHTMLAttributes - OnlyButtonProps
// We do this so onClick, and other props that overlap with html attributes,
// have the type defined by OnlyButtonProps.
type HtmlAttributes = Pick<
  React.AllHTMLAttributes<HTMLElement>,
  Exclude<keyof React.AllHTMLAttributes<HTMLElement>, keyof OnlyButtonProps>
>;

// This ends up being the Button prop API
type ButtonProps = HtmlAttributes & OnlyButtonProps

Running extract-react-types on this type throws the following error:

../packages/core/button/src/components/Button.tsx (../node_modules/extract-react-types-loader!../packages/core/button/src/components/Button.tsx)
Module build failed (from ../node_modules/extract-react-types-loader/index.js):
Error: Missing converter for: TSTypeOperator

It's pretty understandable something like this would not be supported by extract-react-types. This issue is really to discuss how type system specific features should (or shouldn't) be supported. My workaround at the moment is just creating a new file with a dummy component with the prop types I want extracted.

Maybe this highlights a need for a magic comment that extract-react-types will look for and extract if it exists.

Parse PropTypes from vanilla js components

Add support for components using vanilla js and prop-types.
For example:

import PropTypes from 'prop-types';

class Greeting extends React.Component {
  render() {
    return (
      <h1>Hello, {this.props.name}</h1>
    );
  }
}

Greeting.propTypes = {
  name: PropTypes.string
};

Currently, If ERT attempts to parse a vanilla js file it will result in an error:

ERROR in ../packages/core/droplist/src/components/Group.js (../node_modules/extract-react-types-loader!../packages/core/droplist/src/components/Group.js)
Module build failed (from ../node_modules/extract-react-types-loader/index.js):
TypeError: Cannot read property 'params' of undefined

Add eslint

There is no eslint and this is probably bad. Add it, and a sane set of rules. Possibly use react-beautiful-dnd as a template.

Support for both React.FC and FC

We currently dont support this:

const MyComponent: React.FC<Props> = () => ()

but do support this:

const MyComponent:FC<Props> = () => ()

1.0.3 breaks build

Hi, with latest version pretty-proptypes dist folder (cjs) refers to a local folder that doens't exist. Seems its build failed, reverting to 1.0.2 works

kind2string custom converters

Hi there, I'm working on a simple type renderer like pretty-proptypes and have been digging through the kind2string package. Essentially, I am looking to render something close vscode's type inspector where if a referenceIdName exists, we render that instead of expanding the type.

Screen Shot 2019-10-23 at 9 23 34 AM

The package's readme says

It also exposes its converter object, allowing you to overwrite converters of your choice, if you wish to perform some other action other than the default string converter.

But it looks like the converters are not exported. Do we mind if I send a PR to do so? Right now, my solution is to vendor all of the source code into my project.

Feat: Add a way to display types that are not props

When we want to display a type that is not a prop type we do a hack like this.

import { AttributesType } from '../src/types';

export default function Attributes(props: AttributesType) {
  return null;
}

It would be nice to be able to get the type directly from a file without using it in this way.

Running into uncaught exceptions when trying to extract from flow

When trying to extract from flow, I get the following uncaught exception:

{ Error: (client) ../src/button/button.js (../node_modules/extract-react-types-loader!../src/button/button.js)
Module build failed (from ../node_modules/extract-react-types-loader/index.js):
TypeError: Cannot read property 'kind' of undefined
    at props.members.forEach.p ($/node_modules/extract-react-types/index.js:52:29)
    at Array.forEach (<anonymous>)
    at getPropFromObject ($/node_modules/extract-react-types/index.js:45:17)
    at getProp ($/node_modules/extract-react-types/index.js:74:12)
    at defaultProps.forEach.property ($/node_modules/extract-react-types/index.js:237:18)
    at Array.forEach (<anonymous>)
    at addDefaultProps ($/node_modules/extract-react-types/index.js:232:16)
    at convertReactComponentClass ($/node_modules/extract-react-types/index.js:263:10)
    at converters.Program ($/node_modules/extract-react-types/index.js:182:19)
    at convert ($/node_modules/extract-react-types/index.js:1433:16)
    at extractReactTypes ($/node_modules/extract-react-types/index.js:1470:10)
    at Object.extractReactTypesLoader ($/node_modules/extract-react-types-loader/index.js:65:17)

Repro: https://github.com/uber-web/baseui/pull/714/files#diff-6fabde4439377979ef1bfcd17147b620R43

Default values for props are not always extracted

The default value for props does not always seem to get extracted by extract-react-types.

One place where this is very visible is https://atlassian.design/components/button/code which has no default value information (despite having default values for many of the props).

As @danieldelcore indicated on Slack this is likely because:

I suspect it only pulls defaults off a component from the Component.defaultProps property or via static defaultProps rather than destructured function args

It would be great if extract-react-types could support extracting default values for props that are defined through default values on destructured function args πŸ˜„

Add converter for TSMappedType

I am using XOR type from here: https://bitbucket.org/atlassian/atlaskit-mk-2/src/31638e072721303cafbea43fe78c94a1ef92fd11/packages/core/type-helpers/src/index.ts#lines-159
To specify a type that is one or another, but not both here:
https://bitbucket.org/atlassian/atlaskit-mk-2/pull-requests/6112/ms-1987-step-31-move-editor-and-other/diff#Lpackages/editor/editor-core/src/plugins/media/types.tsT79

Basically, I want this prop to accept either

viewMediaClientConfig: MediaClientConfig;

or

viewContext: Promise<Context>;

but not both.

Add ability to hide selected fields

We have a use case where the component whose props we want to expose is wrapped by a HOC to inject analytics. The types that ended up loaded are the union of the base and analytics props but we want to keep the latter internal.
We have a workaround now but it's not ideal. It'd be great if we'd be able to hide certain fields from being loaded so we can display this for documentation. The solution might make more sense to come from extract-react-types-loader but I was unable to find where to submit an issue there.

Feat: Add support for memo wrapped around a component as an assigned variable

🀩 Currently type inference works for the following cases:

memo((props: ComponentProps) => {}); 
memo(forwardRef<HTMLElement, ComponentProps>(() => {}));

const Component = forwardRef<HTMLElement, ComponentProps>(() => {});
memo<ComponentProps>(Component);

🀯 It would be cool for it to also work for this pls:

const Component = forwardRef<HTMLElement, ComponentProps>(() => {});
memo(Component);

Idea: Prop type skip

In some cases where a component's props are obscured by higher order components we should to define an override so that ERT can still access the relevant props of the component

My idea is a comment override:

/** @ert-override Props */ 

// πŸ‘† this statement tells ERT which props to access 

interface Props {
  bar: string;
}

const MyComponent: FC<Props> = props => null;

export default withBadHOC(withAnotherHOC(MyComponent)); // πŸ’₯ ERT is unable to grab these props because they're obscured by this HOC

Avoid including `ts-ignore` and `eslint-disable-next-line` in docs

In this case, the eslint-disable will be included in the actual docs of the component. We need to define a way to omit these comments

interface Props {
  // eslint-disable-next-line @repo/internal/react/consistent-props-definitions
  /** This is my prop doc */
  bar: 'Foo';
}

Type support checklist βœ…

The way ERT evaluates complex types and language specific features is largely a manual process. We have to manually tell ERT how to interpret things like extends and React.FC<P>, so inevitably there will be things that we don't support or haven't considered.

This issue aims to track what features we do and don't support and in cases where a feature is unsupported, we hope to provide a suitable bail-out, similar to what is suggested here #141

If there's something missing on this list, please leave a comment below πŸ˜„

TypeScript

  • primitive types boolean, array, string, number βœ…
  • any βœ…
  • unknown βœ…
  • void βœ…
  • functions βœ…
  • array[] & Array<> βœ…
  • interfaces βœ…
  • types type foo = string βœ…
  • union types 'foo' | 'bar' βœ…
  • intersection types Foo & Bar βœ…
  • extends βœ…
  • keyof ❌
  • as ❌
  • template literal types ❌
  • index signatures ❌
  • conditional types ❌

TypeScript utility types

  • Omit<> ❌
  • Partial<> ❌
  • Record<> ❌
  • Pick<> ❌
  • ReturnType<> ❌

TypeScript via @types/react

  • React.FC<Props> βœ…
  • React.memo<Props> βœ…
  • React.forwardRef<ElementType, Props> βœ…

Flow

TBD

Related issues

#59
#141

Add converter for LogicalExpression

Use case:
when there is any logical operator in defaultProps

class Button extends React.Component<{}> {
  static defaultProps = {
    and: window && window.location.href,
    or: 'me' || 'you',
  }
}

Running extract-react-types on this type throws the following error:

Error: Missing converter for: LogicalExpression
    at convert (webpack:///../packages/extract-react-types/src/index.js?:1775:25)
    at converters.ObjectProperty (webpack:///../packages/extract-react-types/src/index.js?:489:12)
    at convert (webpack:///../packages/extract-react-types/src/index.js?:1776:16)
    at eval (webpack:///../packages/extract-react-types/src/index.js?:764:15)

Would it be a valid use case to be covered? Or is there any recommendation to work around this? Thanks.

Extended union not showing

Hi,

When extending the type with an union & (DivProps | AnchorProps) this does not show in <PrettyPropTypes />.

The data is available in the response of extract-react-types-loader. Only kind2string does not know how to deal with this data was expecting to reduce to an object and could not {kind: "union", types: Array(2)}

Component:

export type AnchorProps = JSX.IntrinsicElements['a']
export type ButtonProps = JSX.IntrinsicElements['button']

export type ComponentProps = GlProgressStepBaseProps & (AnchorProps | ButtonProps)

const Component = (props: ComponentProps) => {}

Data returned from extract-react-types-loader:

{
  "kind": "union",
  "types": [
    {
      "kind": "generic",
      "value": {
        "kind": "generic",
        "value": {
          "kind": "id",
          "name": "JSX.IntrinsicElements['a']"
        },
        "referenceIdName": "AnchorProps"
      }
    },
    {
      "kind": "generic",
      "value": {
        "kind": "generic",
        "value": {
          "kind": "id",
          "name": "JSX.IntrinsicElements['button']"
        },
        "referenceIdName": "ButtonProps"
      }
    }
  ]
}

I'm happy to submit a PR for this, just wanted to see if that would be something you'd be open to supporting? And perhaps you have some ideas on how this should be solved?

Thanks! And many thanks for creating these awesome packages.

TypeScript: types not accessible from non-relative module imports

Summary

I'm finding that ERT is not able to successfully access types from non-relative imports, ie (import { MyInterface } from '@atlaskit/foo';)

After some debugging, we found that when ERT attempts to collect the types for this module, the file path points to the dist entrypoint (/atlaskit/node_modules/@atlaskit/analytics-next/dist/cjs/index.js), which is listed in package.json and not the compiled type definition file that typescript outputs with the corresponding type definitions (/atlaskit/node_modules/@atlaskit/analytics-next/dist/cjs/index.d.ts).

Reproduction

This manifests into the following errror: TypeError: Cannot read property β€˜kind’ of undefined.

Example reproduction:

import { BarProps } from '@atlaskit/analytics-next';

export interface FooProps extends BarProps { // BarProps is undefined
  hello: string;
}

Solution

As a possible solution, we could potentially get the types entrypoint from package.json similar to the way we're getting the dist entrypoint and read types from there instead. πŸ€·β€β™‚οΈ

Ping: @gwyneplaine @Noviny @qburst-aparna @mitchellhamilton

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.