├── .babelrc.js ├── .github └── workflows │ ├── release.yml │ └── test.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .yarnrc.yml ├── CHANGELOG.md ├── CODEOWNERS ├── LICENSE ├── README.md ├── examples ├── vite │ ├── .gitignore │ ├── .storybook │ │ ├── main.ts │ │ └── preview.ts │ ├── index.html │ ├── package.json │ ├── public │ │ └── vite.svg │ ├── src │ │ ├── App.css │ │ ├── App.tsx │ │ ├── assets │ │ │ └── react.svg │ │ ├── index.css │ │ ├── main.tsx │ │ ├── stories │ │ │ ├── Button.stories.tsx │ │ │ ├── Button.tsx │ │ │ ├── Header.stories.tsx │ │ │ ├── Header.tsx │ │ │ ├── Page.stories.tsx │ │ │ ├── Page.tsx │ │ │ ├── assets │ │ │ │ ├── code-brackets.svg │ │ │ │ ├── colors.svg │ │ │ │ ├── comments.svg │ │ │ │ ├── direction.svg │ │ │ │ ├── flow.svg │ │ │ │ ├── plugin.svg │ │ │ │ ├── repo.svg │ │ │ │ └── stackalt.svg │ │ │ ├── button.css │ │ │ ├── header.css │ │ │ └── page.css │ │ └── vite-env.d.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── vite.config.ts │ └── yarn.lock └── webpack5 │ ├── .babelrc │ ├── .nycrc.json │ ├── .storybook │ ├── main.ts │ └── preview.ts │ ├── README.md │ ├── package.json │ ├── src │ └── index.tsx │ ├── stories │ ├── Button.stories.tsx │ ├── Button.tsx │ ├── Header.stories.tsx │ ├── Header.tsx │ ├── Page.stories.tsx │ ├── Page.tsx │ ├── assets │ │ ├── code-brackets.svg │ │ ├── colors.svg │ │ ├── comments.svg │ │ ├── direction.svg │ │ ├── flow.svg │ │ ├── plugin.svg │ │ ├── repo.svg │ │ └── stackalt.svg │ ├── button.css │ ├── header.css │ └── page.css │ ├── tsconfig.json │ ├── webpack.config.js │ └── yarn.lock ├── package.json ├── preset.js ├── src ├── constants.ts ├── index.ts ├── loader │ └── webpack5-istanbul-loader.ts ├── nyc-config.ts ├── preset.ts ├── types.ts └── webpack5-exclude.ts ├── tsconfig.json └── yarn.lock /.babelrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | shippedProposals: true, 7 | useBuiltIns: "usage", 8 | corejs: "3", 9 | targets: { node: "14" }, 10 | }, 11 | ], 12 | "@babel/preset-typescript", 13 | "@babel/preset-react", 14 | ], 15 | env: { 16 | esm: { 17 | presets: [ 18 | [ 19 | "@babel/preset-env", 20 | { 21 | shippedProposals: true, 22 | useBuiltIns: "usage", 23 | corejs: "3", 24 | modules: false, 25 | targets: { chrome: "100" }, 26 | }, 27 | ], 28 | ], 29 | }, 30 | }, 31 | }; 32 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: [push] 4 | 5 | jobs: 6 | release: 7 | runs-on: ubuntu-latest 8 | if: "!contains(github.event.head_commit.message, 'ci skip') && !contains(github.event.head_commit.message, 'skip ci')" 9 | steps: 10 | - uses: actions/checkout@v3 11 | 12 | - name: Enable Corepack 13 | run: corepack enable 14 | 15 | - name: Prepare repository 16 | run: git fetch --unshallow --tags 17 | 18 | - name: Setup Node.js 19 | uses: actions/setup-node@v4 20 | 21 | - name: Install dependencies 22 | uses: bahmutov/npm-install@v1 23 | 24 | - name: Create Release 25 | env: 26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 28 | run: | 29 | yarn release -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | 11 | - name: Enable Corepack 12 | run: corepack enable 13 | 14 | - name: Setup Node.js 15 | uses: actions/setup-node@v4 16 | 17 | - name: Build coverage addon 18 | run: | 19 | yarn install 20 | yarn build 21 | 22 | - name: Run tests in webpack example 23 | run: | 24 | yarn install 25 | npx playwright install --with-deps 26 | yarn test-storybook:ci-coverage 27 | working-directory: examples/webpack5 28 | 29 | - name: Run tests in vite example 30 | run: | 31 | yarn install 32 | npx playwright install --with-deps 33 | yarn test-storybook:ci-coverage 34 | working-directory: examples/vite 35 | 36 | - name: Generate code coverage 37 | uses: codecov/codecov-action@v3 38 | with: 39 | verbose: true 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | storybook-static/ 4 | build-storybook.log 5 | .DS_Store 6 | .env 7 | yarn-*.log 8 | coverage 9 | .nyc_output 10 | 11 | # Yarn berry 12 | /**/.yarn/* 13 | !/**/.yarn/releases 14 | !/**/.yarn/patches 15 | !/**/.yarn/plugins 16 | !/**/.yarn/sdks 17 | !/**/.yarn/versions 18 | /**/.pnp.* 19 | 20 | examples/webpack5/.yarn 21 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v1.0.5 (Tue Dec 24 2024) 2 | 3 | #### 🐛 Bug Fix 4 | 5 | - fix: Handling of rawSourceMap string type in webpack5-istanbul-loader [#44](https://github.com/storybookjs/addon-coverage/pull/44) ([@mschimk1](https://github.com/mschimk1)) 6 | 7 | #### Authors: 1 8 | 9 | - Michael Schimko ([@mschimk1](https://github.com/mschimk1)) 10 | 11 | --- 12 | 13 | # v1.0.4 (Fri May 24 2024) 14 | 15 | #### 🐛 Bug Fix 16 | 17 | - Fix sourcemap generation for Webpack5 projects [#42](https://github.com/storybookjs/addon-coverage/pull/42) ([@valentinpalkovic](https://github.com/valentinpalkovic)) 18 | 19 | #### Authors: 1 20 | 21 | - Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic)) 22 | 23 | --- 24 | 25 | # v1.0.3 (Tue May 07 2024) 26 | 27 | #### 🐛 Bug Fix 28 | 29 | - Fix: Handle scenario where rawSourceMap is a string [#33](https://github.com/storybookjs/addon-coverage/pull/33) ([@hkaikai](https://github.com/hkaikai)) 30 | 31 | #### Authors: 1 32 | 33 | - [@hkaikai](https://github.com/hkaikai) 34 | 35 | --- 36 | 37 | # v1.0.2 (Tue May 07 2024) 38 | 39 | #### 🐛 Bug Fix 40 | 41 | - Add more files to default filter [#41](https://github.com/storybookjs/addon-coverage/pull/41) ([@yannbf](https://github.com/yannbf)) 42 | 43 | #### Authors: 1 44 | 45 | - Yann Braga ([@yannbf](https://github.com/yannbf)) 46 | 47 | --- 48 | 49 | # v1.0.1 (Wed Feb 28 2024) 50 | 51 | #### 🐛 Bug Fix 52 | 53 | - Fix: Add index entrypoint [#39](https://github.com/storybookjs/addon-coverage/pull/39) ([@yannbf](https://github.com/yannbf)) 54 | 55 | #### 📝 Documentation 56 | 57 | - Docs: Adds troubleshooting for test flag [#36](https://github.com/storybookjs/addon-coverage/pull/36) ([@jonniebigodes](https://github.com/jonniebigodes)) 58 | 59 | #### Authors: 2 60 | 61 | - [@jonniebigodes](https://github.com/jonniebigodes) 62 | - Yann Braga ([@yannbf](https://github.com/yannbf)) 63 | 64 | --- 65 | 66 | # v1.0.0 (Tue Nov 14 2023) 67 | 68 | ### Release Notes 69 | 70 | #### [Breaking Change]: Replace babel-istanbul-plugin and replace it by a webpack loader ([#27](https://github.com/storybookjs/addon-coverage/pull/27)) 71 | 72 | - [BREAKING CHANGE]: Dropping Storybook < 7 support 73 | - Replaced `babel-istanbul-plugin` with a custom Webpack loader to support coverage independently of the compiler used for Webpack5 projects. Before, the coverage addon only worked if Babel was used with Webpack5. Now, users might use other compilers like, for example, SWC. 74 | 75 | --- 76 | 77 | #### 💥 Breaking Change 78 | 79 | - [Breaking Change]: Replace babel-istanbul-plugin and replace it by a webpack loader [#27](https://github.com/storybookjs/addon-coverage/pull/27) ([@yannbf](https://github.com/yannbf) [@valentinpalkovic](https://github.com/valentinpalkovic)) 80 | 81 | #### 🐛 Bug Fix 82 | 83 | - Migrate examples to Storybook 7 [#20](https://github.com/storybookjs/addon-coverage/pull/20) ([@yannbf](https://github.com/yannbf)) 84 | 85 | #### Authors: 2 86 | 87 | - Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic)) 88 | - Yann Braga ([@yannbf](https://github.com/yannbf)) 89 | 90 | --- 91 | 92 | # v0.0.9 (Thu Jul 20 2023) 93 | 94 | #### ⚠️ Pushed to `main` 95 | 96 | - Create CODEOWNERS ([@valentinpalkovic](https://github.com/valentinpalkovic)) 97 | 98 | #### Authors: 1 99 | 100 | - Valentin Palkovic ([@valentinpalkovic](https://github.com/valentinpalkovic)) 101 | 102 | --- 103 | 104 | # v0.0.8 (Mon Feb 06 2023) 105 | 106 | #### 🐛 Bug Fix 107 | 108 | - Set `forceBuildInstrument` only in production builds [#16](https://github.com/storybookjs/addon-coverage/pull/16) ([@yannbf](https://github.com/yannbf)) 109 | 110 | #### Authors: 1 111 | 112 | - Yann Braga ([@yannbf](https://github.com/yannbf)) 113 | 114 | --- 115 | 116 | # v0.0.7 (Mon Nov 21 2022) 117 | 118 | #### 🐛 Bug Fix 119 | 120 | - Vite: generate code coverage in prod mode [#12](https://github.com/storybookjs/addon-coverage/pull/12) ([@yannbf](https://github.com/yannbf)) 121 | 122 | #### Authors: 1 123 | 124 | - Yann Braga ([@yannbf](https://github.com/yannbf)) 125 | 126 | --- 127 | 128 | # v0.0.6 (Mon Nov 21 2022) 129 | 130 | #### 🐛 Bug Fix 131 | 132 | - Typescript: Provide types for Babel and Vite istanbul options [#10](https://github.com/storybookjs/addon-coverage/pull/10) ([@bryanjtc](https://github.com/bryanjtc) [@yannbf](https://github.com/yannbf)) 133 | 134 | #### Authors: 2 135 | 136 | - Bryan Thomas ([@bryanjtc](https://github.com/bryanjtc)) 137 | - Yann Braga ([@yannbf](https://github.com/yannbf)) 138 | 139 | --- 140 | 141 | # v0.0.5 (Mon Oct 10 2022) 142 | 143 | ### Release Notes 144 | 145 | #### Add Vite support ([#7](https://github.com/storybookjs/addon-coverage/pull/7)) 146 | 147 | The addon now supports Vite projects out of the box! If you had a custom instrumentation because this addon didn't support Vite, you are now free to remove it and just register it in the addons section of `.storybook/main.js` 🎉 148 | 149 | --- 150 | 151 | #### 🐛 Bug Fix 152 | 153 | - Add Vite support [#7](https://github.com/storybookjs/addon-coverage/pull/7) ([@yannbf](https://github.com/yannbf)) 154 | - chore: move examples to a separate folder [#6](https://github.com/storybookjs/addon-coverage/pull/6) ([@yannbf](https://github.com/yannbf)) 155 | 156 | #### 📝 Documentation 157 | 158 | - Docs - fix cwd default value [#5](https://github.com/storybookjs/addon-coverage/pull/5) ([@penx](https://github.com/penx)) 159 | 160 | #### Authors: 2 161 | 162 | - Alasdair McLeay ([@penx](https://github.com/penx)) 163 | - Yann Braga ([@yannbf](https://github.com/yannbf)) 164 | 165 | --- 166 | 167 | # v0.0.4 (Mon Aug 15 2022) 168 | 169 | #### 🐛 Bug Fix 170 | 171 | - fix scenario where plugins do not exist [#4](https://github.com/storybookjs/addon-coverage/pull/4) ([@yannbf](https://github.com/yannbf)) 172 | 173 | #### Authors: 1 174 | 175 | - Yann Braga ([@yannbf](https://github.com/yannbf)) 176 | 177 | --- 178 | 179 | # v0.0.3 (Wed Aug 10 2022) 180 | 181 | #### 🐛 Bug Fix 182 | 183 | - Fix: update babel targets to support Storybook 7.0 [#3](https://github.com/storybookjs/addon-coverage/pull/3) ([@yannbf](https://github.com/yannbf)) 184 | 185 | #### Authors: 1 186 | 187 | - Yann Braga ([@yannbf](https://github.com/yannbf)) 188 | 189 | --- 190 | 191 | # v0.0.2 (Mon Jul 11 2022) 192 | 193 | #### 🐛 Bug Fix 194 | 195 | - fix: add .storybook to exclude list [#2](https://github.com/storybookjs/addon-coverage/pull/2) ([@yannbf](https://github.com/yannbf)) 196 | 197 | #### Authors: 1 198 | 199 | - Yann Braga ([@yannbf](https://github.com/yannbf)) 200 | 201 | --- 202 | 203 | # v0.0.1 (Tue Jun 21 2022) 204 | 205 | #### 🐛 Bug Fix 206 | 207 | - Add initial preset [#1](https://github.com/storybookjs/addon-coverage/pull/1) ([@yannbf](https://github.com/yannbf)) 208 | 209 | #### ⚠️ Pushed to `main` 210 | 211 | - Initial commit ([@yannbf](https://github.com/yannbf)) 212 | 213 | #### Authors: 1 214 | 215 | - Yann Braga ([@yannbf](https://github.com/yannbf)) 216 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @yannbf @kasperpeulen 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Storybook contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Storybook Addon Coverage 2 | 3 | Tools to support code coverage in Storybook and the [Storybook test runner](https://github.com/storybookjs/test-runner). It supports Storybook projects that use **Webpack5** or **Vite**. 4 | 5 | ### Installation 6 | 7 | Install this addon by adding the `@storybook/addon-coverage` dependency: 8 | 9 | ```sh 10 | yarn add -D @storybook/addon-coverage 11 | ``` 12 | 13 | And by registering it in your `.storybook/main.js`: 14 | 15 | ```js 16 | export default { 17 | addons: ["@storybook/addon-coverage"], 18 | }; 19 | ``` 20 | 21 | ### Configuring the addon 22 | 23 | This addon instruments your code by using a custom wrapper around [istanbul-lib-instrument](https://www.npmjs.com/package/istanbul-lib-instrument) if your project uses Webpack5 or [vite-plugin-istanbul](https://github.com/iFaxity/vite-plugin-istanbul) if your project uses Vite. It provides some default configuration, but if you want to add yours, you can do so by setting the options in your `.storybook/main.js`: 24 | 25 | ```js 26 | export default { 27 | addons: [ 28 | { 29 | name: "@storybook/addon-coverage", 30 | options: { 31 | istanbul: { 32 | include: ["**/stories/**"], 33 | }, 34 | }, 35 | }, 36 | ], 37 | }; 38 | ``` 39 | 40 | **The available options if your project uses Webpack5 are as follows:** 41 | 42 | | Option name | Description | Type | Default | 43 | | ---------------------- | -------------------------------------------------------------------------------------------------------------- | --------------- | -------------------------------------------------------------------------------------------- | 44 | | `cwd` | Set the working directory | `String` | `process.cwd()` | 45 | | `nycrcPath` | Path to specific nyc config to use instead of automatically searching for a nycconfig. | `string` | - | 46 | | `include` | Glob pattern to include files. It has precedence over the include definition from your nyc config | `Array` | - | 47 | | `exclude` | Glob pattern to exclude files. It has precedence over the exclude definition from your nyc config | `Array` | `defaultExclude` in https://github.com/storybookjs/addon-coverage/blob/main/src/constants.ts | 48 | | `extension` | List of supported extensions. It has precedence over the extension definition from your nyc config | `Array` | `['.js', '.cjs', '.mjs', '.ts', '.tsx', '.jsx', '.vue', '.svelte]` | 49 | | `coverageVariable` | The global variable name that Istanbul will use to store coverage results. | `string` | - | 50 | | `preserveComments` | Indicates whether comments in the code should be preserved during the instrumentation process. | `boolean` | `true` | 51 | | `compact` | Controls whether the output of instrumented code is compacted. Useful for debugging when set to `false`. | `boolean` | `false` | 52 | | `esModules` | Determines whether the code to be instrumented uses ES Module syntax. | `boolean` | `true` | 53 | | `autoWrap` | When set to `true`, wraps program code in a function to enable top-level return statements. | `boolean` | `true` | 54 | | `produceSourceMap` | If `true`, instructs Istanbul to produce a source map for the instrumented code. | `boolean` | `true` | 55 | | `sourceMapUrlCallback` | A callback function that gets invoked with the filename and the source map URL when a source map is generated. | `function` | - | 56 | | `debug` | Enables the debug mode, providing additional logging information during the instrumentation process. | `boolean` | - | 57 | 58 | > **Note:** 59 | > If you're using TypeScript, you can import the type for the options like so: 60 | > 61 | > ```ts 62 | > import type { AddonOptionsWebpack } from "@storybook/addon-coverage"; 63 | > ``` 64 | 65 | **The available options if your project uses Vite are as follows:** 66 | 67 | | Option name | Description | Type | Default | 68 | | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- | -------------------------------------------------------------------------------- | 69 | | `cwd` | Set the working directory | `String` | `process.cwd()` | 70 | | `include` | See [here](https://github.com/istanbuljs/nyc#selecting-files-for-coverage) for more info | `Array` or `string` | `['**']` | 71 | | `exclude` | See [here](https://github.com/istanbuljs/nyc#selecting-files-for-coverage) for more info | `Array` or `string` | [list](https://github.com/storybookjs/addon-coverage/blob/main/src/constants.ts) | 72 | | `extension` | List of extensions that nyc should attempt to handle in addition to `.js` | `Array` or `string` | `['.js', '.cjs', '.mjs', '.ts', '.tsx', '.jsx', '.vue', '.svelte]` | 73 | | `requireEnv ` | Optional boolean to require the environment variable (defaults to VITE_COVERAGE) to equal true in order to instrument the code. Otherwise it will instrument even if env variable is not set. However if requireEnv is not set the instrumentation will stop if the environment variable is equal to false. | `boolean` | `-` | 74 | | `cypress ` | Optional boolean to change the environment variable to CYPRESS_COVERAGE instead of VITE_COVERAGE. For ease of use with `@cypress/code-coverage` coverage | `boolean` | `-` | 75 | | `checkProd ` | Optional boolean to enforce the plugin to skip instrumentation for production environments. Looks at Vite's isProduction key from the ResolvedConfig. | `boolean` | `-` | 76 | | `forceBuildInstrument ` | Optional boolean to enforce the plugin to add instrumentation in build mode. | `boolean` | `false` | 77 | | `nycrcPath ` | Path to specific nyc config to use instead of automatically searching for a nycconfig. This parameter is just passed down to @istanbuljs/load-nyc-config. | `string` | `-` | 78 | 79 | > **Note:** 80 | > If you're using TypeScript, you can import the type for the options like so: 81 | > 82 | > ```ts 83 | > import type { AddonOptionsVite } from "@storybook/addon-coverage"; 84 | > ``` 85 | 86 | 87 | ## Troubleshooting 88 | 89 | ### The coverage addon doesn't support optimized builds 90 | 91 | The `--test` flag is designed to be as fast as possible, removing addons known to slow down the build and are not needed for functional testing. One of these addons is `@storybook/addon-coverage`, which is used in conjunction with the Storybook Test runner to collect coverage information for your stories. 92 | 93 | If you are using `addon-coverage` **AND** running the test runner against your _built_ Storybook, the `--test` flag will strip out the coverage information. To configure the `--test` build to keep coverage information (at the expense of a slightly slower build), update your Storybook configuration file (i.e.,`.storybook/main.js|ts`) and include the [`disabledAddons`](https://storybook.js.org/docs/api/main-config-build#testdisabledaddons) option. 94 | 95 | ```js 96 | // main.js 97 | 98 | export default { 99 | // Your Storybook configuration goes here 100 | build: { 101 | test: { 102 | disabledAddons: [ 103 | '@storybook/addon-docs', 104 | '@storybook/addon-essentials/docs', 105 | ], 106 | }, 107 | }, 108 | } 109 | ``` 110 | 111 | ### Development scripts 112 | 113 | - `yarn start` runs babel in watch mode 114 | - `yarn build` build and package your addon code 115 | 116 | To run the examples, choose one of the projects in the `examples` directory then run: 117 | 118 | - `yarn` to install dependencies and link the addon locally 119 | - `yarn storybook` to run Storybook 120 | - `yarn test-storybook --coverage` to test coverage report generation 121 | 122 | ### License 123 | 124 | [MIT](https://github.com/storybookjs/addon-coverage/blob/main/LICENSE) 125 | -------------------------------------------------------------------------------- /examples/vite/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | # Yarn berry 27 | /**/.yarn/* 28 | !/**/.yarn/releases 29 | !/**/.yarn/patches 30 | !/**/.yarn/plugins 31 | !/**/.yarn/sdks 32 | !/**/.yarn/versions 33 | /**/.pnp.* -------------------------------------------------------------------------------- /examples/vite/.storybook/main.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"], 3 | 4 | addons: [ 5 | "@storybook/addon-essentials", 6 | "@storybook/addon-interactions", 7 | "@storybook/addon-coverage", 8 | ], 9 | 10 | framework: { 11 | name: "@storybook/react-vite", 12 | options: {}, 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /examples/vite/.storybook/preview.ts: -------------------------------------------------------------------------------- 1 | export const decorators = [(StoryFn) => { 2 | console.log(globalThis.__coverage__) 3 | return StoryFn() 4 | }] 5 | 6 | export const parameters = { 7 | controls: { 8 | matchers: { 9 | color: /(background|color)$/i, 10 | date: /Date$/, 11 | }, 12 | }, 13 | } -------------------------------------------------------------------------------- /examples/vite/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/vite/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview", 10 | "storybook": "npx storybook dev -p 6006", 11 | "build-storybook": "npx storybook build", 12 | "test-storybook": "test-storybook --coverage", 13 | "test-storybook:ci-coverage": "concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"yarn build-storybook --quiet && npx serve storybook-static -l 6006\" \"wait-on tcp:6006 && yarn test-storybook --coverage\"" 14 | }, 15 | "dependencies": { 16 | "react": "^18.2.0", 17 | "react-dom": "^18.2.0" 18 | }, 19 | "devDependencies": { 20 | "@storybook/addon-coverage": "portal:../..", 21 | "@storybook/addon-essentials": "^8.4.7", 22 | "@storybook/addon-interactions": "^8.4.7", 23 | "@storybook/react": "^8.4.7", 24 | "@storybook/react-vite": "^8.4.7", 25 | "@storybook/test": "^8.4.7", 26 | "@storybook/test-runner": "^0.21.0", 27 | "@types/react": "^18.0.17", 28 | "@types/react-dom": "^18.0.6", 29 | "@vitejs/plugin-react": "^3.1.0", 30 | "concurrently": "^9.1.0", 31 | "storybook": "^8.4.7", 32 | "typescript": "^4.6.4", 33 | "vite": "^4.2.1", 34 | "wait-on": "^8.0.1" 35 | }, 36 | "packageManager": "yarn@4.5.3" 37 | } 38 | -------------------------------------------------------------------------------- /examples/vite/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/vite/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | } 13 | .logo:hover { 14 | filter: drop-shadow(0 0 2em #646cffaa); 15 | } 16 | .logo.react:hover { 17 | filter: drop-shadow(0 0 2em #61dafbaa); 18 | } 19 | 20 | @keyframes logo-spin { 21 | from { 22 | transform: rotate(0deg); 23 | } 24 | to { 25 | transform: rotate(360deg); 26 | } 27 | } 28 | 29 | @media (prefers-reduced-motion: no-preference) { 30 | a:nth-of-type(2) .logo { 31 | animation: logo-spin infinite 20s linear; 32 | } 33 | } 34 | 35 | .card { 36 | padding: 2em; 37 | } 38 | 39 | .read-the-docs { 40 | color: #888; 41 | } 42 | -------------------------------------------------------------------------------- /examples/vite/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import reactLogo from './assets/react.svg' 3 | import './App.css' 4 | 5 | function App() { 6 | const [count, setCount] = useState(0) 7 | 8 | return ( 9 |
10 | 18 |

