strothj / react-docgen-typescript-loader

Webpack loader to generate docgen information from Typescript React components.
Other
360 stars 47 forks source link

Not pulling props #10

Open dhanson358 opened 6 years ago

dhanson358 commented 6 years ago

Hope I'm not being daft and missing something obvious here, but I'm following the README and not having luck getting this to operate like I would expect.

Given a simple test component app/javascript/components/Form/TextInput/TextInput.tsx:

import React from 'react';
import FieldText from '@atlaskit/field-text'

export interface ITextInputProps {
  /**
   * Name of field
   *
   * @default "input"
   **/
  name: string,
}

/**
 * Simple form input
 */
export class TextInput extends React.Component<ITextInputProps> {

  render() {
    return (
      <FieldText
        {...this.props}
      />
    );
  }
}

export default TextInput;

and it's story app/javascript/components/Form/TextInput/TextInput.stories.js

import React from 'react';

import { storiesOf } from '@storybook/react';
import { withInfo } from '@storybook/addon-info';

import TextInput from './TextInput'

const stories = storiesOf("Form", module);

stories.add('TextInput', withInfo()(() => (
    <TextInput
      label="Required with default value"
      required
      pattern="[A-Z]{3}"
      value="A default value!"
      name="example-text"
    />
  )));

and the .storybook/webpack.config.js of

const path = require("path");

module.exports = (baseConfig, env, config) => {
  config.module.rules.push({
    test: /\.(ts|tsx)$/,
    include: path.resolve(__dirname, "../app/javascript"),
    use: [
      require.resolve("ts-loader"),
      require.resolve("react-docgen-typescript-loader"),
    ],
  });
  config.resolve.extensions.push(".ts", ".tsx");

  return config;
};

and stories/index.stores.js of

import React from 'react';

import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { linkTo } from '@storybook/addon-links';
import { withInfo, setDefaults } from '@storybook/addon-info';

setDefaults({
  header: false, // Toggles display of header with component name and description
  inline: true,
});

I'm getting the story, but no props displayed: image

Appreciate any guidance!

strothj commented 6 years ago

Hello,

On first glance, can you try changing this:

export interface ITextInputProps {
  /**
   * Name of field
   *
   * @default "input"
   **/
  name: string,
}

to this:

export interface ITextInputProps {
  /**
   * Name of field
   *
   * @default "input"
   */
  name: string,
}

Notice the single asterisks at the bottom of the jsdoc. The detection for jsdocs is handled by the TypeScript compiler, so maybe that has something to do with it.

I'll be unavailable to look into this further for the remainder of the day due to a deadline. I can followup tomorrow if you're still having an issue.

Even better would be a sample repo I can have a look at. Everything else looks good to me.

dhanson358 commented 6 years ago

Changing the asterisks did not impact it. I'll see if I can pull together a repo... it's a bit more complicated because I'm using Webpacker on a rails project for this (which could be part of the issue). I may try it with a "clean" non-rails project and see if I get a different result?

strothj commented 6 years ago

@dhanson358 Were you still interested in working this issue out? Let me know so I can close this issue or see what else I can do to assist you.

dhanson358 commented 6 years ago

Yeah I'm still stuck on this, would appreciate any guidance you can provide.

StevenLangbroek commented 6 years ago

We're experiencing this same issue. There seems to be a difference in the way class components are handled vs functional components too. I'll add more info after debugging a bit further with my colleague.

strothj commented 6 years ago

Any information you can provide would be awesome.

If you feel you need to add a way to get some debug output easier, feel free to make a pull request for that.

StevenLangbroek commented 6 years ago

So I just updated all our deps, this is the versions we're using (I've tried upgrading to webpack 4 but I can't, it's not a direct dependency):

