Merge pull request #18046 from storybookjs/tech/telemetry-pr

Core: Add optional telemetry and crash reporting
This commit is contained in:
Michael Shilman 2022-05-02 22:20:57 +08:00 committed by GitHub
commit 612b03d75c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
95 changed files with 3051 additions and 365 deletions

View File

@ -15,6 +15,7 @@ function __setMockFiles(newMockFiles) {
const readFile = async (filePath) => mockFiles[filePath];
const readFileSync = (filePath = '') => mockFiles[filePath];
const existsSync = (filePath) => !!mockFiles[filePath];
const readJsonSync = (filePath = '') => JSON.parse(mockFiles[filePath]);
const lstatSync = (filePath) => ({
isFile: () => !!mockFiles[filePath],
});
@ -23,6 +24,7 @@ const lstatSync = (filePath) => ({
fs.__setMockFiles = __setMockFiles;
fs.readFile = readFile;
fs.readFileSync = readFileSync;
fs.readJsonSync = readJsonSync;
fs.existsSync = existsSync;
fs.lstatSync = lstatSync;

View File

@ -21,11 +21,11 @@ export const buildStandaloneErrorHandler = (error: any): any => {
logger.line();
return error.close
? dedent`
FATAL broken build!, will close the process,
Fix the error below and restart storybook.
`
FATAL broken build!, will close the process,
Fix the error below and restart storybook.
`
: dedent`
Broken build, fix the error above.
You may need to refresh the browser.
`;
Broken build, fix the error above.
You may need to refresh the browser.
`;
};

View File

@ -18,10 +18,10 @@ Usage: start-storybook [options]
| `-V`, `--version` | Output the version number <br/>`start-storybook -V` |
| `-p`, `--port [number]` | Port to run Storybook <br/>`start-storybook -p 9009` |
| `-h`, `--host [string]` | Host to run Storybook <br/>`start-storybook -h my-host.com` |
| `-s`, `--static-dir` | **Deprecated** [see note](#static-dir-deprecation). Directory where to load static files from, comma-separated list <br/>`start-storybook -s public` |
| `-s`, `--static-dir` | **Deprecated** [see note](#static-dir-deprecation). Directory where to load static files from, comma-separated list<br/>`start-storybook -s public` |
| `-c`, `--config-dir [dir-name]` | Directory where to load Storybook configurations from <br/>`start-storybook -c .storybook` |
| `--https` | Serve Storybook over HTTPS. Note: You must provide your own certificate information. <br/>`start-storybook --https` |
| `--ssl-ca` | Provide an SSL certificate authority. (Optional with --https, required if using a self-signed certificate) <br/>`start-storybook --ssl-ca my-certificate` |
| `--https` | Serve Storybook over HTTPS. Note: You must provide your own certificate information<br/>`start-storybook --https` |
| `--ssl-ca` | Provide an SSL certificate authority. (Optional with --https, required if using a self-signed certificate)<br/>`start-storybook --ssl-ca my-certificate` |
| `--ssl-cert` | Provide an SSL certificate. (Required with --https)<br/>`start-storybook --ssl-cert my-ssl-certificate` |
| `--ssl-key` | Provide an SSL key. (Required with --https)<br/>`start-storybook --ssl-key my-ssl-key` |
| `--smoke-test` | Exit after successful start<br/>`start-storybook --smoke-test` |
@ -32,7 +32,8 @@ Usage: start-storybook [options]
| `--debug-webpack` | Display final webpack configurations for debugging purposes<br/>`start-storybook --debug-webpack` |
| `--webpack-stats-json` | Write Webpack Stats JSON to disk<br/>`start-storybook --webpack-stats-json /tmp/webpack-stats` |
| `--docs` | Starts Storybook in documentation mode. Learn more about it in [here](../writing-docs/build-documentation.md#preview-storybooks-documentation)<br/>`start-storybook --docs` |
| `--no-manager-cache` | Disables Storybook's manager caching mechanism. See note below.<br/>`start-storybook --no-manager-cache` |
| `--no-manager-cache` | Disables Storybook's manager caching mechanism. See note below<br/>`start-storybook --no-manager-cache` |
| `--disable-telemetry` | Disables Storybook's telemetry. Learn more about it [here](../configure/telemetry.md)<br/>`start-storybook --disable-telemetry` |
<div class="aside">
💡 The flag <code>--no-manager-cache</code> disables the internal caching of Storybook and can severely impact your Storybook loading time, so only use it when you need to refresh Storybook's UI, such as when editing themes.
@ -54,15 +55,16 @@ Usage: build-storybook [options]
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `-h`, `--help` | Output usage information<br/>`build-storybook --help` |
| `-V`, `--version` | Output the version number<br/>`build-storybook -V` |
| `-s`, `--static-dir` | **Deprecated** [see note](#static-dir-deprecation). Directory where to load static files from, comma-separated list<br/>`build-storybook -s public` |
| `-s`, `--static-dir` | **Deprecated** [see note](#static-dir-deprecation).<br/> Directory where to load static files from, comma-separated list<br/>`build-storybook -s public` |
| `-o`, `--output-dir [dir-name]` | Directory where to store built files<br/>`build-storybook -o /my-deployed-storybook` |
| `-c`, `--config-dir [dir-name]` | Directory where to load Storybook configurations from<br/>`build-storybook -c .storybook` |
| `--loglevel [level]` | Controls level of logging during build. Can be one of: [silly, verbose, info (default), warn, error, silent]<br/>`build-storybook --loglevel warn` |
| `--loglevel [level]` | Controls level of logging during build.<br/> Available options: `silly`, `verbose`, `info` (default), `warn`, `error`, `silent`<br/>`build-storybook --loglevel warn` |
| `--quiet` | Suppress verbose build output<br/>`build-storybook --quiet` |
| `--no-dll` | Do not use dll reference (no-op)<br/>`build-storybook --no-dll` |
| `--debug-webpack` | Display final webpack configurations for debugging purposes<br/>`build-storybook --debug-webpack` |
| `--webpack-stats-json` | Write Webpack Stats JSON to disk<br/>`build-storybook --webpack-stats-json /my-storybook/webpack-stats` |
| `--docs` | Builds Storybook in documentation mode. Learn more about it in [here](../writing-docs/build-documentation.md#publish-storybooks-documentation)<br/>`build-storybook --docs` |
| `--disable-telemetry` | Disables Storybook's telemetry. Learn more about it [here](../configure/telemetry.md).<br/>`build-storybook --disable-telemetry` |
<div class="aside">
💡 If you're using npm instead of yarn to publish Storybook, the commands work slightly different. For example, <code>npm run build-storybook -- -o ./path/to/build</code>.

174
docs/configure/telemetry.md Normal file
View File

@ -0,0 +1,174 @@
---
title: 'Telemetry'
---
Storybook collects completely anonymous data to help us improve user experience. Participation in this anonymous program is optional, and you may opt-out if you'd not like to share any information.
## Why is telemetry collected?
Hundreds of thousands of developers use Storybook daily to build, test, and document components. Storybook is framework agnostic and integrates with the front-end ecosystem:
- **JavaScript frameworks** such as [React](https://reactjs.org/), [Vue](https://vuejs.org/), and [Svelte](https://svelte.dev/)
- **Libraries** such as [Styled-Components](https://styled-components.com/), [Tailwind](https://tailwindcss.com/), [Redux](https://redux.js.org/)
- **Design tools** such as [Figma](https://figma.com/), [Sketch](https://www.sketch.com/), [Zeplin](https://zeplin.io/) and [InVision](https://www.invisionapp.com/)
- **Workflow tools** such as [Notion](https://www.notion.so/product), [Confluence](https://www.atlassian.com/software/confluence), and [Jira](https://www.atlassian.com/software/jira)
In the past, our improvement process relied on manually gathering feedback. But with a growing userbase and the need to support a wide variety of integrations, we need a more accurate method for gauging Storybook usage and pain points.
These telemetry data help us (the maintainers) to prioritize the highest impact projects. That allows us to keep up with trends in the front-end ecosystem and verify that our community's hard work achieves the intended result.
## What is being collected?
We collect general usage details, including command invocation, Storybook version, addons, and the view layer.
Specifically, we track the following information in our telemetry events:
- Timestamp of the occurrence.
- Command invoked (e.g., `init`, `upgrade`, `start-storybook`).
- Storybook unique identifier: One-way hash generated during Storybook installation process.
- One way hash of the IP address where the event occurred for spam detection.
- Story count.
- Storybook version.
- Storybook metadata:
- Language (e.g., TypeScript, JavaScript).
- Supported view layers (e.g., React, Vue, Angular, Svelte).
- Builder (e.g., Webpack4, Webpack5, Vite).
- Meta framework (e.g., [Next](https://nextjs.org/), [Gatsby](https://www.gatsbyjs.com/), [CRA](https://create-react-app.dev/)).
- [Addons](/addons) (e.g., [Essentials](../essentials/introduction), [Accessibility](https://storybook.js.org/addons/@storybook/addon-a11y/)).
- [Feature flags](./overview.md#feature-flags) (e.g., `buildStoriesJson`).
- Whether the command was invoked on CI or not.
Access to the raw data is highly controlled, limited to select members of Storybook's core team who maintain the telemetry. We cannot identify individual users from the dataset: it is anonymized and untraceable back to the user.
## What about sensitive information?
We take your privacy and our security very seriously. We perform additional steps to ensure that secure data (e.g., environment variables or other forms of sensitive data) **do not** make their way into our analytics. You can view all the information we collect by setting the `STORYBOOK_TELEMETRY_DEBUG` to `1` to print out the information gathered. For example:
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'common/storybook-telemetry-preview-event.yarn.js.mdx',
'common/storybook-telemetry-preview-event.npm.js.mdx',
]}
/>
<!-- prettier-ignore-end -->
Will generate the following output:
```json
{
"anonymousId": "8bcfdfd5f9616a1923dd92adf89714331b2d18693c722e05152a47f8093392bb",
"eventType": "start",
"payload": {
"storyIndex": {
"storyCount": 4,
"version": 3
}
},
"inCI": false,
"metadata": {
"generatedAt": 1648233198722,
"builder": {
"name": "webpack4"
},
"hasCustomBabel": false,
"hasCustomWebpack": true,
"hasStaticDirs": true,
"hasStorybookEslint": false,
"refCount": 0,
"metaFramework": {
"name": "CRA",
"packageName": "react-scripts",
"version": "4.0.3"
},
"features": {
"buildStoriesJson": true
},
"storybookVersion": "6.5.0",
"language": "typescript",
"storybookPackages": {
"@storybook/addons": {
"version": "6.5.0"
},
"@storybook/builder-webpack4": {
"version": "6.5.0"
},
"@storybook/react": {
"version": "6.5.0"
}
},
"framework": {
"name": "react"
},
"addons": {
"@storybook/preset-create-react-app": {
"version": "3.2.0"
},
"@storybook/addon-ie11": {
"version": "0.0.7--canary.5e87b64.0"
},
"@storybook/addon-essentials": {
"options": {
"viewport": false
},
"version": "6.5.0"
}
}
}
}
```
## Will this data be shared?
The data we collect is anonymous, not traceable to the source, and only meaningful in aggregate form. No data we collect is personally identifiable.
In the future, we plan to share relevant data with the community through public dashboards (or similar data representation formats).
## How to opt-out
You may opt-out of the telemetry by setting Storybook's configuration element `disableTelemetry` to `true`, using the `--disable-telemetry` flag, or setting the environment variable`STORYBOOK_DISABLE_TELEMETRY` to `1`. For example:
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'common/storybook-main-disable-telemetry.main-js.js.mdx',
'common/storybook-main-disable-telemetry.main-ts.ts.mdx',
'common/storybook-disable-telemetry-flag.yarn.js.mdx',
'common/storybook-disable-telemetry-flag.npm.js.mdx',
'common/storybook-disable-telemetry-env.env-var.js.mdx',
]}
/>
<!-- prettier-ignore-end -->
## Crash reports (disabled by default)
In addition to general usage telemetry, you may also choose to share crash reports. Storybook will then sanitize the error object (removing all user paths) and append it to the telemetry event. To enable crash reporting, you can set the `enableCrashReports` configuration element to `true`, using the `--enable-crash-reports` flag, or set the `STORYBOOK_ENABLE_CRASH_REPORTS` environment variable to `1`. For example:
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'common/storybook-telemetry-main-enable-crash-reports.main-js.js.mdx',
'common/storybook-telemetry-main-enable-crash-reports.main-ts.ts.mdx',
'common/storybook-telemetry-storybook-enable-crash-reports.yarn.js.mdx',
'common/storybook-telemetry-storybook-enable-crash-reports.npm.js.mdx',
'common/storybook-telemetry-storybook-enable-crash-reports.env-var.js.mdx',
]}
/>
<!-- prettier-ignore-end -->
Generates the following item in the telemetry event:
<!-- prettier-ignore-start -->
<CodeSnippets
paths={[
'common/storybook-telemetry-crash-report-event.js.mdx',
]}
/>
<!-- prettier-ignore-end -->

View File

@ -41,6 +41,7 @@ The command above will make the following changes to your local environment:
- 🛠 Setup the necessary scripts to run and build Storybook.
- 🛠 Add the default Storybook configuration.
- 📝 Add some boilerplate stories to get you started.
- 📡 Set up telemetry to help us improve Storybook. Read more about it [here](../configure/telemetry.md).
Depending on your framework, first, build your app and then check that everything worked by running:
@ -92,4 +93,4 @@ If all else fails, try asking for [help](https://storybook.js.org/support)
</details>
Now that you installed Storybook successfully, lets take a look at a story that was written for us.
Now that you installed Storybook successfully, lets take a look at a story that was written for us.

View File

@ -0,0 +1,3 @@
```shell
STORYBOOK_DISABLE_TELEMETRY=1 yarn storybook
```

View File

@ -0,0 +1,3 @@
```shell
npm run storybook -- --disable-telemetry
```

View File

@ -0,0 +1,3 @@
```shell
yarn storybook --disable-telemetry
```

View File

@ -0,0 +1,18 @@
```js
// .storybook/main.js
module.exports = {
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/preset-create-react-app',
],
framework: '@storybook/react',
core: {
builder: 'webpack5',
disableTelemetry: true, // 👈 Disables telemetry
},
};
```

View File

@ -0,0 +1,23 @@
```ts
// .storybook/main.ts
// Imports Storybook's configuration API
import type { StorybookConfig } from '@storybook/core-common';
const config: StorybookConfig = {
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/preset-create-react-app',
],
framework: '@storybook/react',
core: {
builder: 'webpack5',
disableTelemetry: true, // 👈 Disables telemetry
},
};
module.exports = config;
```

View File

@ -0,0 +1,13 @@
```js
{
stack: 'Error: Your button is not working\n' +
' at Object.<anonymous> ($SNIP/test.js:39:27)\n' +
' at Module._compile (node:internal/modules/cjs/loader:1103:14)\n' +
' at Object.Module._extensions..js (node:internal/modules/cjs/loader:1157:10)\n' +
' at Module.load (node:internal/modules/cjs/loader:981:32)\n' +
' at Function.Module._load (node:internal/modules/cjs/loader:822:12)\n' +
' at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)\n' +
' at node:internal/main/run_main_module:17:47',
message: 'Your button is not working'
}
```

View File

@ -0,0 +1,18 @@
```js
// .storybook/main.js
module.exports = {
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/preset-create-react-app',
],
framework: '@storybook/react',
core: {
builder: 'webpack5',
enableCrashReports: true, // 👈 Appends the crash reports to the telemetry events
},
};
```

View File

@ -0,0 +1,23 @@
```ts
// .storybook/main.ts
// Imports Storybook's configuration API
import type { StorybookConfig } from '@storybook/core-common';
const config: StorybookConfig = {
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/preset-create-react-app',
],
framework: '@storybook/react',
core: {
builder: 'webpack5',
enableCrashReports: true, // 👈 Appends the crash reports to the telemetry events
},
};
module.exports = config;
```

View File

@ -0,0 +1,3 @@
```shell
STORYBOOK_TELEMETRY_DEBUG=1 npm run storybook
```

View File

@ -0,0 +1,3 @@
```shell
STORYBOOK_TELEMETRY_DEBUG=1 yarn storybook
```

View File

@ -0,0 +1,3 @@
```shell
STORYBOOK_ENABLE_CRASH_REPORTS=1 yarn storybook
```

View File

@ -0,0 +1,3 @@
```shell
npm run storybook -- --enable-crash-reports
```

View File

@ -0,0 +1,3 @@
```shell
yarn storybook --enable-crash-reports
```

View File

@ -357,6 +357,11 @@ module.exports = {
title: 'Upgrading',
type: 'link',
},
{
pathSegment: 'telemetry',
title: 'Telemetry',
type: 'link',
},
{
pathSegment: '',
title: 'Integration',

View File

@ -16,6 +16,7 @@ module.exports = {
],
core: {
builder: 'webpack4',
disableTelemetry: true,
},
angularOptions: {
enableIvy: true,

View File

@ -32,6 +32,7 @@ module.exports = {
},
core: {
builder: 'webpack4',
disableTelemetry: true,
},
staticDirs: ['../public'],
features: {

View File

@ -20,6 +20,7 @@ module.exports = {
},
core: {
builder: 'webpack4',
disableTelemetry: true,
},
staticDirs: ['../public'],
};

View File

@ -26,6 +26,7 @@ const mainConfig: StorybookConfig = {
},
core: {
builder: 'webpack4',
disableTelemetry: true,
},
staticDirs: ['../public'],
features: {

View File

@ -28,6 +28,7 @@ module.exports = {
},
core: {
builder: 'webpack4',
disableTelemetry: true,
},
staticDirs: ['../public'],
features: {

View File

@ -28,6 +28,7 @@ module.exports = {
},
core: {
builder: 'webpack4',
disableTelemetry: true,
},
staticDirs: ['../ember-output'],
features: {

View File

@ -23,6 +23,7 @@ module.exports = {
],
core: {
builder: 'webpack4',
disableTelemetry: true,
},
features: {
buildStoriesJson: true,

View File

@ -34,6 +34,7 @@ const config: StorybookConfig = {
],
core: {
builder: 'webpack4',
disableTelemetry: true,
},
logLevel: 'debug',
features: {

View File

@ -23,6 +23,7 @@ module.exports = {
},
core: {
builder: 'webpack4',
disableTelemetry: true,
},
staticDirs: ['../public'],
features: {

View File

@ -19,6 +19,7 @@ const config: StorybookConfig = {
],
core: {
builder: 'webpack4',
disableTelemetry: true,
},
typescript: {
check: true,

View File

@ -24,6 +24,7 @@ const config: StorybookConfig = {
core: {
builder: { name: 'webpack4' },
channelOptions: { allowFunction: false, maxDepth: 10 },
disableTelemetry: true,
},
features: {
postcss: false,

View File

@ -35,6 +35,7 @@ module.exports = {
},
core: {
builder: 'webpack4',
disableTelemetry: true,
},
staticDirs: ['../public'],
};

View File

@ -7,6 +7,7 @@ module.exports = {
],
core: {
builder: 'webpack4',
disableTelemetry: true,
},
features: {
buildStoriesJson: true,

View File

@ -9,6 +9,7 @@ module.exports = {
],
core: {
builder: 'webpack4',
disableTelemetry: true,
},
features: {
buildStoriesJson: true,

View File

@ -14,6 +14,7 @@ module.exports = {
],
core: {
builder: 'webpack4',
disableTelemetry: true,
},
staticDirs: ['../public'],
features: {

View File

@ -15,6 +15,7 @@ module.exports = {
],
core: {
builder: 'webpack4',
disableTelemetry: true,
},
features: {
interactionsDebugger: true,

View File

@ -50,6 +50,7 @@
"@storybook/router": "portal:../../lib/router",
"@storybook/source-loader": "portal:../../lib/source-loader",
"@storybook/store": "portal:../../lib/store",
"@storybook/telemetry": "portal:../../lib/telemetry",
"@storybook/testing-library": "^0.0.7",
"@storybook/theming": "portal:../../lib/theming",
"@storybook/ui": "portal:../../lib/ui",

View File

@ -1600,7 +1600,7 @@ __metadata:
languageName: node
linkType: hard
"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.0, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.0, @babel/runtime@npm:^7.8.4":
"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.0, @babel/runtime@npm:^7.8.4":
version: 7.14.8
resolution: "@babel/runtime@npm:7.14.8"
dependencies:
@ -1609,6 +1609,15 @@ __metadata:
languageName: node
linkType: hard
"@babel/runtime@npm:^7.17.8":
version: 7.17.9
resolution: "@babel/runtime@npm:7.17.9"
dependencies:
regenerator-runtime: ^0.13.4
checksum: 758ce8855a75408555ed9d196c82c86350257765095a5d3e05df35875d1b0cd42223c6f62356f000b1e1efe8e345d6312c60ae98e8727a2a49909a656f0fd805
languageName: node
linkType: hard
"@babel/template@npm:^7.12.7, @babel/template@npm:^7.14.5, @babel/template@npm:^7.3.3, @babel/template@npm:^7.7.0":
version: 7.14.5
resolution: "@babel/template@npm:7.14.5"
@ -2114,14 +2123,14 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/addon-a11y@portal:../../addons/a11y::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@storybook/addons": 6.5.0-alpha.56
"@storybook/api": 6.5.0-alpha.56
"@storybook/channels": 6.5.0-alpha.56
"@storybook/client-logger": 6.5.0-alpha.56
"@storybook/components": 6.5.0-alpha.56
"@storybook/core-events": 6.5.0-alpha.56
"@storybook/csf": 0.0.2--canary.7c6c115.0
"@storybook/theming": 6.5.0-alpha.56
"@storybook/addons": 6.5.0-beta.1
"@storybook/api": 6.5.0-beta.1
"@storybook/channels": 6.5.0-beta.1
"@storybook/client-logger": 6.5.0-beta.1
"@storybook/components": 6.5.0-beta.1
"@storybook/core-events": 6.5.0-beta.1
"@storybook/csf": 0.0.2--canary.4566f4d.1
"@storybook/theming": 6.5.0-beta.1
axe-core: ^4.2.0
core-js: ^3.8.2
global: ^4.4.0
@ -2145,18 +2154,18 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/addon-actions@portal:../../addons/actions::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@storybook/addons": 6.5.0-alpha.56
"@storybook/api": 6.5.0-alpha.56
"@storybook/client-logger": 6.5.0-alpha.56
"@storybook/components": 6.5.0-alpha.56
"@storybook/core-events": 6.5.0-alpha.56
"@storybook/csf": 0.0.2--canary.7c6c115.0
"@storybook/theming": 6.5.0-alpha.56
"@storybook/addons": 6.5.0-beta.1
"@storybook/api": 6.5.0-beta.1
"@storybook/client-logger": 6.5.0-beta.1
"@storybook/components": 6.5.0-beta.1
"@storybook/core-events": 6.5.0-beta.1
"@storybook/csf": 0.0.2--canary.4566f4d.1
"@storybook/theming": 6.5.0-beta.1
core-js: ^3.8.2
fast-deep-equal: ^3.1.3
global: ^4.4.0
lodash: ^4.17.21
polished: ^4.0.5
polished: ^4.2.2
prop-types: ^15.7.2
react-inspector: ^5.1.0
regenerator-runtime: ^0.13.7
@ -2179,13 +2188,13 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/addon-backgrounds@portal:../../addons/backgrounds::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@storybook/addons": 6.5.0-alpha.56
"@storybook/api": 6.5.0-alpha.56
"@storybook/client-logger": 6.5.0-alpha.56
"@storybook/components": 6.5.0-alpha.56
"@storybook/core-events": 6.5.0-alpha.56
"@storybook/csf": 0.0.2--canary.7c6c115.0
"@storybook/theming": 6.5.0-alpha.56
"@storybook/addons": 6.5.0-beta.1
"@storybook/api": 6.5.0-beta.1
"@storybook/client-logger": 6.5.0-beta.1
"@storybook/components": 6.5.0-beta.1
"@storybook/core-events": 6.5.0-beta.1
"@storybook/csf": 0.0.2--canary.4566f4d.1
"@storybook/theming": 6.5.0-beta.1
core-js: ^3.8.2
global: ^4.4.0
memoizerific: ^1.11.3
@ -2207,15 +2216,15 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/addon-controls@portal:../../addons/controls::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@storybook/addons": 6.5.0-alpha.56
"@storybook/api": 6.5.0-alpha.56
"@storybook/client-logger": 6.5.0-alpha.56
"@storybook/components": 6.5.0-alpha.56
"@storybook/core-common": 6.5.0-alpha.56
"@storybook/csf": 0.0.2--canary.7c6c115.0
"@storybook/node-logger": 6.5.0-alpha.56
"@storybook/store": 6.5.0-alpha.56
"@storybook/theming": 6.5.0-alpha.56
"@storybook/addons": 6.5.0-beta.1
"@storybook/api": 6.5.0-beta.1
"@storybook/client-logger": 6.5.0-beta.1
"@storybook/components": 6.5.0-beta.1
"@storybook/core-common": 6.5.0-beta.1
"@storybook/csf": 0.0.2--canary.4566f4d.1
"@storybook/node-logger": 6.5.0-beta.1
"@storybook/store": 6.5.0-beta.1
"@storybook/theming": 6.5.0-beta.1
core-js: ^3.8.2
lodash: ^4.17.21
ts-dedent: ^2.0.0
@ -2238,20 +2247,20 @@ __metadata:
"@babel/preset-env": ^7.12.11
"@jest/transform": ^26.6.2
"@mdx-js/react": ^1.6.22
"@storybook/addons": 6.5.0-alpha.56
"@storybook/api": 6.5.0-alpha.56
"@storybook/components": 6.5.0-alpha.56
"@storybook/core-common": 6.5.0-alpha.56
"@storybook/core-events": 6.5.0-alpha.56
"@storybook/csf": 0.0.2--canary.7c6c115.0
"@storybook/docs-tools": 6.5.0-alpha.56
"@storybook/addons": 6.5.0-beta.1
"@storybook/api": 6.5.0-beta.1
"@storybook/components": 6.5.0-beta.1
"@storybook/core-common": 6.5.0-beta.1
"@storybook/core-events": 6.5.0-beta.1
"@storybook/csf": 0.0.2--canary.4566f4d.1
"@storybook/docs-tools": 6.5.0-beta.1
"@storybook/mdx1-csf": canary
"@storybook/node-logger": 6.5.0-alpha.56
"@storybook/postinstall": 6.5.0-alpha.56
"@storybook/preview-web": 6.5.0-alpha.56
"@storybook/source-loader": 6.5.0-alpha.56
"@storybook/store": 6.5.0-alpha.56
"@storybook/theming": 6.5.0-alpha.56
"@storybook/node-logger": 6.5.0-beta.1
"@storybook/postinstall": 6.5.0-beta.1
"@storybook/preview-web": 6.5.0-beta.1
"@storybook/source-loader": 6.5.0-beta.1
"@storybook/store": 6.5.0-beta.1
"@storybook/theming": 6.5.0-beta.1
babel-loader: ^8.0.0
core-js: ^3.8.2
fast-deep-equal: ^3.1.3
@ -2306,11 +2315,11 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/addon-links@portal:../../addons/links::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@storybook/addons": 6.5.0-alpha.56
"@storybook/client-logger": 6.5.0-alpha.56
"@storybook/core-events": 6.5.0-alpha.56
"@storybook/csf": 0.0.2--canary.7c6c115.0
"@storybook/router": 6.5.0-alpha.56
"@storybook/addons": 6.5.0-beta.1
"@storybook/client-logger": 6.5.0-beta.1
"@storybook/core-events": 6.5.0-beta.1
"@storybook/csf": 0.0.2--canary.4566f4d.1
"@storybook/router": 6.5.0-beta.1
"@types/qs": ^6.9.5
core-js: ^3.8.2
global: ^4.4.0
@ -2334,13 +2343,13 @@ __metadata:
resolution: "@storybook/addon-storyshots@portal:../../addons/storyshots/storyshots-core::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@jest/transform": ^26.6.2
"@storybook/addons": 6.5.0-alpha.56
"@storybook/addons": 6.5.0-beta.1
"@storybook/babel-plugin-require-context-hook": 1.0.1
"@storybook/client-api": 6.5.0-alpha.56
"@storybook/core": 6.5.0-alpha.56
"@storybook/core-client": 6.5.0-alpha.56
"@storybook/core-common": 6.5.0-alpha.56
"@storybook/csf": 0.0.2--canary.7c6c115.0
"@storybook/client-api": 6.5.0-beta.1
"@storybook/core": 6.5.0-beta.1
"@storybook/core-client": 6.5.0-beta.1
"@storybook/core-common": 6.5.0-beta.1
"@storybook/csf": 0.0.2--canary.4566f4d.1
"@types/glob": ^7.1.3
"@types/jest": ^26.0.16
"@types/jest-specific-snapshot": ^0.5.3
@ -2409,13 +2418,13 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/addon-storysource@portal:../../addons/storysource::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@storybook/addons": 6.5.0-alpha.56
"@storybook/api": 6.5.0-alpha.56
"@storybook/client-logger": 6.5.0-alpha.56
"@storybook/components": 6.5.0-alpha.56
"@storybook/router": 6.5.0-alpha.56
"@storybook/source-loader": 6.5.0-alpha.56
"@storybook/theming": 6.5.0-alpha.56
"@storybook/addons": 6.5.0-beta.1
"@storybook/api": 6.5.0-beta.1
"@storybook/client-logger": 6.5.0-beta.1
"@storybook/components": 6.5.0-beta.1
"@storybook/router": 6.5.0-beta.1
"@storybook/source-loader": 6.5.0-beta.1
"@storybook/theming": 6.5.0-beta.1
core-js: ^3.8.2
estraverse: ^5.2.0
loader-utils: ^2.0.0
@ -2437,12 +2446,12 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/addon-viewport@portal:../../addons/viewport::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@storybook/addons": 6.5.0-alpha.56
"@storybook/api": 6.5.0-alpha.56
"@storybook/client-logger": 6.5.0-alpha.56
"@storybook/components": 6.5.0-alpha.56
"@storybook/core-events": 6.5.0-alpha.56
"@storybook/theming": 6.5.0-alpha.56
"@storybook/addons": 6.5.0-beta.1
"@storybook/api": 6.5.0-beta.1
"@storybook/client-logger": 6.5.0-beta.1
"@storybook/components": 6.5.0-beta.1
"@storybook/core-events": 6.5.0-beta.1
"@storybook/theming": 6.5.0-beta.1
core-js: ^3.8.2
global: ^4.4.0
memoizerific: ^1.11.3
@ -2463,13 +2472,13 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/addons@portal:../../lib/addons::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@storybook/api": 6.5.0-alpha.56
"@storybook/channels": 6.5.0-alpha.56
"@storybook/client-logger": 6.5.0-alpha.56
"@storybook/core-events": 6.5.0-alpha.56
"@storybook/csf": 0.0.2--canary.7c6c115.0
"@storybook/router": 6.5.0-alpha.56
"@storybook/theming": 6.5.0-alpha.56
"@storybook/api": 6.5.0-beta.1
"@storybook/channels": 6.5.0-beta.1
"@storybook/client-logger": 6.5.0-beta.1
"@storybook/core-events": 6.5.0-beta.1
"@storybook/csf": 0.0.2--canary.4566f4d.1
"@storybook/router": 6.5.0-beta.1
"@storybook/theming": 6.5.0-beta.1
"@types/webpack-env": ^1.16.0
core-js: ^3.8.2
global: ^4.4.0
@ -2484,13 +2493,13 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/api@portal:../../lib/api::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@storybook/channels": 6.5.0-alpha.56
"@storybook/client-logger": 6.5.0-alpha.56
"@storybook/core-events": 6.5.0-alpha.56
"@storybook/csf": 0.0.2--canary.7c6c115.0
"@storybook/router": 6.5.0-alpha.56
"@storybook/channels": 6.5.0-beta.1
"@storybook/client-logger": 6.5.0-beta.1
"@storybook/core-events": 6.5.0-beta.1
"@storybook/csf": 0.0.2--canary.4566f4d.1
"@storybook/router": 6.5.0-beta.1
"@storybook/semver": ^7.3.2
"@storybook/theming": 6.5.0-alpha.56
"@storybook/theming": 6.5.0-beta.1
core-js: ^3.8.2
fast-deep-equal: ^3.1.3
global: ^4.4.0
@ -2519,22 +2528,22 @@ __metadata:
resolution: "@storybook/builder-webpack4@portal:../../lib/builder-webpack4::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@babel/core": ^7.12.10
"@storybook/addons": 6.5.0-alpha.56
"@storybook/api": 6.5.0-alpha.56
"@storybook/channel-postmessage": 6.5.0-alpha.56
"@storybook/channels": 6.5.0-alpha.56
"@storybook/client-api": 6.5.0-alpha.56
"@storybook/client-logger": 6.5.0-alpha.56
"@storybook/components": 6.5.0-alpha.56
"@storybook/core-common": 6.5.0-alpha.56
"@storybook/core-events": 6.5.0-alpha.56
"@storybook/node-logger": 6.5.0-alpha.56
"@storybook/preview-web": 6.5.0-alpha.56
"@storybook/router": 6.5.0-alpha.56
"@storybook/addons": 6.5.0-beta.1
"@storybook/api": 6.5.0-beta.1
"@storybook/channel-postmessage": 6.5.0-beta.1
"@storybook/channels": 6.5.0-beta.1
"@storybook/client-api": 6.5.0-beta.1
"@storybook/client-logger": 6.5.0-beta.1
"@storybook/components": 6.5.0-beta.1
"@storybook/core-common": 6.5.0-beta.1
"@storybook/core-events": 6.5.0-beta.1
"@storybook/node-logger": 6.5.0-beta.1
"@storybook/preview-web": 6.5.0-beta.1
"@storybook/router": 6.5.0-beta.1
"@storybook/semver": ^7.3.2
"@storybook/store": 6.5.0-alpha.56
"@storybook/theming": 6.5.0-alpha.56
"@storybook/ui": 6.5.0-alpha.56
"@storybook/store": 6.5.0-beta.1
"@storybook/theming": 6.5.0-beta.1
"@storybook/ui": 6.5.0-beta.1
"@types/node": ^14.0.10 || ^16.0.0
"@types/webpack": ^4.41.26
autoprefixer: ^9.8.6
@ -2578,9 +2587,9 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/channel-postmessage@portal:../../lib/channel-postmessage::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@storybook/channels": 6.5.0-alpha.56
"@storybook/client-logger": 6.5.0-alpha.56
"@storybook/core-events": 6.5.0-alpha.56
"@storybook/channels": 6.5.0-beta.1
"@storybook/client-logger": 6.5.0-beta.1
"@storybook/core-events": 6.5.0-beta.1
core-js: ^3.8.2
global: ^4.4.0
qs: ^6.10.0
@ -2592,8 +2601,8 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/channel-websocket@portal:../../lib/channel-websocket::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@storybook/channels": 6.5.0-alpha.56
"@storybook/client-logger": 6.5.0-alpha.56
"@storybook/channels": 6.5.0-beta.1
"@storybook/client-logger": 6.5.0-beta.1
core-js: ^3.8.2
global: ^4.4.0
telejson: ^5.3.3
@ -2614,13 +2623,13 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/client-api@portal:../../lib/client-api::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@storybook/addons": 6.5.0-alpha.56
"@storybook/channel-postmessage": 6.5.0-alpha.56
"@storybook/channels": 6.5.0-alpha.56
"@storybook/client-logger": 6.5.0-alpha.56
"@storybook/core-events": 6.5.0-alpha.56
"@storybook/csf": 0.0.2--canary.7c6c115.0
"@storybook/store": 6.5.0-alpha.56
"@storybook/addons": 6.5.0-beta.1
"@storybook/channel-postmessage": 6.5.0-beta.1
"@storybook/channels": 6.5.0-beta.1
"@storybook/client-logger": 6.5.0-beta.1
"@storybook/core-events": 6.5.0-beta.1
"@storybook/csf": 0.0.2--canary.4566f4d.1
"@storybook/store": 6.5.0-beta.1
"@types/qs": ^6.9.5
"@types/webpack-env": ^1.16.0
core-js: ^3.8.2
@ -2653,9 +2662,9 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/components@portal:../../lib/components::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@storybook/client-logger": 6.5.0-alpha.56
"@storybook/csf": 0.0.2--canary.7c6c115.0
"@storybook/theming": 6.5.0-alpha.56
"@storybook/client-logger": 6.5.0-beta.1
"@storybook/csf": 0.0.2--canary.4566f4d.1
"@storybook/theming": 6.5.0-beta.1
core-js: ^3.8.2
regenerator-runtime: ^0.13.7
peerDependencies:
@ -2668,16 +2677,16 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/core-client@portal:../../lib/core-client::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@storybook/addons": 6.5.0-alpha.56
"@storybook/channel-postmessage": 6.5.0-alpha.56
"@storybook/channel-websocket": 6.5.0-alpha.56
"@storybook/client-api": 6.5.0-alpha.56
"@storybook/client-logger": 6.5.0-alpha.56
"@storybook/core-events": 6.5.0-alpha.56
"@storybook/csf": 0.0.2--canary.7c6c115.0
"@storybook/preview-web": 6.5.0-alpha.56
"@storybook/store": 6.5.0-alpha.56
"@storybook/ui": 6.5.0-alpha.56
"@storybook/addons": 6.5.0-beta.1
"@storybook/channel-postmessage": 6.5.0-beta.1
"@storybook/channel-websocket": 6.5.0-beta.1
"@storybook/client-api": 6.5.0-beta.1
"@storybook/client-logger": 6.5.0-beta.1
"@storybook/core-events": 6.5.0-beta.1
"@storybook/csf": 0.0.2--canary.4566f4d.1
"@storybook/preview-web": 6.5.0-beta.1
"@storybook/store": 6.5.0-beta.1
"@storybook/ui": 6.5.0-beta.1
airbnb-js-shims: ^2.2.1
ansi-to-html: ^0.6.11
core-js: ^3.8.2
@ -2724,7 +2733,7 @@ __metadata:
"@babel/preset-react": ^7.12.10
"@babel/preset-typescript": ^7.12.7
"@babel/register": ^7.12.1
"@storybook/node-logger": 6.5.0-alpha.56
"@storybook/node-logger": 6.5.0-beta.1
"@storybook/semver": ^7.3.2
"@types/node": ^14.0.10 || ^16.0.0
"@types/pretty-hrtime": ^1.0.0
@ -2774,16 +2783,17 @@ __metadata:
resolution: "@storybook/core-server@portal:../../lib/core-server::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@discoveryjs/json-ext": ^0.5.3
"@storybook/builder-webpack4": 6.5.0-alpha.56
"@storybook/core-client": 6.5.0-alpha.56
"@storybook/core-common": 6.5.0-alpha.56
"@storybook/core-events": 6.5.0-alpha.56
"@storybook/csf": 0.0.2--canary.7c6c115.0
"@storybook/csf-tools": 6.5.0-alpha.56
"@storybook/manager-webpack4": 6.5.0-alpha.56
"@storybook/node-logger": 6.5.0-alpha.56
"@storybook/builder-webpack4": 6.5.0-beta.1
"@storybook/core-client": 6.5.0-beta.1
"@storybook/core-common": 6.5.0-beta.1
"@storybook/core-events": 6.5.0-beta.1
"@storybook/csf": 0.0.2--canary.4566f4d.1
"@storybook/csf-tools": 6.5.0-beta.1
"@storybook/manager-webpack4": 6.5.0-beta.1
"@storybook/node-logger": 6.5.0-beta.1
"@storybook/semver": ^7.3.2
"@storybook/store": 6.5.0-alpha.56
"@storybook/store": 6.5.0-beta.1
"@storybook/telemetry": 6.5.0-beta.1
"@types/node": ^14.0.10 || ^16.0.0
"@types/node-fetch": ^2.5.7
"@types/pretty-hrtime": ^1.0.0
@ -2834,8 +2844,8 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/core@portal:../../lib/core::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@storybook/core-client": 6.5.0-alpha.56
"@storybook/core-server": 6.5.0-alpha.56
"@storybook/core-client": 6.5.0-beta.1
"@storybook/core-server": 6.5.0-beta.1
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@ -2861,7 +2871,7 @@ __metadata:
"@babel/preset-env": ^7.12.11
"@babel/traverse": ^7.12.11
"@babel/types": ^7.12.11
"@storybook/csf": 0.0.2--canary.7c6c115.0
"@storybook/csf": 0.0.2--canary.4566f4d.1
"@storybook/mdx1-csf": canary
core-js: ^3.8.2
fs-extra: ^9.0.1
@ -2876,12 +2886,12 @@ __metadata:
languageName: node
linkType: soft
"@storybook/csf@npm:0.0.2--canary.7c6c115.0":
version: 0.0.2--canary.7c6c115.0
resolution: "@storybook/csf@npm:0.0.2--canary.7c6c115.0"
"@storybook/csf@npm:0.0.2--canary.4566f4d.1":
version: 0.0.2--canary.4566f4d.1
resolution: "@storybook/csf@npm:0.0.2--canary.4566f4d.1"
dependencies:
lodash: ^4.17.15
checksum: 85a179664d18eeca8462c1b6ff36f9b68b856c9f9c5143aa6f19b17e4cc97bc08ed69921a5287a61d8c90f61366ff6a5ab89930d158402e7c04d07a3ffaad8bb
checksum: dc0fe9940a47fbba9762275083816953da07a188f0315a631c307716b16a7073586a4d229df6b177dfb4b01604667e2bb24c13d6bfcb137d2f4d306874a590f4
languageName: node
linkType: hard
@ -2890,8 +2900,8 @@ __metadata:
resolution: "@storybook/docs-tools@portal:../../lib/docs-tools::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@babel/core": ^7.12.10
"@storybook/csf": 0.0.2--canary.7c6c115.0
"@storybook/store": 6.5.0-alpha.56
"@storybook/csf": 0.0.2--canary.4566f4d.1
"@storybook/store": 6.5.0-beta.1
core-js: ^3.8.2
doctrine: ^3.0.0
lodash: ^4.17.21
@ -2906,12 +2916,12 @@ __metadata:
"@babel/core": ^7.12.10
"@babel/plugin-transform-template-literals": ^7.12.1
"@babel/preset-react": ^7.12.10
"@storybook/addons": 6.5.0-alpha.56
"@storybook/core-client": 6.5.0-alpha.56
"@storybook/core-common": 6.5.0-alpha.56
"@storybook/node-logger": 6.5.0-alpha.56
"@storybook/theming": 6.5.0-alpha.56
"@storybook/ui": 6.5.0-alpha.56
"@storybook/addons": 6.5.0-beta.1
"@storybook/core-client": 6.5.0-beta.1
"@storybook/core-common": 6.5.0-beta.1
"@storybook/node-logger": 6.5.0-beta.1
"@storybook/theming": 6.5.0-beta.1
"@storybook/ui": 6.5.0-beta.1
"@types/node": ^14.0.10 || ^16.0.0
"@types/webpack": ^4.41.26
babel-loader: ^8.0.0
@ -2990,12 +3000,12 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/preview-web@portal:../../lib/preview-web::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@storybook/addons": 6.5.0-alpha.56
"@storybook/channel-postmessage": 6.5.0-alpha.56
"@storybook/client-logger": 6.5.0-alpha.56
"@storybook/core-events": 6.5.0-alpha.56
"@storybook/csf": 0.0.2--canary.7c6c115.0
"@storybook/store": 6.5.0-alpha.56
"@storybook/addons": 6.5.0-beta.1
"@storybook/channel-postmessage": 6.5.0-beta.1
"@storybook/client-logger": 6.5.0-beta.1
"@storybook/core-events": 6.5.0-beta.1
"@storybook/csf": 0.0.2--canary.4566f4d.1
"@storybook/store": 6.5.0-beta.1
ansi-to-html: ^0.6.11
core-js: ^3.8.2
global: ^4.4.0
@ -3016,7 +3026,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/router@portal:../../lib/router::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@storybook/client-logger": 6.5.0-alpha.56
"@storybook/client-logger": 6.5.0-beta.1
core-js: ^3.8.2
regenerator-runtime: ^0.13.7
peerDependencies:
@ -3041,9 +3051,9 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/source-loader@portal:../../lib/source-loader::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@storybook/addons": 6.5.0-alpha.56
"@storybook/client-logger": 6.5.0-alpha.56
"@storybook/csf": 0.0.2--canary.7c6c115.0
"@storybook/addons": 6.5.0-beta.1
"@storybook/client-logger": 6.5.0-beta.1
"@storybook/csf": 0.0.2--canary.4566f4d.1
core-js: ^3.8.2
estraverse: ^5.2.0
global: ^4.4.0
@ -3061,10 +3071,10 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/store@portal:../../lib/store::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@storybook/addons": 6.5.0-alpha.56
"@storybook/client-logger": 6.5.0-alpha.56
"@storybook/core-events": 6.5.0-alpha.56
"@storybook/csf": 0.0.2--canary.7c6c115.0
"@storybook/addons": 6.5.0-beta.1
"@storybook/client-logger": 6.5.0-beta.1
"@storybook/core-events": 6.5.0-beta.1
"@storybook/csf": 0.0.2--canary.4566f4d.1
core-js: ^3.8.2
fast-deep-equal: ^3.1.3
global: ^4.4.0
@ -3082,11 +3092,29 @@ __metadata:
languageName: node
linkType: soft
"@storybook/telemetry@portal:../../lib/telemetry::locator=web-components-kitchen-sink%40workspace%3A.":
version: 0.0.0-use.local
resolution: "@storybook/telemetry@portal:../../lib/telemetry::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@storybook/client-logger": 6.5.0-beta.1
"@storybook/core-common": 6.5.0-beta.1
chalk: ^4.1.0
core-js: ^3.8.2
detect-package-manager: ^2.0.1
fetch-retry: ^5.0.2
fs-extra: ^9.0.1
global: ^4.4.0
isomorphic-unfetch: ^3.1.0
nanoid: ^3.3.1
read-pkg-up: ^7.0.1
languageName: node
linkType: soft
"@storybook/theming@portal:../../lib/theming::locator=web-components-kitchen-sink%40workspace%3A.":
version: 0.0.0-use.local
resolution: "@storybook/theming@portal:../../lib/theming::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@storybook/client-logger": 6.5.0-alpha.56
"@storybook/client-logger": 6.5.0-beta.1
core-js: ^3.8.2
regenerator-runtime: ^0.13.7
peerDependencies:
@ -3099,15 +3127,15 @@ __metadata:
version: 0.0.0-use.local
resolution: "@storybook/ui@portal:../../lib/ui::locator=web-components-kitchen-sink%40workspace%3A."
dependencies:
"@storybook/addons": 6.5.0-alpha.56
"@storybook/api": 6.5.0-alpha.56
"@storybook/channels": 6.5.0-alpha.56
"@storybook/client-logger": 6.5.0-alpha.56
"@storybook/components": 6.5.0-alpha.56
"@storybook/core-events": 6.5.0-alpha.56
"@storybook/router": 6.5.0-alpha.56
"@storybook/addons": 6.5.0-beta.1
"@storybook/api": 6.5.0-beta.1
"@storybook/channels": 6.5.0-beta.1
"@storybook/client-logger": 6.5.0-beta.1
"@storybook/components": 6.5.0-beta.1
"@storybook/core-events": 6.5.0-beta.1
"@storybook/router": 6.5.0-beta.1
"@storybook/semver": ^7.3.2
"@storybook/theming": 6.5.0-alpha.56
"@storybook/theming": 6.5.0-beta.1
core-js: ^3.8.2
regenerator-runtime: ^0.13.7
resolve-from: ^5.0.0
@ -3124,15 +3152,15 @@ __metadata:
"@babel/plugin-syntax-dynamic-import": ^7.8.3
"@babel/plugin-syntax-import-meta": ^7.10.4
"@babel/preset-env": ^7.12.11
"@storybook/addons": 6.5.0-alpha.56
"@storybook/client-api": 6.5.0-alpha.56
"@storybook/client-logger": 6.5.0-alpha.56
"@storybook/core": 6.5.0-alpha.56
"@storybook/core-common": 6.5.0-alpha.56
"@storybook/csf": 0.0.2--canary.7c6c115.0
"@storybook/docs-tools": 6.5.0-alpha.56
"@storybook/preview-web": 6.5.0-alpha.56
"@storybook/store": 6.5.0-alpha.56
"@storybook/addons": 6.5.0-beta.1
"@storybook/client-api": 6.5.0-beta.1
"@storybook/client-logger": 6.5.0-beta.1
"@storybook/core": 6.5.0-beta.1
"@storybook/core-common": 6.5.0-beta.1
"@storybook/csf": 0.0.2--canary.4566f4d.1
"@storybook/docs-tools": 6.5.0-beta.1
"@storybook/preview-web": 6.5.0-beta.1
"@storybook/store": 6.5.0-beta.1
"@types/node": ^14.14.20 || ^16.0.0
"@types/webpack-env": ^1.16.0
babel-plugin-bundled-import-meta: ^0.3.1
@ -5919,6 +5947,15 @@ __metadata:
languageName: node
linkType: hard
"detect-package-manager@npm:^2.0.1":
version: 2.0.1
resolution: "detect-package-manager@npm:2.0.1"
dependencies:
execa: ^5.1.1
checksum: 56ffd65228d1ff3ead5ea7f8ab951a517a29270de27510b790c9a8b77d4f36efbd61493e170ca77ee3dc13cbb5218583ce65b78ad14a59dc48565c9bcbbf3c71
languageName: node
linkType: hard
"detect-port@npm:^1.3.0":
version: 1.3.0
resolution: "detect-port@npm:1.3.0"
@ -6456,7 +6493,7 @@ __metadata:
languageName: node
linkType: hard
"execa@npm:^5.0.0":
"execa@npm:^5.0.0, execa@npm:^5.1.1":
version: 5.1.1
resolution: "execa@npm:5.1.1"
dependencies:
@ -6678,6 +6715,13 @@ __metadata:
languageName: node
linkType: hard
"fetch-retry@npm:^5.0.2":
version: 5.0.2
resolution: "fetch-retry@npm:5.0.2"
checksum: 694fae18ceec4c88c508daf682fccbf1e0736fa679e95daad50946e003df7e261d9a4d36388f6f9eab2426d1796b4ee054ced904794f1edad3ffdc55b2d4b785
languageName: node
linkType: hard
"figgy-pudding@npm:^3.5.1":
version: 3.5.2
resolution: "figgy-pudding@npm:3.5.2"
@ -8368,6 +8412,16 @@ fsevents@^1.2.7:
languageName: node
linkType: hard
"isomorphic-unfetch@npm:^3.1.0":
version: 3.1.0
resolution: "isomorphic-unfetch@npm:3.1.0"
dependencies:
node-fetch: ^2.6.1
unfetch: ^4.2.0
checksum: d3b61fca06304db692b7f76bdfd3a00f410e42cfa7403c3b250546bf71589d18cf2f355922f57198e4cc4a9872d3647b20397a5c3edf1a347c90d57c83cf2a89
languageName: node
linkType: hard
"istanbul-lib-coverage@npm:^3.0.0":
version: 3.0.0
resolution: "istanbul-lib-coverage@npm:3.0.0"
@ -10066,6 +10120,15 @@ fsevents@^1.2.7:
languageName: node
linkType: hard
"nanoid@npm:^3.3.1":
version: 3.3.3
resolution: "nanoid@npm:3.3.3"
bin:
nanoid: bin/nanoid.cjs
checksum: d7ab68893cdb92dd2152d505e56571d571c65b71a9815f9dfb3c9a8cbf943fe43c9777d9a95a3b81ef01e442fec8409a84375c08f90a5753610a9f22672d953a
languageName: node
linkType: hard
"nanomatch@npm:^1.2.9":
version: 1.2.13
resolution: "nanomatch@npm:1.2.13"
@ -10130,7 +10193,7 @@ fsevents@^1.2.7:
languageName: node
linkType: hard
"node-fetch@npm:^2.6.7":
"node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.7":
version: 2.6.7
resolution: "node-fetch@npm:2.6.7"
dependencies:
@ -10961,12 +11024,12 @@ fsevents@^1.2.7:
languageName: node
linkType: hard
"polished@npm:^4.0.5":
version: 4.1.3
resolution: "polished@npm:4.1.3"
"polished@npm:^4.2.2":
version: 4.2.2
resolution: "polished@npm:4.2.2"
dependencies:
"@babel/runtime": ^7.14.0
checksum: 8e5328057804b10ac37160d0f17a551276193b47193721a91d6bc7094fbbd73ad69ed0a9faf92a95011611e91df6c554548d0be53261aff4268497b8a258cf20
"@babel/runtime": ^7.17.8
checksum: 1d054d1fea18ac7d921ca91504ffcf1ef0f505eda6acbfec6e205a98ebfea80b658664995deb35907dabc5f75f287dc2894812503a8aed28285bb91f25cf7400
languageName: node
linkType: hard

View File

@ -81,6 +81,7 @@ module.exports = {
'/__mocks-ng-workspace__/',
'/__testfixtures__/',
'^.*\\.stories\\.[jt]sx?$',
'typings.d.ts$',
],
globals: {
DOCS_MODE: false,

View File

@ -52,6 +52,7 @@
"@storybook/csf-tools": "6.5.0-beta.1",
"@storybook/node-logger": "6.5.0-beta.1",
"@storybook/semver": "^7.3.2",
"@storybook/telemetry": "6.5.0-beta.1",
"boxen": "^5.1.2",
"chalk": "^4.1.0",
"commander": "^6.2.1",

View File

@ -2,9 +2,9 @@ import chalk from 'chalk';
import dedent from 'ts-dedent';
import { ConfigFile, readConfig, writeConfig } from '@storybook/csf-tools';
import { getStorybookInfo } from '@storybook/core-common';
import { Fix } from '../types';
import { getStorybookInfo } from '../helpers/getStorybookInfo';
import { PackageJson, writePackageJson } from '../../js-package-manager';
const logger = console;

View File

@ -1,9 +1,9 @@
import chalk from 'chalk';
import dedent from 'ts-dedent';
import { ConfigFile, readConfig, writeConfig } from '@storybook/csf-tools';
import { getStorybookInfo } from '@storybook/core-common';
import { findEslintFile, SUPPORTED_ESLINT_EXTENSIONS } from '../helpers/getEslintInfo';
import { getStorybookInfo } from '../helpers/getStorybookInfo';
import type { Fix } from '../types';

View File

@ -3,8 +3,8 @@ import dedent from 'ts-dedent';
import semver from '@storybook/semver';
import { ConfigFile, readConfig, writeConfig } from '@storybook/csf-tools';
import { getStorybookInfo } from '@storybook/core-common';
import { getStorybookInfo } from '../helpers/getStorybookInfo';
import { Fix } from '../types';
const logger = console;

View File

@ -2,8 +2,8 @@ import chalk from 'chalk';
import dedent from 'ts-dedent';
import semver from '@storybook/semver';
import { ConfigFile, readConfig, writeConfig } from '@storybook/csf-tools';
import { getStorybookInfo } from '@storybook/core-common';
import { Fix } from '../types';
import { getStorybookInfo } from '../helpers/getStorybookInfo';
import { PackageJsonWithDepsAndDevDeps } from '../../js-package-manager';
const logger = console;

View File

@ -90,7 +90,7 @@ const getFrameworkPreset = (
return matcherFunction(matcher) ? preset : null;
};
export function detectFrameworkPreset(packageJson = {}) {
export function detectFrameworkPreset(packageJson = {} as PackageJson) {
const result = [...supportedTemplates, unsupportedTemplate].find((framework) => {
return getFrameworkPreset(packageJson, framework) !== null;
});
@ -171,7 +171,12 @@ export function isStorybookInstalled(dependencies: PackageJson | false, force?:
export function detectLanguage() {
let language = SupportedLanguage.JAVASCRIPT;
const packageJson = readPackageJson();
let packageJson;
try {
packageJson = readPackageJson();
} catch (err) {
//
}
const bowerJson = getBowerJson();
if (!packageJson && !bowerJson) {
return language;
@ -185,7 +190,12 @@ export function detectLanguage() {
}
export function detect(options: { force?: boolean; html?: boolean } = {}) {
const packageJson = readPackageJson();
let packageJson;
try {
packageJson = readPackageJson();
} catch (err) {
//
}
const bowerJson = getBowerJson();
if (!packageJson && !bowerJson) {

View File

@ -18,6 +18,15 @@ const pkg = sync({ cwd: __dirname }).packageJson;
const logger = console;
program.option(
'--disable-telemetry',
'disable sending telemetry data',
// default value is false, but if the user sets STORYBOOK_DISABLE_TELEMETRY, it can be true
process.env.STORYBOOK_DISABLE_TELEMETRY && process.env.STORYBOOK_DISABLE_TELEMETRY !== 'false'
);
program.option('--enable-crash-reports', 'enable sending crash reports to telemetry data');
program
.command('init')
.description('Initialize Storybook into your project.')

View File

@ -1,8 +1,9 @@
import { UpdateNotifier, Package } from 'update-notifier';
import chalk from 'chalk';
import prompts from 'prompts';
import { detect, isStorybookInstalled, detectLanguage, detectBuilder } from './detect';
import { telemetry } from '@storybook/telemetry';
import { installableProjectTypes, ProjectType, Builder } from './project_types';
import { detect, isStorybookInstalled, detectLanguage, detectBuilder } from './detect';
import { commandLog, codeLog, paddedLog } from './helpers';
import angularGenerator from './generators/ANGULAR';
import aureliaGenerator from './generators/AURELIA';
@ -26,7 +27,7 @@ import preactGenerator from './generators/PREACT';
import svelteGenerator from './generators/SVELTE';
import raxGenerator from './generators/RAX';
import serverGenerator from './generators/SERVER';
import { JsPackageManagerFactory, readPackageJson } from './js-package-manager';
import { JsPackageManagerFactory, JsPackageManager } from './js-package-manager';
import { NpmOptions } from './NpmOptions';
import { automigrate } from './automigrate';
@ -43,11 +44,14 @@ type CommandOptions = {
builder?: Builder;
linkable?: boolean;
commonJs?: boolean;
disableTelemetry?: boolean;
};
const installStorybook = (projectType: ProjectType, options: CommandOptions): Promise<void> => {
const packageManager = JsPackageManagerFactory.getPackageManager(options.useNpm);
const installStorybook = (
projectType: ProjectType,
packageManager: JsPackageManager,
options: CommandOptions
): Promise<void> => {
const npmOptions: NpmOptions = {
installAsDevDependencies: true,
skipInstall: options.skipInstall,
@ -247,7 +251,7 @@ const installStorybook = (projectType: ProjectType, options: CommandOptions): Pr
// Add a new line for the clear visibility.
logger.log();
return projectTypeInquirer(options);
return projectTypeInquirer(options, packageManager);
}
};
@ -257,7 +261,10 @@ const installStorybook = (projectType: ProjectType, options: CommandOptions): Pr
});
};
const projectTypeInquirer = async (options: { yes?: boolean }) => {
const projectTypeInquirer = async (
options: { yes?: boolean },
packageManager: JsPackageManager
) => {
const manualAnswer = options.yes
? true
: await prompts([
@ -280,15 +287,20 @@ const projectTypeInquirer = async (options: { yes?: boolean }) => {
})),
},
]);
return installStorybook(frameworkAnswer.manualFramework, options);
return installStorybook(frameworkAnswer.manualFramework, packageManager, options);
}
return Promise.resolve();
};
export async function initiate(options: CommandOptions, pkg: Package): Promise<void> {
const packageManager = JsPackageManagerFactory.getPackageManager(options.useNpm);
const welcomeMessage = 'sb init - the simplest way to add a Storybook to your project.';
logger.log(chalk.inverse(`\n ${welcomeMessage} \n`));
if (!options.disableTelemetry) {
telemetry('init');
}
// Update notify code.
new UpdateNotifier({
pkg,
@ -302,7 +314,7 @@ export async function initiate(options: CommandOptions, pkg: Package): Promise<v
: 'Detecting project type';
const done = commandLog(infoText);
const packageJson = readPackageJson();
const packageJson = packageManager.retrievePackageJson();
const isEsm = packageJson && packageJson.type === 'module';
try {
@ -329,7 +341,7 @@ export async function initiate(options: CommandOptions, pkg: Package): Promise<v
}
done();
await installStorybook(projectType, {
await installStorybook(projectType, packageManager, {
...options,
...(isEsm ? { commonJs: true } : undefined),
});

View File

@ -60,13 +60,12 @@ export abstract class JsPackageManager {
* If there is no `package.json` it will create one.
*/
public retrievePackageJson(): PackageJsonWithDepsAndDevDeps {
let packageJson = readPackageJson();
if (!packageJson) {
// It will create a new package.json file
let packageJson;
try {
packageJson = readPackageJson();
} catch (err) {
this.initPackageJson();
// read the newly created package.json file
packageJson = readPackageJson() || {};
packageJson = readPackageJson();
}
return {

View File

@ -1,11 +1,5 @@
export type PackageJson = {
dependencies?: Record<string, string>;
devDependencies?: Record<string, string>;
peerDependencies?: Record<string, string>;
scripts?: Record<string, string>;
eslintConfig?: any;
type?: 'module';
};
import type { PackageJson } from '@storybook/core-common';
export type { PackageJson } from '@storybook/core-common';
export type PackageJsonWithDepsAndDevDeps = PackageJson &
Required<Pick<PackageJson, 'dependencies' | 'devDependencies'>>;

View File

@ -1,11 +1,11 @@
import path from 'path';
import fs from 'fs';
import { PackageJson } from './PackageJson';
import type { PackageJson } from '@storybook/core-common';
export function readPackageJson(): PackageJson | false {
export function readPackageJson(): PackageJson {
const packageJsonPath = path.resolve('package.json');
if (!fs.existsSync(packageJsonPath)) {
return false;
throw new Error(`Could not read package.json file at ${packageJsonPath}`);
}
const jsonContent = fs.readFileSync(packageJsonPath, 'utf8');

View File

@ -1,4 +1,5 @@
import { sync as spawnSync } from 'cross-spawn';
import { telemetry } from '@storybook/telemetry';
import semver from '@storybook/semver';
import { logger } from '@storybook/node-logger';
import {
@ -139,12 +140,23 @@ interface UpgradeOptions {
useNpm: boolean;
dryRun: boolean;
yes: boolean;
disableTelemetry: boolean;
}
export const upgrade = async ({ prerelease, skipCheck, useNpm, dryRun, yes }: UpgradeOptions) => {
export const upgrade = async ({
prerelease,
skipCheck,
useNpm,
dryRun,
yes,
...options
}: UpgradeOptions) => {
const packageManager = JsPackageManagerFactory.getPackageManager(useNpm);
commandLog(`Checking for latest versions of '@storybook/*' packages`);
if (!options.disableTelemetry) {
telemetry('upgrade', { prerelease });
}
let flags = [];
if (!dryRun) flags.push('--upgrade');

View File

@ -11,6 +11,9 @@ export * from './utils/interpret-require';
export * from './utils/load-custom-babel-config';
export * from './utils/load-custom-presets';
export * from './utils/load-custom-webpack-config';
export * from './utils/load-main-config';
export * from './utils/get-storybook-configuration';
export * from './utils/get-storybook-info';
export * from './utils/load-manager-or-addons-file';
export * from './utils/load-preview-or-config-file';
export * from './utils/log-config';

View File

@ -50,6 +50,20 @@ export interface CoreConfig {
builder: BuilderConfig;
disableWebpackDefaults?: boolean;
channelOptions?: Partial<TelejsonOptions>;
/**
* Disables the generation of project.json, a file containing Storybook metadata
*/
disableProjectJson?: boolean;
/**
* Disables Storybook telemetry
* @see https://storybook.js.org/telemetry
*/
disableTelemetry?: boolean;
/**
* Enable crash reports to be sent to Storybook telemetry
* @see https://storybook.js.org/telemetry
*/
enableCrashReports?: boolean;
/**
* enable CORS headings to run document in a "secure context"
* see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements
@ -141,6 +155,11 @@ export interface PackageJson {
version: string;
dependencies?: Record<string, string>;
devDependencies?: Record<string, string>;
peerDependencies?: Record<string, string>;
scripts?: Record<string, string>;
eslintConfig?: Record<string, any>;
type?: 'module';
[key: string]: any;
}
// TODO: This could be exported to the outside world and used in `options.ts` file of each `@storybook/APP`
@ -165,6 +184,8 @@ export interface CLIOptions {
ignorePreview?: boolean;
previewUrl?: string;
forceBuildPreview?: boolean;
disableTelemetry?: boolean;
enableCrashReports?: boolean;
host?: string;
/**
* @deprecated Use 'staticDirs' Storybook Configuration option instead

View File

@ -1,4 +1,4 @@
import { getStorybookConfiguration } from './getStorybookConfiguration';
import { getStorybookConfiguration } from './get-storybook-configuration';
describe('getStorybookConfiguration', () => {
it('handles short names', () => {

View File

@ -9,6 +9,10 @@ export function getStorybookConfiguration(
shortName: string,
longName: string
) {
if (!storybookScript) {
return null;
}
const parts = storybookScript.split(/[\s='"]+/);
let index = parts.indexOf(longName);
if (index === -1) {

View File

@ -1,7 +1,7 @@
import path from 'path';
import fse from 'fs-extra';
import { PackageJsonWithDepsAndDevDeps } from '../../js-package-manager';
import { getStorybookConfiguration } from './getStorybookConfiguration';
import { getStorybookConfiguration } from './get-storybook-configuration';
import { PackageJson } from '../types';
interface StorybookInfo {
framework: string;
@ -32,7 +32,7 @@ const viewLayers: Record<string, string> = {
const logger = console;
const findDependency = (
{ dependencies, devDependencies, peerDependencies }: PackageJsonWithDepsAndDevDeps,
{ dependencies, devDependencies, peerDependencies }: PackageJson,
predicate: (entry: [string, string]) => string
) => [
Object.entries(dependencies || {}).find(predicate),
@ -40,7 +40,7 @@ const findDependency = (
Object.entries(peerDependencies || {}).find(predicate),
];
const getFrameworkInfo = (packageJson: PackageJsonWithDepsAndDevDeps) => {
const getFrameworkInfo = (packageJson: PackageJson) => {
// Pull the viewlayer from dependencies in package.json
const [dep, devDep, peerDep] = findDependency(packageJson, ([key]) => viewLayers[key]);
const [pkg, version] = dep || devDep || peerDep || [];
@ -70,7 +70,7 @@ const findConfigFile = (prefix: string, configDir: string) => {
return extension ? `${filePrefix}.${extension}` : null;
};
const getConfigInfo = (packageJson: PackageJsonWithDepsAndDevDeps) => {
const getConfigInfo = (packageJson: PackageJson) => {
let configDir = '.storybook';
const storybookScript = packageJson.scripts?.storybook;
if (storybookScript) {
@ -86,7 +86,7 @@ const getConfigInfo = (packageJson: PackageJsonWithDepsAndDevDeps) => {
};
};
export const getStorybookInfo = (packageJson: PackageJsonWithDepsAndDevDeps) => {
export const getStorybookInfo = (packageJson: PackageJson) => {
const frameworkInfo = getFrameworkInfo(packageJson);
const configInfo = getConfigInfo(packageJson);

View File

@ -0,0 +1,10 @@
import path from 'path';
import { serverRequire } from './interpret-require';
import { validateConfigurationFiles } from './validate-configuration-files';
import { StorybookConfig } from '../types';
export function loadMainConfig({ configDir }: { configDir: string }): StorybookConfig {
validateConfigurationFiles(configDir);
return serverRequire(path.resolve(configDir, 'main'));
}

View File

@ -0,0 +1,31 @@
import chalk from 'chalk';
import { cache } from './cache';
const TELEMETRY_KEY_NOTIFY_DATE = 'telemetry-notification-date';
const logger = console;
export const notifyTelemetry = async () => {
const telemetryNotifyDate = await cache.get(TELEMETRY_KEY_NOTIFY_DATE, null);
// The end-user has already been notified about our telemetry integration. We
// don't need to constantly annoy them about it.
// We will re-inform users about the telemetry if significant changes are
// ever made.
if (telemetryNotifyDate) {
return;
}
cache.set(TELEMETRY_KEY_NOTIFY_DATE, Date.now().toString());
logger.log(
`${chalk.magenta.bold(
'Attention'
)}: Storybook now collects completely anonymous telemetry regarding usage.`
);
logger.log(`This information is used to shape Storybook's roadmap and prioritize features.`);
logger.log(
`You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:`
);
logger.log(chalk.cyan('https://storybook.js.org/telemetry'));
logger.log();
};

View File

@ -50,6 +50,7 @@
"@storybook/node-logger": "6.5.0-beta.1",
"@storybook/semver": "^7.3.2",
"@storybook/store": "6.5.0-beta.1",
"@storybook/telemetry": "6.5.0-beta.1",
"@types/node": "^14.0.10 || ^16.0.0",
"@types/node-fetch": "^2.5.7",
"@types/pretty-hrtime": "^1.0.0",

View File

@ -1,5 +1,262 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`cra-ts-essentials manager dev 1`] = `
Object {
"entry": Array [
"NODE_MODULES/@storybook/addon-ie11/dist/event-source-polyfill.js",
"ROOT/lib/core-client/dist/esm/globals/polyfills.js",
"ROOT/lib/core-client/dist/esm/manager/index.js",
"ROOT/addons/docs/manager.js",
"ROOT/addons/controls/manager.js",
"ROOT/addons/actions/manager.js",
"ROOT/addons/backgrounds/manager.js",
"ROOT/addons/toolbars/manager.js",
"ROOT/addons/measure/manager.js",
"ROOT/addons/outline/manager.js",
],
"keys": Array [
"name",
"mode",
"bail",
"devtool",
"entry",
"output",
"watchOptions",
"plugins",
"module",
"resolve",
"resolveLoader",
"recordsPath",
"performance",
"optimization",
],
"module": Object {
"rules": Array [
Object {
"exclude": Array [
"NODE_MODULES/",
"/dist/",
],
"include": Array [
"ROOT",
],
"test": "/\\\\.(mjs|tsx?|jsx?)$/",
"use": Array [
Object {
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
"babelrc": false,
"configFile": false,
"plugins": Array [
"NODE_MODULES/@babel/plugin-transform-shorthand-properties/lib/index.js",
"NODE_MODULES/@babel/plugin-transform-block-scoping/lib/index.js",
Array [
"NODE_MODULES/@babel/plugin-proposal-decorators/lib/index.js",
Object {
"legacy": true,
},
],
Array [
"NODE_MODULES/@babel/plugin-proposal-class-properties/lib/index.js",
Object {
"loose": true,
},
],
Array [
"NODE_MODULES/@babel/plugin-proposal-private-property-in-object/lib/index.js",
Object {
"loose": true,
},
],
Array [
"NODE_MODULES/@babel/plugin-proposal-private-methods/lib/index.js",
Object {
"loose": true,
},
],
"NODE_MODULES/@babel/plugin-proposal-export-default-from/lib/index.js",
"NODE_MODULES/@babel/plugin-syntax-dynamic-import/lib/index.js",
Array [
"NODE_MODULES/@babel/plugin-proposal-object-rest-spread/lib/index.js",
Object {
"loose": true,
"useBuiltIns": true,
},
],
"NODE_MODULES/@babel/plugin-transform-classes/lib/index.js",
"NODE_MODULES/@babel/plugin-transform-arrow-functions/lib/index.js",
"NODE_MODULES/@babel/plugin-transform-parameters/lib/index.js",
"NODE_MODULES/@babel/plugin-transform-destructuring/lib/index.js",
"NODE_MODULES/@babel/plugin-transform-spread/lib/index.js",
"NODE_MODULES/@babel/plugin-transform-for-of/lib/index.js",
"NODE_MODULES/babel-plugin-macros/dist/index.js",
"NODE_MODULES/@babel/plugin-proposal-optional-chaining/lib/index.js",
"NODE_MODULES/@babel/plugin-proposal-nullish-coalescing-operator/lib/index.js",
Array [
"NODE_MODULES/babel-plugin-polyfill-corejs3/lib/index.js",
Object {
"absoluteImports": "NODE_MODULES/core-js/index.js",
"method": "usage-global",
"version": "*",
},
],
"NODE_MODULES/@babel/plugin-transform-template-literals/lib/index.js",
],
"presets": Array [
Array [
"NODE_MODULES/@babel/preset-env/lib/index.js",
Object {
"loose": true,
"shippedProposals": true,
},
],
"NODE_MODULES/@babel/preset-typescript/lib/index.js",
"NODE_MODULES/@babel/preset-react/lib/index.js",
],
"sourceType": "unambiguous",
},
},
],
},
Object {
"include": [Function],
"test": "/\\\\.js$/",
"use": Array [
Object {
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
"plugins": Array [
"NODE_MODULES/@babel/plugin-transform-shorthand-properties/lib/index.js",
"NODE_MODULES/@babel/plugin-transform-block-scoping/lib/index.js",
Array [
"NODE_MODULES/@babel/plugin-proposal-decorators/lib/index.js",
Object {
"legacy": true,
},
],
Array [
"NODE_MODULES/@babel/plugin-proposal-class-properties/lib/index.js",
Object {
"loose": true,
},
],
Array [
"NODE_MODULES/@babel/plugin-proposal-private-property-in-object/lib/index.js",
Object {
"loose": true,
},
],
Array [
"NODE_MODULES/@babel/plugin-proposal-private-methods/lib/index.js",
Object {
"loose": true,
},
],
"NODE_MODULES/@babel/plugin-proposal-export-default-from/lib/index.js",
"NODE_MODULES/@babel/plugin-syntax-dynamic-import/lib/index.js",
Array [
"NODE_MODULES/@babel/plugin-proposal-object-rest-spread/lib/index.js",
Object {
"loose": true,
"useBuiltIns": true,
},
],
"NODE_MODULES/@babel/plugin-transform-classes/lib/index.js",
"NODE_MODULES/@babel/plugin-transform-arrow-functions/lib/index.js",
"NODE_MODULES/@babel/plugin-transform-parameters/lib/index.js",
"NODE_MODULES/@babel/plugin-transform-destructuring/lib/index.js",
"NODE_MODULES/@babel/plugin-transform-spread/lib/index.js",
"NODE_MODULES/@babel/plugin-transform-for-of/lib/index.js",
"NODE_MODULES/babel-plugin-macros/dist/index.js",
"NODE_MODULES/@babel/plugin-proposal-optional-chaining/lib/index.js",
"NODE_MODULES/@babel/plugin-proposal-nullish-coalescing-operator/lib/index.js",
Array [
"NODE_MODULES/babel-plugin-polyfill-corejs3/lib/index.js",
Object {
"absoluteImports": "NODE_MODULES/core-js/index.js",
"method": "usage-global",
"version": "*",
},
],
],
"presets": Array [
Array [
"NODE_MODULES/@babel/preset-env/lib/index.js",
Object {
"loose": true,
"modules": false,
"shippedProposals": true,
"targets": "defaults",
},
],
"NODE_MODULES/@babel/preset-react/lib/index.js",
],
"sourceType": "unambiguous",
},
},
],
},
Object {
"test": "/\\\\.css$/",
"use": Array [
"NODE_MODULES/style-loader/dist/cjs.js",
Object {
"loader": "NODE_MODULES/css-loader/dist/cjs.js",
"options": Object {
"importLoaders": 1,
},
},
],
},
Object {
"loader": "NODE_MODULES/file-loader/dist/cjs.js",
"options": Object {
"name": "static/media/[path][name].[ext]",
},
"test": "/\\\\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\\\\?.*)?$/",
},
Object {
"loader": "NODE_MODULES/url-loader/dist/cjs.js",
"options": Object {
"limit": 10000,
"name": "static/media/[path][name].[ext]",
},
"test": "/\\\\.(mp4|webm|wav|mp3|m4a|aac|oga)(\\\\?.*)?$/",
},
Object {
"include": "NODE_MODULES[\\\\\\\\/](@storybook[\\\\\\\\/]node_logger|@testing-library[\\\\\\\\/]dom|@testing-library[\\\\\\\\/]user-event|acorn-jsx|ansi-align|ansi-colors|ansi-escapes|ansi-regex|ansi-styles|better-opn|boxen|camelcase|chalk|color-convert|commander|find-cache-dir|find-up|fs-extra|highlight.js|json5|node-fetch|pkg-dir|prettier|pretty-format|react-dev-utils|resolve-from|semver|slash|strip-ansi|uuid)/",
"test": "/\\\\.js$/",
"use": Array [
Object {
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
"presets": Array [
Array [
"@babel/preset-env",
Object {
"targets": Object {
"ie": "11",
},
},
"storybook-addon-ie11",
],
],
"sourceType": "unambiguous",
},
},
],
},
],
},
"plugins": Array [
"VirtualModulesPlugin",
"HtmlWebpackPlugin",
"CaseSensitivePathsPlugin",
"DefinePlugin",
],
}
`;
exports[`cra-ts-essentials manager prod 1`] = `
Object {
"entry": Array [

View File

@ -1,5 +1,502 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`cra-ts-essentials preview dev 1`] = `
Object {
"entry": Array [
"ROOT/lib/core-client/dist/esm/globals/polyfills.js",
"ROOT/lib/core-client/dist/esm/globals/globals.js",
"NODE_MODULES/@storybook/addon-ie11/dist/event-source-polyfill.js",
"ROOT/storybook-init-framework-entry.js",
"ROOT/app/react/dist/esm/client/docs/config-generated-config-entry.js",
"ROOT/app/react/dist/esm/client/preview/config-generated-config-entry.js",
"ROOT/addons/docs/preview.js-generated-config-entry.js",
"ROOT/addons/actions/preview.js-generated-config-entry.js",
"ROOT/addons/backgrounds/preview.js-generated-config-entry.js",
"ROOT/addons/measure/preview.js-generated-config-entry.js",
"ROOT/addons/outline/preview.js-generated-config-entry.js",
"ROOT/examples/cra-ts-essentials/.storybook/preview.tsx-generated-config-entry.js",
"ROOT/generated-stories-entry.js",
],
"keys": Array [
"name",
"mode",
"bail",
"devtool",
"entry",
"output",
"watchOptions",
"plugins",
"module",
"resolve",
"resolveLoader",
"optimization",
"performance",
],
"module": Object {
"rules": Array [
Object {
"test": "/\\\\.md$/",
"use": Array [
Object {
"loader": "NODE_MODULES/raw-loader/dist/cjs.js",
},
],
},
Object {
"parser": Object {
"requireEnsure": false,
},
},
Object {
"oneOf": Array [
Object {
"loader": "NODE_MODULES/url-loader/dist/cjs.js",
"options": Object {
"limit": 10000,
"mimetype": "image/avif",
"name": "static/media/[name].[hash:8].[ext]",
},
"test": Array [
"/\\\\.avif$/",
],
},
Object {
"loader": "NODE_MODULES/url-loader/dist/cjs.js",
"options": Object {
"limit": 10000,
"name": "static/media/[name].[hash:8].[ext]",
},
"test": Array [
"/\\\\.bmp$/",
"/\\\\.gif$/",
"/\\\\.jpe?g$/",
"/\\\\.png$/",
],
},
Object {
"include": Array [
"ROOT/src",
"ROOT/examples/cra-ts-essentials/.storybook",
],
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
"babelrc": false,
"cacheCompression": false,
"cacheDirectory": true,
"cacheIdentifier": "production:babel-plugin-named-asset-import@:babel-preset-react-app@10.0.1:react-dev-utils@11.0.4:react-scripts@4.0.3",
"compact": true,
"configFile": false,
"customize": "NODE_MODULES/babel-preset-react-app/webpack-overrides.js",
"extends": undefined,
"overrides": Array [
Object {
"plugins": Array [
Array [
"NODE_MODULES/babel-plugin-react-docgen/lib/index.js",
Object {
"DOC_GEN_COLLECTION_NAME": "STORYBOOK_REACT_CLASSES",
},
],
],
"test": "/\\\\.(mjs|jsx?)$/",
},
],
"plugins": Array [
Array [
"NODE_MODULES/babel-plugin-named-asset-import/index.js",
Object {
"loaderMap": Object {
"svg": Object {
"ReactComponent": "@svgr/webpack?-svgo,+titleProp,+ref![path]",
},
},
},
],
],
"presets": Array [
Array [
"@babel/preset-env",
Object {
"targets": Object {
"ie": "11",
},
},
"storybook-addon-ie11",
],
Array [
"NODE_MODULES/babel-preset-react-app/index.js",
Object {
"runtime": "automatic",
},
],
],
},
"test": "/\\\\.(js|mjs|jsx|ts|tsx)$/",
},
Object {
"exclude": "/@babel(?:\\\\/|\\\\\\\\{1,2})runtime/",
"include": Array [
"ROOT/examples/cra-ts-essentials/.storybook",
],
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
"babelrc": false,
"cacheCompression": false,
"cacheDirectory": true,
"cacheIdentifier": "production:babel-plugin-named-asset-import@:babel-preset-react-app@10.0.1:react-dev-utils@11.0.4:react-scripts@4.0.3",
"compact": false,
"configFile": false,
"inputSourceMap": true,
"presets": Array [
Array [
"NODE_MODULES/babel-preset-react-app/dependencies.js",
Object {
"helpers": true,
},
],
],
"sourceMaps": true,
},
"test": "/\\\\.(js|mjs)$/",
},
Object {
"exclude": Array [
"/\\\\.module\\\\.css$/",
"/@storybook/",
],
"include": undefined,
"sideEffects": true,
"test": "/\\\\.css$/",
"use": Array [
Object {
"loader": "NODE_MODULES/mini-css-extract-plugin/dist/loader.js",
"options": Object {
"publicPath": "../../",
},
},
Object {
"loader": "NODE_MODULES/css-loader/dist/cjs.js",
"options": Object {
"importLoaders": 1,
"sourceMap": true,
},
},
Object {
"loader": "NODE_MODULES/postcss-loader/src/index.js",
"options": Object {
"ident": "postcss",
"plugins": [Function],
"sourceMap": true,
},
},
],
},
Object {
"test": "/\\\\.module\\\\.css$/",
"use": Array [
Object {
"loader": "NODE_MODULES/mini-css-extract-plugin/dist/loader.js",
"options": Object {
"publicPath": "../../",
},
},
Object {
"loader": "NODE_MODULES/css-loader/dist/cjs.js",
"options": Object {
"importLoaders": 1,
"modules": Object {
"getLocalIdent": [Function],
},
"sourceMap": true,
},
},
Object {
"loader": "NODE_MODULES/postcss-loader/src/index.js",
"options": Object {
"ident": "postcss",
"plugins": [Function],
"sourceMap": true,
},
},
],
},
Object {
"exclude": "/\\\\.module\\\\.(scss|sass)$/",
"sideEffects": true,
"test": "/\\\\.(scss|sass)$/",
"use": Array [
Object {
"loader": "NODE_MODULES/mini-css-extract-plugin/dist/loader.js",
"options": Object {
"publicPath": "../../",
},
},
Object {
"loader": "NODE_MODULES/css-loader/dist/cjs.js",
"options": Object {
"importLoaders": 3,
"sourceMap": true,
},
},
Object {
"loader": "NODE_MODULES/postcss-loader/src/index.js",
"options": Object {
"ident": "postcss",
"plugins": [Function],
"sourceMap": true,
},
},
Object {
"loader": "NODE_MODULES/resolve-url-loader/index.js",
"options": Object {
"root": "ROOT/src",
"sourceMap": true,
},
},
Object {
"loader": "NODE_MODULES/sass-loader/dist/cjs.js",
"options": Object {
"sourceMap": true,
},
},
],
},
Object {
"test": "/\\\\.module\\\\.(scss|sass)$/",
"use": Array [
Object {
"loader": "NODE_MODULES/mini-css-extract-plugin/dist/loader.js",
"options": Object {
"publicPath": "../../",
},
},
Object {
"loader": "NODE_MODULES/css-loader/dist/cjs.js",
"options": Object {
"importLoaders": 3,
"modules": Object {
"getLocalIdent": [Function],
},
"sourceMap": true,
},
},
Object {
"loader": "NODE_MODULES/postcss-loader/src/index.js",
"options": Object {
"ident": "postcss",
"plugins": [Function],
"sourceMap": true,
},
},
Object {
"loader": "NODE_MODULES/resolve-url-loader/index.js",
"options": Object {
"root": "ROOT/src",
"sourceMap": true,
},
},
Object {
"loader": "NODE_MODULES/sass-loader/dist/cjs.js",
"options": Object {
"sourceMap": true,
},
},
],
},
Object {
"exclude": Array [
"/\\\\.(js|mjs|jsx|ts|tsx)$/",
"/\\\\.html$/",
"/\\\\.json$/",
"/\\\\.(ejs|md|mdx)$/",
],
"loader": "NODE_MODULES/file-loader/dist/cjs.js",
"options": Object {
"name": "static/media/[name].[hash:8].[ext]",
},
},
],
},
Object {
"include": "NODE_MODULES[\\\\\\\\/](@storybook[\\\\\\\\/]node_logger|@testing-library[\\\\\\\\/]dom|@testing-library[\\\\\\\\/]user-event|acorn-jsx|ansi-align|ansi-colors|ansi-escapes|ansi-regex|ansi-styles|better-opn|boxen|camelcase|chalk|color-convert|commander|find-cache-dir|find-up|fs-extra|highlight.js|json5|node-fetch|pkg-dir|prettier|pretty-format|react-dev-utils|resolve-from|semver|slash|strip-ansi|uuid)/",
"test": "/\\\\.js$/",
"use": Array [
Object {
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
"presets": Array [
Array [
"@babel/preset-env",
Object {
"targets": Object {
"ie": "11",
},
},
"storybook-addon-ie11",
],
],
"sourceType": "unambiguous",
},
},
],
},
Object {
"include": "NODE_MODULES\\\\/acorn-jsx/",
"test": "/\\\\.js$/",
"use": Array [
Object {
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
"presets": Array [
Array [
"NODE_MODULES/@babel/preset-env/lib/index.js",
Object {
"modules": "commonjs",
},
],
],
},
},
],
},
Object {
"test": "/(stories|story)\\\\.mdx$/",
"use": Array [
Object {
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
"babelrc": false,
"cacheDirectory": "NODE_MODULES/.cache/storybook/babel",
"configFile": false,
"overrides": Array [
Object {
"plugins": Array [
Array [
"NODE_MODULES/babel-plugin-react-docgen/lib/index.js",
Object {
"DOC_GEN_COLLECTION_NAME": "STORYBOOK_REACT_CLASSES",
},
],
],
"test": "/\\\\.(mjs|jsx?)$/",
},
],
"plugins": Array [
Array [
"NODE_MODULES/@babel/plugin-transform-react-jsx/lib/index.js",
Object {
"pragma": "React.createElement",
"pragmaFrag": "React.Fragment",
},
],
],
"presets": Array [
Array [
"@babel/preset-env",
Object {
"targets": Object {
"ie": "11",
},
},
"storybook-addon-ie11",
],
],
},
},
Object {
"loader": "NODE_MODULES/@storybook/mdx1-csf/loader.js",
},
],
},
Object {
"exclude": "/(stories|story)\\\\.mdx$/",
"test": "/\\\\.mdx$/",
"use": Array [
Object {
"loader": "NODE_MODULES/babel-loader/lib/index.js",
"options": Object {
"babelrc": false,
"cacheDirectory": "NODE_MODULES/.cache/storybook/babel",
"configFile": false,
"overrides": Array [
Object {
"plugins": Array [
Array [
"NODE_MODULES/babel-plugin-react-docgen/lib/index.js",
Object {
"DOC_GEN_COLLECTION_NAME": "STORYBOOK_REACT_CLASSES",
},
],
],
"test": "/\\\\.(mjs|jsx?)$/",
},
],
"plugins": Array [
Array [
"NODE_MODULES/@babel/plugin-transform-react-jsx/lib/index.js",
Object {
"pragma": "React.createElement",
"pragmaFrag": "React.Fragment",
},
],
],
"presets": Array [
Array [
"@babel/preset-env",
Object {
"targets": Object {
"ie": "11",
},
},
"storybook-addon-ie11",
],
],
},
},
Object {
"loader": "NODE_MODULES/@storybook/mdx1-csf/loader.js",
"options": Object {
"remarkPlugins": Array [
[Function],
[Function],
],
"skipCsf": true,
},
},
],
},
Object {
"enforce": "pre",
"loader": "ROOT/lib/source-loader/dist/cjs/index.js",
"options": Object {
"injectStoryParameters": true,
"inspectLocalDependencies": true,
},
"test": "/\\\\.(stories|story)\\\\.[tj]sx?$/",
},
],
},
"plugins": Array [
"FilterWarningsPlugin",
"VirtualModulesPlugin",
"HtmlWebpackPlugin",
"DefinePlugin",
"CaseSensitivePathsPlugin",
"ProgressPlugin",
"InlineChunkHtmlPlugin",
"InterpolateHtmlPlugin",
"ModuleNotFoundPlugin",
"MiniCssExtractPlugin",
"ManifestPlugin",
"IgnorePlugin",
"ForkTsCheckerWebpackPlugin",
"ESLintWebpackPlugin",
"IgnorePlugin",
"DocgenPlugin",
],
}
`;
exports[`cra-ts-essentials preview prod 1`] = `
Object {
"entry": Array [

View File

@ -1,14 +1,18 @@
import { logger, instance as npmLog } from '@storybook/node-logger';
import type {
import prompts from 'prompts';
import {
CLIOptions,
LoadOptions,
BuilderOptions,
Options,
StorybookConfig,
CoreConfig,
resolvePathInStorybookCache,
loadAllPresets,
cache,
} from '@storybook/core-common';
import { resolvePathInStorybookCache, loadAllPresets, cache } from '@storybook/core-common';
import { telemetry } from '@storybook/telemetry';
import dedent from 'ts-dedent';
import prompts from 'prompts';
import global from 'global';
import path from 'path';
@ -130,17 +134,18 @@ export async function buildDevStandalone(options: CLIOptions & LoadOptions & Bui
export async function buildDev(loadOptions: LoadOptions) {
const cliOptions = await getDevCli(loadOptions.packageJson);
const options: CLIOptions & LoadOptions & BuilderOptions = {
...cliOptions,
...loadOptions,
configDir: loadOptions.configDir || cliOptions.configDir || './.storybook',
configType: 'DEVELOPMENT',
ignorePreview: !!cliOptions.previewUrl && !cliOptions.forceBuildPreview,
docsMode: !!cliOptions.docs,
cache,
};
try {
await buildDevStandalone({
...cliOptions,
...loadOptions,
configDir: loadOptions.configDir || cliOptions.configDir || './.storybook',
configType: 'DEVELOPMENT',
ignorePreview: !!cliOptions.previewUrl && !cliOptions.forceBuildPreview,
docsMode: !!cliOptions.docs,
cache,
});
await buildDevStandalone(options);
} catch (error) {
// this is a weird bugfix, somehow 'node-pre-gyp' is polluting the npmLog header
npmLog.heading = '';
@ -171,6 +176,56 @@ export async function buildDev(loadOptions: LoadOptions) {
);
logger.line();
const presets = loadAllPresets({
corePresets: [require.resolve('./presets/common-preset')],
overridePresets: [],
...options,
});
const core = await presets.apply<CoreConfig>('core');
if (!core?.disableTelemetry) {
let enableCrashReports;
if (core.enableCrashReports !== undefined) {
enableCrashReports = core.enableCrashReports;
} else {
const valueFromCache = await cache.get('enableCrashreports');
if (valueFromCache !== undefined) {
enableCrashReports = valueFromCache;
} else {
const valueFromPrompt = await promptCrashReports(options);
if (valueFromPrompt !== undefined) {
enableCrashReports = valueFromPrompt;
}
}
}
await telemetry(
'error-dev',
{ error },
{
immediate: true,
configDir: options.configDir,
enableCrashReports,
}
);
}
process.exit(1);
}
}
const promptCrashReports = async ({ packageJson }: CLIOptions & LoadOptions & BuilderOptions) => {
if (process.env.CI) {
return undefined;
}
const { enableCrashReports } = await prompts({
type: 'confirm',
name: 'enableCrashReports',
message: `Would you like to send crash reports to Storybook?`,
initial: true,
});
await cache.set('enableCrashreports', enableCrashReports);
return enableCrashReports;
};

View File

@ -6,7 +6,7 @@ import dedent from 'ts-dedent';
import global from 'global';
import { logger } from '@storybook/node-logger';
import { telemetry } from '@storybook/telemetry';
import type {
LoadOptions,
CLIOptions,
@ -16,7 +16,7 @@ import type {
StorybookConfig,
CoreConfig,
} from '@storybook/core-common';
import { loadAllPresets, cache, normalizeStories, logConfig } from '@storybook/core-common';
import { normalizeStories, loadAllPresets, cache, logConfig } from '@storybook/core-common';
import { getProdCli } from './cli';
import { outputStats } from './utils/output-stats';
@ -27,6 +27,8 @@ import {
import { getPreviewBuilder } from './utils/get-preview-builder';
import { getManagerBuilder } from './utils/get-manager-builder';
import { extractStoriesJson } from './utils/stories-json';
import { extractStorybookMetadata } from './utils/metadata';
import { StoryIndexGenerator } from './utils/StoryIndexGenerator';
export async function buildStaticStandalone(options: CLIOptions & LoadOptions & BuilderOptions) {
/* eslint-disable no-param-reassign */
@ -92,17 +94,56 @@ export async function buildStaticStandalone(options: CLIOptions & LoadOptions &
const features = await presets.apply<StorybookConfig['features']>('features');
global.FEATURES = features;
const extractTasks = [];
let initializedStoryIndexGenerator: Promise<StoryIndexGenerator> = Promise.resolve(undefined);
if (features?.buildStoriesJson || features?.storyStoreV7) {
const workingDir = process.cwd();
const directories = {
configDir: options.configDir,
workingDir: process.cwd(),
workingDir,
};
const stories = normalizeStories(await presets.apply('stories'), directories);
await extractStoriesJson(path.join(options.outputDir, 'stories.json'), stories, {
const normalizedStories = normalizeStories(await presets.apply('stories'), directories);
const generator = new StoryIndexGenerator(normalizedStories, {
...directories,
storiesV2Compatibility: !features?.breakingChangesV7 && !features?.storyStoreV7,
storyStoreV7: features?.storyStoreV7,
});
initializedStoryIndexGenerator = generator.initialize().then(() => generator);
extractTasks.push(
extractStoriesJson(
path.join(options.outputDir, 'stories.json'),
initializedStoryIndexGenerator
)
);
}
const core = await presets.apply<CoreConfig>('core');
if (!core?.disableTelemetry) {
initializedStoryIndexGenerator.then(async (generator) => {
if (!generator) {
return;
}
const storyIndex = await generator.getIndex();
const payload = storyIndex
? {
storyIndex: {
storyCount: Object.keys(storyIndex.stories).length,
version: storyIndex.v,
},
}
: undefined;
telemetry('build', payload, { configDir: options.configDir });
});
}
if (!core?.disableProjectJson) {
extractTasks.push(
extractStorybookMetadata(path.join(options.outputDir, 'project.json'), options.configDir)
);
}
const fullOptions: Options = {
@ -116,7 +157,6 @@ export async function buildStaticStandalone(options: CLIOptions & LoadOptions &
logConfig('Manager webpack config', await managerBuilder.getConfig(fullOptions));
}
const core = await presets.apply<CoreConfig | undefined>('core');
const builderName = typeof core?.builder === 'string' ? core.builder : core?.builder?.name;
const { getPrebuiltDir } =
builderName === 'webpack5'
@ -152,6 +192,7 @@ export async function buildStaticStandalone(options: CLIOptions & LoadOptions &
await managerBuilder.bail();
throw err;
}),
...extractTasks,
]);
if (options.webpackStatsJson) {
@ -165,21 +206,43 @@ export async function buildStaticStandalone(options: CLIOptions & LoadOptions &
export async function buildStatic({ packageJson, ...loadOptions }: LoadOptions) {
const cliOptions = getProdCli(packageJson);
const options: CLIOptions & LoadOptions & BuilderOptions = {
...cliOptions,
...loadOptions,
packageJson,
configDir: loadOptions.configDir || cliOptions.configDir || './.storybook',
outputDir: loadOptions.outputDir || cliOptions.outputDir || './storybook-static',
ignorePreview:
(!!loadOptions.ignorePreview || !!cliOptions.previewUrl) && !cliOptions.forceBuildPreview,
docsMode: !!cliOptions.docs,
configType: 'PRODUCTION',
cache,
};
try {
await buildStaticStandalone({
...cliOptions,
...loadOptions,
packageJson,
configDir: loadOptions.configDir || cliOptions.configDir || './.storybook',
outputDir: loadOptions.outputDir || cliOptions.outputDir || './storybook-static',
ignorePreview:
(!!loadOptions.ignorePreview || !!cliOptions.previewUrl) && !cliOptions.forceBuildPreview,
docsMode: !!cliOptions.docs,
configType: 'PRODUCTION',
cache,
await buildStaticStandalone(options);
} catch (error) {
logger.error(error);
const presets = loadAllPresets({
corePresets: [require.resolve('./presets/common-preset')],
overridePresets: [],
...options,
});
} catch (e) {
logger.error(e);
const core = await presets.apply<CoreConfig>('core');
if (!core?.disableTelemetry) {
await telemetry(
'error-build',
{ error },
{
immediate: true,
configDir: options.configDir,
enableCrashReports: options.enableCrashReports,
}
);
}
process.exit(1);
}
}

View File

@ -33,6 +33,13 @@ export async function getDevCli(packageJson: {
.option('--loglevel <level>', 'Control level of logging during build')
.option('--quiet', 'Suppress verbose build output')
.option('--no-version-updates', 'Suppress update check', true)
.option(
'--disable-telemetry',
'Disable sending telemetry',
// default value is false, but if the user sets STORYBOOK_DISABLE_TELEMETRY, it can be true
process.env.STORYBOOK_DISABLE_TELEMETRY && process.env.STORYBOOK_DISABLE_TELEMETRY !== 'false'
)
.option('--enable-crash-reports', 'enable sending crash reports to telemetry data')
.option(
'--no-release-notes',
'Suppress automatic redirects to the release notes after upgrading',

View File

@ -47,6 +47,13 @@ export function getProdCli(packageJson: {
.option('--docs', 'Build a documentation-only site using addon-docs')
.option('--modern', 'Use modern browser modules')
.option('--no-manager-cache', 'Do not cache the manager UI')
.option(
'--disable-telemetry',
'Disable sending telemetry',
// default value is false, but if the user sets STORYBOOK_DISABLE_TELEMETRY, it can be true
process.env.STORYBOOK_DISABLE_TELEMETRY && process.env.STORYBOOK_DISABLE_TELEMETRY !== 'false'
)
.option('--enable-crash-reports', 'enable sending crash reports to telemetry data')
.parse(process.argv);
logger.setLevel(program.loglevel);

View File

@ -39,12 +39,31 @@ jest.mock('@storybook/builder-webpack4', () => {
return actualBuilder;
});
jest.mock('./utils/stories-json', () => {
const actualStoriesJson = jest.requireActual('./utils/stories-json');
actualStoriesJson.extractStoriesJson = () => Promise.resolve();
return actualStoriesJson;
jest.mock('@storybook/telemetry', () => ({
getStorybookMetadata: jest.fn(() => ({})),
telemetry: jest.fn(() => ({})),
}));
jest.mock('./utils/StoryIndexGenerator', () => {
const { StoryIndexGenerator } = jest.requireActual('./utils/StoryIndexGenerator');
return {
StoryIndexGenerator: class extends StoryIndexGenerator {
initialize() {
return Promise.resolve(undefined);
}
getIndex() {
return { stories: {}, v: 3 };
}
},
};
});
jest.mock('./utils/stories-json', () => ({
extractStoriesJson: () => Promise.resolve(),
useStoriesJson: () => {},
}));
jest.mock('@storybook/manager-webpack4', () => {
const value = jest.fn();
const actualBuilder = jest.requireActual('@storybook/manager-webpack4');
@ -159,6 +178,7 @@ describe.each([
['prod', buildStaticStandalone],
['dev', buildDevStandalone],
])('%s', async (mode, builder) => {
console.log('running for ', mode, builder);
const options = {
...baseOptions,
...frameworkOptions,

View File

@ -1,33 +1,103 @@
import express, { Router } from 'express';
import compression from 'compression';
import type { Builder, Options, StorybookConfig } from '@storybook/core-common';
import { logConfig } from '@storybook/core-common';
import {
Builder,
CoreConfig,
normalizeStories,
Options,
StorybookConfig,
logConfig,
} from '@storybook/core-common';
import { telemetry } from '@storybook/telemetry';
import { getMiddleware } from './utils/middleware';
import { getServerAddresses } from './utils/server-address';
import { getServer } from './utils/server-init';
import { useStatics } from './utils/server-statics';
import { useStoriesJson } from './utils/stories-json';
import { useStorybookMetadata } from './utils/metadata';
import { getServerChannel } from './utils/get-server-channel';
import { openInBrowser } from './utils/open-in-browser';
import { getPreviewBuilder } from './utils/get-preview-builder';
import { getManagerBuilder } from './utils/get-manager-builder';
import { StoryIndexGenerator } from './utils/StoryIndexGenerator';
// @ts-ignore
export const router: Router = new Router();
export const DEBOUNCE = 100;
export async function storybookDevServer(options: Options) {
const startTime = process.hrtime();
const app = express();
const server = await getServer(app, options);
const serverChannel = getServerChannel(server);
app.use(compression({ level: 1 }));
const features = await options.presets.apply<StorybookConfig['features']>('features');
const core = await options.presets.apply<StorybookConfig['core']>('core');
const core = await options.presets.apply<CoreConfig>('core');
// try get index generator, if failed, send telemetry without storyCount, then rethrow the error
let initializedStoryIndexGenerator: Promise<StoryIndexGenerator> = Promise.resolve(undefined);
if (features?.buildStoriesJson || features?.storyStoreV7) {
try {
const workingDir = process.cwd();
const directories = {
configDir: options.configDir,
workingDir,
};
const normalizedStories = normalizeStories(
await options.presets.apply('stories'),
directories
);
const generator = new StoryIndexGenerator(normalizedStories, {
...directories,
workingDir,
storiesV2Compatibility: !features?.breakingChangesV7 && !features?.storyStoreV7,
storyStoreV7: features?.storyStoreV7,
});
initializedStoryIndexGenerator = generator.initialize().then(() => generator);
useStoriesJson({
router,
initializedStoryIndexGenerator,
normalizedStories,
serverChannel,
workingDir,
});
} catch (err) {
if (!core?.disableTelemetry) {
telemetry('start');
}
throw err;
}
}
if (!core?.disableTelemetry) {
initializedStoryIndexGenerator.then(async (generator) => {
if (!generator) {
return;
}
const storyIndex = await generator.getIndex();
const payload = storyIndex
? {
storyIndex: {
storyCount: Object.keys(storyIndex.stories).length,
version: storyIndex.v,
},
}
: undefined;
telemetry('start', payload, { configDir: options.configDir });
});
}
if (!core?.disableProjectJson) {
useStorybookMetadata(router, options.configDir);
}
app.use(compression({ level: 1 }));
if (typeof options.extendServer === 'function') {
options.extendServer(server);
@ -51,10 +121,6 @@ export async function storybookDevServer(options: Options) {
});
}
if (features?.buildStoriesJson || features?.storyStoreV7) {
await useStoriesJson(router, serverChannel, options);
}
// User's own static files
await useStatics(router, options);

View File

@ -6,6 +6,8 @@ import {
loadCustomBabelConfig,
getStorybookBabelConfig,
loadEnvs,
CoreConfig,
StorybookConfig,
} from '@storybook/core-common';
import type { Options } from '@storybook/core-common';
@ -61,13 +63,52 @@ export const typescript = () => ({
},
});
const optionalEnvToBoolean = (input: string | undefined): boolean | undefined => {
if (input === undefined) {
return undefined;
}
if (input.toUpperCase() === 'FALSE') {
return false;
}
if (input.toUpperCase() === 'TRUE') {
return true;
}
if (typeof input === 'string') {
return true;
}
return undefined;
};
/**
* If for some reason this config is not applied, the reason is that
* likely there is an addon that does `export core = () => ({ someConfig })`,
* instead of `export core = (existing) => ({ ...existing, someConfig })`,
* just overwriting everything and not merging with the existing values.
*/
export const core = async (existing: CoreConfig, options: Options): Promise<CoreConfig> => ({
...existing,
disableTelemetry: options.disableTelemetry === true,
enableCrashReports:
options.enableCrashReports || optionalEnvToBoolean(process.env.STORYBOOK_ENABLE_CRASH_REPORTS),
});
export const config = async (base: any, options: Options) => {
return [...(await options.presets.apply('previewAnnotations', [], options)), ...base];
};
export const features = async (existing: Record<string, boolean>) => ({
export const features = async (
existing: StorybookConfig['features']
): Promise<StorybookConfig['features']> => ({
...existing,
postcss: true,
emotionAlias: false, // TODO remove in 7.0, this no longer does anything
warnOnLegacyHierarchySeparator: true,
buildStoriesJson: false,
storyStoreV7: false,
modernInlineRender: false,
breakingChangesV7: false,
interactionsDebugger: false,
babelModeV7: false,
argTypeTargetsV7: false,
previewMdx2: false,
});

View File

@ -7,7 +7,7 @@ export async function getPreviewBuilder(configDir: Options['configDir']) {
const mainFile = getInterpretedFile(main);
const { core } = mainFile ? serverRequire(mainFile) : { core: null };
let builderPackage: string;
if (core) {
if (core?.builder) {
const builderName = typeof core.builder === 'string' ? core.builder : core.builder?.name;
builderPackage = require.resolve(
['webpack4', 'webpack5'].includes(builderName)

View File

@ -0,0 +1,17 @@
import fs from 'fs-extra';
import { Request, Response, Router } from 'express';
import { getStorybookMetadata } from '@storybook/telemetry';
export async function extractStorybookMetadata(outputFile: string, configDir: string) {
const storybookMetadata = await getStorybookMetadata(configDir);
await fs.writeJson(outputFile, storybookMetadata);
}
export function useStorybookMetadata(router: Router, configDir?: string) {
router.use('/project.json', async (req: Request, res: Response) => {
const storybookMetadata = await getStorybookMetadata(configDir);
res.header('Content-Type', 'application/json');
res.send(JSON.stringify(storybookMetadata));
});
}

View File

@ -6,16 +6,32 @@ import Events from '@storybook/core-events';
import { useStoriesJson, DEBOUNCE } from './stories-json';
import { ServerChannel } from './get-server-channel';
import { StoryIndexGenerator } from './StoryIndexGenerator';
jest.mock('watchpack');
jest.mock('lodash/debounce');
const options: Parameters<typeof useStoriesJson>[2] = {
configDir: path.join(__dirname, '__mockdata__'),
presets: {
apply: async () => ['./src/**/*.stories.(ts|js|jsx)'] as any,
const workingDir = path.join(__dirname, '__mockdata__');
const normalizedStories = [
{
titlePrefix: '',
directory: './src',
files: '**/*.stories.@(ts|js|jsx)',
importPathMatcher:
/^\.[\\/](?:src(?:\/(?!\.)(?:(?:(?!(?:^|\/)\.).)*?)\/|\/|$)(?!\.)(?=.)[^/]*?\.stories\.(ts|js|jsx))$/,
},
} as any;
];
const getInitializedStoryIndexGenerator = async () => {
const generator = new StoryIndexGenerator(normalizedStories, {
configDir: workingDir,
workingDir,
storiesV2Compatibility: true,
storyStoreV7: true,
});
await generator.initialize();
return generator;
};
describe('useStoriesJson', () => {
const use = jest.fn();
@ -34,7 +50,7 @@ describe('useStoriesJson', () => {
on: jest.fn(),
} as any;
beforeEach(() => {
beforeEach(async () => {
use.mockClear();
send.mockClear();
write.mockClear();
@ -48,7 +64,13 @@ describe('useStoriesJson', () => {
describe('JSON endpoint', () => {
it('scans and extracts stories', async () => {
const mockServerChannel = { emit: jest.fn() } as any as ServerChannel;
await useStoriesJson(router, mockServerChannel, options, options.configDir);
useStoriesJson({
router,
serverChannel: mockServerChannel,
workingDir,
normalizedStories,
initializedStoryIndexGenerator: getInitializedStoryIndexGenerator(),
});
expect(use).toHaveBeenCalledTimes(1);
const route = use.mock.calls[0][1];
@ -145,7 +167,14 @@ describe('useStoriesJson', () => {
it('can handle simultaneous access', async () => {
const mockServerChannel = { emit: jest.fn() } as any as ServerChannel;
await useStoriesJson(router, mockServerChannel, options, options.configDir);
useStoriesJson({
router,
serverChannel: mockServerChannel,
workingDir,
normalizedStories,
initializedStoryIndexGenerator: getInitializedStoryIndexGenerator(),
});
expect(use).toHaveBeenCalledTimes(1);
const route = use.mock.calls[0][1];
@ -171,7 +200,13 @@ describe('useStoriesJson', () => {
it('sends invalidate events', async () => {
const mockServerChannel = { emit: jest.fn() } as any as ServerChannel;
await useStoriesJson(router, mockServerChannel, options, options.configDir);
useStoriesJson({
router,
serverChannel: mockServerChannel,
workingDir,
normalizedStories,
initializedStoryIndexGenerator: getInitializedStoryIndexGenerator(),
});
expect(use).toHaveBeenCalledTimes(1);
const route = use.mock.calls[0][1];
@ -194,7 +229,13 @@ describe('useStoriesJson', () => {
it('only sends one invalidation when multiple event listeners are listening', async () => {
const mockServerChannel = { emit: jest.fn() } as any as ServerChannel;
await useStoriesJson(router, mockServerChannel, options, options.configDir);
useStoriesJson({
router,
serverChannel: mockServerChannel,
workingDir,
normalizedStories,
initializedStoryIndexGenerator: getInitializedStoryIndexGenerator(),
});
expect(use).toHaveBeenCalledTimes(1);
const route = use.mock.calls[0][1];
@ -223,7 +264,13 @@ describe('useStoriesJson', () => {
(debounce as jest.Mock).mockImplementation(jest.requireActual('lodash/debounce'));
const mockServerChannel = { emit: jest.fn() } as any as ServerChannel;
await useStoriesJson(router, mockServerChannel, options, options.configDir);
useStoriesJson({
router,
serverChannel: mockServerChannel,
workingDir,
normalizedStories,
initializedStoryIndexGenerator: getInitializedStoryIndexGenerator(),
});
expect(use).toHaveBeenCalledTimes(1);
const route = use.mock.calls[0][1];

View File

@ -1,10 +1,8 @@
import { Router, Request, Response } from 'express';
import fs from 'fs-extra';
import type { Options, NormalizedStoriesSpecifier, StorybookConfig } from '@storybook/core-common';
import { normalizeStories } from '@storybook/core-common';
import Events from '@storybook/core-events';
import debounce from 'lodash/debounce';
import type { NormalizedStoriesSpecifier } from '@storybook/core-common';
import { debounce } from 'lodash';
import { STORY_INDEX_INVALIDATED } from '@storybook/core-events';
import { StoryIndexGenerator } from './StoryIndexGenerator';
import { watchStorySpecifiers } from './watch-story-specifiers';
import { ServerChannel } from './get-server-channel';
@ -13,65 +11,38 @@ export const DEBOUNCE = 100;
export async function extractStoriesJson(
outputFile: string,
normalizedStories: NormalizedStoriesSpecifier[],
options: {
configDir: string;
workingDir: string;
storiesV2Compatibility: boolean;
storyStoreV7: boolean;
}
initializedStoryIndexGenerator: Promise<StoryIndexGenerator>
) {
const generator = new StoryIndexGenerator(normalizedStories, options);
await generator.initialize();
const index = await generator.getIndex();
await fs.writeJson(outputFile, index);
const generator = await initializedStoryIndexGenerator;
const storyIndex = await generator.getIndex();
await fs.writeJson(outputFile, storyIndex);
}
export async function useStoriesJson(
router: Router,
serverChannel: ServerChannel,
options: Options,
workingDir: string = process.cwd()
) {
const normalizedStories = normalizeStories(await options.presets.apply('stories'), {
configDir: options.configDir,
workingDir,
export function useStoriesJson({
router,
initializedStoryIndexGenerator,
workingDir = process.cwd(),
serverChannel,
normalizedStories,
}: {
router: Router;
initializedStoryIndexGenerator: Promise<StoryIndexGenerator>;
serverChannel: ServerChannel;
workingDir?: string;
normalizedStories: NormalizedStoriesSpecifier[];
}) {
const maybeInvalidate = debounce(() => serverChannel.emit(STORY_INDEX_INVALIDATED), DEBOUNCE, {
leading: true,
});
const features = await options.presets.apply<StorybookConfig['features']>('features');
const generator = new StoryIndexGenerator(normalizedStories, {
configDir: options.configDir,
workingDir,
storiesV2Compatibility: !features?.breakingChangesV7 && !features?.storyStoreV7,
storyStoreV7: features?.storyStoreV7,
watchStorySpecifiers(normalizedStories, { workingDir }, async (specifier, path, removed) => {
const generator = await initializedStoryIndexGenerator;
generator.invalidate(specifier, path, removed);
maybeInvalidate();
});
// Wait until someone actually requests `stories.json` before we start generating/watching.
// This is mainly for testing purposes.
const maybeInvalidate = debounce(
() => serverChannel.emit(Events.STORY_INDEX_INVALIDATED),
DEBOUNCE,
{ leading: true }
);
let startedPromise: Promise<void>;
async function ensureStarted() {
if (!startedPromise) {
startedPromise = (async () => {
watchStorySpecifiers(normalizedStories, { workingDir }, (specifier, path, removed) => {
generator.invalidate(specifier, path, removed);
maybeInvalidate();
});
await generator.initialize();
})();
}
return startedPromise;
}
router.use('/stories.json', async (req: Request, res: Response) => {
await ensureStarted();
try {
const generator = await initializedStoryIndexGenerator;
const index = await generator.getIndex();
res.header('Content-Type', 'application/json');
res.send(JSON.stringify(index));

5
lib/telemetry/README.md Normal file
View File

@ -0,0 +1,5 @@
# Storybook Telemetry
Storybook collects completely anonymous telemetry data about general usage. Participation in this program is optional and its easy to opt-out.
For more information visit: [storybook.js.org/telemetry](https://storybook.js.org/telemetry)

View File

@ -0,0 +1,60 @@
{
"name": "@storybook/telemetry",
"version": "6.5.0-beta.1",
"description": "Telemetry logging for crash reports and usage statistics",
"keywords": [
"storybook"
],
"homepage": "https://github.com/storybookjs/storybook/tree/main/lib/telemetry",
"bugs": {
"url": "https://github.com/storybookjs/storybook/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/storybookjs/storybook.git",
"directory": "lib/telemetry"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/storybook"
},
"license": "MIT",
"sideEffects": false,
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/ts3.9/index.d.ts",
"typesVersions": {
"<3.8": {
"dist/ts3.9/*": [
"dist/ts3.4/*"
]
}
},
"files": [
"dist/**/*",
"README.md",
"*.js",
"*.d.ts"
],
"scripts": {
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/client-logger": "6.5.0-beta.1",
"@storybook/core-common": "6.5.0-beta.1",
"chalk": "^4.1.0",
"core-js": "^3.8.2",
"detect-package-manager": "^2.0.1",
"fetch-retry": "^5.0.2",
"fs-extra": "^9.0.1",
"global": "^4.4.0",
"isomorphic-unfetch": "^3.1.0",
"nanoid": "^3.3.1",
"read-pkg-up": "^7.0.1"
},
"publishConfig": {
"access": "public"
},
"gitHead": "a64dc8426d21a70970728878c3bba0ac0a377a48",
"sbmodern": "dist/modern/index.js"
}

View File

@ -0,0 +1,44 @@
import path from 'path';
import { execSync } from 'child_process';
import { oneWayHash } from './one-way-hash';
export const getProjectRoot = () => {
let projectRoot;
try {
const projectRootBuffer = execSync(`git rev-parse --show-toplevel`, {
timeout: 1000,
stdio: `pipe`,
});
projectRoot = String(projectRootBuffer).trimEnd();
// eslint-disable-next-line no-empty
} catch (_) {}
return projectRoot;
};
let anonymousProjectId: string;
export const getAnonymousProjectId = () => {
if (anonymousProjectId) {
return anonymousProjectId;
}
let unhashedProjectId;
try {
const projectRoot = getProjectRoot();
const projectRootPath = path.relative(projectRoot, process.cwd());
const originBuffer = execSync(`git config --local --get remote.origin.url`, {
timeout: 1000,
stdio: `pipe`,
});
// we use a combination of remoteUrl and working directory
// to separate multiple storybooks from the same project (e.g. monorepo)
unhashedProjectId = `${String(originBuffer).trim()}${projectRootPath}`;
// eslint-disable-next-line no-empty
} catch (_) {}
anonymousProjectId = oneWayHash(unhashedProjectId);
return anonymousProjectId;
};

View File

@ -0,0 +1,53 @@
/* eslint-disable no-underscore-dangle */
import path from 'path';
import { getMonorepoType, monorepoConfigs } from './get-monorepo-type';
// eslint-disable-next-line global-require, jest/no-mocks-import
jest.mock('fs-extra', () => require('../../../__mocks__/fs-extra'));
jest.mock('./anonymous-id', () => ({
getProjectRoot: () => 'root',
}));
const checkMonorepoType = ({ monorepoConfigFile, isYarnWorkspace = false }) => {
const mockFiles = {
[path.join('root', 'package.json')]: isYarnWorkspace ? '{ "workspaces": [] }' : '{}',
};
if (monorepoConfigFile) {
mockFiles[path.join('root', monorepoConfigFile)] = '{}';
}
// eslint-disable-next-line global-require
require('fs-extra').__setMockFiles(mockFiles);
return getMonorepoType();
};
describe('getMonorepoType', () => {
describe('Monorepos from json files', () => {
it.each(Object.entries(monorepoConfigs))(
'should detect %p from %s file',
(monorepoName, monorepoConfigFile) => {
expect(checkMonorepoType({ monorepoConfigFile })).toEqual(monorepoName);
}
);
});
describe('Yarn|NPM workspaces', () => {
it('should detect Workspaces from package.json', () => {
expect(checkMonorepoType({ monorepoConfigFile: undefined, isYarnWorkspace: true })).toEqual(
'Workspaces'
);
});
});
describe('Non-monorepos', () => {
it('should return undefined', () => {
expect(checkMonorepoType({ monorepoConfigFile: undefined, isYarnWorkspace: false })).toEqual(
undefined
);
});
});
});

View File

@ -0,0 +1,38 @@
import fs from 'fs-extra';
import path from 'path';
import type { PackageJson } from '@storybook/core-common';
import { getProjectRoot } from './anonymous-id';
export const monorepoConfigs = {
Nx: 'nx.json',
Turborepo: 'turbo.json',
Lerna: 'lerna.json',
Rush: 'rush.json',
Lage: 'lage.config.json',
} as const;
export type MonorepoType = keyof typeof monorepoConfigs | 'Workspaces' | undefined;
export const getMonorepoType = (): MonorepoType => {
const projectRootPath = getProjectRoot();
const monorepoType = Object.keys(monorepoConfigs).find(
(monorepo: keyof typeof monorepoConfigs) => {
const configFile = path.join(projectRootPath, monorepoConfigs[monorepo]);
return fs.existsSync(configFile);
}
) as MonorepoType;
if (monorepoType) {
return monorepoType;
}
const packageJson = fs.readJsonSync(path.join(projectRootPath, 'package.json')) as PackageJson;
if (packageJson?.workspaces) {
return 'Workspaces';
}
return undefined;
};

View File

@ -0,0 +1,40 @@
import { logger } from '@storybook/client-logger';
import type { EventType, Payload, Options, TelemetryData } from './types';
import { getStorybookMetadata } from './storybook-metadata';
import { sendTelemetry } from './telemetry';
import { notify } from './notify';
import { sanitizeError } from './sanitize';
export * from './storybook-metadata';
export const telemetry = async (
eventType: EventType,
payload: Payload = {},
options?: Partial<Options>
) => {
await notify();
const telemetryData: TelemetryData = {
eventType,
payload,
};
try {
telemetryData.metadata = await getStorybookMetadata(options.configDir);
} catch (error) {
if (!telemetryData.payload.error) telemetryData.payload.error = error;
} finally {
const { error } = telemetryData.payload;
if (error) {
// make sure to anonymise possible paths from error messages
telemetryData.payload.error = sanitizeError(error);
}
if (!telemetryData.payload.error || options.enableCrashReports) {
if (process.env?.STORYBOOK_DEBUG_TELEMETRY) {
logger.info('\n[telemetry]');
logger.info(JSON.stringify(telemetryData, null, 2));
} else {
await sendTelemetry(telemetryData, options);
}
}
}
};

View File

@ -0,0 +1,32 @@
import chalk from 'chalk';
import { cache } from '@storybook/core-common';
const TELEMETRY_KEY_NOTIFY_DATE = 'telemetry-notification-date';
const logger = console;
export const notify = async () => {
const telemetryNotifyDate = await cache.get(TELEMETRY_KEY_NOTIFY_DATE, null);
// The end-user has already been notified about our telemetry integration. We
// don't need to constantly annoy them about it.
// We will re-inform users about the telemetry if significant changes are
// ever made.
if (telemetryNotifyDate) {
return;
}
cache.set(TELEMETRY_KEY_NOTIFY_DATE, Date.now());
logger.log();
logger.log(
`${chalk.magenta.bold(
'attention'
)} => Storybook now collects completely anonymous telemetry regarding usage.`
);
logger.log(`This information is used to shape Storybook's roadmap and prioritize features.`);
logger.log(
`You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:`
);
logger.log(chalk.cyan('https://storybook.js.org/telemetry'));
logger.log();
};

View File

@ -0,0 +1,14 @@
import { BinaryLike, createHash } from 'crypto';
export const oneWayHash = (payload: BinaryLike): string => {
const hash = createHash('sha256');
// Always prepend the payload value with salt. This ensures the hash is truly
// one-way.
hash.update('storybook-telemetry-salt');
// Update is an append operation, not a replacement. The salt from the prior
// update is still present!
hash.update(payload);
return hash.digest('hex');
};

View File

@ -0,0 +1,21 @@
import path from 'path';
import { Dependency } from './types';
export const getActualPackageVersions = async (packages: Record<string, Partial<Dependency>>) => {
const packageNames = Object.keys(packages);
return Promise.all(packageNames.map(getActualPackageVersion));
};
export const getActualPackageVersion = async (packageName: string) => {
try {
// eslint-disable-next-line import/no-dynamic-require,global-require
const packageJson = require(path.join(packageName, 'package.json'));
return {
name: packageName,
version: packageJson.version,
};
} catch (err) {
return { name: packageName, version: null };
}
};

View File

@ -0,0 +1,102 @@
import { sanitizeError, cleanPaths } from './sanitize';
describe(`Errors Helpers`, () => {
describe(`sanitizeError`, () => {
it(`Sanitizes current path from error stacktraces`, () => {
const errorMessage = `this is a test`;
let e;
try {
throw new Error(errorMessage);
} catch (error) {
e = error;
}
expect(e).toBeDefined();
expect(e.message).toEqual(errorMessage);
expect(e.stack).toEqual(expect.stringContaining(process.cwd()));
const sanitizedError = sanitizeError(e);
expect(sanitizedError.message).toEqual(expect.stringContaining(errorMessage));
expect(sanitizedError.message).toEqual(
expect.not.stringContaining(process.cwd().replaceAll(`\\`, `\\\\`))
);
});
it(`Sanitizes a section of the current path from error stacktrace`, () => {
const errorMessage = `this is a test`;
const e = {
message: errorMessage,
stack: `
Error: this is an error
at Object.<anonymous> (/Users/username/Code/storybook-app/storybook-config.js:1:32)
at Object.<anonymous> (/Users/username/Code/storybook-app/node_module/storybook-telemetry/blah.js:1:69)
at Object.<anonymous> (/Users/username/Code/storybook-app/node_module/fake-path/index.js:1:41)
at Object.<anonymous> (/Users/username/.fake-path/index.js:1:69)
at Module._compile (internal/modules/cjs/loader.js:736:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:747:10)
at Module.load (internal/modules/cjs/loader.js:628:32)
at tryModuleLoad (internal/modules/cjs/loader.js:568:12)
at Function.Module._load (internal/modules/cjs/loader.js:560:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:801:12)
at executeUserCode (internal/bootstrap/node.js:526:15)
at startMainThreadExecution (internal/bootstrap/node.js:439:3)
`,
};
expect(e).toBeDefined();
expect(e.message).toEqual(errorMessage);
expect(e.stack).toBeDefined();
const mockCwd = jest
.spyOn(process, `cwd`)
.mockImplementation(() => `/Users/username/Code/storybook-app`);
expect(e.stack).toEqual(expect.stringContaining(`username`));
const sanitizedError = sanitizeError(e as Error, `/`);
expect(sanitizedError.message.includes(errorMessage)).toBe(true);
expect(sanitizedError.stack).toEqual(expect.not.stringContaining(`username`));
const result = sanitizedError.stack.match(/\$SNIP/g) as Array<string>;
expect(result.length).toBe(4);
mockCwd.mockRestore();
});
});
describe(`cleanPaths`, () => {
it.each([`storybook-config.js`, `src/pages/index.js`])(
`should clean path on unix: %s`,
(filePath) => {
const cwdMockPath = `/Users/username/storybook-app`;
const fullPath = `${cwdMockPath}/${filePath}`;
const mockCwd = jest.spyOn(process, `cwd`).mockImplementation(() => cwdMockPath);
const errorMessage = `This path ${fullPath} is a test ${fullPath}`;
expect(cleanPaths(errorMessage, `/`)).toBe(
`This path $SNIP/${filePath} is a test $SNIP/${filePath}`
);
mockCwd.mockRestore();
}
);
it.each([`storybook-config.js`, `src\\pages\\index.js`])(
`should clean path on windows: %s`,
(filePath) => {
const cwdMockPath = `C:\\Users\\username\\storybook-app`;
const fullPath = `${cwdMockPath}\\${filePath}`;
const mockCwd = jest.spyOn(process, `cwd`).mockImplementation(() => cwdMockPath);
const errorMessage = `This path ${fullPath} is a test ${fullPath}`;
expect(cleanPaths(errorMessage, `\\`)).toBe(
`This path $SNIP\\${filePath} is a test $SNIP\\${filePath}`
);
mockCwd.mockRestore();
}
);
});
});

View File

@ -0,0 +1,43 @@
/* eslint-disable no-param-reassign */
import { sep } from 'path';
export interface IErrorWithStdErrAndStdOut {
stderr?: Buffer | string;
stdout?: Buffer | string;
[key: string]: unknown;
}
// Removes all user paths
function regexpEscape(str: string): string {
return str.replace(/[-[/{}()*+?.\\^$|]/g, `\\$&`);
}
export function cleanPaths(str: string, separator: string = sep): string {
if (!str) return str;
const stack = process.cwd().split(separator);
while (stack.length > 1) {
const currentPath = stack.join(separator);
const currentRegex = new RegExp(regexpEscape(currentPath), `g`);
str = str.replace(currentRegex, `$SNIP`);
const currentPath2 = stack.join(separator + separator);
const currentRegex2 = new RegExp(regexpEscape(currentPath2), `g`);
str = str.replace(currentRegex2, `$SNIP`);
stack.pop();
}
return str;
}
// Takes an Error and returns a sanitized JSON String
export function sanitizeError(error: Error, pathSeparator: string = sep): string {
// Hack because Node
error = JSON.parse(JSON.stringify(error, Object.getOwnPropertyNames(error)));
// Removes all user paths
const errorString = cleanPaths(JSON.stringify(error), pathSeparator);
return JSON.parse(errorString);
}

View File

@ -0,0 +1,200 @@
import type { PackageJson, StorybookConfig } from '@storybook/core-common';
import { computeStorybookMetadata, metaFrameworks } from './storybook-metadata';
const packageJsonMock: PackageJson = {
name: 'some-user-project',
version: 'x.x.x',
};
const mainJsMock: StorybookConfig = {
stories: [],
};
jest.mock('./package-versions', () => {
const getActualPackageVersion = jest.fn((name) =>
Promise.resolve({
name,
version: 'x.x.x',
})
);
const getActualPackageVersions = jest.fn((packages) =>
Promise.all(Object.keys(packages).map(getActualPackageVersion))
);
return {
getActualPackageVersions,
getActualPackageVersion,
};
});
jest.mock('./get-monorepo-type', () => ({
getMonorepoType: () => 'Nx',
}));
jest.mock('detect-package-manager', () => ({
detect: () => 'Yarn',
getNpmVersion: () => '3.1.1',
}));
describe('await computeStorybookMetadata', () => {
test('should return frameworkOptions from mainjs', async () => {
const reactResult = await computeStorybookMetadata({
packageJson: {
...packageJsonMock,
devDependencies: {
'@storybook/react': 'x.x.x',
},
},
mainConfig: {
...mainJsMock,
reactOptions: {
fastRefresh: false,
},
},
});
expect(reactResult.framework).toEqual({ name: 'react', options: { fastRefresh: false } });
const angularResult = await computeStorybookMetadata({
packageJson: {
...packageJsonMock,
devDependencies: {
'@storybook/angular': 'x.x.x',
},
},
mainConfig: {
...mainJsMock,
angularOptions: {
enableIvy: true,
},
},
});
expect(angularResult.framework).toEqual({ name: 'angular', options: { enableIvy: true } });
});
test('should separate storybook packages and addons', async () => {
const result = await computeStorybookMetadata({
packageJson: {
...packageJsonMock,
devDependencies: {
'@storybook/react': 'x.y.z',
'@storybook/addon-essentials': 'x.x.x',
'storybook-addon-deprecated': 'x.x.x',
},
},
mainConfig: {
...mainJsMock,
addons: ['@storybook/addon-essentials', 'storybook-addon-deprecated/register'],
},
});
expect(result.addons).toMatchInlineSnapshot(`
Object {
"@storybook/addon-essentials": Object {
"options": undefined,
"version": "x.x.x",
},
"storybook-addon-deprecated": Object {
"options": undefined,
"version": "x.x.x",
},
}
`);
expect(result.storybookPackages).toMatchInlineSnapshot(`
Object {
"@storybook/react": Object {
"version": "x.x.x",
},
}
`);
});
test('should return user specified features', async () => {
const features = {
interactionsDebugger: true,
};
const result = await computeStorybookMetadata({
packageJson: packageJsonMock,
mainConfig: {
...mainJsMock,
features,
},
});
expect(result.features).toEqual(features);
});
test('should handle different types of builders', async () => {
const simpleBuilder = 'webpack4';
const complexBuilder = {
name: 'webpack5',
options: {
lazyCompilation: true,
},
};
expect(
(
await computeStorybookMetadata({
packageJson: packageJsonMock,
mainConfig: {
...mainJsMock,
core: {
builder: complexBuilder,
},
},
})
).builder
).toEqual(complexBuilder);
expect(
(
await computeStorybookMetadata({
packageJson: packageJsonMock,
mainConfig: {
...mainJsMock,
core: {
builder: simpleBuilder,
},
},
})
).builder
).toEqual({ name: simpleBuilder });
});
test('should return the number of refs', async () => {
const res = await computeStorybookMetadata({
packageJson: packageJsonMock,
mainConfig: {
...mainJsMock,
refs: {
a: { title: '', url: '' },
b: { title: '', url: '' },
},
},
});
expect(res.refCount).toEqual(2);
});
test.each(Object.entries(metaFrameworks))(
'should detect the supported metaframework: %s',
async (metaFramework, name) => {
const res = await computeStorybookMetadata({
packageJson: {
...packageJsonMock,
dependencies: {
[metaFramework]: 'x.x.x',
},
},
mainConfig: mainJsMock,
});
expect(res.metaFramework).toEqual({
name,
packageName: metaFramework,
version: 'x.x.x',
});
}
);
});

View File

@ -0,0 +1,202 @@
import readPkgUp from 'read-pkg-up';
import { detect, getNpmVersion } from 'detect-package-manager';
import {
loadMainConfig,
getStorybookInfo,
getStorybookConfiguration,
} from '@storybook/core-common';
import type { StorybookConfig, PackageJson } from '@storybook/core-common';
import type { StorybookMetadata, Dependency, StorybookAddon } from './types';
import { getActualPackageVersion, getActualPackageVersions } from './package-versions';
import { getMonorepoType } from './get-monorepo-type';
import { getProjectRoot } from './anonymous-id';
let cachedMetadata: StorybookMetadata;
export const getStorybookMetadata = async (_configDir: string) => {
if (cachedMetadata) {
return cachedMetadata;
}
const packageJson = readPkgUp.sync({ cwd: process.cwd() }).packageJson as PackageJson;
const configDir =
(_configDir ||
(getStorybookConfiguration(packageJson.scripts.storybook, '-c', '--config-dir') as string)) ??
'.storybook';
const mainConfig = loadMainConfig({ configDir });
cachedMetadata = await computeStorybookMetadata({ mainConfig, packageJson });
return cachedMetadata;
};
export const metaFrameworks = {
next: 'Next',
'react-scripts': 'CRA',
gatsby: 'Gatsby',
'@nuxtjs/storybook': 'nuxt',
'@nrwl/storybook': 'nx',
'@vue/cli-service': 'vue-cli',
'@sveltejs/kit': 'svelte-kit',
} as Record<string, string>;
// @TODO: This should be removed in 7.0 as the framework.options field in main.js will replace this
const getFrameworkOptions = (mainConfig: any) => {
const possibleOptions = [
'angular',
'ember',
'html',
'preact',
'react',
'server',
'svelte',
'vue',
'vue3',
'webComponents',
].map((opt) => `${opt}Options`);
// eslint-disable-next-line no-restricted-syntax
for (const opt of possibleOptions) {
if (opt in mainConfig) {
return mainConfig[opt] as any;
}
}
return undefined;
};
// Analyze a combination of information from main.js and package.json
// to provide telemetry over a Storybook project
export const computeStorybookMetadata = async ({
packageJson,
mainConfig,
}: {
packageJson: PackageJson;
mainConfig: StorybookConfig & Record<string, any>;
}): Promise<StorybookMetadata> => {
const metadata: Partial<StorybookMetadata> = {
generatedAt: new Date().getTime(),
builder: { name: 'webpack4' },
hasCustomBabel: false,
hasCustomWebpack: false,
hasStaticDirs: false,
hasStorybookEslint: false,
refCount: 0,
};
const allDependencies = {
...packageJson?.dependencies,
...packageJson?.devDependencies,
...packageJson?.peerDependencies,
};
const metaFramework = Object.keys(allDependencies).find((dep) => !!metaFrameworks[dep]);
if (metaFramework) {
const { version } = await getActualPackageVersion(metaFramework);
metadata.metaFramework = {
name: metaFrameworks[metaFramework],
packageName: metaFramework,
version,
};
}
const monorepoType = getMonorepoType();
if (monorepoType) {
metadata.monorepo = monorepoType;
}
try {
const packageManagerType = await detect({ cwd: getProjectRoot() });
const packageManagerVerson = await getNpmVersion(packageManagerType);
metadata.packageManager = {
type: packageManagerType,
version: packageManagerVerson,
};
// Better be safe than sorry, some codebases/paths might end up breaking with something like "spawn pnpm ENOENT"
// so we just set the package manager if the detection is successful
// eslint-disable-next-line no-empty
} catch (err) {}
metadata.hasCustomBabel = !!mainConfig.babel;
metadata.hasCustomWebpack = !!mainConfig.webpackFinal;
metadata.hasStaticDirs = !!mainConfig.staticDirs;
if (mainConfig.typescript) {
metadata.typescriptOptions = mainConfig.typescript;
}
if (mainConfig.core?.builder) {
const { builder } = mainConfig.core;
metadata.builder = {
name: typeof builder === 'string' ? builder : builder.name,
options: typeof builder === 'string' ? undefined : builder?.options ?? undefined,
};
}
if (mainConfig.refs) {
metadata.refCount = Object.keys(mainConfig.refs).length;
}
if (mainConfig.features) {
metadata.features = mainConfig.features;
}
const addons: Record<string, StorybookAddon> = {};
if (mainConfig.addons) {
mainConfig.addons.forEach((addon) => {
let result;
let options;
if (typeof addon === 'string') {
result = addon.replace('/register', '');
} else {
options = addon.options;
result = addon.name;
}
addons[result] = {
options,
version: undefined,
};
});
}
const addonVersions = await getActualPackageVersions(addons);
addonVersions.forEach(({ name, version }) => {
addons[name].version = version;
});
const addonNames = Object.keys(addons);
// all Storybook deps minus the addons
const storybookPackages = Object.keys(allDependencies)
.filter((dep) => dep.includes('storybook') && !addonNames.includes(dep))
.reduce((acc, dep) => {
return {
...acc,
[dep]: { version: undefined },
};
}, {}) as Record<string, Dependency>;
const storybookPackageVersions = await getActualPackageVersions(storybookPackages);
storybookPackageVersions.forEach(({ name, version }) => {
storybookPackages[name].version = version;
});
const language = allDependencies.typescript ? 'typescript' : 'javascript';
const hasStorybookEslint = !!allDependencies['eslint-plugin-storybook'];
const storybookInfo = getStorybookInfo(packageJson);
return {
...metadata,
storybookVersion: storybookInfo.version,
language,
storybookPackages,
framework: {
name: storybookInfo.framework,
options: getFrameworkOptions(mainConfig),
},
addons,
hasStorybookEslint,
};
};

View File

@ -0,0 +1,85 @@
/* eslint-disable no-plusplus */
import fetch from 'isomorphic-unfetch';
import { sendTelemetry } from './telemetry';
jest.mock('isomorphic-unfetch');
const fetchMock = fetch as jest.Mock;
beforeEach(() => {
fetchMock.mockResolvedValue({ status: 200 });
});
it('makes a fetch request with name and data', async () => {
fetchMock.mockClear();
await sendTelemetry({ eventType: 'start', payload: { foo: 'bar' } });
expect(fetch).toHaveBeenCalledTimes(1);
const body = JSON.parse(fetchMock.mock.calls[0][1].body);
expect(body).toMatchObject({
eventType: 'start',
payload: { foo: 'bar' },
});
});
it('retries if fetch fails with a 503', async () => {
fetchMock.mockClear().mockResolvedValueOnce({ status: 503 });
await sendTelemetry(
{
eventType: 'start',
payload: { foo: 'bar' },
},
{ retryDelay: 0 }
);
expect(fetch).toHaveBeenCalledTimes(2);
});
it('gives up if fetch repeatedly fails', async () => {
fetchMock.mockClear().mockResolvedValue({ status: 503 });
await sendTelemetry(
{
eventType: 'start',
payload: { foo: 'bar' },
},
{ retryDelay: 0 }
);
expect(fetch).toHaveBeenCalledTimes(4);
});
it('await all pending telemetry when passing in immediate = true', async () => {
let numberOfResolvedTasks = 0;
// when we call sendTelemetry with immediate = true
// all pending tasks will be awaited
// to test this we add a few telemetry tasks that will be in the 'queue'
// we do NOT await these tasks!
sendTelemetry({
eventType: 'init',
payload: { foo: 'bar' },
}).then(() => {
numberOfResolvedTasks++;
});
sendTelemetry({
eventType: 'start',
payload: { foo: 'bar' },
}).then(() => {
numberOfResolvedTasks++;
});
// here we await
await sendTelemetry(
{
eventType: 'error-dev',
payload: { foo: 'bar' },
},
{ retryDelay: 0, immediate: true }
).then(() => {
numberOfResolvedTasks++;
});
expect(fetch).toHaveBeenCalledTimes(3);
expect(numberOfResolvedTasks).toBe(3);
});

View File

@ -0,0 +1,57 @@
import originalFetch from 'isomorphic-unfetch';
import retry from 'fetch-retry';
import { nanoid } from 'nanoid';
import { Options, TelemetryData } from './types';
import { getAnonymousProjectId } from './anonymous-id';
const URL = 'https://storybook.js.org/event-log';
const fetch = retry(originalFetch);
let tasks: Promise<any>[] = [];
// getStorybookMetadata -> packagejson + Main.js
// event specific data: sessionId, ip, etc..
// send telemetry
const sessionId = nanoid();
export async function sendTelemetry(
data: TelemetryData,
options: Partial<Options> = { retryDelay: 1000, immediate: false }
) {
// We use this id so we can de-dupe events that arrive at the index multiple times due to the
// use of retries. There are situations in which the request "5xx"s (or times-out), but
// the server actually gets the request and stores it anyway.
// flatten the data before we send it
const { payload, metadata, ...rest } = data;
const context = {
anonymousId: getAnonymousProjectId(),
inCI: process.env.CI === 'true',
};
const eventId = nanoid();
const body = { ...rest, eventId, sessionId, metadata, payload, context };
let request: Promise<any>;
try {
request = fetch(URL, {
method: 'POST',
body: JSON.stringify(body),
headers: { 'Content-Type': 'application/json' },
retries: 3,
retryOn: [503, 504],
retryDelay: (attempt: number) => 2 ** attempt * options.retryDelay,
});
tasks.push(request);
if (options.immediate) {
await Promise.all(tasks);
} else {
await request;
}
} catch (err) {
//
} finally {
tasks = tasks.filter((task) => task !== request);
}
}

View File

@ -0,0 +1,71 @@
import type { StorybookConfig, TypescriptOptions } from '@storybook/core-common';
import type { PM } from 'detect-package-manager';
import type { MonorepoType } from './get-monorepo-type';
export type EventType =
| 'start'
| 'build'
| 'upgrade'
| 'init'
| 'error-build'
| 'error-dev'
| 'error-metadata';
export interface Dependency {
version: string;
}
export interface StorybookAddon extends Dependency {
options: any;
}
export type StorybookMetadata = {
storybookVersion: string;
generatedAt?: number;
language: 'typescript' | 'javascript';
framework: {
name: string;
options?: any;
};
builder?: {
name: string;
options?: Record<string, any>;
};
monorepo?: MonorepoType;
packageManager?: {
type: PM;
version: string;
};
typescriptOptions?: Partial<TypescriptOptions>;
addons?: Record<string, StorybookAddon>;
storybookPackages?: Record<string, Dependency>;
metaFramework?: {
name: string;
packageName: string;
version: string;
};
hasStorybookEslint?: boolean;
hasStaticDirs?: boolean;
hasCustomWebpack?: boolean;
hasCustomBabel?: boolean;
features?: StorybookConfig['features'];
refCount?: number;
};
export interface Payload {
[key: string]: any;
}
export interface Options {
retryDelay: number;
immediate: boolean;
configDir?: string;
enableCrashReports?: boolean;
}
export interface TelemetryData {
eventType: EventType;
payload: Payload;
metadata?: StorybookMetadata;
}

1
lib/telemetry/src/typings.d.ts vendored Normal file
View File

@ -0,0 +1 @@
declare module 'global';

View File

@ -0,0 +1,20 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"types": [
"node"
]
},
"include": [
"src/**/*"
],
"exclude": [
"src/**/*.test.*",
"src/**/tests/**/*",
"src/**/__tests__/**/*",
"src/**/*.stories.*",
"src/**/*.mockdata.*",
"src/**/__testfixtures__/**"
]
}

View File

@ -239,6 +239,9 @@
"@storybook/source-loader": {
"implicitDependencies": []
},
"@storybook/telemetry": {
"implicitDependencies": []
},
"@storybook/theming": {
"implicitDependencies": []
},

View File

@ -183,6 +183,7 @@
"@storybook/source-loader": "workspace:*",
"@storybook/store": "workspace:*",
"@storybook/svelte": "workspace:*",
"@storybook/telemetry": "workspace:*",
"@storybook/testing-library": "^0.0.7",
"@storybook/theming": "workspace:*",
"@storybook/ui": "workspace:*",

View File

@ -1,4 +1,7 @@
module.exports = {
// Should be kept in sync with addons listed in `baseGenerator.ts`
addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
core: {
disableTelemetry: true,
},
};

View File

@ -2,14 +2,25 @@ const { buildStaticStandalone } = require('../lib/core-server/dist/cjs/build-sta
process.env.NODE_ENV = 'production';
buildStaticStandalone({
ignorePreview: true,
outputDir: './lib/manager-webpack4/prebuilt',
configDir: './scripts/build-manager-config',
});
const logger = console;
buildStaticStandalone({
ignorePreview: true,
outputDir: './lib/manager-webpack5/prebuilt',
configDir: './scripts/build-manager-config',
const run = async () => {
logger.log('Building Webpack4 Manager');
await buildStaticStandalone({
ignorePreview: true,
outputDir: './lib/manager-webpack4/prebuilt',
configDir: './scripts/build-manager-config',
});
logger.log('Building Webpack5 Manager');
await buildStaticStandalone({
ignorePreview: true,
outputDir: './lib/manager-webpack5/prebuilt',
configDir: './scripts/build-manager-config',
});
};
run().catch((err) => {
logger.log(err);
process.exit(1);
});

View File

@ -293,6 +293,10 @@
"root": "lib/store",
"type": "library"
},
"@storybook/telemetry": {
"root": "lib/telemetry",
"type": "library"
},
"@storybook/theming": {
"root": "lib/theming",
"type": "library"

View File

@ -7387,6 +7387,7 @@ __metadata:
"@storybook/csf-tools": 6.5.0-beta.1
"@storybook/node-logger": 6.5.0-beta.1
"@storybook/semver": ^7.3.2
"@storybook/telemetry": 6.5.0-beta.1
"@types/cross-spawn": ^6.0.2
"@types/prompts": ^2.0.9
"@types/puppeteer-core": ^2.1.0
@ -7668,6 +7669,7 @@ __metadata:
"@storybook/node-logger": 6.5.0-beta.1
"@storybook/semver": ^7.3.2
"@storybook/store": 6.5.0-beta.1
"@storybook/telemetry": 6.5.0-beta.1
"@types/compression": ^1.7.0
"@types/ip": ^1.1.0
"@types/node": ^14.0.10 || ^16.0.0
@ -8496,6 +8498,7 @@ __metadata:
"@storybook/source-loader": "workspace:*"
"@storybook/store": "workspace:*"
"@storybook/svelte": "workspace:*"
"@storybook/telemetry": "workspace:*"
"@storybook/testing-library": ^0.0.7
"@storybook/theming": "workspace:*"
"@storybook/ui": "workspace:*"
@ -8830,6 +8833,24 @@ __metadata:
languageName: unknown
linkType: soft
"@storybook/telemetry@6.5.0-beta.1, @storybook/telemetry@workspace:*, @storybook/telemetry@workspace:lib/telemetry":
version: 0.0.0-use.local
resolution: "@storybook/telemetry@workspace:lib/telemetry"
dependencies:
"@storybook/client-logger": 6.5.0-beta.1
"@storybook/core-common": 6.5.0-beta.1
chalk: ^4.1.0
core-js: ^3.8.2
detect-package-manager: ^2.0.1
fetch-retry: ^5.0.2
fs-extra: ^9.0.1
global: ^4.4.0
isomorphic-unfetch: ^3.1.0
nanoid: ^3.3.1
read-pkg-up: ^7.0.1
languageName: unknown
linkType: soft
"@storybook/testing-library@npm:^0.0.7":
version: 0.0.7
resolution: "@storybook/testing-library@npm:0.0.7"
@ -19522,6 +19543,15 @@ __metadata:
languageName: node
linkType: hard
"detect-package-manager@npm:^2.0.1":
version: 2.0.1
resolution: "detect-package-manager@npm:2.0.1"
dependencies:
execa: ^5.1.1
checksum: 56ffd65228d1ff3ead5ea7f8ab951a517a29270de27510b790c9a8b77d4f36efbd61493e170ca77ee3dc13cbb5218583ce65b78ad14a59dc48565c9bcbbf3c71
languageName: node
linkType: hard
"detect-port-alt@npm:1.1.6":
version: 1.1.6
resolution: "detect-port-alt@npm:1.1.6"
@ -22285,7 +22315,7 @@ __metadata:
languageName: node
linkType: hard
"execa@npm:^5.0.0":
"execa@npm:^5.0.0, execa@npm:^5.1.1":
version: 5.1.1
resolution: "execa@npm:5.1.1"
dependencies:
@ -22767,6 +22797,13 @@ __metadata:
languageName: node
linkType: hard
"fetch-retry@npm:^5.0.2":
version: 5.0.2
resolution: "fetch-retry@npm:5.0.2"
checksum: 694fae18ceec4c88c508daf682fccbf1e0736fa679e95daad50946e003df7e261d9a4d36388f6f9eab2426d1796b4ee054ced904794f1edad3ffdc55b2d4b785
languageName: node
linkType: hard
"figgy-pudding@npm:^3.4.1, figgy-pudding@npm:^3.5.1":
version: 3.5.2
resolution: "figgy-pudding@npm:3.5.2"