Vite + React

19 |
20 | 23 |

24 | Edit src/App.tsx and save to test HMR 25 |

26 |
27 |

28 | Click on the Vite and React logos to learn more 29 |

30 |
31 | ) 32 | } 33 | 34 | export default App 35 | -------------------------------------------------------------------------------- /examples/vite/src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/vite/src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, Avenir, Helvetica, Arial, sans-serif; 3 | font-size: 16px; 4 | line-height: 24px; 5 | font-weight: 400; 6 | 7 | color-scheme: light dark; 8 | color: rgba(255, 255, 255, 0.87); 9 | background-color: #242424; 10 | 11 | font-synthesis: none; 12 | text-rendering: optimizeLegibility; 13 | -webkit-font-smoothing: antialiased; 14 | -moz-osx-font-smoothing: grayscale; 15 | -webkit-text-size-adjust: 100%; 16 | } 17 | 18 | a { 19 | font-weight: 500; 20 | color: #646cff; 21 | text-decoration: inherit; 22 | } 23 | a:hover { 24 | color: #535bf2; 25 | } 26 | 27 | body { 28 | margin: 0; 29 | display: flex; 30 | place-items: center; 31 | min-width: 320px; 32 | min-height: 100vh; 33 | } 34 | 35 | h1 { 36 | font-size: 3.2em; 37 | line-height: 1.1; 38 | } 39 | 40 | button { 41 | border-radius: 8px; 42 | border: 1px solid transparent; 43 | padding: 0.6em 1.2em; 44 | font-size: 1em; 45 | font-weight: 500; 46 | font-family: inherit; 47 | background-color: #1a1a1a; 48 | cursor: pointer; 49 | transition: border-color 0.25s; 50 | } 51 | button:hover { 52 | border-color: #646cff; 53 | } 54 | button:focus, 55 | button:focus-visible { 56 | outline: 4px auto -webkit-focus-ring-color; 57 | } 58 | 59 | @media (prefers-color-scheme: light) { 60 | :root { 61 | color: #213547; 62 | background-color: #ffffff; 63 | } 64 | a:hover { 65 | color: #747bff; 66 | } 67 | button { 68 | background-color: #f9f9f9; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /examples/vite/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 7 | 8 | 9 | 10 | ) 11 | -------------------------------------------------------------------------------- /examples/vite/src/stories/Button.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { StoryFn, Meta } from "@storybook/react"; 2 | 3 | import { Button } from "./Button"; 4 | 5 | // More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export 6 | export default { 7 | title: "Example/Button", 8 | component: Button, 9 | // More on argTypes: https://storybook.js.org/docs/react/api/argtypes 10 | argTypes: { 11 | backgroundColor: { control: "color" }, 12 | }, 13 | } as Meta; 14 | 15 | // More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args 16 | const Template: StoryFn = (args) => ( 17 | 47 | ); 48 | }; 49 | -------------------------------------------------------------------------------- /examples/vite/src/stories/Header.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { StoryFn, Meta } from "@storybook/react"; 2 | 3 | import { Header } from "./Header"; 4 | 5 | export default { 6 | title: "Example/Header", 7 | component: Header, 8 | parameters: { 9 | // More on Story layout: https://storybook.js.org/docs/react/configure/story-layout 10 | layout: "fullscreen", 11 | }, 12 | } as Meta; 13 | 14 | const Template: StoryFn = (args) => ( 15 |
16 | ); 17 | 18 | export const LoggedIn = Template.bind({}); 19 | LoggedIn.args = { 20 | user: { 21 | name: "Jane Doe", 22 | }, 23 | }; 24 | 25 | export const LoggedOut = Template.bind({}); 26 | LoggedOut.args = {}; 27 | -------------------------------------------------------------------------------- /examples/vite/src/stories/Header.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Button } from './Button'; 4 | import './header.css'; 5 | 6 | type User = { 7 | name: string; 8 | }; 9 | 10 | interface HeaderProps { 11 | user?: User; 12 | onLogin: () => void; 13 | onLogout: () => void; 14 | onCreateAccount: () => void; 15 | } 16 | 17 | export const Header = ({ user, onLogin, onLogout, onCreateAccount }: HeaderProps) => ( 18 |
19 |
20 |
21 | 22 | 23 | 27 | 31 | 35 | 36 | 37 |

Acme

38 |
39 |
40 | {user ? ( 41 | <> 42 | 43 | Welcome, {user.name}! 44 | 45 |
54 |
55 |
56 | ); 57 | -------------------------------------------------------------------------------- /examples/vite/src/stories/Page.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { StoryFn, Meta } from "@storybook/react"; 2 | import { within, userEvent } from "@storybook/test"; 3 | import { Page } from "./Page"; 4 | 5 | export default { 6 | title: "Example/Page", 7 | component: Page, 8 | parameters: { 9 | // More on Story layout: https://storybook.js.org/docs/react/configure/story-layout 10 | layout: "fullscreen", 11 | }, 12 | } as Meta; 13 | 14 | const Template: StoryFn = (args) => ; 15 | 16 | export const LoggedOut = Template.bind({}); 17 | 18 | export const LoggedIn = Template.bind({}); 19 | 20 | // More on interaction testing: https://storybook.js.org/docs/react/writing-tests/interaction-testing 21 | LoggedIn.play = async ({ canvasElement }) => { 22 | const canvas = within(canvasElement); 23 | const loginButton = await canvas.getByRole("button", { name: /Log in/i }); 24 | await userEvent.click(loginButton); 25 | }; 26 | -------------------------------------------------------------------------------- /examples/vite/src/stories/Page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Header } from './Header'; 4 | import './page.css'; 5 | 6 | type User = { 7 | name: string; 8 | }; 9 | 10 | export const Page: React.VFC = () => { 11 | const [user, setUser] = React.useState(); 12 | 13 | return ( 14 |
15 |
setUser({ name: 'Jane Doe' })} 18 | onLogout={() => setUser(undefined)} 19 | onCreateAccount={() => setUser({ name: 'Jane Doe' })} 20 | /> 21 | 22 |
23 |

Pages in Storybook

24 |

25 | We recommend building UIs with a{' '} 26 | 27 | component-driven 28 | {' '} 29 | process starting with atomic components and ending with pages. 30 |

31 |

32 | Render pages with mock data. This makes it easy to build and review page states without 33 | needing to navigate to them in your app. Here are some handy patterns for managing page 34 | data in Storybook: 35 |

36 |
    37 |
  • 38 | Use a higher-level connected component. Storybook helps you compose such data from the 39 | "args" of child component stories 40 |
  • 41 |
  • 42 | Assemble data in the page component from your services. You can mock these services out 43 | using Storybook. 44 |
  • 45 |
46 |

47 | Get a guided tutorial on component-driven development at{' '} 48 | 49 | Storybook tutorials 50 | 51 | . Read more in the{' '} 52 | 53 | docs 54 | 55 | . 56 |

57 |
58 | Tip Adjust the width of the canvas with the{' '} 59 | 60 | 61 | 66 | 67 | 68 | Viewports addon in the toolbar 69 |
70 |
71 |
72 | ); 73 | }; 74 | -------------------------------------------------------------------------------- /examples/vite/src/stories/assets/code-brackets.svg: -------------------------------------------------------------------------------- 1 | illustration/code-brackets -------------------------------------------------------------------------------- /examples/vite/src/stories/assets/colors.svg: -------------------------------------------------------------------------------- 1 | illustration/colors -------------------------------------------------------------------------------- /examples/vite/src/stories/assets/comments.svg: -------------------------------------------------------------------------------- 1 | illustration/comments -------------------------------------------------------------------------------- /examples/vite/src/stories/assets/direction.svg: -------------------------------------------------------------------------------- 1 | illustration/direction -------------------------------------------------------------------------------- /examples/vite/src/stories/assets/flow.svg: -------------------------------------------------------------------------------- 1 | illustration/flow -------------------------------------------------------------------------------- /examples/vite/src/stories/assets/plugin.svg: -------------------------------------------------------------------------------- 1 | illustration/plugin -------------------------------------------------------------------------------- /examples/vite/src/stories/assets/repo.svg: -------------------------------------------------------------------------------- 1 | illustration/repo -------------------------------------------------------------------------------- /examples/vite/src/stories/assets/stackalt.svg: -------------------------------------------------------------------------------- 1 | illustration/stackalt -------------------------------------------------------------------------------- /examples/vite/src/stories/button.css: -------------------------------------------------------------------------------- 1 | .storybook-button { 2 | font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | font-weight: 700; 4 | border: 0; 5 | border-radius: 3em; 6 | cursor: pointer; 7 | display: inline-block; 8 | line-height: 1; 9 | } 10 | .storybook-button--primary { 11 | color: white; 12 | background-color: #1ea7fd; 13 | } 14 | .storybook-button--secondary { 15 | color: #333; 16 | background-color: transparent; 17 | box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset; 18 | } 19 | .storybook-button--small { 20 | font-size: 12px; 21 | padding: 10px 16px; 22 | } 23 | .storybook-button--medium { 24 | font-size: 14px; 25 | padding: 11px 20px; 26 | } 27 | .storybook-button--large { 28 | font-size: 16px; 29 | padding: 12px 24px; 30 | } 31 | -------------------------------------------------------------------------------- /examples/vite/src/stories/header.css: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | border-bottom: 1px solid rgba(0, 0, 0, 0.1); 4 | padding: 15px 20px; 5 | display: flex; 6 | align-items: center; 7 | justify-content: space-between; 8 | } 9 | 10 | svg { 11 | display: inline-block; 12 | vertical-align: top; 13 | } 14 | 15 | h1 { 16 | font-weight: 900; 17 | font-size: 20px; 18 | line-height: 1; 19 | margin: 6px 0 6px 10px; 20 | display: inline-block; 21 | vertical-align: top; 22 | } 23 | 24 | button + button { 25 | margin-left: 10px; 26 | } 27 | 28 | .welcome { 29 | color: #333; 30 | font-size: 14px; 31 | margin-right: 10px; 32 | } 33 | -------------------------------------------------------------------------------- /examples/vite/src/stories/page.css: -------------------------------------------------------------------------------- 1 | section { 2 | font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | font-size: 14px; 4 | line-height: 24px; 5 | padding: 48px 20px; 6 | margin: 0 auto; 7 | max-width: 600px; 8 | color: #333; 9 | } 10 | 11 | section h2 { 12 | font-weight: 900; 13 | font-size: 32px; 14 | line-height: 1; 15 | margin: 0 0 4px; 16 | display: inline-block; 17 | vertical-align: top; 18 | } 19 | 20 | section p { 21 | margin: 1em 0; 22 | } 23 | 24 | section a { 25 | text-decoration: none; 26 | color: #1ea7fd; 27 | } 28 | 29 | section ul { 30 | padding-left: 30px; 31 | margin: 1em 0; 32 | } 33 | 34 | section li { 35 | margin-bottom: 8px; 36 | } 37 | 38 | section .tip { 39 | display: inline-block; 40 | border-radius: 1em; 41 | font-size: 11px; 42 | line-height: 12px; 43 | font-weight: 700; 44 | background: #e7fdd8; 45 | color: #66bf3c; 46 | padding: 4px 12px; 47 | margin-right: 10px; 48 | vertical-align: top; 49 | } 50 | 51 | section .tip-wrapper { 52 | font-size: 13px; 53 | line-height: 20px; 54 | margin-top: 40px; 55 | margin-bottom: 40px; 56 | } 57 | 58 | section .tip-wrapper svg { 59 | display: inline-block; 60 | height: 12px; 61 | width: 12px; 62 | margin-right: 4px; 63 | vertical-align: top; 64 | margin-top: 3px; 65 | } 66 | 67 | section .tip-wrapper svg path { 68 | fill: #1ea7fd; 69 | } 70 | -------------------------------------------------------------------------------- /examples/vite/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/vite/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "bundler", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /examples/vite/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /examples/vite/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/webpack5/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "targets": { 7 | "chrome": "100" 8 | } 9 | } 10 | ], 11 | [ 12 | "@babel/preset-react", 13 | { 14 | "runtime": "automatic" 15 | } 16 | ], 17 | "@babel/preset-typescript" 18 | ] 19 | } -------------------------------------------------------------------------------- /examples/webpack5/.nycrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["**/*.tsx"] 3 | } 4 | -------------------------------------------------------------------------------- /examples/webpack5/.storybook/main.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | stories: ["../stories/**/*.mdx", "../stories/**/*.stories.@(js|jsx|ts|tsx)"], 3 | 4 | addons: [ 5 | "@storybook/addon-essentials", 6 | "@storybook/addon-interactions", 7 | "@storybook/addon-coverage", 8 | "@storybook/addon-webpack5-compiler-babel" 9 | ], 10 | 11 | framework: { 12 | name: "@storybook/react-webpack5", 13 | options: {}, 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /examples/webpack5/.storybook/preview.ts: -------------------------------------------------------------------------------- 1 | export const decorators = [(StoryFn) => { 2 | console.log(globalThis.__coverage__) 3 | return StoryFn() 4 | }] 5 | 6 | export const parameters = { 7 | controls: { 8 | matchers: { 9 | color: /(background|color)$/i, 10 | date: /Date$/, 11 | }, 12 | }, 13 | } -------------------------------------------------------------------------------- /examples/webpack5/README.md: -------------------------------------------------------------------------------- 1 | # react-webpack 2 | 3 | Please do not use this for actual projects. 4 | We wrote this to test storybook with. 5 | 6 | Storybook is a OSS tool that bootstraps on top of existing projects, like CRA, NextJS etc. 7 | But it also can be initialized on top of custom projects. 8 | That is what this is to simulate. 9 | 10 | If you have any questions, please reach out to us on Discord. -------------------------------------------------------------------------------- /examples/webpack5/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-example", 3 | "version": "1.0.0", 4 | "description": "", 5 | "private": true, 6 | "author": "", 7 | "scripts": { 8 | "dev": "webpack serve --open", 9 | "build": "webpack build", 10 | "storybook": "npx storybook dev -p 6006", 11 | "build-storybook": "npx storybook build", 12 | "test-storybook": "test-storybook --coverage", 13 | "test-storybook:ci-coverage": "concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"yarn build-storybook --quiet && npx serve storybook-static -l 6006\" \"wait-on tcp:6006 && yarn test-storybook --coverage\"" 14 | }, 15 | "dependencies": { 16 | "@babel/core": "latest", 17 | "@babel/preset-typescript": "latest", 18 | "@types/react": "^18", 19 | "@types/react-dom": "^18", 20 | "babel-loader": "latest", 21 | "babel-preset-react-app": "latest", 22 | "html-webpack-plugin": "latest", 23 | "react": "^18", 24 | "react-dom": "^18", 25 | "typescript": "^4.8", 26 | "webpack": "^5", 27 | "webpack-cli": "latest", 28 | "webpack-dev-server": "latest" 29 | }, 30 | "devDependencies": { 31 | "@storybook/addon-coverage": "portal:../..", 32 | "@storybook/addon-essentials": "^8.4.7", 33 | "@storybook/addon-interactions": "^8.4.7", 34 | "@storybook/addon-webpack5-compiler-babel": "^3.0.3", 35 | "@storybook/react": "^8.4.7", 36 | "@storybook/react-webpack5": "^8.4.7", 37 | "@storybook/test": "^8.4.7", 38 | "@storybook/test-runner": "^0.21.0", 39 | "concurrently": "^9.1.0", 40 | "storybook": "^8.4.7", 41 | "wait-on": "^8.0.1" 42 | }, 43 | "packageManager": "yarn@4.5.3" 44 | } 45 | -------------------------------------------------------------------------------- /examples/webpack5/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { StrictMode } from 'react'; 2 | import * as ReactDOMClient from 'react-dom/client'; 3 | 4 | const root = ReactDOMClient.createRoot(document.body); 5 | 6 | function App() { 7 | return ( 8 |
9 |

Hello

10 |
11 | ); 12 | } 13 | 14 | root.render( 15 | 16 | 17 | 18 | ); 19 | -------------------------------------------------------------------------------- /examples/webpack5/stories/Button.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import type { StoryFn, Meta } from '@storybook/react'; 3 | 4 | import { Button } from './Button'; 5 | 6 | // More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export 7 | export default { 8 | title: 'Example/Button', 9 | component: Button, 10 | // More on argTypes: https://storybook.js.org/docs/react/api/argtypes 11 | argTypes: { 12 | backgroundColor: { control: 'color' }, 13 | }, 14 | } as Meta; 15 | 16 | // More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args 17 | const Template: StoryFn = (args) => 47 | ); 48 | }; 49 | -------------------------------------------------------------------------------- /examples/webpack5/stories/Header.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import type { StoryFn, Meta } from '@storybook/react'; 3 | 4 | import { Header } from './Header'; 5 | 6 | export default { 7 | title: 'Example/Header', 8 | component: Header, 9 | parameters: { 10 | // More on Story layout: https://storybook.js.org/docs/react/configure/story-layout 11 | layout: 'fullscreen', 12 | }, 13 | } as Meta; 14 | 15 | const Template: StoryFn = (args) =>
; 16 | 17 | export const LoggedIn = Template.bind({}); 18 | LoggedIn.args = { 19 | user: { 20 | name: 'Jane Doe', 21 | }, 22 | }; 23 | 24 | export const LoggedOut = Template.bind({}); 25 | LoggedOut.args = {}; 26 | -------------------------------------------------------------------------------- /examples/webpack5/stories/Header.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Button } from './Button'; 4 | import './header.css'; 5 | 6 | type User = { 7 | name: string; 8 | }; 9 | 10 | interface HeaderProps { 11 | user?: User; 12 | onLogin: () => void; 13 | onLogout: () => void; 14 | onCreateAccount: () => void; 15 | } 16 | 17 | export const Header = ({ user, onLogin, onLogout, onCreateAccount }: HeaderProps) => ( 18 |
19 |
20 |
21 | 22 | 23 | 27 | 31 | 35 | 36 | 37 |

Acme

38 |
39 |
40 | {user ? ( 41 | <> 42 | 43 | Welcome, {user.name}! 44 | 45 |
54 |
55 |
56 | ); 57 | -------------------------------------------------------------------------------- /examples/webpack5/stories/Page.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import type { StoryFn, Meta } from '@storybook/react'; 3 | import { within, userEvent } from '@storybook/test'; 4 | import { Page } from './Page'; 5 | 6 | export default { 7 | title: 'Example/Page', 8 | component: Page, 9 | parameters: { 10 | // More on Story layout: https://storybook.js.org/docs/react/configure/story-layout 11 | layout: 'fullscreen', 12 | }, 13 | } as Meta; 14 | 15 | const Template: StoryFn = (args) => ; 16 | 17 | export const LoggedOut = Template.bind({}); 18 | 19 | export const LoggedIn = Template.bind({}); 20 | 21 | // More on interaction testing: https://storybook.js.org/docs/react/writing-tests/interaction-testing 22 | LoggedIn.play = async ({ canvasElement }) => { 23 | const canvas = within(canvasElement); 24 | const loginButton = await canvas.getByRole('button', { name: /Log in/i }); 25 | await userEvent.click(loginButton); 26 | }; 27 | -------------------------------------------------------------------------------- /examples/webpack5/stories/Page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Header } from './Header'; 4 | import './page.css'; 5 | 6 | type User = { 7 | name: string; 8 | }; 9 | 10 | export const Page: React.VFC = () => { 11 | const [user, setUser] = React.useState(); 12 | 13 | return ( 14 |
15 |
setUser({ name: 'Jane Doe' })} 18 | onLogout={() => setUser(undefined)} 19 | onCreateAccount={() => setUser({ name: 'Jane Doe' })} 20 | /> 21 | 22 |
23 |

Pages in Storybook

24 |

25 | We recommend building UIs with a{' '} 26 | 27 | component-driven 28 | {' '} 29 | process starting with atomic components and ending with pages. 30 |

31 |

32 | Render pages with mock data. This makes it easy to build and review page states without 33 | needing to navigate to them in your app. Here are some handy patterns for managing page 34 | data in Storybook: 35 |

36 |
    37 |
  • 38 | Use a higher-level connected component. Storybook helps you compose such data from the 39 | "args" of child component stories 40 |
  • 41 |
  • 42 | Assemble data in the page component from your services. You can mock these services out 43 | using Storybook. 44 |
  • 45 |
46 |

47 | Get a guided tutorial on component-driven development at{' '} 48 | 49 | Storybook tutorials 50 | 51 | . Read more in the{' '} 52 | 53 | docs 54 | 55 | . 56 |

57 |
58 | Tip Adjust the width of the canvas with the{' '} 59 | 60 | 61 | 66 | 67 | 68 | Viewports addon in the toolbar 69 |
70 |
71 |
72 | ); 73 | }; 74 | -------------------------------------------------------------------------------- /examples/webpack5/stories/assets/code-brackets.svg: -------------------------------------------------------------------------------- 1 | illustration/code-brackets -------------------------------------------------------------------------------- /examples/webpack5/stories/assets/colors.svg: -------------------------------------------------------------------------------- 1 | illustration/colors -------------------------------------------------------------------------------- /examples/webpack5/stories/assets/comments.svg: -------------------------------------------------------------------------------- 1 | illustration/comments -------------------------------------------------------------------------------- /examples/webpack5/stories/assets/direction.svg: -------------------------------------------------------------------------------- 1 | illustration/direction -------------------------------------------------------------------------------- /examples/webpack5/stories/assets/flow.svg: -------------------------------------------------------------------------------- 1 | illustration/flow -------------------------------------------------------------------------------- /examples/webpack5/stories/assets/plugin.svg: -------------------------------------------------------------------------------- 1 | illustration/plugin -------------------------------------------------------------------------------- /examples/webpack5/stories/assets/repo.svg: -------------------------------------------------------------------------------- 1 | illustration/repo -------------------------------------------------------------------------------- /examples/webpack5/stories/assets/stackalt.svg: -------------------------------------------------------------------------------- 1 | illustration/stackalt -------------------------------------------------------------------------------- /examples/webpack5/stories/button.css: -------------------------------------------------------------------------------- 1 | .storybook-button { 2 | font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | font-weight: 700; 4 | border: 0; 5 | border-radius: 3em; 6 | cursor: pointer; 7 | display: inline-block; 8 | line-height: 1; 9 | } 10 | .storybook-button--primary { 11 | color: white; 12 | background-color: #1ea7fd; 13 | } 14 | .storybook-button--secondary { 15 | color: #333; 16 | background-color: transparent; 17 | box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset; 18 | } 19 | .storybook-button--small { 20 | font-size: 12px; 21 | padding: 10px 16px; 22 | } 23 | .storybook-button--medium { 24 | font-size: 14px; 25 | padding: 11px 20px; 26 | } 27 | .storybook-button--large { 28 | font-size: 16px; 29 | padding: 12px 24px; 30 | } 31 | -------------------------------------------------------------------------------- /examples/webpack5/stories/header.css: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | border-bottom: 1px solid rgba(0, 0, 0, 0.1); 4 | padding: 15px 20px; 5 | display: flex; 6 | align-items: center; 7 | justify-content: space-between; 8 | } 9 | 10 | svg { 11 | display: inline-block; 12 | vertical-align: top; 13 | } 14 | 15 | h1 { 16 | font-weight: 900; 17 | font-size: 20px; 18 | line-height: 1; 19 | margin: 6px 0 6px 10px; 20 | display: inline-block; 21 | vertical-align: top; 22 | } 23 | 24 | button + button { 25 | margin-left: 10px; 26 | } 27 | 28 | .welcome { 29 | color: #333; 30 | font-size: 14px; 31 | margin-right: 10px; 32 | } 33 | -------------------------------------------------------------------------------- /examples/webpack5/stories/page.css: -------------------------------------------------------------------------------- 1 | section { 2 | font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | font-size: 14px; 4 | line-height: 24px; 5 | padding: 48px 20px; 6 | margin: 0 auto; 7 | max-width: 600px; 8 | color: #333; 9 | } 10 | 11 | section h2 { 12 | font-weight: 900; 13 | font-size: 32px; 14 | line-height: 1; 15 | margin: 0 0 4px; 16 | display: inline-block; 17 | vertical-align: top; 18 | } 19 | 20 | section p { 21 | margin: 1em 0; 22 | } 23 | 24 | section a { 25 | text-decoration: none; 26 | color: #1ea7fd; 27 | } 28 | 29 | section ul { 30 | padding-left: 30px; 31 | margin: 1em 0; 32 | } 33 | 34 | section li { 35 | margin-bottom: 8px; 36 | } 37 | 38 | section .tip { 39 | display: inline-block; 40 | border-radius: 1em; 41 | font-size: 11px; 42 | line-height: 12px; 43 | font-weight: 700; 44 | background: #e7fdd8; 45 | color: #66bf3c; 46 | padding: 4px 12px; 47 | margin-right: 10px; 48 | vertical-align: top; 49 | } 50 | 51 | section .tip-wrapper { 52 | font-size: 13px; 53 | line-height: 20px; 54 | margin-top: 40px; 55 | margin-bottom: 40px; 56 | } 57 | 58 | section .tip-wrapper svg { 59 | display: inline-block; 60 | height: 12px; 61 | width: 12px; 62 | margin-right: 4px; 63 | vertical-align: top; 64 | margin-top: 3px; 65 | } 66 | 67 | section .tip-wrapper svg path { 68 | fill: #1ea7fd; 69 | } 70 | -------------------------------------------------------------------------------- /examples/webpack5/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src/*", "../stories"], 3 | "compilerOptions": { 4 | "target": "es5", 5 | "jsx": "react", 6 | "allowSyntheticDefaultImports": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/webpack5/webpack.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 2 | 3 | process.env.NODE_ENV = 'development'; 4 | const host = process.env.HOST || 'localhost'; 5 | 6 | module.exports = { 7 | mode: 'development', 8 | devtool: 'inline-source-map', 9 | entry: './src/index.tsx', 10 | output: { 11 | filename: 'static/js/bundle.js', 12 | }, 13 | devServer: { 14 | compress: true, 15 | hot: true, 16 | host, 17 | port: 3000, 18 | }, 19 | plugins: [ 20 | new HtmlWebpackPlugin(), 21 | ], 22 | module: { 23 | rules: [ 24 | { 25 | test: /\.m?[jt]sx$/, 26 | exclude: /node_modules/, 27 | use: { 28 | loader: 'babel-loader', 29 | }, 30 | }, 31 | ], 32 | }, 33 | resolve: { 34 | extensions: ['.mjs', '.js', '.cjs', '.jsx', '.tsx', '.ts'], 35 | modules: ['node_modules'], 36 | }, 37 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@storybook/addon-coverage", 3 | "version": "1.0.5", 4 | "description": "Tools to support code coverage in Storybook", 5 | "keywords": [ 6 | "storybook-addons", 7 | "coverage", 8 | "test", 9 | "testing", 10 | "test-runner", 11 | "storybook-addons" 12 | ], 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/storybookjs/addon-coverage" 16 | }, 17 | "author": "Yann Braga ", 18 | "license": "MIT", 19 | "main": "dist/cjs/index.js", 20 | "module": "dist/esm/index.js", 21 | "types": "dist/ts/types.d.ts", 22 | "files": [ 23 | "dist/**/*", 24 | "README.md", 25 | "*.js", 26 | "*.d.ts" 27 | ], 28 | "scripts": { 29 | "clean": "rimraf ./dist", 30 | "buildBabel": "concurrently \"yarn buildBabel:cjs\" \"yarn buildBabel:esm\"", 31 | "buildBabel:cjs": "babel ./src -d ./dist/cjs --extensions \".js,.jsx,.ts,.tsx\"", 32 | "buildBabel:esm": "babel ./src -d ./dist/esm --env-name esm --extensions \".js,.jsx,.ts,.tsx\"", 33 | "buildTsc": "tsc --declaration --emitDeclarationOnly --outDir ./dist/ts", 34 | "prebuild": "yarn clean", 35 | "build": "concurrently \"yarn buildBabel\" \"yarn buildTsc\"", 36 | "build:watch": "concurrently \"yarn buildBabel:cjs -- --watch\" \"yarn buildTsc -- --watch\"", 37 | "test": "echo \"Error: no test specified\" && exit 1", 38 | "start": "yarn build:watch", 39 | "release": "yarn build && auto shipit" 40 | }, 41 | "devDependencies": { 42 | "@babel/cli": "^7.12.1", 43 | "@babel/core": "^7.12.3", 44 | "@babel/preset-env": "^7.12.1", 45 | "@babel/preset-react": "^7.12.5", 46 | "@babel/preset-typescript": "^7.13.0", 47 | "@storybook/core-common": "^7.0.0-alpha.34", 48 | "@types/convert-source-map": "^2.0.3", 49 | "@types/istanbul-lib-instrument": "^1.7.7", 50 | "@types/test-exclude": "^6.0.2", 51 | "auto": "^11.1.1", 52 | "concurrently": "^6.2.0", 53 | "prettier": "^2.3.1", 54 | "prop-types": "^15.7.2", 55 | "react": "^17.0.1", 56 | "react-dom": "^17.0.1", 57 | "rimraf": "^3.0.2", 58 | "typescript": "^4.2.4", 59 | "vite": "^4.1.0", 60 | "webpack": "^5.89.0" 61 | }, 62 | "publishConfig": { 63 | "access": "public" 64 | }, 65 | "storybook": { 66 | "displayName": "Addon coverage", 67 | "supportedFrameworks": [ 68 | "react", 69 | "preact", 70 | "vue", 71 | "web-components", 72 | "html", 73 | "svelte" 74 | ], 75 | "icon": "https://user-images.githubusercontent.com/321738/63501763-88dbf600-c4cc-11e9-96cd-94adadc2fd72.png" 76 | }, 77 | "dependencies": { 78 | "@istanbuljs/load-nyc-config": "^1.1.0", 79 | "@jsdevtools/coverage-istanbul-loader": "^3.0.5", 80 | "@types/istanbul-lib-coverage": "^2.0.4", 81 | "convert-source-map": "^2.0.0", 82 | "espree": "^9.6.1", 83 | "istanbul-lib-instrument": "^6.0.1", 84 | "test-exclude": "^6.0.0", 85 | "vite-plugin-istanbul": "^3.0.1" 86 | }, 87 | "packageManager": "yarn@4.5.3" 88 | } 89 | -------------------------------------------------------------------------------- /preset.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist/cjs/preset') -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | const commonExtensions = [".js", ".cjs", ".mjs", ".ts", ".cts", ".mts"]; 2 | export const defaultExtensions = [ 3 | ...commonExtensions, 4 | ".tsx", 5 | ".jsx", 6 | ".vue", 7 | ".svelte", 8 | ]; 9 | const testFileExtensions = defaultExtensions 10 | .map((extension) => extension.slice(1)) 11 | .join(","); 12 | 13 | const configFileExtensions = commonExtensions 14 | .map((extension) => extension.slice(1)) 15 | .join(","); 16 | 17 | export const defaultExclude = [ 18 | "**/node_modules/**", 19 | ".storybook/**", 20 | "coverage/**", 21 | "packages/*/test{,s}/**", 22 | "**/*.d.ts", 23 | "**/*.mock.*", 24 | "test{,s}/**", 25 | `test{,-*}.{${testFileExtensions}}`, 26 | `**/*{.,-}{spec,story,stories,types}.{${testFileExtensions}}`, 27 | "**/__tests__/**", 28 | "**/*-entry.js", 29 | 30 | /* Exclude common development tool configuration files */ 31 | `**/{ava,babel,nyc}.config.{${configFileExtensions}}`, 32 | `**/{jest,vitest}.config.{${configFileExtensions}}`, 33 | `**/{karma,rollup,webpack,vite}.config.{${configFileExtensions}}`, 34 | `**/.{eslint,mocha}rc.{${configFileExtensions}}`, 35 | ]; 36 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export default {}; 2 | -------------------------------------------------------------------------------- /src/loader/webpack5-istanbul-loader.ts: -------------------------------------------------------------------------------- 1 | import { Instrumenter, InstrumenterOptions } from "istanbul-lib-instrument"; 2 | import { fromSource, fromMapFileSource } from "convert-source-map"; 3 | 4 | // @ts-expect-error no types 5 | import * as espree from "espree"; 6 | import fs from "fs"; 7 | import path from "path"; 8 | import { LoaderContext } from "webpack"; 9 | 10 | import { AddonOptionsWebpack } from "../types"; 11 | 12 | export type Options = Partial & 13 | AddonOptionsWebpack & { 14 | instrumenter: Instrumenter; 15 | }; 16 | 17 | type RawSourceMap = { 18 | version: number; 19 | sources: string[]; 20 | mappings: string; 21 | file?: string; 22 | sourceRoot?: string; 23 | sourcesContent?: string[]; 24 | names?: string[]; 25 | }; 26 | 27 | function sanitizeSourceMap(rawSourceMap: RawSourceMap | string): RawSourceMap { 28 | return typeof rawSourceMap === "string" ? JSON.parse(rawSourceMap) : rawSourceMap; 29 | } 30 | 31 | export default function ( 32 | this: LoaderContext, 33 | source: string, 34 | sourceMap?: RawSourceMap 35 | ) { 36 | let map = sourceMap 37 | ? sanitizeSourceMap(sourceMap) 38 | : getInlineSourceMap.call(this, source); 39 | const options = this.getOptions(); 40 | const callback = this.async(); 41 | 42 | if (!map) { 43 | callback(null, source, sourceMap); 44 | return; 45 | } 46 | 47 | // Instrument the code 48 | const instrumenter = options.instrumenter; 49 | 50 | const code = instrumenter.instrumentSync(source, this.resourcePath, map); 51 | 52 | const lastSourceMap = instrumenter.lastSourceMap(); 53 | 54 | callback(null, code, lastSourceMap as any); 55 | } 56 | 57 | /** 58 | * If the source code has an inline base64-encoded source map, 59 | * then this function decodes it, parses it, and returns it. 60 | */ 61 | function getInlineSourceMap( 62 | this: LoaderContext, 63 | source: string 64 | ): RawSourceMap | undefined { 65 | try { 66 | // Check for an inline source map 67 | const inlineSourceMap = 68 | fromSource(source) || 69 | fromMapFileSource(source, function (filename) { 70 | return fs.readFileSync( 71 | path.resolve(path.dirname(this.resourcePath), filename), 72 | "utf-8" 73 | ); 74 | }); 75 | 76 | if (inlineSourceMap) { 77 | // Use the inline source map 78 | return inlineSourceMap.sourcemap as RawSourceMap; 79 | } 80 | } catch (e) { 81 | // Exception is thrown by fromMapFileSource when there is no source map file 82 | if ( 83 | e instanceof Error && 84 | e.message.includes( 85 | "An error occurred while trying to read the map file at" 86 | ) 87 | ) { 88 | this.emitWarning(e); 89 | } else { 90 | throw e; 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/nyc-config.ts: -------------------------------------------------------------------------------- 1 | import { AddonOptionsWebpack } from "./types"; 2 | // @ts-expect-error no types 3 | import { loadNycConfig } from "@istanbuljs/load-nyc-config"; 4 | 5 | export async function getNycConfig( 6 | opts: Pick = {} 7 | ) { 8 | const cwd = opts.cwd ?? process.cwd(); 9 | 10 | return loadNycConfig({ 11 | cwd, 12 | nycrcPath: opts.nycrcPath, 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /src/preset.ts: -------------------------------------------------------------------------------- 1 | import type { Options } from "@storybook/core-common"; 2 | import { defaultExclude, defaultExtensions } from "./constants"; 3 | import type { AddonOptionsVite, AddonOptionsWebpack } from "./types"; 4 | import { createTestExclude } from "./webpack5-exclude"; 5 | import { getNycConfig } from "./nyc-config"; 6 | import { 7 | InstrumenterOptions, 8 | createInstrumenter, 9 | } from "istanbul-lib-instrument"; 10 | 11 | export const viteFinal = async ( 12 | viteConfig: Record, 13 | options: Options & AddonOptionsVite 14 | ) => { 15 | const istanbul = require("vite-plugin-istanbul"); 16 | 17 | console.log("[addon-coverage] Adding istanbul plugin to Vite config"); 18 | viteConfig.build = viteConfig.build || {}; 19 | viteConfig.build.sourcemap = true; 20 | 21 | viteConfig.plugins ||= []; 22 | viteConfig.plugins.push( 23 | istanbul({ 24 | forceBuildInstrument: options.configType === "PRODUCTION", 25 | ...options.istanbul, 26 | include: Array.from(options.istanbul?.include || []), 27 | exclude: [ 28 | options.configDir + "/**", 29 | ...defaultExclude, 30 | ...Array.from(options.istanbul?.exclude || []), 31 | ], 32 | extension: options.istanbul?.extension || defaultExtensions, 33 | }) 34 | ); 35 | 36 | return viteConfig; 37 | }; 38 | 39 | const defaultOptions: Partial = { 40 | preserveComments: true, 41 | produceSourceMap: true, 42 | autoWrap: true, 43 | esModules: true, 44 | compact: false, 45 | }; 46 | 47 | export const webpackFinal = async ( 48 | webpackConfig: Record, 49 | options: Options & AddonOptionsWebpack 50 | ) => { 51 | webpackConfig.module.rules ||= []; 52 | const nycConfig = await getNycConfig(options.istanbul); 53 | const extensions = 54 | options.istanbul?.extension ?? nycConfig.extension ?? defaultExtensions; 55 | 56 | console.log("[addon-coverage] Adding istanbul loader to Webpack config"); 57 | 58 | const testExclude = await createTestExclude(options.istanbul); 59 | 60 | let instrumenterOptions = Object.assign(defaultOptions, options.istanbul); 61 | let instrumenter = createInstrumenter(instrumenterOptions); 62 | 63 | webpackConfig.module.rules.unshift({ 64 | test: new RegExp(extensions?.join("|").replace(/\./g, "\\.")), 65 | loader: require.resolve("./loader/webpack5-istanbul-loader"), 66 | enforce: "post", 67 | options: { 68 | ...(options.istanbul ?? {}), 69 | instrumenter, 70 | }, 71 | include: (modulePath: string) => testExclude.shouldInstrument(modulePath), 72 | }); 73 | 74 | return webpackConfig; 75 | }; 76 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import type { IstanbulPluginOptions as IstanbulOptionsVite } from "vite-plugin-istanbul"; 2 | import type { FileCoverage } from "istanbul-lib-coverage"; 3 | 4 | interface IstanbulOptionsBabel { 5 | cwd?: string; 6 | include?: string[]; 7 | exclude?: string[]; 8 | extension?: string[]; 9 | excludeNodeModules?: boolean; 10 | ignoreClassMethods?: string[]; 11 | useInlineSourceMaps?: boolean; 12 | inputSourceMap?: object; 13 | nycrcPath?: string; 14 | onCover?: (fileName: string, fileCoverage: FileCoverage) => unknown; 15 | fileName?: string; 16 | } 17 | 18 | export interface AddonOptionsBabel { 19 | istanbul?: IstanbulOptionsBabel; 20 | } 21 | 22 | export interface AddonOptionsVite { 23 | istanbul?: IstanbulOptionsVite; 24 | } 25 | 26 | export type AddonOptionsWebpack = { 27 | istanbul?: { 28 | cwd?: string; 29 | nycrcPath?: string; 30 | include?: string[]; 31 | exclude?: string[]; 32 | extension?: string[]; 33 | }; 34 | }; 35 | -------------------------------------------------------------------------------- /src/webpack5-exclude.ts: -------------------------------------------------------------------------------- 1 | import TestExclude from "test-exclude"; 2 | import { defaultExclude, defaultExtensions } from "./constants"; 3 | import { AddonOptionsWebpack } from "./types"; 4 | import { getNycConfig } from "./nyc-config"; 5 | 6 | export async function createTestExclude( 7 | opts: AddonOptionsWebpack["istanbul"] = {} 8 | ): Promise<{ shouldInstrument(filename: string): boolean }> { 9 | const { nycrcPath, include, exclude, extension } = opts; 10 | const cwd = opts.cwd ?? process.cwd(); 11 | 12 | const nycConfig = await getNycConfig({ 13 | cwd, 14 | nycrcPath, 15 | }); 16 | 17 | return new TestExclude({ 18 | cwd, 19 | include: include ?? nycConfig.include, 20 | exclude: exclude ?? nycConfig.exclude ?? defaultExclude, 21 | extension: extension ?? nycConfig.extension ?? defaultExtensions, 22 | excludeNodeModules: true, 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "baseUrl": ".", 5 | "emitDecoratorMetadata": true, 6 | "esModuleInterop": true, 7 | "experimentalDecorators": true, 8 | "incremental": false, 9 | "isolatedModules": true, 10 | "jsx": "react", 11 | "lib": ["es2017", "dom"], 12 | "module": "commonjs", 13 | "noImplicitAny": true, 14 | "rootDir": "./src", 15 | "skipLibCheck": true, 16 | "target": "es5" 17 | }, 18 | "include": [ 19 | "src/**/*" 20 | ], 21 | } --------------------------------------------------------------------------------