web: moved Storybook config into a new package (#19569)

* web: moved Storybook config into a new package

* web: added storybook package to client/README

* web: checkin @types/terser-webpack-plugin

* web: yarn deduplicate

* web: prettier

* web: yarn.lock carriage-return

* web: yarn deduplicate

* web: removed redundant eslint command

* web: restored manager-head.html

* web: removed @storybook/core-common dependency for now

* web: moved cover-storybook back to root package.json

* web: updated stories glob

* web: fixed babel-plugin-istanbul cwd

* web: added storybook workspace to exclude list in nyc config

* web: removed redundant change
This commit is contained in:
Valery Bugakov 2021-04-08 17:16:26 +08:00 committed by GitHub
parent c43dc2e1fa
commit 7cf15290a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 394 additions and 318 deletions

View File

@ -27,5 +27,4 @@ GH2SG.bookmarklet.js
docker-images/grafana/config/provisioning/dashboards/sourcegraph/
storybook-static/
browser/code-intel-extensions/
!/.storybook/**
monitoring/monitoring/README.md

View File

@ -1,147 +0,0 @@
const path = require('path')
const { remove } = require('lodash')
const { DefinePlugin, ProgressPlugin } = require('webpack')
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')
const monacoEditorPaths = [path.resolve(__dirname, '..', 'node_modules', 'monaco-editor')]
const shouldMinify = !!process.env.MINIFY
const config = {
stories: ['../client/**/*.story.tsx'],
addons: [
'@storybook/addon-knobs',
'@storybook/addon-actions',
'storybook-addon-designs',
'storybook-dark-mode',
'@storybook/addon-a11y',
'@storybook/addon-toolbars',
'./redesign-toggle-toolbar/preset.js',
],
/**
* @param config {import('webpack').Configuration}
* @returns {import('webpack').Configuration}
*/
webpackFinal: config => {
// Include sourcemaps
config.mode = shouldMinify ? 'production' : 'development'
config.devtool = shouldMinify ? 'source-map' : 'cheap-module-eval-source-map'
const definePlugin = config.plugins.find(plugin => plugin instanceof DefinePlugin)
// @ts-ignore
definePlugin.definitions.NODE_ENV = JSON.stringify(config.mode)
// @ts-ignore
definePlugin.definitions['process.env'].NODE_ENV = JSON.stringify(config.mode)
if (shouldMinify) {
config.optimization = {
minimize: true,
minimizer: [
new TerserPlugin({
sourceMap: true,
terserOptions: {
compress: {
// // Don't inline functions, which causes name collisions with uglify-es:
// https://github.com/mishoo/UglifyJS2/issues/2842
inline: 1,
},
},
}),
],
namedModules: false,
}
}
// We don't use Storybook's default Babel config for our repo, it doesn't include everything we need.
config.module.rules.splice(0, 1)
if (process.env.CI) {
remove(config.plugins, plugin => plugin instanceof ProgressPlugin)
}
config.module.rules.push({
test: /\.tsx?$/,
loader: require.resolve('babel-loader'),
options: {
configFile: path.resolve(__dirname, '..', 'babel.config.js'),
},
})
config.plugins.push(
new MonacoWebpackPlugin({
languages: ['json'],
features: [
'bracketMatching',
'clipboard',
'coreCommands',
'cursorUndo',
'find',
'format',
'hover',
'inPlaceReplace',
'iPadShowKeyboard',
'links',
'suggest',
],
})
)
const storybookDirectory = path.resolve(__dirname, '../node_modules/@storybook')
// Put our style rules at the beginning so they're processed by the time it
// gets to storybook's style rules.
config.module.rules.unshift({
test: /\.(sass|scss)$/,
use: [
'to-string-loader',
'css-loader',
{
loader: 'postcss-loader',
},
{
loader: 'sass-loader',
options: {
sassOptions: {
includePaths: [path.resolve(__dirname, '..', 'node_modules')],
},
},
},
],
// Make sure Storybook styles get handled by the Storybook config
exclude: storybookDirectory,
})
// Make sure Storybook style loaders are only evaluated for Storybook styles.
config.module.rules.find(rule => rule.test?.toString() === /\.css$/.toString()).include = storybookDirectory
config.module.rules.unshift({
// CSS rule for external plain CSS (skip SASS and PostCSS for build perf)
test: /\.css$/,
// Make sure Storybook styles get handled by the Storybook config
exclude: [storybookDirectory, ...monacoEditorPaths],
use: ['to-string-loader', 'css-loader'],
})
config.module.rules.unshift({
// CSS rule for monaco-editor, it expects styles to be loaded with `style-loader`.
test: /\.css$/,
include: monacoEditorPaths,
// Make sure Storybook styles get handled by the Storybook config
exclude: [storybookDirectory],
use: ['style-loader', 'css-loader'],
})
config.module.rules.unshift({
test: /\.ya?ml$/,
use: ['raw-loader'],
})
Object.assign(config.entry, {
'editor.worker': 'monaco-editor/esm/vs/editor/editor.worker.js',
'json.worker': 'monaco-editor/esm/vs/language/json/json.worker',
})
return config
},
}
module.exports = config

