Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Maintenance: Migrate @storybook/preset-create-react-app to strict TS #22395

Merged
merged 16 commits into from
Nov 27, 2023
Merged
2 changes: 1 addition & 1 deletion code/presets/create-react-app/src/helpers/checkPresets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const incompatiblePresets = ['@storybook/preset-scss', '@storybook/preset-typesc
export const checkPresets = (options: PluginOptions): void => {
const { presetsList } = options;

presetsList.forEach((preset: string | { name: string }) => {
presetsList?.forEach((preset: string | { name: string }) => {
const presetName = typeof preset === 'string' ? preset : preset.name;
if (incompatiblePresets.includes(presetName)) {
logger.warn(
Expand Down
19 changes: 10 additions & 9 deletions code/presets/create-react-app/src/helpers/mergePlugins.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import type { Configuration } from 'webpack';
import type { Configuration, WebpackPluginInstance } from 'webpack';
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';

export const mergePlugins = (...args: Configuration['plugins']): Configuration['plugins'] =>
args.reduce((plugins, plugin) => {
export const mergePlugins = (...args: WebpackPluginInstance[]): Configuration['plugins'] =>
args?.reduce((plugins, plugin) => {
if (
plugins.some(
(includedPlugin) => includedPlugin.constructor.name === plugin.constructor.name
plugins?.some(
(includedPlugin: WebpackPluginInstance) =>
includedPlugin?.constructor.name === plugin?.constructor.name
) ||
plugin.constructor.name === 'WebpackManifestPlugin'
plugin?.constructor.name === 'WebpackManifestPlugin'
) {
return plugins;
}
let updatedPlugin = plugin;
if (plugin.constructor.name === 'ReactRefreshPlugin') {
if (plugin?.constructor.name === 'ReactRefreshPlugin') {
// Storybook uses webpack-hot-middleware
// https://github.com/storybookjs/presets/issues/177

Expand All @@ -22,5 +23,5 @@ export const mergePlugins = (...args: Configuration['plugins']): Configuration['
},
});
}
return [...plugins, updatedPlugin];
}, [] as Configuration['plugins']);
return [...(plugins ?? []), updatedPlugin];
}, [] as WebpackPluginInstance[]);
6 changes: 4 additions & 2 deletions code/presets/create-react-app/src/helpers/processCraConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ export const processCraConfig = (
const storybookVersion = semver.coerce(options.packageJson.version) || '';
const isStorybook530 = semver.gte(storybookVersion, '5.3.0');

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return craWebpackConfig.module!.rules.reduce((rules, rule): RuleSetRule[] => {
if (!craWebpackConfig?.module?.rules) return [];

return craWebpackConfig.module.rules.reduce((rules, rule): RuleSetRule[] => {
const { oneOf, include } = rule as RuleSetRule;

// Add our `configDir` to support JSX and TypeScript in that folder.
Expand All @@ -61,6 +62,7 @@ export const processCraConfig = (
return [
...rules,
{
// @ts-expect-error (not type correct)
oneOf: oneOf.map((oneOfRule: RuleSetRule): RuleSetRule => {
if (oneOfRule.type === 'asset/resource') {
if (isStorybook530) {
Expand Down
19 changes: 10 additions & 9 deletions code/presets/create-react-app/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable import/no-extraneous-dependencies */
import { join, relative, dirname } from 'path';
import type { Configuration, RuleSetRule } from 'webpack';
import type { Configuration, RuleSetRule, WebpackPluginInstance } from 'webpack';
import semver from 'semver';
import { logger } from '@storybook/node-logger';
import PnpWebpackPlugin from 'pnp-webpack-plugin';
Expand Down Expand Up @@ -78,12 +78,13 @@ const webpack = async (

// Remove existing rules related to JavaScript and TypeScript.
logger.info(`=> Removing existing JavaScript and TypeScript rules.`);
const filteredRules =
webpackConfig.module &&
webpackConfig.module.rules.filter(
({ test }: RuleSetRule) =>
!(test instanceof RegExp && ((test && test.test('.js')) || test.test('.ts')))
);
const filteredRules = (webpackConfig.module?.rules as RuleSetRule[])?.filter((rule) => {
if (typeof rule === 'string') {
return false;
}
const { test } = rule;
return !(test instanceof RegExp && (test?.test('.js') || test?.test('.ts')));
});

// Require the CRA config and set the appropriate mode.
const craWebpackConfigPath = join(scriptsPath, 'config', 'webpack.config');
Expand Down Expand Up @@ -133,8 +134,8 @@ const webpack = async (
// NOTE: this prioritizes the storybook version of a plugin
// when there are duplicates between SB and CRA
plugins: mergePlugins(
...(webpackConfig.plugins || []),
...(craWebpackConfig.plugins ?? []),
...((webpackConfig.plugins ?? []) as WebpackPluginInstance[]),
...((craWebpackConfig.plugins ?? []) as WebpackPluginInstance[]),
...tsDocgenPlugin
),
resolve: {
Expand Down
2 changes: 1 addition & 1 deletion code/presets/create-react-app/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"strict": false
"strict": true
},
"include": ["src/**/*"]
}