strothj / react-docgen-typescript-loader

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

Not working with React.StatelessComponent #25

Closed teddybradford closed 5 years ago

teddybradford commented 5 years ago

I am trying to use react-docgen-typescript-loader v3 with storybook v4 and the storybook info addon, but am running into an issue where the component name shows up as an empty string (null?) and the prop types info displays "No propTypes defined" even when the component has a props interface.

Here's my Webpack config for Storybook:

const path = require("path");

module.exports = (baseConfig, env, defaultConfig) => {
  // TypeScript module rule for components
  defaultConfig.module.rules.push({
    test: /\.tsx?$/,
    include: path.resolve(__dirname, "../src"),
    use: ["ts-loader", "react-docgen-typescript-loader"]
  });

  // TypeScript module rule for stories
  defaultConfig.module.rules.push({
    test: /\.tsx?$/,
    include: path.resolve(__dirname, "../stories"),
    use: ["ts-loader"]
  });

  // CSS module rule replacement
  const cssTest = /\.css$/;
  const cssRuleIndex = defaultConfig.module.rules.findIndex(
    obj => obj.test.toString() === cssTest.toString()
  );
  defaultConfig.module.rules[cssRuleIndex] = {
    test: cssTest,
    use: [
      "style-loader",
      {
        loader: "css-loader",
        options: {
          importLoader: 1
        }
      },
      "postcss-loader"
    ]
  };

  defaultConfig.resolve.extensions.push(".ts", ".tsx");

  return defaultConfig;
};

And here's a component that isn't working:

import React from "react";
import { FontFamily, FontSize, LineHeight, Text } from "./text";

interface HeadingProps {
  /** Level of hierarchy */
  level: "1" | "2" | "3";
}

export const Heading: React.StatelessComponent<HeadingProps> = props => {
  let fontSize;
  switch (props.level) {
    case "3":
      fontSize = FontSize.Large;
      break;
    case "2":
      fontSize = FontSize.Huge;
      break;
    default:
      fontSize = FontSize.Giant;
  }

  return (
    <Text fontSize={fontSize}>
      {props.children}
    </Text>
  );
};

Any idea what's going wrong here?

strothj commented 5 years ago

Can you please provide the source for the Storybook story? Thank you.

teddybradford commented 5 years ago

@strothj Sure thing! Here's the story for the Heading component:

import { select, text, withKnobs } from "@storybook/addon-knobs";
import { storiesOf } from "@storybook/react";
import React from "react";
import { Heading } from "../../src/components/heading";

const stories = storiesOf("Components|Text", module);
stories.addDecorator(withKnobs);

stories.add("Heading", () => (
  <Heading level={select("level", ["1", "2", "3"], "1")}>
    {text("text", "My heading")}
  </Heading>
));
strothj commented 5 years ago

Sorry for the long delay.

The following change in your Storybook configuration should allow this to work. From my testing it seems to work fine:

  const compilerOptions = {
    ...require("../tsconfig.json").compilerOptions,
    // For allowSyntheticDefaultImports/esModuleInterop, a target of "es6" or
    // above is required. ts-loader will load the project tsconfig.json normally
    // with its compilation settings.
    target: "esnext",
  };
  // If you have the compiler option "moduleResolution", delete it. You'll get
  // an error otherwise.
  delete compilerOptions.moduleResolution;

  config.module.rules.push({
    test: /\.tsx?$/,
    include: path.resolve(__dirname, "../src"),
    use: [
      require.resolve("ts-loader"),
      {
        loader: require.resolve("react-docgen-typescript-loader"),
        // Pass along our customized compiler options to enable synthetic
        // default imports.
        options: { compilerOptions },
      },
    ],
  });

The base package react-docgen-typescript has a default config that it uses when no options are supplied:

const defaultOptions: ts.CompilerOptions = {
  jsx: ts.JsxEmit.React,
  module: ts.ModuleKind.CommonJS,
  target: ts.ScriptTarget.Latest
};

You might be able to just use the loader option: tsconfigPath. I haven't tested it yet.

teddybradford commented 5 years ago

@strothj Thank you! I can confirm that this was the issue with my setup. The default compilerOptions were conflicting with the settings in my tsconfig.json file. Aligning the loader config with tsconfig.json fixed the issue. 🙂