View File

@ -1,50 +0,0 @@
import 'focus-visible'
import { configureActions } from '@storybook/addon-actions'
import { withConsole } from '@storybook/addon-console'
import { setLinkComponent, AnchorLink } from '../client/shared/src/components/Link'
import { withDesign } from 'storybook-addon-designs'
import isChromatic from 'chromatic/isChromatic'
import * as themes from './themes'
export const decorators = [withDesign, (storyFn, context) => withConsole()(storyFn)(context)]
export const parameters = {
darkMode: {
stylePreview: true,
darkClass: 'theme-dark',
lightClass: 'theme-light',
light: themes.light,
dark: themes.dark,
},
}
configureActions({ depth: 100, limit: 20 })
setLinkComponent(AnchorLink)
// Default to light theme for Chromatic and "Open canvas in new tab" button.
// addon-dark-mode will override this if it's running.
if (!document.body.classList.contains('theme-dark')) {
document.body.classList.add('theme-light')
}
if (isChromatic()) {
const style = document.createElement('style')
style.innerHTML = `
.monaco-editor .cursor {
visibility: hidden !important;
}
`
document.head.append(style)
}
// @ts-ignore
window.MonacoEnvironment = {
getWorkerUrl(_, label) {
if (label === 'json') {
return '/json.worker.bundle.js'
}
return '/editor.worker.bundle.js'
},
}

View File

@ -1,5 +0,0 @@
module.exports = {
managerEntries: (entry = []) => {
return [...entry, require.resolve('./register')]
},
}

View File

@ -1,64 +0,0 @@
import React from 'react'
import addons, { types } from '@storybook/addons'
import { Icons, IconButton } from '@storybook/components'
import { useRedesignToggle, REDESIGN_CLASS_NAME } from '../../client/shared/src/util/useRedesignToggle'
const toggleRedesignClass = (element, isRedesignEnabled) => {
element.classList.toggle(REDESIGN_CLASS_NAME, !isRedesignEnabled)
}
const updatePreview = isRedesignEnabled => {
const iframe = document.getElementById('storybook-preview-iframe')
if (!iframe) {
return
}
const iframeDocument = iframe.contentDocument || iframe.contentWindow?.document
const body = iframeDocument?.body
toggleRedesignClass(body, isRedesignEnabled)
}
const updateManager = isRedesignEnabled => {
const manager = document.querySelector('body')
if (!manager) {
return
}
toggleRedesignClass(manager, isRedesignEnabled)
}
const RedesignToggleStorybook = () => {
const { isRedesignEnabled, setIsRedesignEnabled } = useRedesignToggle()
const handleRedesignToggle = () => {
setIsRedesignEnabled(!isRedesignEnabled)
updatePreview(isRedesignEnabled)
updateManager(isRedesignEnabled)
}
return (
<IconButton
key="redesign-toolbar"
active={isRedesignEnabled}
title={isRedesignEnabled ? 'Disable redesign theme' : 'Enable redesign theme'}
onClick={handleRedesignToggle}
>
<Icons icon="beaker" />
</IconButton>
)
}
/**
* Custom toolbar which renders button to toggle redesign theme global CSS class.
*/
addons.register('sourcegraph/redesign-toggle-toolbar', () => {
addons.add('sourcegraph/redesign-toggle-toolbar', {
title: 'Redesign toggle toolbar',
type: types.TOOL,
match: ({ viewMode }) => viewMode === 'story' || viewMode === 'docs',
render: RedesignToggleStorybook,
})
})

View File

