Browse Source

ref(storybook): Convert config to typescript (#27267)

Evan Purkhiser 3 years ago
parent
commit
78859f9ea0
6 changed files with 94 additions and 74 deletions
  1. 0 7
      .storybook/.babelrc.js
  2. 0 37
      .storybook/main.js
  3. 49 0
      .storybook/main.ts
  4. 1 1
      .storybook/manager.ts
  5. 3 4
      .storybook/preview.tsx
  6. 41 25
      .storybook/webpack.config.ts

+ 0 - 7
.storybook/.babelrc.js

@@ -1,7 +0,0 @@
-const config = require('../babel.config.js');
-
-// Why do we have to do this?
-// See: https://github.com/babel/babel/issues/11622#issuecomment-638609015
-config.plugins = [...config.plugins, '@babel/plugin-proposal-private-methods'];
-
-module.exports = config;

+ 0 - 37
.storybook/main.js

@@ -1,37 +0,0 @@
-/* eslint-env node */
-/* eslint import/no-nodejs-modules:0 */
-const path = require('path');
-
-const toPath = p => path.join(process.cwd(), p);
-
-module.exports = {
-  stories: ['../docs-ui/components/*.stories.*'],
-  core: {
-    builder: 'webpack5',
-  },
-  addons: [
-    {
-      name: '@storybook/addon-essentials',
-      options: {},
-    },
-    '@storybook/addon-a11y',
-  ],
-
-  // XXX(emotion11): Workaround because storybook still uses emotion 10
-  // internally. See https://github.com/storybookjs/storybook/issues/13145
-  webpackFinal: async config => {
-    return {
-      ...config,
-      resolve: {
-        ...config.resolve,
-        alias: {
-          ...config.resolve.alias,
-          '@emotion/core': toPath('node_modules/@emotion/react'),
-          '@emotion/styled': toPath('node_modules/@emotion/styled'),
-          'emotion-theming': toPath('node_modules/@emotion/react'),
-          '@babel/preset-react': toPath('node_modules/@babel/preset-react'),
-        },
-      },
-    };
-  },
-};

+ 49 - 0
.storybook/main.ts

@@ -0,0 +1,49 @@
+/* eslint-env node */
+/* eslint import/no-nodejs-modules:0 */
+import path from 'path';
+
+import {StorybookConfig} from '@storybook/core-common';
+
+import babelConfig from '../babel.config';
+
+const toPath = (p: string) => path.join(process.cwd(), p);
+
+const config: StorybookConfig = {
+  stories: ['../docs-ui/components/*.stories.*'],
+  core: {
+    builder: 'webpack5',
+  },
+  addons: [
+    {
+      name: '@storybook/addon-essentials',
+      options: {},
+    },
+    '@storybook/addon-a11y',
+  ],
+
+  // For whatever reason the `babel` config override is not present in
+  // storybooks StorybookConfig type.
+  //
+  // See https://github.com/storybookjs/storybook/issues/15502
+  //
+  // @ts-expect-error
+  babel: babelConfig,
+
+  // XXX(emotion11): Workaround because storybook still uses emotion 10
+  // internally. See https://github.com/storybookjs/storybook/issues/13145
+  webpackFinal: async webpackConf => ({
+    ...webpackConf,
+    resolve: {
+      ...webpackConf?.resolve,
+      alias: {
+        ...webpackConf?.resolve?.alias,
+        '@emotion/core': toPath('node_modules/@emotion/react'),
+        '@emotion/styled': toPath('node_modules/@emotion/styled'),
+        'emotion-theming': toPath('node_modules/@emotion/react'),
+        '@babel/preset-react': toPath('node_modules/@babel/preset-react'),
+      },
+    },
+  }),
+};
+
+export default config;

+ 1 - 1
.storybook/manager.js → .storybook/manager.ts

@@ -1,5 +1,5 @@
 import {addons} from '@storybook/addons';
-import {create} from '@storybook/theming/create';
+import {create} from '@storybook/theming';
 
 const theme = create({
   base: 'light',

+ 3 - 4
.storybook/preview.js → .storybook/preview.tsx

@@ -1,14 +1,13 @@
 import 'focus-visible';
 import '../docs-ui/index.js';
 
-import React from 'react';
-import {addDecorator, addParameters} from '@storybook/react';
+import {addDecorator, addParameters, DecoratorFn, Parameters} from '@storybook/react';
 import {ThemeProvider} from 'emotion-theming';
 
 import GlobalStyles from '../static/app/styles/global';
 import {darkTheme, lightTheme} from '../static/app/utils/theme';
 
-const withTheme = (Story, context) => {
+const withTheme: DecoratorFn = (Story, context) => {
   const isDark = context.globals.theme === 'dark';
   const currentTheme = isDark ? darkTheme : lightTheme;
 
@@ -123,7 +122,7 @@ export const globalTypes = {
   },
 };
 
-export const parameters = {
+export const parameters: Parameters = {
   /**
    * @storybook/addon-backgrounds background is controlled via theme
    */

+ 41 - 25
.storybook/webpack.config.js → .storybook/webpack.config.ts

@@ -1,8 +1,15 @@
 /* eslint-env node */
 /* eslint import/no-nodejs-modules:0 */
-const path = require('path');
-const webpack = require('webpack');
-const appConfig = require('../webpack.config');
+
+import path from 'path';
+
+import webpack from 'webpack';
+
+import appConfigAny from '../webpack.config';
+
+// TODO(epurkhiser): Once we convert our webpack.config over to typescript we
+// can remove this
+const appConfig = appConfigAny as any;
 
 const staticPath = path.resolve(__dirname, '..', 'static', 'app');
 
@@ -11,24 +18,37 @@ const staticPath = path.resolve(__dirname, '..', 'static', 'app');
  * to an empty object specifically for eslint, since it will load this config
  * without passing in a config object.
  */
-const emptyConfig = {
+const emptyConfig: webpack.Configuration = {
   module: {rules: []},
   resolve: {alias: {}, extensions: []},
   plugins: [],
 };
 
-module.exports = ({config} = {config: emptyConfig}) => {
-  const [firstRule, ...rules] = config.module.rules;
+type Opts = {
+  config: webpack.Configuration;
+};
+
+const configBuilder = ({config}: Opts = {config: emptyConfig}) => {
+  const [firstRule, ...rules] = (config.module?.rules ?? []) as webpack.RuleSetRule[];
 
   const filteredRules = rules.filter(rule => {
-    return (
-      (!rule.loader || !rule.loader.includes('file-loader')) &&
-      (!Array.isArray(rule.use) ||
-        !rule.use.find(({loader}) => loader && loader.includes('postcss-loader')))
-    );
+    const isFileLoader = !!rule?.loader?.includes('file-loader');
+
+    const isPostCssLoader =
+      Array.isArray(rule.use) &&
+      rule.use.find(
+        use => typeof use === 'object' && use?.loader?.includes('postcss-loader')
+      );
+
+    return !isFileLoader && !isPostCssLoader;
   });
 
-  const newConfig = {
+  const extensions = new Set([
+    ...(config.resolve?.extensions ?? []),
+    ...(appConfig.resolve?.extensions ?? []),
+  ]);
+
+  const newConfig: webpack.Configuration = {
     ...config,
     module: {
       ...config.module,
@@ -65,27 +85,21 @@ module.exports = ({config} = {config: emptyConfig}) => {
     },
 
     plugins: [
-      ...config.plugins,
-      new webpack.ProvidePlugin({
-        jQuery: 'jquery',
-      }),
-      new webpack.DefinePlugin({
-        'process.env.FIXED_DYNAMIC_CONTENT': true,
-      }),
+      ...(config.plugins ?? []),
+      new webpack.ProvidePlugin({jQuery: 'jquery'}),
+      new webpack.DefinePlugin({'process.env.FIXED_DYNAMIC_CONTENT': true}),
     ],
 
     resolve: {
       ...config.resolve,
-      extensions: Array.from(
-        new Set([...config.resolve.extensions, ...appConfig.resolve.extensions])
-      ),
+      extensions: Array.from(extensions),
       alias: {
-        ...config.resolve.alias,
-        ...appConfig.resolve.alias,
+        ...config.resolve?.alias,
+        ...appConfig.resolve?.alias,
         app: staticPath,
       },
       fallback: {
-        ...appConfig.resolve.fallback,
+        ...appConfig.resolve?.fallback,
         // XXX(epurkhiser): As per [0] assert is required for
         // @storybook/addons-docs, but seems we can just noop the pollyfill.
         //
@@ -97,3 +111,5 @@ module.exports = ({config} = {config: emptyConfig}) => {
 
   return newConfig;
 };
+
+export default configBuilder;