So for us it works for functional components, but not for class components (typed as class MyComponent extends React.Component<IProps, IState>{}. What further info can I provide you that would make a fix easier?

strothj commented 6 years ago

Thanks for taking the time to get that information for me.

A minimal reproduction in a repo would help a lot. This library passes along the source code to a third party package, then inserts a generated code snippet. I'd like to get my hands on a sample repo to determine where the problem lies.

The actual parsing and docgen generation is handled by the library react-docgen-typescript (https://github.com/styleguidist/react-docgen-typescript). It might be worth it to check their issues and see if something sounds like the issue you describe.

StevenLangbroek commented 6 years ago

Lol ok, found at least the hilarious root cause:

Doesn't work:

import React from 'react';

interface IProps {
  whatever?: string;
};

export default class MyComponent extends React.Component<IProps> {}

Works:

import React, { Component } from 'react';

export default class MyComponent extends Component<IProps> {}
dhanson358 commented 6 years ago

Sure enough, @StevenLangbroek 's solution fixes it for me as well! How did you even think to try that haha?

strothj commented 6 years ago

I’m going to leave this issue open until I find some time to update the documentation.

Nice work!

Also will update the dependency to their project. I need to review their recent commits. There's a chance this has been fixed on their end.

strothj commented 6 years ago

I tested with an updated version of react-docgen-typescript. The issue is still present. I've documented the issue in the readme to hopefully save others some time.

chengcyber commented 5 years ago

for anyone run into this issue, my solution is to specify custom tsconfig for this loader

// webpack.config.js
      {
        loader: 'react-docgen-typescript-loader',
        options: {
          tsconfigPath:  path.resolve(__dirname, 'path/to/your/tsconfig.json')
        },
      },
// my tsconfig.json
{
  "compilerOptions": {
    "sourceMap": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "module": "esnext",
    "moduleResolution": "node",
    "target": "es6",
    "jsx": "preserve",
    "allowSyntheticDefaultImports": true,
    "experimentalDecorators": true,
    "noResolve": false,
    "importHelpers": true,
    "lib": [
      "dom",
      "es5",
      "es6",
      "esnext"
    ]
  },
  "exclude": [
    "node_modules",
    "lib",
    "es"
  ]
}

Now, the loader can generate correctly for the syntax like React.Component<IProps, any>

parkerbossier commented 5 years ago

Interestingly, merging your proposed config into my root tsconfig.json had no effect. I actually had to create docgen-tsonfig.json and reference that.

EDIT: On further review, I'm wrong. All you have to do is provide your existing tsconfig path as seen above.

StevenLangbroek commented 5 years ago

Sure enough, @StevenLangbroek 's solution fixes it for me as well! How did you even think to try that haha?

I've worked with AST's, and to do so I've read through existing codemods and compiler toolchains. Building a robust visitor that detects whether something is "a component" (quite a fuzzy term to begin with), is more difficult than you'd hope and has a lot of potential edge cases (a very obvious one is import renaming). I started out with the obvious ones and this turned out to be the case :).

dmahnkopf commented 5 years ago

for anyone run into this issue, my solution is to specify custom tsconfig for this loader

// webpack.config.js
      {
        loader: 'react-docgen-typescript-loader',
        options: {
          tsconfigPath:  path.resolve(__dirname, 'path/to/your/tsconfig.json')
        },
      },
// my tsconfig.json
{
  "compilerOptions": {
    "sourceMap": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "module": "esnext",
    "moduleResolution": "node",
    "target": "es6",
    "jsx": "preserve",
    "allowSyntheticDefaultImports": true,
    "experimentalDecorators": true,
    "noResolve": false,
    "importHelpers": true,
    "lib": [
      "dom",
      "es5",
      "es6",
      "esnext"
    ]
  },
  "exclude": [
    "node_modules",
    "lib",
    "es"
  ]
}

Now, the loader can generate correctly for the syntax like React.Component<IProps, any>

This also seems to fix importing default exported components!

amast09 commented 4 years ago

I ran into a similar issue for the following types of components

React.Component React.PureComponent React.FC

the thing that fixed it was by importing React in the component itself using the following syntax,

import * as React from 'react';

dgreene1 commented 4 years ago

Is there any chance we can reopen this issue @strothj since I can reproduce it even when following the instructions in the readme and when I'm specifying the tsconfig path like it's recommended above?

Note: this can only be reproduced for 'FunctionComponent' but not for 'Component'

dgreene1 commented 4 years ago

Update from before: when I disabled @storybook/preset-create-react-app from the addons section the Prop Types section was populated. So perhaps it was a conflict between the CRA config and my webpackFinal function in .storybook/main.js?

strothj commented 4 years ago

Update from before: when I disabled @storybook/preset-create-react-app from the addons section the Prop Types section was populated. So perhaps it was a conflict between the CRA config and my webpackFinal function in .storybook/main.js?

There's a version of this tool from the Storybook team that handles inserting docgen information for JavaScript projects. Their preset should be disabling it when you're using TypeScript if I'm not mistaken. We've had some issues before when both were active on the Webpack rule handling transpiling your source. Most likely this is a configuration problem.