@ -1,40 +0,0 @@
import { themes } from '@storybook/theming'
import openColor from 'open-color'
// @ts-ignore
import brandImage from '../ui/assets/img/wildcard-design-system.svg'
// Themes use the colors from our webapp.
/** @type {Partial<import('@storybook/theming').ThemeVars>} */
const common = {
colorPrimary: openColor.blue[6],
colorSecondary: openColor.blue[6],
brandImage,
brandTitle: 'Sourcegraph Wildcard design system',
fontBase:
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"',
fontCode: 'sfmono-regular, consolas, menlo, dejavu sans mono, monospace',
}
/** @type {import('@storybook/theming').ThemeVars} */
export const dark = {
...themes.dark,
...common,
appBg: '#1c2736',
appContentBg: '#151c28',
appBorderColor: '#2b3750',
barBg: '#0e121b',
barTextColor: '#a2b0cd',
textColor: '#f2f4f8',
inputTextColor: '#ffffff',
}
/** @type {import('@storybook/theming').ThemeVars} */
export const light = {
...themes.light,
...common,
appBg: '#fbfdff',
textColor: '#2b3750',
barTextColor: '#566e9f',
inputTextColor: '#2b3750',
}

View File

@ -1,6 +1,7 @@
// @ts-check
const logger = require('gulplog')
const semver = require('semver')
const path = require('path')
/** @type {import('@babel/core').ConfigFunction} */
module.exports = api => {
@ -19,7 +20,7 @@ module.exports = api => {
return {
presets: [
// Can't put this in plugins because it needs to run as the last plugin.
...(instrument ? [{ plugins: [['babel-plugin-istanbul', { exclude: ['node_modules/**'] }]] }] : []),
...(instrument ? [{ plugins: [['babel-plugin-istanbul', { cwd: path.resolve(__dirname) }]] }] : []),
[
'@babel/preset-env',
{

View File

@ -10,6 +10,7 @@
- **shared**: Contains common TypeScript/React/SCSS client code shared between the browser extension and the web app. Everything in this package is code-host agnostic.
- **branded**: Contains React components and implements the visual design language we use across our web app and e.g. in the options menu of the browser extension. Over time, components from `shared` and `branded` packages should be moved into the `wildcard` package.
- **wildcard**: Package that encapsulates storybook configuration and contains our Wildcard design system components. If we're using a component in two or more different areas (e.g. `web-app` and `browser-extension`) then it should live in the `wildcard` package. Otherwise the components should be better colocated with the code where they're actually used.
- **storybook**: Storybook configuration.
## Further migration plan

View File

@ -0,0 +1 @@
out/

View File

@ -0,0 +1,13 @@
// @ts-check
const baseConfig = require('../../.eslintrc.js')
module.exports = {
extends: '../../.eslintrc.js',
parserOptions: {
...baseConfig.parserOptions,
project: [__dirname + '/tsconfig.json'],
},
rules: {},
overrides: baseConfig.overrides,
}

View File

@ -0,0 +1,12 @@
# Storybook configuration
## Usage
Storybook configuration is setup as a `yarn workspace` symlink.
Important commands are expose via root `package.json`:
```sh
yarn storybook
yarn build-storybook
```

View File

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@ -0,0 +1,5 @@
// @ts-check
module.exports = {
extends: '../../babel.config.js',
}

9
client/storybook/globals.d.ts vendored Normal file
View File

@ -0,0 +1,9 @@
declare module '@storybook/addon-console' {
export declare const withConsole: () => (storyFn: any) => (context: StoryContext) => React.ReactElement
}
declare interface Window {
MonacoEnvironment: {
getWorkerUrl(moduleId: string, label: string): string
}
}

View File

@ -1,6 +1,6 @@
// @ts-check
const config = require('../jest.config.base')
const config = require('../../jest.config.base')
const exportedConfig = {
...config,

View File

@ -0,0 +1,14 @@
{
"private": true,
"name": "@sourcegraph/storybook",
"version": "0.0.1",
"description": "Sourcegraph Storybook configuration",
"sideEffects": false,
"license": "Apache-2.0",
"scripts": {
"eslint": "eslint --cache 'src/**/*.[jt]s?(x)'",
"start": "start-storybook -p 9001 -c ./src -s ./assets",
"build": "build-storybook -c ./src -s ./assets",
"test": "jest"
}
}

View File

@ -1,8 +1,8 @@
import path from 'path'
import { pathToFileURL } from 'url'
import initStoryshots from '@storybook/addon-storyshots'
import { puppeteerTest } from '@storybook/addon-storyshots-puppeteer'
import * as path from 'path'
import { pathToFileURL } from 'url'
import { recordCoverage } from '../client/shared/src/testing/coverage'
import { recordCoverage } from '@sourcegraph/shared/src/testing/coverage'
// This test suite does not actually test anything.
// It just loads up the storybook in Puppeteer and records its coverage,

View File

@ -0,0 +1,151 @@
import path from 'path'
import { remove } from 'lodash'
import TerserPlugin from 'terser-webpack-plugin'
import MonacoWebpackPlugin from 'monaco-editor-webpack-plugin'
import { Configuration, DefinePlugin, ProgressPlugin, RuleSetRule } from 'webpack'
const rootPath = path.resolve(__dirname, '../../../')
const monacoEditorPaths = [path.resolve(rootPath, 'node_modules', 'monaco-editor')]
const storiesGlob = path.resolve(rootPath, 'client/**/*.story.tsx')
const shouldMinify = !!process.env.MINIFY
const config = {
stories: [storiesGlob],
addons: [
'@storybook/addon-knobs',
'@storybook/addon-actions',
'storybook-addon-designs',
'storybook-dark-mode',
'@storybook/addon-a11y',
'@storybook/addon-toolbars',
'./redesign-toggle-toolbar/register.ts',
],
webpackFinal: (config: Configuration) => {
// Include sourcemaps
config.mode = shouldMinify ? 'production' : 'development'
config.devtool = shouldMinify ? 'source-map' : 'cheap-module-eval-source-map'
config.plugins?.push(
new DefinePlugin({
NODE_ENV: JSON.stringify(config.mode),
'process.env.NODE_ENV': JSON.stringify(config.mode),
})
)
if (shouldMinify) {
config.optimization = {
namedModules: false,
minimize: true,
minimizer: [
new TerserPlugin({
terserOptions: {
sourceMap: true,
compress: {
// Don't inline functions, which causes name collisions with uglify-es:
// https://github.com/mishoo/UglifyJS2/issues/2842
inline: 1,
},
},
}),
],
}
}
// We don't use Storybook's default Babel config for our repo, it doesn't include everything we need.
config.module?.rules.splice(0, 1)
if (process.env.CI) {
remove(config.plugins || [], plugin => plugin instanceof ProgressPlugin)
}
config.module?.rules.push({
test: /\.tsx?$/,
loader: require.resolve('babel-loader'),
options: {
configFile: path.resolve(rootPath, 'babel.config.js'),
},
})
config.plugins?.push(
new MonacoWebpackPlugin({
languages: ['json'],
features: [
'bracketMatching',
'clipboard',
'coreCommands',
'cursorUndo',
'find',
'format',
'hover',
'inPlaceReplace',
'iPadShowKeyboard',
'links',
'suggest',
],
})
)
const storybookDirectory = path.resolve(rootPath, 'node_modules/@storybook')
config.resolve?.modules?.push('src')
// Put our style rules at the beginning so they're processed by the time it
// gets to storybook's style rules.
config.module?.rules.unshift({
test: /\.(sass|scss)$/,
use: [
'to-string-loader',
'css-loader',
{
loader: 'postcss-loader',
},
{
loader: 'sass-loader',
options: {
sassOptions: {
includePaths: [path.resolve(rootPath, 'node_modules')],
},
},
},
],
// Make sure Storybook styles get handled by the Storybook config
exclude: storybookDirectory,
})
// Make sure Storybook style loaders are only evaluated for Storybook styles.
const cssRule = config.module?.rules.find(rule => rule.test?.toString() === /\.css$/.toString()) as RuleSetRule
cssRule.include = storybookDirectory
config.module?.rules.unshift({
// CSS rule for external plain CSS (skip SASS and PostCSS for build perf)
test: /\.css$/,
// Make sure Storybook styles get handled by the Storybook config
exclude: [storybookDirectory, ...monacoEditorPaths],
use: ['to-string-loader', 'css-loader'],
})
config.module?.rules.unshift({
// CSS rule for monaco-editor, it expects styles to be loaded with `style-loader`.
test: /\.css$/,
include: monacoEditorPaths,
// Make sure Storybook styles get handled by the Storybook config
exclude: [storybookDirectory],
use: ['style-loader', 'css-loader'],
})
config.module?.rules.unshift({
test: /\.ya?ml$/,
use: ['raw-loader'],
})
Object.assign(config.entry, {
'editor.worker': 'monaco-editor/esm/vs/editor/editor.worker.js',
'json.worker': 'monaco-editor/esm/vs/language/json/json.worker',
})
return config
},
}
module.exports = config

View File

@ -0,0 +1,54 @@
import 'focus-visible'
import { ReactElement } from 'react'
import isChromatic from 'chromatic/isChromatic'
import { withDesign } from 'storybook-addon-designs'
import { DecoratorFunction } from '@storybook/addons'
import { configureActions } from '@storybook/addon-actions'
import { withConsole } from '@storybook/addon-console'
import { setLinkComponent, AnchorLink } from '@sourcegraph/shared/src/components/Link'
import * as themes from './themes'
const withConsoleDecorator: DecoratorFunction<ReactElement> = (storyFn, context): ReactElement =>
withConsole()(storyFn)(context)
export const decorators = [withDesign, withConsoleDecorator]
export const parameters = {
darkMode: {
stylePreview: true,
darkClass: 'theme-dark',
lightClass: 'theme-light',
light: themes.light,
dark: themes.dark,
},
}
configureActions({ depth: 100, limit: 20 })
setLinkComponent(AnchorLink)
// Default to light theme for Chromatic and "Open canvas in new tab" button.
// addon-dark-mode will override this if it's running.
if (!document.body.classList.contains('theme-dark')) {
document.body.classList.add('theme-light')
}
if (isChromatic()) {
const style = document.createElement('style')
style.innerHTML = `
.monaco-editor .cursor {
visibility: hidden !important;
}
`
document.head.append(style)
}
window.MonacoEnvironment = {
getWorkerUrl(moduleId: string, label: string) {
if (label === 'json') {
return '/json.worker.bundle.js'
}
return '/editor.worker.bundle.js'
},
}

View File

@ -0,0 +1,48 @@
import React, { ReactElement } from 'react'
import { Icons, IconButton } from '@storybook/components'
import { useRedesignToggle, REDESIGN_CLASS_NAME } from '@sourcegraph/shared/src/util/useRedesignToggle'
const toggleRedesignClass = (element: HTMLElement, isRedesignEnabled: boolean): void => {
element.classList.toggle(REDESIGN_CLASS_NAME, !isRedesignEnabled)
}
const updatePreview = (isRedesignEnabled: boolean): void => {
const iframe = document.querySelector('#storybook-preview-iframe') as HTMLIFrameElement | undefined
const iframeDocument = iframe?.contentDocument || iframe?.contentWindow?.document
const body = iframeDocument?.body
if (body) {
toggleRedesignClass(body, isRedesignEnabled)
}
}
const updateManager = (isRedesignEnabled: boolean): void => {
const manager = document.querySelector('body')
if (manager) {
toggleRedesignClass(manager, isRedesignEnabled)
}
}
export const RedesignToggleStorybook = (): ReactElement => {
const { isRedesignEnabled, setIsRedesignEnabled } = useRedesignToggle()
const handleRedesignToggle = (): void => {
setIsRedesignEnabled(!isRedesignEnabled)
updatePreview(isRedesignEnabled)
updateManager(isRedesignEnabled)
}
return (
<IconButton
key="redesign-toolbar"
active={isRedesignEnabled}
title={isRedesignEnabled ? 'Disable redesign theme' : 'Enable redesign theme'}
// eslint-disable-next-line react/jsx-no-bind
onClick={handleRedesignToggle}
>
<Icons icon="beaker" />
</IconButton>
)
}

View File

@ -0,0 +1,14 @@
import addons, { types } from '@storybook/addons'
import { RedesignToggleStorybook } from './RedesignToggleStorybook'
/**
* Custom toolbar which renders button to toggle redesign theme global CSS class.
*/
addons.register('sourcegraph/redesign-toggle-toolbar', () => {
addons.add('sourcegraph/redesign-toggle-toolbar', {
title: 'Redesign toggle toolbar',
type: types.TOOL,
match: ({ viewMode }) => viewMode === 'story' || viewMode === 'docs',
render: RedesignToggleStorybook,
})
})

View File

@ -0,0 +1,35 @@
import { ThemeVars, themes } from '@storybook/theming'
import openColor from 'open-color'
// Themes use the colors from our webapp.
const common: Omit<ThemeVars, 'base'> = {
colorPrimary: openColor.blue[6],
colorSecondary: openColor.blue[6],
brandTitle: 'Sourcegraph Wildcard design system',
brandUrl: 'https://sourcegraph.com',
brandImage: '/img/wildcard-design-system.svg',
fontBase:
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"',
fontCode: 'sfmono-regular, consolas, menlo, dejavu sans mono, monospace',
}
export const dark: ThemeVars = {
...themes.dark,
...common,
appBg: '#1c2736',
appContentBg: '#151c28',
appBorderColor: '#2b3750',
barBg: '#0e121b',
barTextColor: '#a2b0cd',
textColor: '#f2f4f8',
inputTextColor: '#ffffff',
}
export const light: ThemeVars = {
...themes.light,
...common,
appBg: '#fbfdff',
textColor: '#2b3750',
barTextColor: '#566e9f',
inputTextColor: '#2b3750',
}

View File

@ -0,0 +1,13 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"module": "commonjs",
"sourceRoot": "src",
"rootDir": ".",
"outDir": "./out",
"baseUrl": "./src",
"jsx": "react",
},
"references": [{ "path": "../shared" }],
"include": ["./src/**/*", "./*.ts"],
}

View File

@ -19,6 +19,7 @@ DIRS=(
client/extension-api
client/eslint-plugin-sourcegraph
client/extension-api-types
client/storybook
dev/release
dev/ts-morph
)

View File

@ -11,6 +11,6 @@ module.exports = {
'client/branded/jest.config.js',
'client/web/jest.config.js',
'client/wildcard/jest.config.js',
'.storybook/jest.config.js',
'client/storybook/jest.config.js',
],
}

View File

@ -27,9 +27,9 @@
"cover-integration": "nyc --hook-require=false yarn test-integration",
"test-e2e": "TS_NODE_PROJECT=client/web/src/end-to-end/tsconfig.json mocha ./client/web/src/end-to-end/end-to-end.test.ts",
"cover-e2e": "nyc --hook-require=false --silent=true yarn test-e2e",
"storybook": "start-storybook -p 9001 -c .storybook -s ui/assets",
"build-storybook": "build-storybook -c .storybook -s ui/assets",
"cover-storybook": "nyc --hook-require=false yarn jest .storybook/coverage",
"storybook": "yarn workspace @sourcegraph/storybook run start",
"build-storybook": "yarn workspace @sourcegraph/storybook run build",
"cover-storybook": "nyc --hook-require=false yarn jest client/storybook/src/coverage",
"deduplicate": "yarn-deduplicate -s fewer",
"release": "cd dev/release && yarn run release",
"docsite:serve": "./dev/docsite.sh -config doc/docsite.json serve -http=localhost:5080",
@ -58,7 +58,8 @@
"exclude": [
"node_modules",
"**/*.d.ts",
"**/*.@(test|story).ts?(x)"
"**/*.@(test|story).ts?(x)",
"client/storybook"
]
},
"jscpd": {
@ -175,6 +176,7 @@
"@types/sinon": "9.0.4",
"@types/socket.io": "2.1.10",
"@types/socket.io-client": "1.4.33",
"@types/terser-webpack-plugin": "^4.2.0",
"@types/testing-library__jest-dom": "^5.9.5",
"@types/textarea-caret": "3.0.0",
"@types/uuid": "8.0.1",

View File

@ -13,6 +13,7 @@
{ "path": "client/browser/src/end-to-end" },
{ "path": "client/extension-api" },
{ "path": "client/extension-api-types" },
{ "path": "client/storybook" },
{ "path": "dev/release" },
{ "path": "schema" },
],

View File

@ -4373,6 +4373,14 @@
resolved "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.6.tgz#a9ca4b70a18b270ccb2bc0aaafefd1d486b7ea74"
integrity sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA==
"@types/terser-webpack-plugin@^4.2.0":
version "4.2.0"
resolved "https://registry.npmjs.org/@types/terser-webpack-plugin/-/terser-webpack-plugin-4.2.0.tgz#fe39917d334287c5cf25abcf370867a31ed59cd6"
integrity sha512-oGfGZzjwKY7s8gAYLZJuVuu9GXuc/ACo7bL/DQg7ROFkEMFQULB1W7qZjQrTXf2SkTfQx7/zcerfuLkUCVFGhg==
dependencies:
"@types/webpack" "*"
terser "^4.6.13"
"@types/testing-library__jest-dom@^5.9.1", "@types/testing-library__jest-dom@^5.9.5":
version "5.9.5"
resolved "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.9.5.tgz#5bf25c91ad2d7b38f264b12275e5c92a66d849b0"
@ -21129,7 +21137,7 @@ terser-webpack-plugin@^4.2.3:
terser "^5.3.4"
webpack-sources "^1.4.3"
terser@^4.1.2, terser@^4.6.3, terser@^4.8.0:
terser@^4.1.2, terser@^4.6.13, terser@^4.6.3, terser@^4.8.0:
version "4.8.0"
resolved "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17"
integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==