├── .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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | Logo for project react-docgen-typescript-loader 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 | Example screenshot of Storybook with TypeScript props 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 | ![Example image, how to include component description](/example-story-name.png) 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 | 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 | 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 | --------------------------------------------------------------------------------