├── .nvmrc
├── .prettierignore
├── src
├── index.ts
├── loader-utils.d.ts
├── webpack-contrib__schema-utils.d.ts
├── __fixtures__
│ └── components
│ │ ├── TextOnlyComponent.tsx
│ │ ├── Simple.tsx
│ │ ├── HyphenatedPropName.tsx
│ │ ├── MultilineDescription.tsx
│ │ ├── MultiProps.tsx
│ │ ├── MultilinePropDescription.tsx
│ │ └── DefaultPropValue.tsx
├── loader.spec.ts
├── generateDocgenCodeBlock.spec.ts
├── validateOptions.ts
├── validateOptions.spec.ts
├── LoaderOptions.ts
├── webpack.spec.ts
├── loader.ts
├── __snapshots__
│ ├── generateDocgenCodeBlock.spec.ts.snap
│ └── webpack.spec.ts.snap
└── generateDocgenCodeBlock.ts
├── logo.png
├── example.png
├── .gitignore
├── example-story-name.png
├── .babelrc
├── examples
└── storybook
│ ├── .storybook
│ ├── addons.js
│ ├── preview-head.html
│ ├── config.js
│ └── webpack.config.js
│ ├── src
│ └── components
│ │ ├── TicTacToeGameBoard
│ │ ├── index.ts
│ │ ├── TicTacToeGameBoard.css
│ │ ├── TicTacToeGameBoard.tsx
│ │ └── TicTacToeGameBoard.stories.tsx
│ │ └── TicTacToeCell
│ │ ├── index.ts
│ │ ├── TicTacToeCell.css
│ │ ├── TicTacToeCellProps.tsx
│ │ ├── TicTacToeCell.tsx
│ │ └── TicTacToeCell.stories.tsx
│ ├── tsconfig.json
│ ├── package.json
│ └── LICENSE.md
├── .editorconfig
├── jest.config.js
├── tsconfig.build.json
├── appveyor.yml
├── .prettierrc
├── .travis.yml
├── tslint.json
├── tsconfig.json
├── LICENSE.md
├── package.json
├── CHANGELOG.md
└── README.md
/.nvmrc:
--------------------------------------------------------------------------------
1 | 8.10.0
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | package.json
2 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export { default } from "./loader";
2 |
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strothj/react-docgen-typescript-loader/HEAD/logo.png
--------------------------------------------------------------------------------
/example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strothj/react-docgen-typescript-loader/HEAD/example.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .vscode/
3 | .idea/
4 | .rpt2_cache/
5 | dist/
6 | *error.log
7 | storybook-static/
8 |
--------------------------------------------------------------------------------
/example-story-name.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strothj/react-docgen-typescript-loader/HEAD/example-story-name.png
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-typescript"],
3 | "plugins": ["@babel/plugin-transform-modules-commonjs"]
4 | }
5 |
--------------------------------------------------------------------------------
/examples/storybook/.storybook/addons.js:
--------------------------------------------------------------------------------
1 | import "@storybook/addon-actions/register";
2 | import "@storybook/addon-links/register";
3 |
--------------------------------------------------------------------------------
/examples/storybook/src/components/TicTacToeGameBoard/index.ts:
--------------------------------------------------------------------------------
1 | export { TicTacToeGameBoard as default } from "./TicTacToeGameBoard";
2 |
--------------------------------------------------------------------------------
/examples/storybook/src/components/TicTacToeCell/index.ts:
--------------------------------------------------------------------------------
1 | export { TicTacToeCell as default, TicTacToeCellProps } from "./TicTacToeCell";
2 |
--------------------------------------------------------------------------------
/src/loader-utils.d.ts:
--------------------------------------------------------------------------------
1 | declare module "loader-utils" {
2 | import { loader } from "webpack";
3 |
4 | export function getOptions(context: loader.LoaderContext): object | null;
5 | }
6 |
--------------------------------------------------------------------------------
/examples/storybook/.storybook/preview-head.html:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | end_of_line = lf
7 | insert_final_newline = true
8 | trim_trailing_whitespace = true
9 | charset = utf-8
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | testEnvironment: "node",
3 | // transform: {
4 | // "^.+\\.tsx?$": "ts-jest",
5 | // },
6 | testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
7 | moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
8 | };
9 |
--------------------------------------------------------------------------------
/src/webpack-contrib__schema-utils.d.ts:
--------------------------------------------------------------------------------
1 | declare module "@webpack-contrib/schema-utils" {
2 | export interface Options {
3 | name: string;
4 | schema: object;
5 | target: object;
6 | }
7 |
8 | export default function validate(options: Options): void;
9 | }
10 |
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": true,
5 | "sourceMap": true,
6 | "outDir": "dist",
7 | "noEmit": false
8 | },
9 | "exclude": ["**/*.spec.ts", "**/__fixtures__", "**/__mocks__"]
10 | }
11 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | environment:
2 | nodejs_version: "8.10"
3 |
4 | install:
5 | - ps: Install-Product node $env:nodejs_version
6 | - yarn
7 |
8 | test_script:
9 | - node --version
10 | - yarn --version
11 | - yarn lint
12 | - yarn test
13 | - yarn build
14 |
15 | build: off
16 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "tabWidth": 2,
4 | "useTabs": false,
5 | "semi": true,
6 | "singleQuote": false,
7 | "trailingComma": "all",
8 | "bracketSpacing": true,
9 | "jsxBracketSameLine": false,
10 | "arrowParens": "avoid",
11 | "proseWrap": "preserve"
12 | }
13 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | os:
4 | - linux
5 | - osx
6 |
7 | before_install:
8 | - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.5.1
9 | - export PATH="$HOME/.yarn/bin:$PATH"
10 |
11 | script:
12 | - yarn lint
13 | - yarn test
14 | - yarn build
15 |
--------------------------------------------------------------------------------
/src/__fixtures__/components/TextOnlyComponent.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | /**
4 | * A component with only text content wrapped in a div.
5 | *
6 | * Ref: https://github.com/strothj/react-docgen-typescript-loader/issues/7
7 | */
8 | export const SimpleComponent: React.SFC<{}> = () => (
9 |
Test only component
10 | );
11 |
--------------------------------------------------------------------------------
/examples/storybook/.storybook/config.js:
--------------------------------------------------------------------------------
1 | import { configure } from "@storybook/react";
2 |
3 | // automatically import all files ending in *.stories.tsx
4 | const req = require.context("../src/components", true, /.stories.tsx$/);
5 | function loadStories() {
6 | req.keys().forEach(filename => req(filename));
7 | }
8 |
9 | configure(loadStories, module);
10 |
--------------------------------------------------------------------------------
/examples/storybook/src/components/TicTacToeCell/TicTacToeCell.css:
--------------------------------------------------------------------------------
1 | .ttt-cell {
2 | display: block;
3 | width: 100%;
4 | height: 100%;
5 | padding: 0;
6 | border: none;
7 | background-color: transparent;
8 | font-size: 400%;
9 | color: #fff;
10 | outline: none;
11 | cursor: pointer;
12 | }
13 |
14 | .ttt-cell--hidden {
15 | cursor: inherit;
16 | }
17 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["tslint-config-airbnb", "tslint-config-prettier"],
3 | "rulesDirectory": ["tslint-plugin-prettier"],
4 | "rules": {
5 | "object-shorthand-properties-first": false,
6 | "import-name": false,
7 | "variable-name": false,
8 | "strict-boolean-expressions": false,
9 | "no-unused-variable": false,
10 | "prettier": true
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/__fixtures__/components/Simple.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | interface SimpleComponentProps {
4 | /** Button color. */
5 | color: "blue" | "green";
6 | }
7 |
8 | /**
9 | * A simple component.
10 | */
11 | export const SimpleComponent: React.SFC = props => (
12 | {props.children}
13 | );
14 |
--------------------------------------------------------------------------------
/examples/storybook/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "esnext",
5 | "lib": ["esnext"],
6 | "jsx": "react",
7 | "strict": true,
8 | "moduleResolution": "node",
9 | "noUnusedLocals": true,
10 | "noUnusedParameters": true,
11 | "noImplicitReturns": true,
12 | "noFallthroughCasesInSwitch": true,
13 | "allowSyntheticDefaultImports": true,
14 | "esModuleInterop": true
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/__fixtures__/components/HyphenatedPropName.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | interface HyphenatedPropNameProps {
4 | /** Button color. */
5 | "button-color": "blue" | "green";
6 | }
7 |
8 | /**
9 | * A component with a hyphenated prop name.
10 | */
11 | export const HyphenatedPropNameComponent: React.SFC<
12 | HyphenatedPropNameProps
13 | > = props => (
14 |
15 | {props.children}
16 |
17 | );
18 |
--------------------------------------------------------------------------------
/src/__fixtures__/components/MultilineDescription.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | interface MultilineDescriptionProps {
4 | /** Button color. */
5 | color: "blue" | "green";
6 | }
7 |
8 | /**
9 | * A component with a multiline description.
10 | *
11 | * Second line.
12 | */
13 | export const MultilineDescriptionComponent: React.SFC<
14 | MultilineDescriptionProps
15 | > = props => (
16 | {props.children}
17 | );
18 |
--------------------------------------------------------------------------------
/src/__fixtures__/components/MultiProps.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | interface MultiPropsComponentProps {
4 | /** Button color. */
5 | color: "blue" | "green";
6 |
7 | /** Button size. */
8 | size: "small" | "large";
9 | }
10 |
11 | /**
12 | * This is a component with multiple props.
13 | */
14 | export const MultiPropsComponent: React.SFC<
15 | MultiPropsComponentProps
16 | > = props => (
17 | {props.children}
18 | );
19 |
--------------------------------------------------------------------------------
/examples/storybook/src/components/TicTacToeCell/TicTacToeCellProps.tsx:
--------------------------------------------------------------------------------
1 | export interface TicTacToeCellProps {
2 | /**
3 | * Value to display, either empty (" ") or "X" / "O".
4 | *
5 | * @default " "
6 | **/
7 | value?: " " | "X" | "O";
8 |
9 | /**
10 | * Cell position on game board.
11 | */
12 | position: {
13 | x: number;
14 | y: number;
15 | };
16 |
17 | /**
18 | * Called when an empty cell is clicked.
19 | */
20 | onClick?: (x: number, y: number) => void;
21 | }
22 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "commonjs",
5 | "lib": ["esnext"],
6 | "jsx": "react",
7 | "rootDir": "src",
8 | "noEmit": true,
9 | "downlevelIteration": true,
10 | "strict": true,
11 | "noUnusedLocals": true,
12 | "noUnusedParameters": true,
13 | "noImplicitReturns": true,
14 | "noFallthroughCasesInSwitch": true,
15 | "moduleResolution": "node",
16 | "allowSyntheticDefaultImports": true,
17 | "esModuleInterop": true
18 | },
19 | "include": ["./src/**/*"]
20 | }
21 |
--------------------------------------------------------------------------------
/src/__fixtures__/components/MultilinePropDescription.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | interface MultilinePropDescriptionComponentProps {
4 | /**
5 | * This is a multiline prop description.
6 | *
7 | * Second line.
8 | */
9 | color: "blue" | "green";
10 | }
11 |
12 | /**
13 | * A component with multiline prop description.
14 | */
15 | export const MultilinePropDescriptionComponent: React.SFC<
16 | MultilinePropDescriptionComponentProps
17 | > = props => (
18 | {props.children}
19 | );
20 |
--------------------------------------------------------------------------------
/examples/storybook/src/components/TicTacToeGameBoard/TicTacToeGameBoard.css:
--------------------------------------------------------------------------------
1 | .ttt-game-board {
2 | position: relative;
3 | padding-bottom: 100%;
4 | background-color: rgb(72, 78, 104);
5 | }
6 |
7 | .ttt-game-board__content {
8 | display: flex;
9 | flex-wrap: wrap;
10 | position: absolute;
11 | width: 100%;
12 | height: 100%;
13 | padding: 8px;
14 | }
15 |
16 | .ttt-game-board__cell {
17 | width: calc(100% / 3);
18 | padding: 8px;
19 | border: 2px transparent;
20 | }
21 |
22 | .ttt-game-board__cell:nth-child(3n - 2),
23 | .ttt-game-board__cell:nth-child(3n - 1) {
24 | border-right-color: #ffc153;
25 | }
26 |
27 | .ttt-game-board__cell:nth-child(-n + 6) {
28 | border-bottom-color: #ffc153;
29 | }
30 |
--------------------------------------------------------------------------------
/examples/storybook/.storybook/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 |
3 | module.exports = ({ config }) => {
4 | config.module.rules.push({
5 | test: /\.tsx?$/,
6 | include: path.resolve(__dirname, "../src"),
7 | use: [
8 | require.resolve("ts-loader"),
9 | {
10 | // loader: require.resolve("react-docgen-typescript-loader"),
11 | loader: require.resolve("../../.."),
12 | options: {
13 | // Provide the path to your tsconfig.json so that your stories can
14 | // display types from outside each individual story.
15 | tsconfigPath: path.resolve(__dirname, "../tsconfig.json"),
16 | },
17 | },
18 | ],
19 | });
20 |
21 | config.resolve.extensions.push(".ts", ".tsx");
22 |
23 | return config;
24 | };
25 |
--------------------------------------------------------------------------------
/src/__fixtures__/components/DefaultPropValue.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | interface DefaultPropValueComponentProps {
4 | /**
5 | * Button color.
6 | *
7 | * @default blue
8 | **/
9 | color: "blue" | "green";
10 |
11 | /**
12 | * Button counter.
13 | */
14 | counter: number;
15 |
16 | /**
17 | * Button disabled.
18 | */
19 | disabled: boolean;
20 | }
21 |
22 | /**
23 | * Component with a prop with a default value.
24 | */
25 | export const DefaultPropValueComponent: React.SFC<
26 | DefaultPropValueComponentProps
27 | > = props => (
28 |
29 | {props.counter}
30 | {props.children}
31 |
32 | );
33 |
34 | DefaultPropValueComponent.defaultProps = {
35 | counter: 123,
36 | disabled: false,
37 | };
38 |
--------------------------------------------------------------------------------
/examples/storybook/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "react-docgen-typescript-loader-example",
4 | "version": "1.0.0",
5 | "main": "index.js",
6 | "license": "MIT",
7 | "scripts": {
8 | "storybook": "start-storybook -p 6006",
9 | "build-storybook": "build-storybook"
10 | },
11 | "dependencies": {
12 | "react": "^16.9.0",
13 | "react-dom": "^16.9.0"
14 | },
15 | "devDependencies": {
16 | "@babel/core": "^7.5.5",
17 | "@storybook/addon-actions": "5.1.11",
18 | "@storybook/addon-info": "5.1.11",
19 | "@storybook/addon-links": "5.1.11",
20 | "@storybook/addons": "5.1.11",
21 | "@storybook/react": "5.1.11",
22 | "@types/react": "^16.9.2",
23 | "@types/storybook__addon-actions": "^3.4.3",
24 | "@types/storybook__addon-info": "^4.1.2",
25 | "@types/storybook__react": "^4.0.2",
26 | "babel-loader": "^8.0.6",
27 | "ts-loader": "^6.0.4",
28 | "typescript": "^3.6.2"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/examples/storybook/src/components/TicTacToeCell/TicTacToeCell.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import "./TicTacToeCell.css";
3 | import { TicTacToeCellProps } from "./TicTacToeCellProps";
4 |
5 | export { TicTacToeCellProps };
6 |
7 | /**
8 | * TicTacToe game cell. Displays a clickable button when the value is " ",
9 | * otherwise displays "X" or "O".
10 | */
11 | export class TicTacToeCell extends Component {
12 | handleClick = () => {
13 | const {
14 | position: { x, y },
15 | onClick,
16 | } = this.props;
17 | if (!onClick) return;
18 |
19 | onClick(x, y);
20 | };
21 |
22 | render() {
23 | const { value = " " } = this.props;
24 | const disabled = value !== " ";
25 | const classes = `ttt-cell ${disabled ? "ttt-cell--hidden" : ""}`;
26 |
27 | return (
28 |
33 | {value}
34 |
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Further resources on the MIT License
4 | Copyright 2018 Jason Strothmann
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7 |
8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 |
--------------------------------------------------------------------------------
/examples/storybook/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Further resources on the MIT License
4 | Copyright 2018 Jason Strothmann
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7 |
8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 |
--------------------------------------------------------------------------------
/examples/storybook/src/components/TicTacToeGameBoard/TicTacToeGameBoard.tsx:
--------------------------------------------------------------------------------
1 | import React, { ReactElement, FC } from "react";
2 | import "./TicTacToeGameBoard.css";
3 |
4 | interface Props {
5 | /**
6 | * An array of 9 React elements to serve as the Tic Tac Toe game cells.
7 | */
8 | cells: ReactElement[];
9 |
10 | /**
11 | * Line style to use when dividing game cells.
12 | *
13 | * @default "solid"
14 | */
15 | lineStyle?: "solid" | "dashed";
16 | }
17 |
18 | /**
19 | * Arranges the React elements provided by "cells" within a Tic Tac Toe board.
20 | * It requires that 9 elements be provided. The game board maintains a 1:1 size
21 | * ratio.
22 | */
23 | export const TicTacToeGameBoard: FC = props => {
24 | const { cells, lineStyle = "solid", ...rest } = props;
25 |
26 | return (
27 |
28 |
29 | {cells.map((cell, index) => (
30 |
35 | {cell}
36 |
37 | ))}
38 |
39 |
40 | );
41 | };
42 |
--------------------------------------------------------------------------------
/examples/storybook/src/components/TicTacToeCell/TicTacToeCell.stories.tsx:
--------------------------------------------------------------------------------
1 | import React, { CSSProperties } from "react";
2 | import { storiesOf } from "@storybook/react";
3 | import { withInfo } from "@storybook/addon-info";
4 | import { action } from "@storybook/addon-actions";
5 | import { TicTacToeCell } from "./TicTacToeCell";
6 |
7 | const stories = storiesOf("Components", module);
8 |
9 | stories.addDecorator(withInfo);
10 | stories.addParameters({ info: { inline: true } });
11 |
12 | stories.add("TicTacToeCell", () => (
13 |
14 |
15 |
20 |
21 |
22 |
27 |
28 |
29 | ));
30 |
31 | const styles: { [key: string]: CSSProperties } = {
32 | container: {
33 | display: "flex",
34 | },
35 | cellContainer: {
36 | width: 100,
37 | height: 100,
38 | backgroundColor: "rgb(72, 78, 104)",
39 | },
40 | };
41 | styles.firstCellContainer = { ...styles.cellContainer, marginRight: 20 };
42 |
--------------------------------------------------------------------------------
/examples/storybook/src/components/TicTacToeGameBoard/TicTacToeGameBoard.stories.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from "react";
2 | import { storiesOf } from "@storybook/react";
3 | import { withInfo } from "@storybook/addon-info";
4 | import { TicTacToeGameBoard } from "./TicTacToeGameBoard";
5 | import TicTacToeCell, { TicTacToeCellProps } from "../TicTacToeCell";
6 |
7 | const stories = storiesOf("Components", module);
8 |
9 | stories.addDecorator(withInfo);
10 | stories.addParameters({ info: { inline: true } });
11 |
12 | stories.add("TicTacToeGameBoard", () => (
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | ));
22 |
23 | const PlaceholderCell: FC = () => (
24 | Cell
25 | );
26 |
27 | const placeholderCells = Array.from({ length: 9 }, (_, index) => (
28 |
29 | ));
30 |
31 | const gameCellValues = "XOX O O X".split("") as TicTacToeCellProps["value"][];
32 | const gameCells = Array.from(gameCellValues, (char, index) => (
33 |
38 | ));
39 |
--------------------------------------------------------------------------------
/src/loader.spec.ts:
--------------------------------------------------------------------------------
1 | import path from "path";
2 | import webpack from "webpack";
3 | import loader from "./loader";
4 |
5 | // TODO: Isolate loader.ts dependencies and test in isolation.
6 | // TODO: Test error handling in asynchronous vs synchronous mode.
7 |
8 | const mockLoaderContextAsyncCallback = jest.fn();
9 | const mockLoaderContextCacheable = jest.fn();
10 | const mockLoaderContextResourcePath = jest.fn();
11 | const mockLoaderContextContext = jest.fn();
12 |
13 | beforeEach(() => {
14 | mockLoaderContextAsyncCallback.mockReset();
15 | mockLoaderContextCacheable.mockReset();
16 | mockLoaderContextResourcePath.mockReset();
17 | mockLoaderContextContext.mockReset();
18 | mockLoaderContextResourcePath.mockImplementation(() =>
19 | path.resolve(__dirname, "./__fixtures__/components/Simple.tsx"),
20 | );
21 | mockLoaderContextContext.mockImplementation(() =>
22 | path.resolve(__dirname, "./__fixtures__/"),
23 | );
24 | });
25 |
26 | it("marks the loader as being cacheable", () => {
27 | executeLoaderWithBoundContext();
28 |
29 | expect(mockLoaderContextCacheable.mock.calls[0][0]).toEqual(true);
30 | });
31 |
32 | // Execute loader with its "this" set to an instance of LoaderContext.
33 | function executeLoaderWithBoundContext() {
34 | loader.call(
35 | ({
36 | async: mockLoaderContextAsyncCallback,
37 | cacheable: mockLoaderContextCacheable,
38 | resourcePath: mockLoaderContextResourcePath(),
39 | context: mockLoaderContextContext(),
40 | } as Pick<
41 | webpack.loader.LoaderContext,
42 | "async" | "cacheable" | "resourcePath" | "context"
43 | >) as webpack.loader.LoaderContext,
44 | "// Original Source Code",
45 | );
46 | }
47 |
--------------------------------------------------------------------------------
/src/generateDocgenCodeBlock.spec.ts:
--------------------------------------------------------------------------------
1 | import fs from "fs";
2 | import path from "path";
3 | import { parse, ParserOptions } from "react-docgen-typescript/lib/parser.js";
4 | import generateDocgenCodeBlock, {
5 | GeneratorOptions,
6 | } from "./generateDocgenCodeBlock";
7 |
8 | const fixtureTests: GeneratorOptions[] = loadFixtureTests();
9 | const simpleFixture = fixtureTests.find(f => f.filename === "Simple.tsx")!;
10 |
11 | describe("component fixture", () => {
12 | fixtureTests.forEach(generatorOptions => {
13 | it(`${generatorOptions.filename} has code block generated`, () => {
14 | expect(generateDocgenCodeBlock(generatorOptions)).toMatchSnapshot();
15 | });
16 | });
17 | });
18 |
19 | it("adds component to docgen collection", () => {
20 | expect(
21 | generateDocgenCodeBlock({
22 | ...simpleFixture,
23 | docgenCollectionName: "STORYBOOK_REACT_CLASSES",
24 | }),
25 | ).toMatchSnapshot();
26 | });
27 |
28 | function getGeneratorOptions(parserOptions: ParserOptions = {}) {
29 | return (filename: string) => {
30 | const filePath = path.resolve(
31 | __dirname,
32 | "__fixtures__/components",
33 | filename,
34 | );
35 |
36 | return {
37 | filename,
38 | source: fs.readFileSync(filePath, "utf8"),
39 | componentDocs: parse(filePath, parserOptions),
40 | docgenCollectionName: null,
41 | setDisplayName: true,
42 | typePropName: "type",
43 | } as GeneratorOptions;
44 | };
45 | }
46 |
47 | function loadFixtureTests(): GeneratorOptions[] {
48 | return fs
49 | .readdirSync(path.resolve(__dirname, "__fixtures__/components"))
50 | .map(getGeneratorOptions());
51 | }
52 |
53 | it("generates value info for enums", () => {
54 | expect(
55 | generateDocgenCodeBlock(
56 | getGeneratorOptions({ shouldExtractLiteralValuesFromEnum: true })(
57 | "DefaultPropValue.tsx",
58 | ),
59 | ),
60 | ).toMatchSnapshot();
61 | });
62 |
--------------------------------------------------------------------------------
/src/validateOptions.ts:
--------------------------------------------------------------------------------
1 | import validate from "@webpack-contrib/schema-utils";
2 | import LoaderOptions from "./LoaderOptions";
3 |
4 | const schema = {
5 | type: "object",
6 | additionalProperties: false,
7 | properties: {
8 | skipPropsWithName: {
9 | anyOf: [
10 | { type: "string", minLength: 1 },
11 | { type: "array", minItems: 1, items: { type: "string", minLength: 1 } },
12 | ],
13 | },
14 |
15 | skipPropsWithoutDoc: {
16 | type: "boolean",
17 | },
18 |
19 | componentNameResolver: {
20 | // this is really { type: "function" }
21 | not: {
22 | oneOf: [
23 | { type: "string" },
24 | { type: "number" },
25 | { type: "object" },
26 | { type: "array" },
27 | ],
28 | },
29 | },
30 |
31 | propFilter: {
32 | // this is really { type: "function" }
33 | not: {
34 | oneOf: [
35 | { type: "string" },
36 | { type: "number" },
37 | { type: "object" },
38 | { type: "array" },
39 | ],
40 | },
41 | },
42 |
43 | tsconfigPath: {
44 | type: "string",
45 | minLength: 1,
46 | },
47 |
48 | compilerOptions: {
49 | type: "object",
50 | },
51 |
52 | docgenCollectionName: {
53 | anyOf: [{ type: "string", minLength: 1 }, { type: "null" }],
54 | },
55 |
56 | setDisplayName: {
57 | type: "boolean",
58 | },
59 |
60 | shouldExtractLiteralValuesFromEnum: {
61 | type: "boolean",
62 | },
63 |
64 | savePropValueAsString: {
65 | type: "boolean",
66 | },
67 |
68 | typePropName: {
69 | type: "string",
70 | },
71 |
72 | shouldRemoveUndefinedFromOptional: {
73 | type: "boolean",
74 | },
75 | },
76 | };
77 |
78 | function validateOptions(options: LoaderOptions = {}) {
79 | validate({
80 | name: "react-docgen-typescript-loader",
81 | schema,
82 | target: options,
83 | });
84 | }
85 |
86 | export default validateOptions;
87 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-docgen-typescript-loader",
3 | "version": "3.7.2",
4 | "description": "Webpack loader to generate docgen information from TypeScript React components.",
5 | "keywords": [
6 | "storybook",
7 | "react",
8 | "docgen",
9 | "typescript",
10 | "webpack",
11 | "loader"
12 | ],
13 | "homepage": "https://github.com/strothj/react-docgen-typescript-loader",
14 | "bugs": "https://github.com/strothj/react-docgen-typescript-loader/issues",
15 | "repository": "https://github.com/strothj/react-docgen-typescript-loader.git",
16 | "author": "strothj (https://github.com/strothj)",
17 | "main": "dist/index.js",
18 | "license": "MIT",
19 | "files": [
20 | "dist/*"
21 | ],
22 | "precommit": [
23 | "lint",
24 | "test",
25 | "clean",
26 | "build"
27 | ],
28 | "scripts": {
29 | "start": "tsc -p ./tsconfig.build.json -w",
30 | "build": "tsc -p ./tsconfig.build.json",
31 | "clean": "rimraf dist",
32 | "test": "jest",
33 | "lint": "tslint -p ./"
34 | },
35 | "dependencies": {
36 | "@webpack-contrib/schema-utils": "^1.0.0-beta.0",
37 | "loader-utils": "^1.2.3",
38 | "react-docgen-typescript": "^1.16.5"
39 | },
40 | "peerDependencies": {
41 | "typescript": "*"
42 | },
43 | "devDependencies": {
44 | "@babel/core": "^7.5.5",
45 | "@babel/plugin-transform-modules-commonjs": "^7.5.0",
46 | "@babel/preset-typescript": "^7.3.3",
47 | "@types/jest": "^24.0.18",
48 | "@types/memory-fs": "^0.3.2",
49 | "@types/node": "^12.7.3",
50 | "@types/react": "^16.9.2",
51 | "@types/webpack": "^4.39.1",
52 | "jest": "^24.9.0",
53 | "memory-fs": "^0.4.1",
54 | "pre-commit": "^1.2.2",
55 | "prettier": "^1.18.2",
56 | "react": "^16.9.0",
57 | "rimraf": "^3.0.0",
58 | "ts-loader": "^6.0.4",
59 | "tslint": "^5.19.0",
60 | "tslint-config-airbnb": "^5.11.0",
61 | "tslint-config-prettier": "^1.13.0",
62 | "tslint-plugin-prettier": "^2.0.0",
63 | "typescript": "^3.6.2",
64 | "webpack": "^4.39.3"
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/validateOptions.spec.ts:
--------------------------------------------------------------------------------
1 | import validateOptions from "./validateOptions";
2 |
3 | it("throws error on unexpected field", () => {
4 | expect(() => validateOptions({ invalidField: "fail" } as any)).toThrowError(
5 | /invalidField.*invalid additional property/,
6 | );
7 | });
8 |
9 | it("does not throw on empty", () => {
10 | expect(() => validateOptions()).not.toThrow();
11 | });
12 |
13 | describe("skipPropsWithName", () => {
14 | it("rejects empty string", () => {
15 | expect(() => validateOptions({ skipPropsWithName: "" })).toThrowError(
16 | /skipPropsWithName.*should NOT be shorter than 1 characters/,
17 | );
18 |
19 | expect(() => validateOptions({ skipPropsWithName: "prop" })).not.toThrow();
20 | });
21 |
22 | it("rejects empty array", () => {
23 | expect(() => validateOptions({ skipPropsWithName: [] })).toThrowError(
24 | /skipPropsWithName.*should NOT have less than 1 items/,
25 | );
26 |
27 | expect(() =>
28 | validateOptions({ skipPropsWithName: ["prop"] }),
29 | ).not.toThrow();
30 | });
31 | });
32 |
33 | describe("component name resolver", () => {
34 | it("accepts function", () => {
35 | expect(() =>
36 | validateOptions({
37 | componentNameResolver: () => "",
38 | } as any),
39 | ).not.toThrow();
40 | });
41 | });
42 |
43 | describe("prop filter", () => {
44 | it("accepts function", () => {
45 | expect(() =>
46 | validateOptions({
47 | propFilter: () => true,
48 | } as any),
49 | ).not.toThrow();
50 | });
51 | });
52 |
53 | describe("compilerOptions", () => {
54 | it("accepts object of any shape", () => {
55 | expect(() =>
56 | validateOptions({
57 | compilerOptions: {
58 | option: "test",
59 | otherOption: { secondField: "test" },
60 | },
61 | } as any),
62 | ).not.toThrow();
63 | });
64 | });
65 |
66 | describe("v2 includes/excludes fields", () => {
67 | it("throws error if included", () => {
68 | expect(() =>
69 | validateOptions({
70 | includes: ["*\\.stories\\.tsx$"],
71 | } as any),
72 | ).toThrowError(/includes.*is an invalid additional property/);
73 | });
74 | });
75 |
--------------------------------------------------------------------------------
/src/LoaderOptions.ts:
--------------------------------------------------------------------------------
1 | import { CompilerOptions } from "typescript";
2 | import {
3 | ComponentNameResolver,
4 | PropFilter,
5 | } from "react-docgen-typescript/lib/parser.js";
6 |
7 | export default interface LoaderOptions {
8 | /** Avoid including docgen information for the prop or props specified. */
9 | skipPropsWithName?: string[] | string;
10 |
11 | /** Avoid including docgen information for props without documentation. */
12 | skipPropsWithoutDoc?: boolean;
13 |
14 | /**
15 | * If a string is returned, then the component will use that name. Else it will fallback to the default logic of parser.
16 | */
17 | componentNameResolver?: ComponentNameResolver;
18 |
19 | /**
20 | * Specify function to filter props.
21 | * If either skipPropsWithName or skipPropsWithoutDoc will be specified this will not be used.
22 | */
23 | propFilter?: PropFilter;
24 |
25 | /**
26 | * Specify the location of the tsconfig.json to use. Can not be used with
27 | * compilerOptions.
28 | **/
29 | tsconfigPath?: string;
30 |
31 | /** Specify TypeScript compiler options. Can not be used with tsconfigPath. */
32 | compilerOptions?: CompilerOptions;
33 |
34 | /**
35 | * Specify the docgen collection name to use. All docgen information will
36 | * be collected into this global object. Set to null to disable.
37 | *
38 | * @default STORYBOOK_REACT_CLASSES
39 | * @see https://github.com/gongreg/react-storybook-addon-docgen
40 | **/
41 | docgenCollectionName?: string | null;
42 |
43 | /**
44 | * Automatically set the component's display name. If you want to set display
45 | * names yourself or are using another plugin to do this, you should disable
46 | * this option.
47 | *
48 | * ```
49 | * class MyComponent extends React.Component {
50 | * ...
51 | * }
52 | *
53 | * MyComponent.displayName = "MyComponent";
54 | * ```
55 | *
56 | * @default true
57 | */
58 | setDisplayName?: boolean;
59 |
60 | /**
61 | * If set to true, string enums and unions will be converted to docgen enum format.
62 | * Useful if you use Storybook and want to generate knobs automatically using [addon-smart-knobs](https://github.com/storybookjs/addon-smart-knobs).
63 | * @see https://github.com/styleguidist/react-docgen-typescript#parseroptions
64 | * */
65 | shouldExtractLiteralValuesFromEnum?: boolean;
66 |
67 | /**
68 | * If set to true, defaultValue to props will be string.
69 | * @see https://github.com/styleguidist/react-docgen-typescript#parseroptions
70 | * */
71 | savePropValueAsString?: boolean;
72 |
73 | /**
74 | * Specifiy the name of the property for docgen info prop type.
75 | *
76 | * @default "type"
77 | */
78 | typePropName?: string;
79 |
80 | /**
81 | * If set to true, types that are optional will not display " | undefined" in the type.
82 | *
83 | * @default false
84 | */
85 | shouldRemoveUndefinedFromOptional?: boolean;
86 | }
87 |
--------------------------------------------------------------------------------
/src/webpack.spec.ts:
--------------------------------------------------------------------------------
1 | import fs from "fs";
2 | import path from "path";
3 | import webpack from "webpack";
4 | // import MemoryFS = require("memory-fs");
5 | import MemoryFS from "memory-fs";
6 |
7 | describe("all component fixtures compile successfully", () => {
8 | const fixtureFilenames = fs
9 | .readdirSync(path.resolve(__dirname, "__fixtures__/components"))
10 | .sort();
11 |
12 | fixtureFilenames.forEach(filename => {
13 | it(`fixture: ${filename}`, async () => {
14 | const content = await compileFixture(filename);
15 | expect(content).toMatchSnapshot();
16 | });
17 | });
18 | });
19 |
20 | function compileFixture(filename: string): Promise {
21 | const fs = new MemoryFS();
22 | const compiler = webpack(createWebpackConfig(filename));
23 | // @ts-ignore
24 | // TODO: The type definition for MemoryFS is missing the purge method, which
25 | // is now expected by the type definitions for Webpack.
26 | compiler.outputFileSystem = fs;
27 |
28 | // The following executes the Webpack compiler and checks for the three
29 | // possible error conditions (fatal webpack errors, compilation errors,
30 | // compilation warnings).
31 | // See: https://webpack.js.org/api/node/#error-handling
32 | return new Promise((resolve, reject) => {
33 | compiler.run((err, stats) => {
34 | if (err) {
35 | reject(err);
36 | return;
37 | }
38 |
39 | const fileContents = fs.readFileSync("/dist/component.js", "utf8");
40 | const info = stats.toJson();
41 |
42 | if (stats.hasErrors()) {
43 | console.log(fileContents);
44 | reject(info.errors);
45 | return;
46 | }
47 |
48 | if (stats.hasWarnings()) {
49 | console.log(fileContents);
50 | reject(info.warnings);
51 | return;
52 | }
53 |
54 | resolve(fileContents);
55 | });
56 | });
57 | }
58 |
59 | function createWebpackConfig(filename: string): webpack.Configuration {
60 | return {
61 | context: path.resolve(__dirname, ".."),
62 | entry: path.resolve(__dirname, `./__fixtures__/components/${filename}`),
63 | output: {
64 | filename: "component.js",
65 | path: "/dist",
66 | },
67 | module: {
68 | rules: [
69 | {
70 | test: /\.tsx?$/,
71 | use: [
72 | {
73 | loader: "ts-loader",
74 | options: {
75 | compilerOptions: {
76 | noEmit: false,
77 | },
78 | },
79 | },
80 | {
81 | loader: path.resolve(__dirname, "./loader"),
82 | },
83 | ],
84 | },
85 | ],
86 | },
87 | resolve: {
88 | extensions: [".js", ".jsx", ".ts", ".tsx"],
89 | },
90 | resolveLoader: {
91 | extensions: [".ts"],
92 | },
93 | externals: {
94 | react: "react",
95 | },
96 | // Make snapshots more readable by preventing source lines from being
97 | // wrapped in eval statements.
98 | devtool: false,
99 | // Make snapshots more readable by preventing minification.
100 | mode: "development",
101 | };
102 | }
103 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [3.7.2] - 2020-03-29
4 |
5 | - Don't mutate options object. Thanks @adammockor: https://github.com/strothj/react-docgen-typescript-loader/pull/92
6 |
7 | ## [3.7.1] - 2020-03-09
8 |
9 | ### Fixed
10 |
11 | - Reverted incorrectly published documentation update: https://github.com/strothj/react-docgen-typescript-loader/issues/89
12 |
13 | ## [3.6.0] - 2019-11-24
14 |
15 | ### Added
16 |
17 | Thanks @patricklafrance: https://github.com/strothj/react-docgen-typescript-loader/pull/73
18 |
19 | - Expose the `savePropAsValueString` parser option. If set to true, defaultValue to props will be string. https://github.com/styleguidist/react-docgen-typescript#parseroptions
20 |
21 | ## [3.5.0] - 2019-11-20
22 |
23 | ### Added
24 |
25 | Thanks @folz: https://github.com/strothj/react-docgen-typescript-loader/pull/72
26 |
27 | - Expose the `componentNameResolver` parser option. This allows override the
28 | name resolution for components.
29 |
30 | ## [3.4.0] - 2019-11-15
31 |
32 | ### Added
33 |
34 | Thanks @patricklafrance: https://github.com/strothj/react-docgen-typescript-loader/pull/69
35 |
36 | - Make the name of the `type` property attached to generated docs configurable.
37 |
38 | ## [3.3.0] - 2019-09-30
39 |
40 | ### Added
41 |
42 | Thanks @evless: https://github.com/strothj/react-docgen-typescript-loader/pull/64
43 |
44 | - Default values can be displayed as something other than just string types.
45 |
46 | ## [3.2.1] - 2019-09-17
47 |
48 | ### Changed
49 |
50 | - Bump `react-docgen-typescript` to `v1.14.1`. [@hipstersmoothie](https://github.com/strothj/react-docgen-typescript-loader/pull/60)
51 |
52 | ## [3.2.0] - 2019-09-01
53 |
54 | ### Added
55 |
56 | Thanks @nekitk: https://github.com/strothj/react-docgen-typescript-loader/pull/59
57 |
58 | - Default values for stateless components can now be generated from props destructuring.
59 | - Passing new parser option (shouldExtractLiteralValuesFromEnum) which allows to parse TypeScript enums and string unions to docgen enum of their values:
60 | If false (like before): `type: { name: "\"blue\" | \"green\"" }` or `type: { name: "ColorEnum" }`
61 | If true: type: `{ name: "enum", value: [ { value: "\"blue\"" }, { value: "\"green\""} ] }`
62 |
63 | ## [3.1.1] - 2019-08-09
64 |
65 | ### Fix
66 |
67 | - Fixed stories are not updated on reload. Thanks @nekitk.
68 | https://github.com/strothj/react-docgen-typescript-loader/issues/43
69 |
70 | ## [3.1.0] - 2019-04-02
71 |
72 | - Increased performance by reusing TypeScript program instance. Thanks @denieler (https://github.com/strothj/react-docgen-typescript-loader/pull/40)
73 |
74 | ## [3.0.1] - 2019-01-20
75 |
76 | - Republish to resolve potential file permissions issue due to publishing from Windows. Closes [#35](https://github.com/strothj/react-docgen-typescript-loader/issues/35).
77 |
78 | ## [3.0.0] - 2018-09-20
79 |
80 | ### Changed
81 |
82 | - Bump `react-docgen-typescript` dependency to `v1.9.0`
83 | - Removed the loader options `includes` and `excludes`. Closes [#15](https://github.com/strothj/react-docgen-typescript-loader/issues/15)
84 | - Use the `loader-utils` Webpack page to process loader options. Closes [22](https://github.com/strothj/react-docgen-typescript-loader/issues/22).
85 |
86 | ## [2.2.0] - 2018-08-11
87 |
88 | ### Added
89 |
90 | - Add the ability to filter props using a filtering function. Thanks @rkostrzewski.
91 | https://github.com/strothj/react-docgen-typescript-loader/pull/21
92 |
93 | ## [2.1.1] - 2018-06-13
94 |
95 | ### Changed
96 |
97 | - Bump `react-docgen-typescript` dependency to `v1.6.0`:
98 | > parse can be called with multiple source file paths (thanks to @marionebl PR #91)
99 | > upgraded typescript version and fixed parsing comment problem (thanks to @kbukum PR #97)
100 |
101 | ## [2.1.0] - 2018-05-27
102 |
103 | ### Changed
104 |
105 | - Bump `react-docgen-typescript` dependency to `v1.5.0`:
106 | > Remove spread logic.
107 | > Support spread default props.
108 | > Use folder name if file name is index.
109 | > chore(parser): refactor displayName extraction
110 | > chore(Parser): read displayName
111 | > parse tsconfig.json mirroring TSC’s process
112 | > Added support for referenced default props in stateless components
113 | > support referenced defaultProps
114 | > Extracts default props from stateless components
115 |
116 | ## [2.0.3] - 2018-03-26
117 |
118 | ### Fixed
119 |
120 | - Bump `react-docgen-typescript` dependency to `v1.2.6`:
121 | > Fix React.SFC-typed functional components
122 | > https://github.com/styleguidist/react-docgen-typescript/commit/e9d57f229b9760967ddc0a746b1c1443f06762b0
123 | - Loader option `setDisplayName` now correctly accepts `false` value.
124 |
125 | ## [2.0.2] - 2018-03-03
126 |
127 | ### Fixed
128 |
129 | - Use original source text when generating amended code (resolves [#7](https://github.com/strothj/react-docgen-typescript-loader/issues/7)).
130 |
--------------------------------------------------------------------------------
/src/loader.ts:
--------------------------------------------------------------------------------
1 | import webpack from "webpack";
2 | import * as ts from "typescript";
3 | import path from "path";
4 | import fs from "fs";
5 | // TODO: Import from "react-docgen-typescript" directly when
6 | // https://github.com/styleguidist/react-docgen-typescript/pull/104 is hopefully
7 | // merged in. Will be considering to make a peer dependency as that point.
8 | import {
9 | withDefaultConfig,
10 | withCustomConfig,
11 | withCompilerOptions,
12 | ParserOptions,
13 | FileParser,
14 | } from "react-docgen-typescript/lib/parser.js";
15 | import LoaderOptions from "./LoaderOptions";
16 | import validateOptions from "./validateOptions";
17 | import generateDocgenCodeBlock from "./generateDocgenCodeBlock";
18 | import { getOptions } from "loader-utils";
19 |
20 | export interface TSFile {
21 | text?: string;
22 | version: number;
23 | }
24 |
25 | let languageService: ts.LanguageService | null = null;
26 | const files: Map = new Map();
27 |
28 | export default function loader(
29 | this: webpack.loader.LoaderContext,
30 | source: string,
31 | ) {
32 | // Loaders can operate in either synchronous or asynchronous mode. Errors in
33 | // asynchronous mode should be reported using the supplied callback.
34 |
35 | // Will return a callback if operating in asynchronous mode.
36 | const callback = this.async();
37 |
38 | try {
39 | const newSource = processResource(this, source);
40 |
41 | if (!callback) return newSource;
42 | callback(null, newSource);
43 | return;
44 | } catch (e) {
45 | if (callback) {
46 | callback(e);
47 | return;
48 | }
49 | throw e;
50 | }
51 | }
52 |
53 | function processResource(
54 | context: webpack.loader.LoaderContext,
55 | source: string,
56 | ): string {
57 | // Mark the loader as being cacheable since the result should be
58 | // deterministic.
59 | context.cacheable(true);
60 |
61 | const options: LoaderOptions = Object.assign({}, getOptions(context));
62 | validateOptions(options);
63 |
64 | options.docgenCollectionName =
65 | options.docgenCollectionName || "STORYBOOK_REACT_CLASSES";
66 | if (typeof options.setDisplayName !== "boolean") {
67 | options.setDisplayName = true;
68 | }
69 |
70 | // Convert the loader's flat options into the expected structure for
71 | // react-docgen-typescript.
72 | // See: node_modules/react-docgen-typescript/lib/parser.d.ts
73 | const parserOptions: ParserOptions = {
74 | componentNameResolver: options.componentNameResolver,
75 | propFilter:
76 | options.skipPropsWithName || options.skipPropsWithoutDoc
77 | ? {
78 | skipPropsWithName: options.skipPropsWithName || undefined,
79 | skipPropsWithoutDoc: options.skipPropsWithoutDoc || undefined,
80 | }
81 | : options.propFilter,
82 | shouldExtractLiteralValuesFromEnum:
83 | options.shouldExtractLiteralValuesFromEnum,
84 | savePropValueAsString: options.savePropValueAsString,
85 | shouldRemoveUndefinedFromOptional:
86 | options.shouldRemoveUndefinedFromOptional,
87 | };
88 |
89 | // Configure parser using settings provided to loader.
90 | // See: node_modules/react-docgen-typescript/lib/parser.d.ts
91 | let parser: FileParser = withDefaultConfig(parserOptions);
92 |
93 | let compilerOptions: ts.CompilerOptions = {
94 | allowJs: true,
95 | };
96 | let tsConfigFile: ts.ParsedCommandLine | null = null;
97 |
98 | if (options.tsconfigPath) {
99 | parser = withCustomConfig(options.tsconfigPath, parserOptions);
100 |
101 | tsConfigFile = getTSConfigFile(options.tsconfigPath!);
102 | compilerOptions = tsConfigFile.options;
103 |
104 | const filesToLoad = tsConfigFile.fileNames;
105 | loadFiles(filesToLoad);
106 | } else if (options.compilerOptions) {
107 | parser = withCompilerOptions(options.compilerOptions, parserOptions);
108 | compilerOptions = options.compilerOptions;
109 | }
110 |
111 | if (!tsConfigFile) {
112 | const basePath = path.dirname(context.context);
113 | tsConfigFile = getDefaultTSConfigFile(basePath);
114 |
115 | const filesToLoad = tsConfigFile.fileNames;
116 | loadFiles(filesToLoad);
117 | }
118 |
119 | const componentDocs = parser.parseWithProgramProvider(
120 | context.resourcePath,
121 | () => {
122 | if (languageService) {
123 | return languageService.getProgram()!;
124 | }
125 |
126 | const servicesHost = createServiceHost(compilerOptions, files);
127 |
128 | languageService = ts.createLanguageService(
129 | servicesHost,
130 | ts.createDocumentRegistry(),
131 | );
132 |
133 | return languageService!.getProgram()!;
134 | },
135 | );
136 |
137 | options.typePropName = options.typePropName || "type";
138 |
139 | // Return amended source code if there is docgen information available.
140 | if (componentDocs.length) {
141 | return generateDocgenCodeBlock({
142 | filename: context.resourcePath,
143 | source,
144 | componentDocs,
145 | docgenCollectionName: options.docgenCollectionName,
146 | setDisplayName: options.setDisplayName,
147 | typePropName: options.typePropName,
148 | });
149 | }
150 |
151 | // Return unchanged source code if no docgen information was available.
152 | return source;
153 | }
154 |
155 | function getTSConfigFile(tsconfigPath: string): ts.ParsedCommandLine {
156 | const basePath = path.dirname(tsconfigPath);
157 | const configFile = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
158 | return ts.parseJsonConfigFileContent(
159 | configFile!.config,
160 | ts.sys,
161 | basePath,
162 | {},
163 | tsconfigPath,
164 | );
165 | }
166 |
167 | function getDefaultTSConfigFile(basePath: string): ts.ParsedCommandLine {
168 | return ts.parseJsonConfigFileContent({}, ts.sys, basePath, {});
169 | }
170 |
171 | function loadFiles(filesToLoad: string[]): void {
172 | filesToLoad.forEach(filePath => {
173 | const normalizedFilePath = path.normalize(filePath);
174 | const file = files.get(normalizedFilePath);
175 | const text = fs.readFileSync(normalizedFilePath, "utf-8");
176 |
177 | if (!file) {
178 | files.set(normalizedFilePath, {
179 | text,
180 | version: 0,
181 | });
182 | } else if (file.text !== text) {
183 | files.set(normalizedFilePath, {
184 | text,
185 | version: file.version + 1,
186 | });
187 | }
188 | });
189 | }
190 |
191 | function createServiceHost(
192 | compilerOptions: ts.CompilerOptions,
193 | files: Map,
194 | ): ts.LanguageServiceHost {
195 | return {
196 | getScriptFileNames: () => {
197 | return [...files.keys()];
198 | },
199 | getScriptVersion: fileName => {
200 | const file = files.get(fileName);
201 | return (file && file.version.toString()) || "";
202 | },
203 | getScriptSnapshot: fileName => {
204 | if (!fs.existsSync(fileName)) {
205 | return undefined;
206 | }
207 |
208 | let file = files.get(fileName);
209 |
210 | if (file === undefined) {
211 | const text = fs.readFileSync(fileName).toString();
212 |
213 | file = { version: 0, text };
214 | files.set(fileName, file);
215 | }
216 |
217 | return ts.ScriptSnapshot.fromString(file!.text!);
218 | },
219 | getCurrentDirectory: () => process.cwd(),
220 | getCompilationSettings: () => compilerOptions,
221 | getDefaultLibFileName: options => ts.getDefaultLibFilePath(options),
222 | fileExists: ts.sys.fileExists,
223 | readFile: ts.sys.readFile,
224 | readDirectory: ts.sys.readDirectory,
225 | };
226 | }
227 |
--------------------------------------------------------------------------------
/src/__snapshots__/generateDocgenCodeBlock.spec.ts.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`adds component to docgen collection 1`] = `
4 | "import * as React from \\"react\\";
5 |
6 | interface SimpleComponentProps {
7 | /** Button color. */
8 | color: \\"blue\\" | \\"green\\";
9 | }
10 |
11 | /**
12 | * A simple component.
13 | */
14 | export const SimpleComponent: React.SFC = props => (
15 | {props.children}
16 | );
17 |
18 | try {
19 | // @ts-ignore
20 | SimpleComponent.displayName = \\"SimpleComponent\\";
21 | // @ts-ignore
22 | SimpleComponent.__docgenInfo = { \\"description\\": \\"A simple component.\\", \\"displayName\\": \\"SimpleComponent\\", \\"props\\": { \\"color\\": { \\"defaultValue\\": null, \\"description\\": \\"Button color.\\", \\"name\\": \\"color\\", \\"required\\": true, \\"type\\": { \\"name\\": \\"\\\\\\"blue\\\\\\" | \\\\\\"green\\\\\\"\\" } } } };
23 | // @ts-ignore
24 | if (typeof STORYBOOK_REACT_CLASSES !== \\"undefined\\")
25 | // @ts-ignore
26 | STORYBOOK_REACT_CLASSES[\\"Simple.tsx#SimpleComponent\\"] = { docgenInfo: SimpleComponent.__docgenInfo, name: \\"SimpleComponent\\", path: \\"Simple.tsx#SimpleComponent\\" };
27 | }
28 | catch (__react_docgen_typescript_loader_error) { }"
29 | `;
30 |
31 | exports[`component fixture DefaultPropValue.tsx has code block generated 1`] = `
32 | "import * as React from \\"react\\";
33 |
34 | interface DefaultPropValueComponentProps {
35 | /**
36 | * Button color.
37 | *
38 | * @default blue
39 | **/
40 | color: \\"blue\\" | \\"green\\";
41 |
42 | /**
43 | * Button counter.
44 | */
45 | counter: number;
46 |
47 | /**
48 | * Button disabled.
49 | */
50 | disabled: boolean;
51 | }
52 |
53 | /**
54 | * Component with a prop with a default value.
55 | */
56 | export const DefaultPropValueComponent: React.SFC<
57 | DefaultPropValueComponentProps
58 | > = props => (
59 |
60 | {props.counter}
61 | {props.children}
62 |
63 | );
64 |
65 | DefaultPropValueComponent.defaultProps = {
66 | counter: 123,
67 | disabled: false,
68 | };
69 |
70 | try {
71 | // @ts-ignore
72 | DefaultPropValueComponent.displayName = \\"DefaultPropValueComponent\\";
73 | // @ts-ignore
74 | DefaultPropValueComponent.__docgenInfo = { \\"description\\": \\"Component with a prop with a default value.\\", \\"displayName\\": \\"DefaultPropValueComponent\\", \\"props\\": { \\"color\\": { \\"defaultValue\\": { value: \\"blue\\" }, \\"description\\": \\"Button color.\\", \\"name\\": \\"color\\", \\"required\\": true, \\"type\\": { \\"name\\": \\"\\\\\\"blue\\\\\\" | \\\\\\"green\\\\\\"\\" } }, \\"counter\\": { \\"defaultValue\\": { value: 123 }, \\"description\\": \\"Button counter.\\", \\"name\\": \\"counter\\", \\"required\\": false, \\"type\\": { \\"name\\": \\"number\\" } }, \\"disabled\\": { \\"defaultValue\\": { value: false }, \\"description\\": \\"Button disabled.\\", \\"name\\": \\"disabled\\", \\"required\\": false, \\"type\\": { \\"name\\": \\"boolean\\" } } } };
75 | }
76 | catch (__react_docgen_typescript_loader_error) { }"
77 | `;
78 |
79 | exports[`component fixture HyphenatedPropName.tsx has code block generated 1`] = `
80 | "import * as React from \\"react\\";
81 |
82 | interface HyphenatedPropNameProps {
83 | /** Button color. */
84 | \\"button-color\\": \\"blue\\" | \\"green\\";
85 | }
86 |
87 | /**
88 | * A component with a hyphenated prop name.
89 | */
90 | export const HyphenatedPropNameComponent: React.SFC<
91 | HyphenatedPropNameProps
92 | > = props => (
93 |
94 | {props.children}
95 |
96 | );
97 |
98 | try {
99 | // @ts-ignore
100 | HyphenatedPropNameComponent.displayName = \\"HyphenatedPropNameComponent\\";
101 | // @ts-ignore
102 | HyphenatedPropNameComponent.__docgenInfo = { \\"description\\": \\"A component with a hyphenated prop name.\\", \\"displayName\\": \\"HyphenatedPropNameComponent\\", \\"props\\": { \\"button-color\\": { \\"defaultValue\\": null, \\"description\\": \\"Button color.\\", \\"name\\": \\"button-color\\", \\"required\\": true, \\"type\\": { \\"name\\": \\"\\\\\\"blue\\\\\\" | \\\\\\"green\\\\\\"\\" } } } };
103 | }
104 | catch (__react_docgen_typescript_loader_error) { }"
105 | `;
106 |
107 | exports[`component fixture MultiProps.tsx has code block generated 1`] = `
108 | "import * as React from \\"react\\";
109 |
110 | interface MultiPropsComponentProps {
111 | /** Button color. */
112 | color: \\"blue\\" | \\"green\\";
113 |
114 | /** Button size. */
115 | size: \\"small\\" | \\"large\\";
116 | }
117 |
118 | /**
119 | * This is a component with multiple props.
120 | */
121 | export const MultiPropsComponent: React.SFC<
122 | MultiPropsComponentProps
123 | > = props => (
124 | {props.children}
125 | );
126 |
127 | try {
128 | // @ts-ignore
129 | MultiPropsComponent.displayName = \\"MultiPropsComponent\\";
130 | // @ts-ignore
131 | MultiPropsComponent.__docgenInfo = { \\"description\\": \\"This is a component with multiple props.\\", \\"displayName\\": \\"MultiPropsComponent\\", \\"props\\": { \\"color\\": { \\"defaultValue\\": null, \\"description\\": \\"Button color.\\", \\"name\\": \\"color\\", \\"required\\": true, \\"type\\": { \\"name\\": \\"\\\\\\"blue\\\\\\" | \\\\\\"green\\\\\\"\\" } }, \\"size\\": { \\"defaultValue\\": null, \\"description\\": \\"Button size.\\", \\"name\\": \\"size\\", \\"required\\": true, \\"type\\": { \\"name\\": \\"\\\\\\"small\\\\\\" | \\\\\\"large\\\\\\"\\" } } } };
132 | }
133 | catch (__react_docgen_typescript_loader_error) { }"
134 | `;
135 |
136 | exports[`component fixture MultilineDescription.tsx has code block generated 1`] = `
137 | "import * as React from \\"react\\";
138 |
139 | interface MultilineDescriptionProps {
140 | /** Button color. */
141 | color: \\"blue\\" | \\"green\\";
142 | }
143 |
144 | /**
145 | * A component with a multiline description.
146 | *
147 | * Second line.
148 | */
149 | export const MultilineDescriptionComponent: React.SFC<
150 | MultilineDescriptionProps
151 | > = props => (
152 | {props.children}
153 | );
154 |
155 | try {
156 | // @ts-ignore
157 | MultilineDescriptionComponent.displayName = \\"MultilineDescriptionComponent\\";
158 | // @ts-ignore
159 | MultilineDescriptionComponent.__docgenInfo = { \\"description\\": \\"A component with a multiline description.\\\\n\\\\nSecond line.\\", \\"displayName\\": \\"MultilineDescriptionComponent\\", \\"props\\": { \\"color\\": { \\"defaultValue\\": null, \\"description\\": \\"Button color.\\", \\"name\\": \\"color\\", \\"required\\": true, \\"type\\": { \\"name\\": \\"\\\\\\"blue\\\\\\" | \\\\\\"green\\\\\\"\\" } } } };
160 | }
161 | catch (__react_docgen_typescript_loader_error) { }"
162 | `;
163 |
164 | exports[`component fixture MultilinePropDescription.tsx has code block generated 1`] = `
165 | "import * as React from \\"react\\";
166 |
167 | interface MultilinePropDescriptionComponentProps {
168 | /**
169 | * This is a multiline prop description.
170 | *
171 | * Second line.
172 | */
173 | color: \\"blue\\" | \\"green\\";
174 | }
175 |
176 | /**
177 | * A component with multiline prop description.
178 | */
179 | export const MultilinePropDescriptionComponent: React.SFC<
180 | MultilinePropDescriptionComponentProps
181 | > = props => (
182 | {props.children}
183 | );
184 |
185 | try {
186 | // @ts-ignore
187 | MultilinePropDescriptionComponent.displayName = \\"MultilinePropDescriptionComponent\\";
188 | // @ts-ignore
189 | MultilinePropDescriptionComponent.__docgenInfo = { \\"description\\": \\"A component with multiline prop description.\\", \\"displayName\\": \\"MultilinePropDescriptionComponent\\", \\"props\\": { \\"color\\": { \\"defaultValue\\": null, \\"description\\": \\"This is a multiline prop description.\\\\n\\\\nSecond line.\\", \\"name\\": \\"color\\", \\"required\\": true, \\"type\\": { \\"name\\": \\"\\\\\\"blue\\\\\\" | \\\\\\"green\\\\\\"\\" } } } };
190 | }
191 | catch (__react_docgen_typescript_loader_error) { }"
192 | `;
193 |
194 | exports[`component fixture Simple.tsx has code block generated 1`] = `
195 | "import * as React from \\"react\\";
196 |
197 | interface SimpleComponentProps {
198 | /** Button color. */
199 | color: \\"blue\\" | \\"green\\";
200 | }
201 |
202 | /**
203 | * A simple component.
204 | */
205 | export const SimpleComponent: React.SFC = props => (
206 | {props.children}
207 | );
208 |
209 | try {
210 | // @ts-ignore
211 | SimpleComponent.displayName = \\"SimpleComponent\\";
212 | // @ts-ignore
213 | SimpleComponent.__docgenInfo = { \\"description\\": \\"A simple component.\\", \\"displayName\\": \\"SimpleComponent\\", \\"props\\": { \\"color\\": { \\"defaultValue\\": null, \\"description\\": \\"Button color.\\", \\"name\\": \\"color\\", \\"required\\": true, \\"type\\": { \\"name\\": \\"\\\\\\"blue\\\\\\" | \\\\\\"green\\\\\\"\\" } } } };
214 | }
215 | catch (__react_docgen_typescript_loader_error) { }"
216 | `;
217 |
218 | exports[`component fixture TextOnlyComponent.tsx has code block generated 1`] = `
219 | "import * as React from \\"react\\";
220 |
221 | /**
222 | * A component with only text content wrapped in a div.
223 | *
224 | * Ref: https://github.com/strothj/react-docgen-typescript-loader/issues/7
225 | */
226 | export const SimpleComponent: React.SFC<{}> = () => (
227 | Test only component
228 | );
229 |
230 | try {
231 | // @ts-ignore
232 | SimpleComponent.displayName = \\"SimpleComponent\\";
233 | // @ts-ignore
234 | SimpleComponent.__docgenInfo = { \\"description\\": \\"A component with only text content wrapped in a div.\\\\n\\\\nRef: https://github.com/strothj/react-docgen-typescript-loader/issues/7\\", \\"displayName\\": \\"SimpleComponent\\", \\"props\\": {} };
235 | }
236 | catch (__react_docgen_typescript_loader_error) { }"
237 | `;
238 |
239 | exports[`generates value info for enums 1`] = `
240 | "import * as React from \\"react\\";
241 |
242 | interface DefaultPropValueComponentProps {
243 | /**
244 | * Button color.
245 | *
246 | * @default blue
247 | **/
248 | color: \\"blue\\" | \\"green\\";
249 |
250 | /**
251 | * Button counter.
252 | */
253 | counter: number;
254 |
255 | /**
256 | * Button disabled.
257 | */
258 | disabled: boolean;
259 | }
260 |
261 | /**
262 | * Component with a prop with a default value.
263 | */
264 | export const DefaultPropValueComponent: React.SFC<
265 | DefaultPropValueComponentProps
266 | > = props => (
267 |
268 | {props.counter}
269 | {props.children}
270 |
271 | );
272 |
273 | DefaultPropValueComponent.defaultProps = {
274 | counter: 123,
275 | disabled: false,
276 | };
277 |
278 | try {
279 | // @ts-ignore
280 | DefaultPropValueComponent.displayName = \\"DefaultPropValueComponent\\";
281 | // @ts-ignore
282 | DefaultPropValueComponent.__docgenInfo = { \\"description\\": \\"Component with a prop with a default value.\\", \\"displayName\\": \\"DefaultPropValueComponent\\", \\"props\\": { \\"color\\": { \\"defaultValue\\": { value: \\"blue\\" }, \\"description\\": \\"Button color.\\", \\"name\\": \\"color\\", \\"required\\": true, \\"type\\": { \\"name\\": \\"enum\\", \\"value\\": [{ \\"value\\": \\"\\\\\\"blue\\\\\\"\\" }, { \\"value\\": \\"\\\\\\"green\\\\\\"\\" }] } }, \\"counter\\": { \\"defaultValue\\": { value: 123 }, \\"description\\": \\"Button counter.\\", \\"name\\": \\"counter\\", \\"required\\": false, \\"type\\": { \\"name\\": \\"number\\" } }, \\"disabled\\": { \\"defaultValue\\": { value: false }, \\"description\\": \\"Button disabled.\\", \\"name\\": \\"disabled\\", \\"required\\": false, \\"type\\": { \\"name\\": \\"boolean\\" } } } };
283 | }
284 | catch (__react_docgen_typescript_loader_error) { }"
285 | `;
286 |
--------------------------------------------------------------------------------
/src/generateDocgenCodeBlock.ts:
--------------------------------------------------------------------------------
1 | import path from "path";
2 | import ts from "typescript";
3 | import { ComponentDoc, PropItem } from "react-docgen-typescript/lib/parser.js";
4 |
5 | export interface GeneratorOptions {
6 | filename: string;
7 | source: string;
8 | componentDocs: ComponentDoc[];
9 | docgenCollectionName: string | null;
10 | setDisplayName: boolean;
11 | typePropName: string;
12 | }
13 |
14 | export default function generateDocgenCodeBlock(
15 | options: GeneratorOptions,
16 | ): string {
17 | const sourceFile = ts.createSourceFile(
18 | options.filename,
19 | options.source,
20 | ts.ScriptTarget.ESNext,
21 | );
22 |
23 | const relativeFilename = path
24 | .relative("./", path.resolve("./", options.filename))
25 | .replace(/\\/g, "/");
26 |
27 | const wrapInTryStatement = (statements: ts.Statement[]): ts.TryStatement =>
28 | ts.createTry(
29 | ts.createBlock(statements, true),
30 | ts.createCatchClause(
31 | ts.createVariableDeclaration(
32 | ts.createIdentifier("__react_docgen_typescript_loader_error"),
33 | ),
34 | ts.createBlock([]),
35 | ),
36 | undefined,
37 | );
38 |
39 | const codeBlocks = options.componentDocs.map(d =>
40 | wrapInTryStatement([
41 | options.setDisplayName ? setDisplayName(d) : null,
42 | setComponentDocGen(d, options),
43 | options.docgenCollectionName != null
44 | ? insertDocgenIntoGlobalCollection(
45 | d,
46 | options.docgenCollectionName,
47 | relativeFilename,
48 | )
49 | : null,
50 | ].filter(s => s !== null) as ts.Statement[]),
51 | );
52 |
53 | const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
54 | const printNode = (sourceNode: ts.Node) =>
55 | printer.printNode(ts.EmitHint.Unspecified, sourceNode, sourceFile);
56 |
57 | // Concat original source code with code from generated code blocks.
58 | const result = codeBlocks.reduce(
59 | (acc, node) => `${acc}\n${printNode(node)}`,
60 |
61 | // Use original source text rather than using printNode on the parsed form
62 | // to prevent issue where literals are stripped within components.
63 | // Ref: https://github.com/strothj/react-docgen-typescript-loader/issues/7
64 | options.source,
65 | );
66 |
67 | return result;
68 | }
69 |
70 | /**
71 | * Set component display name.
72 | *
73 | * ```
74 | * SimpleComponent.displayName = "SimpleComponent";
75 | * ```
76 | */
77 | function setDisplayName(d: ComponentDoc): ts.Statement {
78 | return insertTsIgnoreBeforeStatement(
79 | ts.createStatement(
80 | ts.createBinary(
81 | ts.createPropertyAccess(
82 | ts.createIdentifier(d.displayName),
83 | ts.createIdentifier("displayName"),
84 | ),
85 | ts.SyntaxKind.EqualsToken,
86 | ts.createLiteral(d.displayName),
87 | ),
88 | ),
89 | );
90 | }
91 |
92 | /**
93 | * Sets the field `__docgenInfo` for the component specified by the component
94 | * doc with the docgen information.
95 | *
96 | * ```
97 | * SimpleComponent.__docgenInfo = {
98 | * description: ...,
99 | * displayName: ...,
100 | * props: ...,
101 | * }
102 | * ```
103 | *
104 | * @param d Component doc.
105 | * @param options Generator options.
106 | */
107 | function setComponentDocGen(
108 | d: ComponentDoc,
109 | options: GeneratorOptions,
110 | ): ts.Statement {
111 | return insertTsIgnoreBeforeStatement(
112 | ts.createStatement(
113 | ts.createBinary(
114 | // SimpleComponent.__docgenInfo
115 | ts.createPropertyAccess(
116 | ts.createIdentifier(d.displayName),
117 | ts.createIdentifier("__docgenInfo"),
118 | ),
119 | ts.SyntaxKind.EqualsToken,
120 | ts.createObjectLiteral([
121 | // SimpleComponent.__docgenInfo.description
122 | ts.createPropertyAssignment(
123 | ts.createLiteral("description"),
124 | ts.createLiteral(d.description),
125 | ),
126 | // SimpleComponent.__docgenInfo.displayName
127 | ts.createPropertyAssignment(
128 | ts.createLiteral("displayName"),
129 | ts.createLiteral(d.displayName),
130 | ),
131 | // SimpleComponent.__docgenInfo.props
132 | ts.createPropertyAssignment(
133 | ts.createLiteral("props"),
134 | ts.createObjectLiteral(
135 | Object.entries(d.props).map(([propName, prop]) =>
136 | createPropDefinition(propName, prop, options),
137 | ),
138 | ),
139 | ),
140 | ]),
141 | ),
142 | ),
143 | );
144 | }
145 |
146 | /**
147 | * Set a component prop description.
148 | * ```
149 | * SimpleComponent.__docgenInfo.props.someProp = {
150 | * defaultValue: "blue",
151 | * description: "Prop description.",
152 | * name: "someProp",
153 | * required: true,
154 | * type: "'blue' | 'green'",
155 | * }
156 | * ```
157 | *
158 | * @param propName Prop name
159 | * @param prop Prop definition from `ComponentDoc.props`
160 | * @param options Generator options.
161 | */
162 | function createPropDefinition(
163 | propName: string,
164 | prop: PropItem,
165 | options: GeneratorOptions,
166 | ) {
167 | /**
168 | * Set default prop value.
169 | *
170 | * ```
171 | * SimpleComponent.__docgenInfo.props.someProp.defaultValue = null;
172 | * SimpleComponent.__docgenInfo.props.someProp.defaultValue = {
173 | * value: "blue",
174 | * };
175 | * ```
176 | *
177 | * @param defaultValue Default prop value or null if not set.
178 | */
179 | const setDefaultValue = (
180 | defaultValue: { value: string | number | boolean } | null,
181 | ) =>
182 | ts.createPropertyAssignment(
183 | ts.createLiteral("defaultValue"),
184 | // Use a more extensive check on defaultValue. Sometimes the parser
185 | // returns an empty object.
186 | defaultValue != null &&
187 | typeof defaultValue === "object" &&
188 | "value" in defaultValue &&
189 | (typeof defaultValue.value === "string" ||
190 | typeof defaultValue.value === "number" ||
191 | typeof defaultValue.value === "boolean")
192 | ? ts.createObjectLiteral([
193 | ts.createPropertyAssignment(
194 | ts.createIdentifier("value"),
195 | ts.createLiteral(defaultValue!.value),
196 | ),
197 | ])
198 | : ts.createNull(),
199 | );
200 |
201 | /** Set a property with a string value */
202 | const setStringLiteralField = (fieldName: string, fieldValue: string) =>
203 | ts.createPropertyAssignment(
204 | ts.createLiteral(fieldName),
205 | ts.createLiteral(fieldValue),
206 | );
207 |
208 | /**
209 | * ```
210 | * SimpleComponent.__docgenInfo.props.someProp.description = "Prop description.";
211 | * ```
212 | * @param description Prop description.
213 | */
214 | const setDescription = (description: string) =>
215 | setStringLiteralField("description", description);
216 |
217 | /**
218 | * ```
219 | * SimpleComponent.__docgenInfo.props.someProp.name = "someProp";
220 | * ```
221 | * @param name Prop name.
222 | */
223 | const setName = (name: string) => setStringLiteralField("name", name);
224 |
225 | /**
226 | * ```
227 | * SimpleComponent.__docgenInfo.props.someProp.required = true;
228 | * ```
229 | * @param required Whether prop is required or not.
230 | */
231 | const setRequired = (required: boolean) =>
232 | ts.createPropertyAssignment(
233 | ts.createLiteral("required"),
234 | required ? ts.createTrue() : ts.createFalse(),
235 | );
236 |
237 | /**
238 | * ```
239 | * SimpleComponent.__docgenInfo.props.someProp.type = {
240 | * name: "enum",
241 | * value: [ { value: "\"blue\"" }, { value: "\"green\""} ]
242 | * }
243 | * ```
244 | * @param [typeValue] Prop value (for enums)
245 | */
246 | const setValue = (typeValue?: any) =>
247 | Array.isArray(typeValue) &&
248 | typeValue.every(value => typeof value.value === "string")
249 | ? ts.createPropertyAssignment(
250 | ts.createLiteral("value"),
251 | ts.createArrayLiteral(
252 | typeValue.map(value =>
253 | ts.createObjectLiteral([
254 | setStringLiteralField("value", value.value),
255 | ]),
256 | ),
257 | ),
258 | )
259 | : undefined;
260 |
261 | /**
262 | * ```
263 | * SimpleComponent.__docgenInfo.props.someProp.type = { name: "'blue' | 'green'"}
264 | * ```
265 | * @param typeName Prop type name.
266 | * @param [typeValue] Prop value (for enums)
267 | */
268 | const setType = (typeName: string, typeValue?: any) => {
269 | const objectFields = [setStringLiteralField("name", typeName)];
270 | const valueField = setValue(typeValue);
271 |
272 | if (valueField) {
273 | objectFields.push(valueField);
274 | }
275 |
276 | return ts.createPropertyAssignment(
277 | ts.createLiteral(options.typePropName),
278 | ts.createObjectLiteral(objectFields),
279 | );
280 | };
281 |
282 | return ts.createPropertyAssignment(
283 | ts.createLiteral(propName),
284 | ts.createObjectLiteral([
285 | setDefaultValue(prop.defaultValue),
286 | setDescription(prop.description),
287 | setName(prop.name),
288 | setRequired(prop.required),
289 | setType(prop.type.name, prop.type.value),
290 | ]),
291 | );
292 | }
293 |
294 | /**
295 | * Adds a component's docgen info to the global docgen collection.
296 | *
297 | * ```
298 | * if (typeof STORYBOOK_REACT_CLASSES !== "undefined") {
299 | * STORYBOOK_REACT_CLASSES["src/.../SimpleComponent.tsx"] = {
300 | * name: "SimpleComponent",
301 | * docgenInfo: SimpleComponent.__docgenInfo,
302 | * path: "src/.../SimpleComponent.tsx",
303 | * };
304 | * }
305 | * ```
306 | *
307 | * @param d Component doc.
308 | * @param docgenCollectionName Global docgen collection variable name.
309 | * @param relativeFilename Relative file path of the component source file.
310 | */
311 | function insertDocgenIntoGlobalCollection(
312 | d: ComponentDoc,
313 | docgenCollectionName: string,
314 | relativeFilename: string,
315 | ): ts.Statement {
316 | return insertTsIgnoreBeforeStatement(
317 | ts.createIf(
318 | ts.createBinary(
319 | ts.createTypeOf(ts.createIdentifier(docgenCollectionName)),
320 | ts.SyntaxKind.ExclamationEqualsEqualsToken,
321 | ts.createLiteral("undefined"),
322 | ),
323 | insertTsIgnoreBeforeStatement(
324 | ts.createStatement(
325 | ts.createBinary(
326 | ts.createElementAccess(
327 | ts.createIdentifier(docgenCollectionName),
328 | ts.createLiteral(`${relativeFilename}#${d.displayName}`),
329 | ),
330 | ts.SyntaxKind.EqualsToken,
331 | ts.createObjectLiteral([
332 | ts.createPropertyAssignment(
333 | ts.createIdentifier("docgenInfo"),
334 | ts.createPropertyAccess(
335 | ts.createIdentifier(d.displayName),
336 | ts.createIdentifier("__docgenInfo"),
337 | ),
338 | ),
339 | ts.createPropertyAssignment(
340 | ts.createIdentifier("name"),
341 | ts.createLiteral(d.displayName),
342 | ),
343 | ts.createPropertyAssignment(
344 | ts.createIdentifier("path"),
345 | ts.createLiteral(`${relativeFilename}#${d.displayName}`),
346 | ),
347 | ]),
348 | ),
349 | ),
350 | ),
351 | ),
352 | );
353 | }
354 |
355 | /**
356 | * Inserts a ts-ignore comment above the supplied statement.
357 | *
358 | * It is used to work around type errors related to fields like __docgenInfo not
359 | * being defined on types. It also prevents compile errors related to attempting
360 | * to assign to nonexistent components, which can happen due to incorrect
361 | * detection of component names when using the parser.
362 | * ```
363 | * // @ts-ignore
364 | * ```
365 | * @param statement
366 | */
367 | function insertTsIgnoreBeforeStatement(statement: ts.Statement): ts.Statement {
368 | ts.setSyntheticLeadingComments(statement, [
369 | {
370 | text: " @ts-ignore", // Leading space is important here
371 | kind: ts.SyntaxKind.SingleLineCommentTrivia,
372 | pos: -1,
373 | end: -1,
374 | },
375 | ]);
376 | return statement;
377 | }
378 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Webpack loader to generate docgen information from TypeScript React components. The primary use case is to get the prop types table populated in the Storybook Info Addon.
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | Example Storybook project
24 |
25 | Live Storybook
26 |
27 |
28 | ## Guide
29 |
30 | - [Changelog](#changelog)
31 | - [Migrating from V2 to V3](#migrating-from-v2-to-v3)
32 | - [Quick Start](#quick-start)
33 | - [Requirements](#requirements)
34 | - [Package Installation](#package-installation)
35 | - [Webpack Configuration](#webpack-configuration)
36 | - [Storybook 4](#storybook-4)
37 | - [Storybook 3](#storybook-3)
38 | - [Documenting Components with Storybook](#documenting-components-with-storybook)
39 | - [Including Component Description](#including-component-description)
40 | - [Exporting Components](#exporting-components)
41 | - [Loader Options](#loader-options)
42 | - [Performance](#performance)
43 | - [Optional Performance Settings](#optional-performance-settings)
44 | - [Alternative Implementation](#alternative-implementation)
45 | - [Limitations](#limitations)
46 | - [React Component Class Import](#react-component-class-import)
47 | - [Export Names](#export-names)
48 | - [Contributing](#contributing)
49 | - [Credits](#credits)
50 | - [SVG Logos](#svg-logos)
51 | - [Projects](#projects)
52 | - [License](#license)
53 |
54 | ## Changelog
55 |
56 | View [Changelog](https://github.com/strothj/react-docgen-typescript-loader/blob/master/CHANGELOG.md).
57 |
58 | ## Migrating from V2 to V3
59 |
60 | Version 2 supported the options `includes` and `excludes`, which were arrays of regular expressions. If you made use of these options, remove them from and use the Webpack equivalents.
61 |
62 | `includes` would default to `["\\.tsx$"]` which meant that only files ending in the extension `.ts` or `.tsx` would be processed. This default behavior is already covered by Webpack's `test` field.
63 |
64 | `excludes` would default to `["node_modules"]`. This would prevent the processing of files in node_modules. This option was added to allow further filtering to hopefully speed up processing. When this loader is used in monorepo environments, this option would complicate configuration.
65 |
66 | In version 3, the loader no longer performs its own filtering. If you relied on the additional filtering behavior, you should be able to reimplement it using options in Webpack.
67 |
68 | This change shouldn't affect the majority of projects.
69 |
70 | ## Quick Start
71 |
72 | ### Requirements
73 |
74 | The source code generation relies on being able to insert `// @ts-ignore` in a few select places and as such requires TypeScript 2.3 or above.
75 |
76 | ### Package Installation
77 |
78 | ```shell
79 | $ npm install --save-dev react-docgen-typescript-loader
80 |
81 | or
82 |
83 | $ yarn add --dev react-docgen-typescript-loader
84 | ```
85 |
86 | ### Webpack Configuration
87 |
88 | **IMPORTANT:** Webpack loaders are executed right-to-left (or bottom-to-top). `react-docgen-typescript-loader` needs to be added under `ts-loader`.
89 |
90 | Example Storybook config `/.storybook/webpack.config.js`:
91 |
92 | #### Storybook 4
93 |
94 | ```javascript
95 | const path = require("path");
96 |
97 | module.exports = (baseConfig, env, config) => {
98 | config.module.rules.push({
99 | test: /\.tsx?$/,
100 | include: path.resolve(__dirname, "../src"),
101 | use: [
102 | require.resolve("ts-loader"),
103 | {
104 | loader: require.resolve("react-docgen-typescript-loader"),
105 | options: {
106 | // Provide the path to your tsconfig.json so that your stories can
107 | // display types from outside each individual story.
108 | tsconfigPath: path.resolve(__dirname, "../tsconfig.json"),
109 | },
110 | },
111 | ],
112 | });
113 |
114 | config.resolve.extensions.push(".ts", ".tsx");
115 |
116 | return config;
117 | };
118 | ```
119 |
120 | #### Storybook 3
121 |
122 | ```javascript
123 | const path = require("path");
124 | const genDefaultConfig = require("@storybook/react/dist/server/config/defaults/webpack.config.js");
125 |
126 | module.exports = (baseConfig, env) => {
127 | const config = genDefaultConfig(baseConfig);
128 |
129 | config.module.rules.push({
130 | test: /\.tsx?$/,
131 | include: path.resolve(__dirname, "../src"),
132 | use: [
133 | require.resolve("ts-loader"),
134 | {
135 | loader: require.resolve("react-docgen-typescript-loader"),
136 | options: {
137 | // Provide the path to your tsconfig.json so that your stories can
138 | // display types from outside each individual story.
139 | tsconfigPath: path.resolve(__dirname, "../tsconfig.json"),
140 | },
141 | },
142 | ],
143 | });
144 |
145 | config.resolve.extensions.push(".ts", ".tsx");
146 |
147 | return config;
148 | };
149 | ```
150 |
151 | ## Documenting Components with Storybook
152 |
153 | Include the `withInfo` decorator as normal.
154 | Reference the addon documentation for the latest usage instructions:
155 | https://github.com/storybooks/storybook/tree/master/addons/info
156 |
157 | ### Including Component Description
158 |
159 | The Storybook Info Addon is able to populate the component description from your component's documentation. It does this when your story name matches the display name of your component. The prop tables will populate in either case.
160 |
161 | 
162 |
163 | In the first example you are able to see above `Story Source` the component description. The second example shows a story with a name different from the component. In the second example the component description is missing.
164 |
165 | If you have a component named
166 | `TicTacToeCell`, then you would have to use something like: `storiesOf("...", module).add("TicTacToeCell", ...)` to have the story description come from the component description.
167 |
168 | In addition to the description from the component, you may still include story description text using the normal withInfo api.
169 |
170 | ### Exporting Components
171 |
172 | **It is important** to export your component using a named export for docgen information to be generated properly.
173 |
174 | ---
175 |
176 | `TicTacToeCell.tsx`:
177 |
178 | ```javascript
179 | import React, { Component } from "react";
180 | import * as styles from "./TicTacToeCell.css";
181 |
182 | interface Props {
183 | /**
184 | * Value to display, either empty (" ") or "X" / "O".
185 | *
186 | * @default " "
187 | **/
188 | value?: " " | "X" | "O";
189 |
190 | /** Cell position on game board. */
191 | position: { x: number, y: number };
192 |
193 | /** Called when an empty cell is clicked. */
194 | onClick?: (x: number, y: number) => void;
195 | }
196 |
197 | /**
198 | * TicTacToe game cell. Displays a clickable button when the value is " ",
199 | * otherwise displays "X" or "O".
200 | */
201 | // Notice the named export here, this is required for docgen information
202 | // to be generated correctly.
203 | export class TicTacToeCell extends Component {
204 | handleClick = () => {
205 | const {
206 | position: { x, y },
207 | onClick,
208 | } = this.props;
209 | if (!onClick) return;
210 |
211 | onClick(x, y);
212 | };
213 |
214 | render() {
215 | const { value = " " } = this.props;
216 | const disabled = value !== " ";
217 | const classes = `${styles.button} ${disabled ? styles.disabled : ""}`;
218 |
219 | return (
220 |
225 | {value}
226 |
227 | );
228 | }
229 | }
230 |
231 | // Component can still be exported as default.
232 | export default TicTacToeCell;
233 | ```
234 |
235 | `ColorButton.stories.tsx`:
236 |
237 | ```javascript
238 | import React from "react";
239 | import { storiesOf } from "@storybook/react";
240 | import { withInfo } from "@storybook/addon-info";
241 | import { action } from "@storybook/addon-actions";
242 | import TicTacToeCell from "./TicTacToeCell";
243 |
244 | const stories = storiesOf("Components", module);
245 |
246 | stories.add(
247 | "TicTacToeCell",
248 | withInfo({ inline: true })(() => (
249 |
254 | )),
255 | );
256 | ```
257 |
258 | ## Loader Options
259 |
260 | | Option | Type | Description |
261 | | ---------------------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
262 | | skipPropsWithName | string[] or string | Avoid including docgen information for the prop or props specified. |
263 | | skipPropsWithoutDoc | boolean | Avoid including docgen information for props without documentation. |
264 | | componentNameResolver | function | If a string is returned, then the component will use that name. Else it will fallback to the default logic of parser. https://github.com/styleguidist/react-docgen-typescript#parseroptions |
265 | | propFilter | function | Filter props using a function. If skipPropsWithName or skipPropsWithoutDoc is defined the function will not be used. Function accepts two arguments: object with information about prop and an object with information about component. Return true to include prop in documentation. https://github.com/styleguidist/react-docgen-typescript#parseroptions |
266 | | tsconfigPath | string | Specify the location of the tsconfig.json to use. Can not be used with compilerOptions. |
267 | | compilerOptions | typescript.CompilerOptions | Specify TypeScript compiler options. Can not be used with tsconfigPath. |
268 | | docgenCollectionName | string or null | Specify the docgen collection name to use. All docgen information will be collected into this global object. Set to `null` to disable. Defaults to `STORYBOOK_REACT_CLASSES` for use with the Storybook Info Addon. https://github.com/gongreg/react-storybook-addon-docgen |
269 | | setDisplayName | boolean | Automatically set the components' display name. If you want to set display names yourself or are using another plugin to do this, you should disable this option. Defaults to `true`. This is used to preserve component display names during a production build of Storybook. |
270 | | shouldExtractLiteralValuesFromEnum | boolean | If set to true, string enums and unions will be converted to docgen enum format. Useful if you use Storybook and want to generate knobs automatically using [addon-smart-knobs](https://github.com/storybookjs/addon-smart-knobs). https://github.com/styleguidist/react-docgen-typescript#parseroptions |
271 | | savePropValueAsString | boolean | If set to true, defaultValue to props will be string. https://github.com/styleguidist/react-docgen-typescript#parseroptions |
272 | | typePropName | string | Specify the name of the property for docgen info prop type. |
273 | | shouldRemoveUndefinedFromOptional | boolean | If set to true, types that are optional will not display `| undefined` in the type. https://github.com/styleguidist/react-docgen-typescript#parseroptions |
274 |
275 | ## Performance
276 |
277 | There is a significant startup cost due to the initial type parsing. Once the project is running in watch mode, things should be smoother due to Webpack caching.
278 |
279 | ## Alternative Implementation
280 |
281 | This plugin uses a Webpack loader to inject the docgen information. There is also a version which works as a Webpack plugin. I will be supporting both versions. The loader version more accurately generates the injected code blocks and should work with all module types but at the cost of a longer initial startup. The plugin version may be faster.
282 |
283 | The Webpack plugin version is available here:
284 | https://github.com/strothj/react-docgen-typescript-loader/tree/plugin
285 |
286 | ## Limitations
287 |
288 | This plugin makes use of the project:
289 | https://github.com/styleguidist/react-docgen-typescript.
290 | It is subject to the same limitations.
291 |
292 | ### React Component Class Import
293 |
294 | When extending from `React.Component` as opposed to `Component`, docgens don't seem to be created. Ref issue [#10](https://github.com/strothj/react-docgen-typescript-loader/issues/10) (thanks @StevenLangbroek for tracking down the cause).
295 |
296 | Doesn't work:
297 |
298 | ```
299 | import React from 'react';
300 |
301 | interface IProps {
302 | whatever?: string;
303 | };
304 |
305 | export default class MyComponent extends React.Component {}
306 | ```
307 |
308 | Works:
309 |
310 | ```
311 | import React, { Component } from 'react';
312 |
313 | export default class MyComponent extends Component {}
314 | ```
315 |
316 | ### Export Names
317 |
318 | Component docgen information can not be
319 | generated for components that are only exported as default. You can work around
320 | the issue by exporting the component using a named export.
321 |
322 | ```javascript
323 | import * as React from "react";
324 |
325 | interface ColorButtonProps {
326 | /** Buttons background color */
327 | color: "blue" | "green";
328 | }
329 |
330 | /** A button with a configurable background color. */
331 | export const ColorButton: React.SFC = props => (
332 |
340 | {props.children}
341 |
342 | );
343 |
344 | export default ColorButton;
345 | ```
346 |
347 | ## Contributing
348 |
349 | Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
350 |
351 | Please make sure to update tests as appropriate.
352 |
353 | ## Credits
354 |
355 | ### SVG Logos
356 |
357 | - https://prettier.io
358 | - https://seeklogo.com
359 |
360 | ### Projects
361 |
362 | - [react-docgen-typescript](https://github.com/styleguidist/react-docgen-typescript)
363 |
364 | ## License
365 |
366 | [MIT](https://choosealicense.com/licenses/mit/)
367 |
--------------------------------------------------------------------------------
/src/__snapshots__/webpack.spec.ts.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`all component fixtures compile successfully fixture: DefaultPropValue.tsx 1`] = `
4 | "/******/ (function(modules) { // webpackBootstrap
5 | /******/ // The module cache
6 | /******/ var installedModules = {};
7 | /******/
8 | /******/ // The require function
9 | /******/ function __webpack_require__(moduleId) {
10 | /******/
11 | /******/ // Check if module is in cache
12 | /******/ if(installedModules[moduleId]) {
13 | /******/ return installedModules[moduleId].exports;
14 | /******/ }
15 | /******/ // Create a new module (and put it into the cache)
16 | /******/ var module = installedModules[moduleId] = {
17 | /******/ i: moduleId,
18 | /******/ l: false,
19 | /******/ exports: {}
20 | /******/ };
21 | /******/
22 | /******/ // Execute the module function
23 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
24 | /******/
25 | /******/ // Flag the module as loaded
26 | /******/ module.l = true;
27 | /******/
28 | /******/ // Return the exports of the module
29 | /******/ return module.exports;
30 | /******/ }
31 | /******/
32 | /******/
33 | /******/ // expose the modules object (__webpack_modules__)
34 | /******/ __webpack_require__.m = modules;
35 | /******/
36 | /******/ // expose the module cache
37 | /******/ __webpack_require__.c = installedModules;
38 | /******/
39 | /******/ // define getter function for harmony exports
40 | /******/ __webpack_require__.d = function(exports, name, getter) {
41 | /******/ if(!__webpack_require__.o(exports, name)) {
42 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
43 | /******/ }
44 | /******/ };
45 | /******/
46 | /******/ // define __esModule on exports
47 | /******/ __webpack_require__.r = function(exports) {
48 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
49 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
50 | /******/ }
51 | /******/ Object.defineProperty(exports, '__esModule', { value: true });
52 | /******/ };
53 | /******/
54 | /******/ // create a fake namespace object
55 | /******/ // mode & 1: value is a module id, require it
56 | /******/ // mode & 2: merge all properties of value into the ns
57 | /******/ // mode & 4: return value when already ns object
58 | /******/ // mode & 8|1: behave like require
59 | /******/ __webpack_require__.t = function(value, mode) {
60 | /******/ if(mode & 1) value = __webpack_require__(value);
61 | /******/ if(mode & 8) return value;
62 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
63 | /******/ var ns = Object.create(null);
64 | /******/ __webpack_require__.r(ns);
65 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
66 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
67 | /******/ return ns;
68 | /******/ };
69 | /******/
70 | /******/ // getDefaultExport function for compatibility with non-harmony modules
71 | /******/ __webpack_require__.n = function(module) {
72 | /******/ var getter = module && module.__esModule ?
73 | /******/ function getDefault() { return module['default']; } :
74 | /******/ function getModuleExports() { return module; };
75 | /******/ __webpack_require__.d(getter, 'a', getter);
76 | /******/ return getter;
77 | /******/ };
78 | /******/
79 | /******/ // Object.prototype.hasOwnProperty.call
80 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
81 | /******/
82 | /******/ // __webpack_public_path__
83 | /******/ __webpack_require__.p = \\"\\";
84 | /******/
85 | /******/
86 | /******/ // Load entry module and return exports
87 | /******/ return __webpack_require__(__webpack_require__.s = \\"./src/__fixtures__/components/DefaultPropValue.tsx\\");
88 | /******/ })
89 | /************************************************************************/
90 | /******/ ({
91 |
92 | /***/ \\"./src/__fixtures__/components/DefaultPropValue.tsx\\":
93 | /*!**********************************************************!*\\\\
94 | !*** ./src/__fixtures__/components/DefaultPropValue.tsx ***!
95 | \\\\**********************************************************/
96 | /*! no static exports found */
97 | /***/ (function(module, exports, __webpack_require__) {
98 |
99 | \\"use strict\\";
100 |
101 | var __importStar = (this && this.__importStar) || function (mod) {
102 | if (mod && mod.__esModule) return mod;
103 | var result = {};
104 | if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
105 | result[\\"default\\"] = mod;
106 | return result;
107 | };
108 | Object.defineProperty(exports, \\"__esModule\\", { value: true });
109 | var React = __importStar(__webpack_require__(/*! react */ \\"react\\"));
110 | /**
111 | * Component with a prop with a default value.
112 | */
113 | exports.DefaultPropValueComponent = function (props) { return (React.createElement(\\"button\\", { disabled: props.disabled, style: { backgroundColor: props.color } },
114 | props.counter,
115 | props.children)); };
116 | exports.DefaultPropValueComponent.defaultProps = {
117 | counter: 123,
118 | disabled: false,
119 | };
120 | try {
121 | // @ts-ignore
122 | exports.DefaultPropValueComponent.displayName = \\"DefaultPropValueComponent\\";
123 | // @ts-ignore
124 | exports.DefaultPropValueComponent.__docgenInfo = { \\"description\\": \\"Component with a prop with a default value.\\", \\"displayName\\": \\"DefaultPropValueComponent\\", \\"props\\": { \\"color\\": { \\"defaultValue\\": { value: \\"blue\\" }, \\"description\\": \\"Button color.\\", \\"name\\": \\"color\\", \\"required\\": true, \\"type\\": { \\"name\\": \\"\\\\\\"blue\\\\\\" | \\\\\\"green\\\\\\"\\" } }, \\"counter\\": { \\"defaultValue\\": { value: 123 }, \\"description\\": \\"Button counter.\\", \\"name\\": \\"counter\\", \\"required\\": false, \\"type\\": { \\"name\\": \\"number\\" } }, \\"disabled\\": { \\"defaultValue\\": { value: false }, \\"description\\": \\"Button disabled.\\", \\"name\\": \\"disabled\\", \\"required\\": false, \\"type\\": { \\"name\\": \\"boolean\\" } } } };
125 | // @ts-ignore
126 | if (typeof STORYBOOK_REACT_CLASSES !== \\"undefined\\")
127 | // @ts-ignore
128 | STORYBOOK_REACT_CLASSES[\\"src/__fixtures__/components/DefaultPropValue.tsx#DefaultPropValueComponent\\"] = { docgenInfo: exports.DefaultPropValueComponent.__docgenInfo, name: \\"DefaultPropValueComponent\\", path: \\"src/__fixtures__/components/DefaultPropValue.tsx#DefaultPropValueComponent\\" };
129 | }
130 | catch (__react_docgen_typescript_loader_error) { }
131 |
132 |
133 | /***/ }),
134 |
135 | /***/ \\"react\\":
136 | /*!************************!*\\\\
137 | !*** external \\"react\\" ***!
138 | \\\\************************/
139 | /*! no static exports found */
140 | /***/ (function(module, exports) {
141 |
142 | module.exports = react;
143 |
144 | /***/ })
145 |
146 | /******/ });"
147 | `;
148 |
149 | exports[`all component fixtures compile successfully fixture: HyphenatedPropName.tsx 1`] = `
150 | "/******/ (function(modules) { // webpackBootstrap
151 | /******/ // The module cache
152 | /******/ var installedModules = {};
153 | /******/
154 | /******/ // The require function
155 | /******/ function __webpack_require__(moduleId) {
156 | /******/
157 | /******/ // Check if module is in cache
158 | /******/ if(installedModules[moduleId]) {
159 | /******/ return installedModules[moduleId].exports;
160 | /******/ }
161 | /******/ // Create a new module (and put it into the cache)
162 | /******/ var module = installedModules[moduleId] = {
163 | /******/ i: moduleId,
164 | /******/ l: false,
165 | /******/ exports: {}
166 | /******/ };
167 | /******/
168 | /******/ // Execute the module function
169 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
170 | /******/
171 | /******/ // Flag the module as loaded
172 | /******/ module.l = true;
173 | /******/
174 | /******/ // Return the exports of the module
175 | /******/ return module.exports;
176 | /******/ }
177 | /******/
178 | /******/
179 | /******/ // expose the modules object (__webpack_modules__)
180 | /******/ __webpack_require__.m = modules;
181 | /******/
182 | /******/ // expose the module cache
183 | /******/ __webpack_require__.c = installedModules;
184 | /******/
185 | /******/ // define getter function for harmony exports
186 | /******/ __webpack_require__.d = function(exports, name, getter) {
187 | /******/ if(!__webpack_require__.o(exports, name)) {
188 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
189 | /******/ }
190 | /******/ };
191 | /******/
192 | /******/ // define __esModule on exports
193 | /******/ __webpack_require__.r = function(exports) {
194 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
195 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
196 | /******/ }
197 | /******/ Object.defineProperty(exports, '__esModule', { value: true });
198 | /******/ };
199 | /******/
200 | /******/ // create a fake namespace object
201 | /******/ // mode & 1: value is a module id, require it
202 | /******/ // mode & 2: merge all properties of value into the ns
203 | /******/ // mode & 4: return value when already ns object
204 | /******/ // mode & 8|1: behave like require
205 | /******/ __webpack_require__.t = function(value, mode) {
206 | /******/ if(mode & 1) value = __webpack_require__(value);
207 | /******/ if(mode & 8) return value;
208 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
209 | /******/ var ns = Object.create(null);
210 | /******/ __webpack_require__.r(ns);
211 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
212 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
213 | /******/ return ns;
214 | /******/ };
215 | /******/
216 | /******/ // getDefaultExport function for compatibility with non-harmony modules
217 | /******/ __webpack_require__.n = function(module) {
218 | /******/ var getter = module && module.__esModule ?
219 | /******/ function getDefault() { return module['default']; } :
220 | /******/ function getModuleExports() { return module; };
221 | /******/ __webpack_require__.d(getter, 'a', getter);
222 | /******/ return getter;
223 | /******/ };
224 | /******/
225 | /******/ // Object.prototype.hasOwnProperty.call
226 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
227 | /******/
228 | /******/ // __webpack_public_path__
229 | /******/ __webpack_require__.p = \\"\\";
230 | /******/
231 | /******/
232 | /******/ // Load entry module and return exports
233 | /******/ return __webpack_require__(__webpack_require__.s = \\"./src/__fixtures__/components/HyphenatedPropName.tsx\\");
234 | /******/ })
235 | /************************************************************************/
236 | /******/ ({
237 |
238 | /***/ \\"./src/__fixtures__/components/HyphenatedPropName.tsx\\":
239 | /*!************************************************************!*\\\\
240 | !*** ./src/__fixtures__/components/HyphenatedPropName.tsx ***!
241 | \\\\************************************************************/
242 | /*! no static exports found */
243 | /***/ (function(module, exports, __webpack_require__) {
244 |
245 | \\"use strict\\";
246 |
247 | var __importStar = (this && this.__importStar) || function (mod) {
248 | if (mod && mod.__esModule) return mod;
249 | var result = {};
250 | if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
251 | result[\\"default\\"] = mod;
252 | return result;
253 | };
254 | Object.defineProperty(exports, \\"__esModule\\", { value: true });
255 | var React = __importStar(__webpack_require__(/*! react */ \\"react\\"));
256 | /**
257 | * A component with a hyphenated prop name.
258 | */
259 | exports.HyphenatedPropNameComponent = function (props) { return (React.createElement(\\"button\\", { style: { backgroundColor: props[\\"button-color\\"] } }, props.children)); };
260 | try {
261 | // @ts-ignore
262 | exports.HyphenatedPropNameComponent.displayName = \\"HyphenatedPropNameComponent\\";
263 | // @ts-ignore
264 | exports.HyphenatedPropNameComponent.__docgenInfo = { \\"description\\": \\"A component with a hyphenated prop name.\\", \\"displayName\\": \\"HyphenatedPropNameComponent\\", \\"props\\": { \\"button-color\\": { \\"defaultValue\\": null, \\"description\\": \\"Button color.\\", \\"name\\": \\"button-color\\", \\"required\\": true, \\"type\\": { \\"name\\": \\"\\\\\\"blue\\\\\\" | \\\\\\"green\\\\\\"\\" } } } };
265 | // @ts-ignore
266 | if (typeof STORYBOOK_REACT_CLASSES !== \\"undefined\\")
267 | // @ts-ignore
268 | STORYBOOK_REACT_CLASSES[\\"src/__fixtures__/components/HyphenatedPropName.tsx#HyphenatedPropNameComponent\\"] = { docgenInfo: exports.HyphenatedPropNameComponent.__docgenInfo, name: \\"HyphenatedPropNameComponent\\", path: \\"src/__fixtures__/components/HyphenatedPropName.tsx#HyphenatedPropNameComponent\\" };
269 | }
270 | catch (__react_docgen_typescript_loader_error) { }
271 |
272 |
273 | /***/ }),
274 |
275 | /***/ \\"react\\":
276 | /*!************************!*\\\\
277 | !*** external \\"react\\" ***!
278 | \\\\************************/
279 | /*! no static exports found */
280 | /***/ (function(module, exports) {
281 |
282 | module.exports = react;
283 |
284 | /***/ })
285 |
286 | /******/ });"
287 | `;
288 |
289 | exports[`all component fixtures compile successfully fixture: MultiProps.tsx 1`] = `
290 | "/******/ (function(modules) { // webpackBootstrap
291 | /******/ // The module cache
292 | /******/ var installedModules = {};
293 | /******/
294 | /******/ // The require function
295 | /******/ function __webpack_require__(moduleId) {
296 | /******/
297 | /******/ // Check if module is in cache
298 | /******/ if(installedModules[moduleId]) {
299 | /******/ return installedModules[moduleId].exports;
300 | /******/ }
301 | /******/ // Create a new module (and put it into the cache)
302 | /******/ var module = installedModules[moduleId] = {
303 | /******/ i: moduleId,
304 | /******/ l: false,
305 | /******/ exports: {}
306 | /******/ };
307 | /******/
308 | /******/ // Execute the module function
309 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
310 | /******/
311 | /******/ // Flag the module as loaded
312 | /******/ module.l = true;
313 | /******/
314 | /******/ // Return the exports of the module
315 | /******/ return module.exports;
316 | /******/ }
317 | /******/
318 | /******/
319 | /******/ // expose the modules object (__webpack_modules__)
320 | /******/ __webpack_require__.m = modules;
321 | /******/
322 | /******/ // expose the module cache
323 | /******/ __webpack_require__.c = installedModules;
324 | /******/
325 | /******/ // define getter function for harmony exports
326 | /******/ __webpack_require__.d = function(exports, name, getter) {
327 | /******/ if(!__webpack_require__.o(exports, name)) {
328 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
329 | /******/ }
330 | /******/ };
331 | /******/
332 | /******/ // define __esModule on exports
333 | /******/ __webpack_require__.r = function(exports) {
334 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
335 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
336 | /******/ }
337 | /******/ Object.defineProperty(exports, '__esModule', { value: true });
338 | /******/ };
339 | /******/
340 | /******/ // create a fake namespace object
341 | /******/ // mode & 1: value is a module id, require it
342 | /******/ // mode & 2: merge all properties of value into the ns
343 | /******/ // mode & 4: return value when already ns object
344 | /******/ // mode & 8|1: behave like require
345 | /******/ __webpack_require__.t = function(value, mode) {
346 | /******/ if(mode & 1) value = __webpack_require__(value);
347 | /******/ if(mode & 8) return value;
348 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
349 | /******/ var ns = Object.create(null);
350 | /******/ __webpack_require__.r(ns);
351 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
352 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
353 | /******/ return ns;
354 | /******/ };
355 | /******/
356 | /******/ // getDefaultExport function for compatibility with non-harmony modules
357 | /******/ __webpack_require__.n = function(module) {
358 | /******/ var getter = module && module.__esModule ?
359 | /******/ function getDefault() { return module['default']; } :
360 | /******/ function getModuleExports() { return module; };
361 | /******/ __webpack_require__.d(getter, 'a', getter);
362 | /******/ return getter;
363 | /******/ };
364 | /******/
365 | /******/ // Object.prototype.hasOwnProperty.call
366 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
367 | /******/
368 | /******/ // __webpack_public_path__
369 | /******/ __webpack_require__.p = \\"\\";
370 | /******/
371 | /******/
372 | /******/ // Load entry module and return exports
373 | /******/ return __webpack_require__(__webpack_require__.s = \\"./src/__fixtures__/components/MultiProps.tsx\\");
374 | /******/ })
375 | /************************************************************************/
376 | /******/ ({
377 |
378 | /***/ \\"./src/__fixtures__/components/MultiProps.tsx\\":
379 | /*!****************************************************!*\\\\
380 | !*** ./src/__fixtures__/components/MultiProps.tsx ***!
381 | \\\\****************************************************/
382 | /*! no static exports found */
383 | /***/ (function(module, exports, __webpack_require__) {
384 |
385 | \\"use strict\\";
386 |
387 | var __importStar = (this && this.__importStar) || function (mod) {
388 | if (mod && mod.__esModule) return mod;
389 | var result = {};
390 | if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
391 | result[\\"default\\"] = mod;
392 | return result;
393 | };
394 | Object.defineProperty(exports, \\"__esModule\\", { value: true });
395 | var React = __importStar(__webpack_require__(/*! react */ \\"react\\"));
396 | /**
397 | * This is a component with multiple props.
398 | */
399 | exports.MultiPropsComponent = function (props) { return (React.createElement(\\"button\\", { style: { backgroundColor: props.color } }, props.children)); };
400 | try {
401 | // @ts-ignore
402 | exports.MultiPropsComponent.displayName = \\"MultiPropsComponent\\";
403 | // @ts-ignore
404 | exports.MultiPropsComponent.__docgenInfo = { \\"description\\": \\"This is a component with multiple props.\\", \\"displayName\\": \\"MultiPropsComponent\\", \\"props\\": { \\"color\\": { \\"defaultValue\\": null, \\"description\\": \\"Button color.\\", \\"name\\": \\"color\\", \\"required\\": true, \\"type\\": { \\"name\\": \\"\\\\\\"blue\\\\\\" | \\\\\\"green\\\\\\"\\" } }, \\"size\\": { \\"defaultValue\\": null, \\"description\\": \\"Button size.\\", \\"name\\": \\"size\\", \\"required\\": true, \\"type\\": { \\"name\\": \\"\\\\\\"small\\\\\\" | \\\\\\"large\\\\\\"\\" } } } };
405 | // @ts-ignore
406 | if (typeof STORYBOOK_REACT_CLASSES !== \\"undefined\\")
407 | // @ts-ignore
408 | STORYBOOK_REACT_CLASSES[\\"src/__fixtures__/components/MultiProps.tsx#MultiPropsComponent\\"] = { docgenInfo: exports.MultiPropsComponent.__docgenInfo, name: \\"MultiPropsComponent\\", path: \\"src/__fixtures__/components/MultiProps.tsx#MultiPropsComponent\\" };
409 | }
410 | catch (__react_docgen_typescript_loader_error) { }
411 |
412 |
413 | /***/ }),
414 |
415 | /***/ \\"react\\":
416 | /*!************************!*\\\\
417 | !*** external \\"react\\" ***!
418 | \\\\************************/
419 | /*! no static exports found */
420 | /***/ (function(module, exports) {
421 |
422 | module.exports = react;
423 |
424 | /***/ })
425 |
426 | /******/ });"
427 | `;
428 |
429 | exports[`all component fixtures compile successfully fixture: MultilineDescription.tsx 1`] = `
430 | "/******/ (function(modules) { // webpackBootstrap
431 | /******/ // The module cache
432 | /******/ var installedModules = {};
433 | /******/
434 | /******/ // The require function
435 | /******/ function __webpack_require__(moduleId) {
436 | /******/
437 | /******/ // Check if module is in cache
438 | /******/ if(installedModules[moduleId]) {
439 | /******/ return installedModules[moduleId].exports;
440 | /******/ }
441 | /******/ // Create a new module (and put it into the cache)
442 | /******/ var module = installedModules[moduleId] = {
443 | /******/ i: moduleId,
444 | /******/ l: false,
445 | /******/ exports: {}
446 | /******/ };
447 | /******/
448 | /******/ // Execute the module function
449 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
450 | /******/
451 | /******/ // Flag the module as loaded
452 | /******/ module.l = true;
453 | /******/
454 | /******/ // Return the exports of the module
455 | /******/ return module.exports;
456 | /******/ }
457 | /******/
458 | /******/
459 | /******/ // expose the modules object (__webpack_modules__)
460 | /******/ __webpack_require__.m = modules;
461 | /******/
462 | /******/ // expose the module cache
463 | /******/ __webpack_require__.c = installedModules;
464 | /******/
465 | /******/ // define getter function for harmony exports
466 | /******/ __webpack_require__.d = function(exports, name, getter) {
467 | /******/ if(!__webpack_require__.o(exports, name)) {
468 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
469 | /******/ }
470 | /******/ };
471 | /******/
472 | /******/ // define __esModule on exports
473 | /******/ __webpack_require__.r = function(exports) {
474 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
475 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
476 | /******/ }
477 | /******/ Object.defineProperty(exports, '__esModule', { value: true });
478 | /******/ };
479 | /******/
480 | /******/ // create a fake namespace object
481 | /******/ // mode & 1: value is a module id, require it
482 | /******/ // mode & 2: merge all properties of value into the ns
483 | /******/ // mode & 4: return value when already ns object
484 | /******/ // mode & 8|1: behave like require
485 | /******/ __webpack_require__.t = function(value, mode) {
486 | /******/ if(mode & 1) value = __webpack_require__(value);
487 | /******/ if(mode & 8) return value;
488 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
489 | /******/ var ns = Object.create(null);
490 | /******/ __webpack_require__.r(ns);
491 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
492 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
493 | /******/ return ns;
494 | /******/ };
495 | /******/
496 | /******/ // getDefaultExport function for compatibility with non-harmony modules
497 | /******/ __webpack_require__.n = function(module) {
498 | /******/ var getter = module && module.__esModule ?
499 | /******/ function getDefault() { return module['default']; } :
500 | /******/ function getModuleExports() { return module; };
501 | /******/ __webpack_require__.d(getter, 'a', getter);
502 | /******/ return getter;
503 | /******/ };
504 | /******/
505 | /******/ // Object.prototype.hasOwnProperty.call
506 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
507 | /******/
508 | /******/ // __webpack_public_path__
509 | /******/ __webpack_require__.p = \\"\\";
510 | /******/
511 | /******/
512 | /******/ // Load entry module and return exports
513 | /******/ return __webpack_require__(__webpack_require__.s = \\"./src/__fixtures__/components/MultilineDescription.tsx\\");
514 | /******/ })
515 | /************************************************************************/
516 | /******/ ({
517 |
518 | /***/ \\"./src/__fixtures__/components/MultilineDescription.tsx\\":
519 | /*!**************************************************************!*\\\\
520 | !*** ./src/__fixtures__/components/MultilineDescription.tsx ***!
521 | \\\\**************************************************************/
522 | /*! no static exports found */
523 | /***/ (function(module, exports, __webpack_require__) {
524 |
525 | \\"use strict\\";
526 |
527 | var __importStar = (this && this.__importStar) || function (mod) {
528 | if (mod && mod.__esModule) return mod;
529 | var result = {};
530 | if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
531 | result[\\"default\\"] = mod;
532 | return result;
533 | };
534 | Object.defineProperty(exports, \\"__esModule\\", { value: true });
535 | var React = __importStar(__webpack_require__(/*! react */ \\"react\\"));
536 | /**
537 | * A component with a multiline description.
538 | *
539 | * Second line.
540 | */
541 | exports.MultilineDescriptionComponent = function (props) { return (React.createElement(\\"button\\", { style: { backgroundColor: props.color } }, props.children)); };
542 | try {
543 | // @ts-ignore
544 | exports.MultilineDescriptionComponent.displayName = \\"MultilineDescriptionComponent\\";
545 | // @ts-ignore
546 | exports.MultilineDescriptionComponent.__docgenInfo = { \\"description\\": \\"A component with a multiline description.\\\\n\\\\nSecond line.\\", \\"displayName\\": \\"MultilineDescriptionComponent\\", \\"props\\": { \\"color\\": { \\"defaultValue\\": null, \\"description\\": \\"Button color.\\", \\"name\\": \\"color\\", \\"required\\": true, \\"type\\": { \\"name\\": \\"\\\\\\"blue\\\\\\" | \\\\\\"green\\\\\\"\\" } } } };
547 | // @ts-ignore
548 | if (typeof STORYBOOK_REACT_CLASSES !== \\"undefined\\")
549 | // @ts-ignore
550 | STORYBOOK_REACT_CLASSES[\\"src/__fixtures__/components/MultilineDescription.tsx#MultilineDescriptionComponent\\"] = { docgenInfo: exports.MultilineDescriptionComponent.__docgenInfo, name: \\"MultilineDescriptionComponent\\", path: \\"src/__fixtures__/components/MultilineDescription.tsx#MultilineDescriptionComponent\\" };
551 | }
552 | catch (__react_docgen_typescript_loader_error) { }
553 |
554 |
555 | /***/ }),
556 |
557 | /***/ \\"react\\":
558 | /*!************************!*\\\\
559 | !*** external \\"react\\" ***!
560 | \\\\************************/
561 | /*! no static exports found */
562 | /***/ (function(module, exports) {
563 |
564 | module.exports = react;
565 |
566 | /***/ })
567 |
568 | /******/ });"
569 | `;
570 |
571 | exports[`all component fixtures compile successfully fixture: MultilinePropDescription.tsx 1`] = `
572 | "/******/ (function(modules) { // webpackBootstrap
573 | /******/ // The module cache
574 | /******/ var installedModules = {};
575 | /******/
576 | /******/ // The require function
577 | /******/ function __webpack_require__(moduleId) {
578 | /******/
579 | /******/ // Check if module is in cache
580 | /******/ if(installedModules[moduleId]) {
581 | /******/ return installedModules[moduleId].exports;
582 | /******/ }
583 | /******/ // Create a new module (and put it into the cache)
584 | /******/ var module = installedModules[moduleId] = {
585 | /******/ i: moduleId,
586 | /******/ l: false,
587 | /******/ exports: {}
588 | /******/ };
589 | /******/
590 | /******/ // Execute the module function
591 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
592 | /******/
593 | /******/ // Flag the module as loaded
594 | /******/ module.l = true;
595 | /******/
596 | /******/ // Return the exports of the module
597 | /******/ return module.exports;
598 | /******/ }
599 | /******/
600 | /******/
601 | /******/ // expose the modules object (__webpack_modules__)
602 | /******/ __webpack_require__.m = modules;
603 | /******/
604 | /******/ // expose the module cache
605 | /******/ __webpack_require__.c = installedModules;
606 | /******/
607 | /******/ // define getter function for harmony exports
608 | /******/ __webpack_require__.d = function(exports, name, getter) {
609 | /******/ if(!__webpack_require__.o(exports, name)) {
610 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
611 | /******/ }
612 | /******/ };
613 | /******/
614 | /******/ // define __esModule on exports
615 | /******/ __webpack_require__.r = function(exports) {
616 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
617 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
618 | /******/ }
619 | /******/ Object.defineProperty(exports, '__esModule', { value: true });
620 | /******/ };
621 | /******/
622 | /******/ // create a fake namespace object
623 | /******/ // mode & 1: value is a module id, require it
624 | /******/ // mode & 2: merge all properties of value into the ns
625 | /******/ // mode & 4: return value when already ns object
626 | /******/ // mode & 8|1: behave like require
627 | /******/ __webpack_require__.t = function(value, mode) {
628 | /******/ if(mode & 1) value = __webpack_require__(value);
629 | /******/ if(mode & 8) return value;
630 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
631 | /******/ var ns = Object.create(null);
632 | /******/ __webpack_require__.r(ns);
633 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
634 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
635 | /******/ return ns;
636 | /******/ };
637 | /******/
638 | /******/ // getDefaultExport function for compatibility with non-harmony modules
639 | /******/ __webpack_require__.n = function(module) {
640 | /******/ var getter = module && module.__esModule ?
641 | /******/ function getDefault() { return module['default']; } :
642 | /******/ function getModuleExports() { return module; };
643 | /******/ __webpack_require__.d(getter, 'a', getter);
644 | /******/ return getter;
645 | /******/ };
646 | /******/
647 | /******/ // Object.prototype.hasOwnProperty.call
648 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
649 | /******/
650 | /******/ // __webpack_public_path__
651 | /******/ __webpack_require__.p = \\"\\";
652 | /******/
653 | /******/
654 | /******/ // Load entry module and return exports
655 | /******/ return __webpack_require__(__webpack_require__.s = \\"./src/__fixtures__/components/MultilinePropDescription.tsx\\");
656 | /******/ })
657 | /************************************************************************/
658 | /******/ ({
659 |
660 | /***/ \\"./src/__fixtures__/components/MultilinePropDescription.tsx\\":
661 | /*!******************************************************************!*\\\\
662 | !*** ./src/__fixtures__/components/MultilinePropDescription.tsx ***!
663 | \\\\******************************************************************/
664 | /*! no static exports found */
665 | /***/ (function(module, exports, __webpack_require__) {
666 |
667 | \\"use strict\\";
668 |
669 | var __importStar = (this && this.__importStar) || function (mod) {
670 | if (mod && mod.__esModule) return mod;
671 | var result = {};
672 | if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
673 | result[\\"default\\"] = mod;
674 | return result;
675 | };
676 | Object.defineProperty(exports, \\"__esModule\\", { value: true });
677 | var React = __importStar(__webpack_require__(/*! react */ \\"react\\"));
678 | /**
679 | * A component with multiline prop description.
680 | */
681 | exports.MultilinePropDescriptionComponent = function (props) { return (React.createElement(\\"button\\", { style: { backgroundColor: props.color } }, props.children)); };
682 | try {
683 | // @ts-ignore
684 | exports.MultilinePropDescriptionComponent.displayName = \\"MultilinePropDescriptionComponent\\";
685 | // @ts-ignore
686 | exports.MultilinePropDescriptionComponent.__docgenInfo = { \\"description\\": \\"A component with multiline prop description.\\", \\"displayName\\": \\"MultilinePropDescriptionComponent\\", \\"props\\": { \\"color\\": { \\"defaultValue\\": null, \\"description\\": \\"This is a multiline prop description.\\\\n\\\\nSecond line.\\", \\"name\\": \\"color\\", \\"required\\": true, \\"type\\": { \\"name\\": \\"\\\\\\"blue\\\\\\" | \\\\\\"green\\\\\\"\\" } } } };
687 | // @ts-ignore
688 | if (typeof STORYBOOK_REACT_CLASSES !== \\"undefined\\")
689 | // @ts-ignore
690 | STORYBOOK_REACT_CLASSES[\\"src/__fixtures__/components/MultilinePropDescription.tsx#MultilinePropDescriptionComponent\\"] = { docgenInfo: exports.MultilinePropDescriptionComponent.__docgenInfo, name: \\"MultilinePropDescriptionComponent\\", path: \\"src/__fixtures__/components/MultilinePropDescription.tsx#MultilinePropDescriptionComponent\\" };
691 | }
692 | catch (__react_docgen_typescript_loader_error) { }
693 |
694 |
695 | /***/ }),
696 |
697 | /***/ \\"react\\":
698 | /*!************************!*\\\\
699 | !*** external \\"react\\" ***!
700 | \\\\************************/
701 | /*! no static exports found */
702 | /***/ (function(module, exports) {
703 |
704 | module.exports = react;
705 |
706 | /***/ })
707 |
708 | /******/ });"
709 | `;
710 |
711 | exports[`all component fixtures compile successfully fixture: Simple.tsx 1`] = `
712 | "/******/ (function(modules) { // webpackBootstrap
713 | /******/ // The module cache
714 | /******/ var installedModules = {};
715 | /******/
716 | /******/ // The require function
717 | /******/ function __webpack_require__(moduleId) {
718 | /******/
719 | /******/ // Check if module is in cache
720 | /******/ if(installedModules[moduleId]) {
721 | /******/ return installedModules[moduleId].exports;
722 | /******/ }
723 | /******/ // Create a new module (and put it into the cache)
724 | /******/ var module = installedModules[moduleId] = {
725 | /******/ i: moduleId,
726 | /******/ l: false,
727 | /******/ exports: {}
728 | /******/ };
729 | /******/
730 | /******/ // Execute the module function
731 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
732 | /******/
733 | /******/ // Flag the module as loaded
734 | /******/ module.l = true;
735 | /******/
736 | /******/ // Return the exports of the module
737 | /******/ return module.exports;
738 | /******/ }
739 | /******/
740 | /******/
741 | /******/ // expose the modules object (__webpack_modules__)
742 | /******/ __webpack_require__.m = modules;
743 | /******/
744 | /******/ // expose the module cache
745 | /******/ __webpack_require__.c = installedModules;
746 | /******/
747 | /******/ // define getter function for harmony exports
748 | /******/ __webpack_require__.d = function(exports, name, getter) {
749 | /******/ if(!__webpack_require__.o(exports, name)) {
750 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
751 | /******/ }
752 | /******/ };
753 | /******/
754 | /******/ // define __esModule on exports
755 | /******/ __webpack_require__.r = function(exports) {
756 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
757 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
758 | /******/ }
759 | /******/ Object.defineProperty(exports, '__esModule', { value: true });
760 | /******/ };
761 | /******/
762 | /******/ // create a fake namespace object
763 | /******/ // mode & 1: value is a module id, require it
764 | /******/ // mode & 2: merge all properties of value into the ns
765 | /******/ // mode & 4: return value when already ns object
766 | /******/ // mode & 8|1: behave like require
767 | /******/ __webpack_require__.t = function(value, mode) {
768 | /******/ if(mode & 1) value = __webpack_require__(value);
769 | /******/ if(mode & 8) return value;
770 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
771 | /******/ var ns = Object.create(null);
772 | /******/ __webpack_require__.r(ns);
773 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
774 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
775 | /******/ return ns;
776 | /******/ };
777 | /******/
778 | /******/ // getDefaultExport function for compatibility with non-harmony modules
779 | /******/ __webpack_require__.n = function(module) {
780 | /******/ var getter = module && module.__esModule ?
781 | /******/ function getDefault() { return module['default']; } :
782 | /******/ function getModuleExports() { return module; };
783 | /******/ __webpack_require__.d(getter, 'a', getter);
784 | /******/ return getter;
785 | /******/ };
786 | /******/
787 | /******/ // Object.prototype.hasOwnProperty.call
788 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
789 | /******/
790 | /******/ // __webpack_public_path__
791 | /******/ __webpack_require__.p = \\"\\";
792 | /******/
793 | /******/
794 | /******/ // Load entry module and return exports
795 | /******/ return __webpack_require__(__webpack_require__.s = \\"./src/__fixtures__/components/Simple.tsx\\");
796 | /******/ })
797 | /************************************************************************/
798 | /******/ ({
799 |
800 | /***/ \\"./src/__fixtures__/components/Simple.tsx\\":
801 | /*!************************************************!*\\\\
802 | !*** ./src/__fixtures__/components/Simple.tsx ***!
803 | \\\\************************************************/
804 | /*! no static exports found */
805 | /***/ (function(module, exports, __webpack_require__) {
806 |
807 | \\"use strict\\";
808 |
809 | var __importStar = (this && this.__importStar) || function (mod) {
810 | if (mod && mod.__esModule) return mod;
811 | var result = {};
812 | if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
813 | result[\\"default\\"] = mod;
814 | return result;
815 | };
816 | Object.defineProperty(exports, \\"__esModule\\", { value: true });
817 | var React = __importStar(__webpack_require__(/*! react */ \\"react\\"));
818 | /**
819 | * A simple component.
820 | */
821 | exports.SimpleComponent = function (props) { return (React.createElement(\\"button\\", { style: { backgroundColor: props.color } }, props.children)); };
822 | try {
823 | // @ts-ignore
824 | exports.SimpleComponent.displayName = \\"SimpleComponent\\";
825 | // @ts-ignore
826 | exports.SimpleComponent.__docgenInfo = { \\"description\\": \\"A simple component.\\", \\"displayName\\": \\"SimpleComponent\\", \\"props\\": { \\"color\\": { \\"defaultValue\\": null, \\"description\\": \\"Button color.\\", \\"name\\": \\"color\\", \\"required\\": true, \\"type\\": { \\"name\\": \\"\\\\\\"blue\\\\\\" | \\\\\\"green\\\\\\"\\" } } } };
827 | // @ts-ignore
828 | if (typeof STORYBOOK_REACT_CLASSES !== \\"undefined\\")
829 | // @ts-ignore
830 | STORYBOOK_REACT_CLASSES[\\"src/__fixtures__/components/Simple.tsx#SimpleComponent\\"] = { docgenInfo: exports.SimpleComponent.__docgenInfo, name: \\"SimpleComponent\\", path: \\"src/__fixtures__/components/Simple.tsx#SimpleComponent\\" };
831 | }
832 | catch (__react_docgen_typescript_loader_error) { }
833 |
834 |
835 | /***/ }),
836 |
837 | /***/ \\"react\\":
838 | /*!************************!*\\\\
839 | !*** external \\"react\\" ***!
840 | \\\\************************/
841 | /*! no static exports found */
842 | /***/ (function(module, exports) {
843 |
844 | module.exports = react;
845 |
846 | /***/ })
847 |
848 | /******/ });"
849 | `;
850 |
851 | exports[`all component fixtures compile successfully fixture: TextOnlyComponent.tsx 1`] = `
852 | "/******/ (function(modules) { // webpackBootstrap
853 | /******/ // The module cache
854 | /******/ var installedModules = {};
855 | /******/
856 | /******/ // The require function
857 | /******/ function __webpack_require__(moduleId) {
858 | /******/
859 | /******/ // Check if module is in cache
860 | /******/ if(installedModules[moduleId]) {
861 | /******/ return installedModules[moduleId].exports;
862 | /******/ }
863 | /******/ // Create a new module (and put it into the cache)
864 | /******/ var module = installedModules[moduleId] = {
865 | /******/ i: moduleId,
866 | /******/ l: false,
867 | /******/ exports: {}
868 | /******/ };
869 | /******/
870 | /******/ // Execute the module function
871 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
872 | /******/
873 | /******/ // Flag the module as loaded
874 | /******/ module.l = true;
875 | /******/
876 | /******/ // Return the exports of the module
877 | /******/ return module.exports;
878 | /******/ }
879 | /******/
880 | /******/
881 | /******/ // expose the modules object (__webpack_modules__)
882 | /******/ __webpack_require__.m = modules;
883 | /******/
884 | /******/ // expose the module cache
885 | /******/ __webpack_require__.c = installedModules;
886 | /******/
887 | /******/ // define getter function for harmony exports
888 | /******/ __webpack_require__.d = function(exports, name, getter) {
889 | /******/ if(!__webpack_require__.o(exports, name)) {
890 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
891 | /******/ }
892 | /******/ };
893 | /******/
894 | /******/ // define __esModule on exports
895 | /******/ __webpack_require__.r = function(exports) {
896 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
897 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
898 | /******/ }
899 | /******/ Object.defineProperty(exports, '__esModule', { value: true });
900 | /******/ };
901 | /******/
902 | /******/ // create a fake namespace object
903 | /******/ // mode & 1: value is a module id, require it
904 | /******/ // mode & 2: merge all properties of value into the ns
905 | /******/ // mode & 4: return value when already ns object
906 | /******/ // mode & 8|1: behave like require
907 | /******/ __webpack_require__.t = function(value, mode) {
908 | /******/ if(mode & 1) value = __webpack_require__(value);
909 | /******/ if(mode & 8) return value;
910 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
911 | /******/ var ns = Object.create(null);
912 | /******/ __webpack_require__.r(ns);
913 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
914 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
915 | /******/ return ns;
916 | /******/ };
917 | /******/
918 | /******/ // getDefaultExport function for compatibility with non-harmony modules
919 | /******/ __webpack_require__.n = function(module) {
920 | /******/ var getter = module && module.__esModule ?
921 | /******/ function getDefault() { return module['default']; } :
922 | /******/ function getModuleExports() { return module; };
923 | /******/ __webpack_require__.d(getter, 'a', getter);
924 | /******/ return getter;
925 | /******/ };
926 | /******/
927 | /******/ // Object.prototype.hasOwnProperty.call
928 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
929 | /******/
930 | /******/ // __webpack_public_path__
931 | /******/ __webpack_require__.p = \\"\\";
932 | /******/
933 | /******/
934 | /******/ // Load entry module and return exports
935 | /******/ return __webpack_require__(__webpack_require__.s = \\"./src/__fixtures__/components/TextOnlyComponent.tsx\\");
936 | /******/ })
937 | /************************************************************************/
938 | /******/ ({
939 |
940 | /***/ \\"./src/__fixtures__/components/TextOnlyComponent.tsx\\":
941 | /*!***********************************************************!*\\\\
942 | !*** ./src/__fixtures__/components/TextOnlyComponent.tsx ***!
943 | \\\\***********************************************************/
944 | /*! no static exports found */
945 | /***/ (function(module, exports, __webpack_require__) {
946 |
947 | \\"use strict\\";
948 |
949 | var __importStar = (this && this.__importStar) || function (mod) {
950 | if (mod && mod.__esModule) return mod;
951 | var result = {};
952 | if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
953 | result[\\"default\\"] = mod;
954 | return result;
955 | };
956 | Object.defineProperty(exports, \\"__esModule\\", { value: true });
957 | var React = __importStar(__webpack_require__(/*! react */ \\"react\\"));
958 | /**
959 | * A component with only text content wrapped in a div.
960 | *
961 | * Ref: https://github.com/strothj/react-docgen-typescript-loader/issues/7
962 | */
963 | exports.SimpleComponent = function () { return (React.createElement(\\"div\\", null, \\"Test only component\\")); };
964 | try {
965 | // @ts-ignore
966 | exports.SimpleComponent.displayName = \\"SimpleComponent\\";
967 | // @ts-ignore
968 | exports.SimpleComponent.__docgenInfo = { \\"description\\": \\"A component with only text content wrapped in a div.\\\\n\\\\nRef: https://github.com/strothj/react-docgen-typescript-loader/issues/7\\", \\"displayName\\": \\"SimpleComponent\\", \\"props\\": {} };
969 | // @ts-ignore
970 | if (typeof STORYBOOK_REACT_CLASSES !== \\"undefined\\")
971 | // @ts-ignore
972 | STORYBOOK_REACT_CLASSES[\\"src/__fixtures__/components/TextOnlyComponent.tsx#SimpleComponent\\"] = { docgenInfo: exports.SimpleComponent.__docgenInfo, name: \\"SimpleComponent\\", path: \\"src/__fixtures__/components/TextOnlyComponent.tsx#SimpleComponent\\" };
973 | }
974 | catch (__react_docgen_typescript_loader_error) { }
975 |
976 |
977 | /***/ }),
978 |
979 | /***/ \\"react\\":
980 | /*!************************!*\\\\
981 | !*** external \\"react\\" ***!
982 | \\\\************************/
983 | /*! no static exports found */
984 | /***/ (function(module, exports) {
985 |
986 | module.exports = react;
987 |
988 | /***/ })
989 |
990 | /******/ });"
991 | `;
992 |
--------------------------------------------------------------------------------