From ae0ff86d47e19fe55ead731be012321e32a7093f Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Fri, 10 Nov 2023 14:33:21 +0100 Subject: [PATCH 1/3] SWC: Introduce React and Preact specific SWC config --- code/lib/types/src/modules/core-common.ts | 23 ++++++++++--------- code/presets/preact-webpack/src/index.ts | 22 ++++++++++++++++++ code/presets/react-webpack/package.json | 4 ++-- .../src/framework-preset-react.ts | 23 +++++++++++++++++++ 4 files changed, 59 insertions(+), 13 deletions(-) diff --git a/code/lib/types/src/modules/core-common.ts b/code/lib/types/src/modules/core-common.ts index a9d7c7730b88..8ffe22b1d01f 100644 --- a/code/lib/types/src/modules/core-common.ts +++ b/code/lib/types/src/modules/core-common.ts @@ -1,11 +1,12 @@ /* eslint-disable @typescript-eslint/naming-convention */ import type { FileSystemCache } from 'file-system-cache'; - +import type { Options as SWCOptions } from '@swc/core'; import type { Options as TelejsonOptions } from 'telejson'; -import type { TransformOptions } from '@babel/core'; +import type { TransformOptions as BabelOptions } from '@babel/core'; import type { Router } from 'express'; import type { Server } from 'http'; import type { PackageJson as PackageJsonFromTypeFest } from 'type-fest'; + import type { StoriesEntry, Indexer, StoryIndexer } from './indexer'; /** @@ -70,7 +71,8 @@ export interface Presets { args?: Options ): Promise; apply(extension: 'framework', config?: {}, args?: any): Promise; - apply(extension: 'babel', config?: {}, args?: any): Promise; + apply(extension: 'babel', config?: {}, args?: any): Promise; + apply(extension: 'swc', config?: {}, args?: any): Promise; apply(extension: 'entries', config?: [], args?: any): Promise; apply(extension: 'stories', config?: [], args?: any): Promise; apply(extension: 'managerEntries', config: [], args?: any): Promise; @@ -371,10 +373,12 @@ export interface StorybookConfig { /** * Modify or return babel config. */ - babel?: ( - config: TransformOptions, - options: Options - ) => TransformOptions | Promise; + babel?: (config: BabelOptions, options: Options) => BabelOptions | Promise; + + /** + * Modify or return swc config. + */ + swc?: (config: SWCOptions, options: Options) => SWCOptions | Promise; /** * Modify or return env config. @@ -384,10 +388,7 @@ export interface StorybookConfig { /** * Modify or return babel config. */ - babelDefault?: ( - config: TransformOptions, - options: Options - ) => TransformOptions | Promise; + babelDefault?: (config: BabelOptions, options: Options) => BabelOptions | Promise; /** * Add additional scripts to run in the preview a la `.storybook/preview.js` diff --git a/code/presets/preact-webpack/src/index.ts b/code/presets/preact-webpack/src/index.ts index 758012bf73ab..e6cb9524dca3 100644 --- a/code/presets/preact-webpack/src/index.ts +++ b/code/presets/preact-webpack/src/index.ts @@ -47,3 +47,25 @@ export const webpackFinal: StorybookConfig['webpackFinal'] = (config) => { }, }; }; + +export const swc: StorybookConfig['swc'] = (config) => { + const isDevelopment = process.env.NODE_ENV !== 'production'; + + return { + ...config, + jsc: { + ...(config?.jsc ?? {}), + transform: { + ...(config?.jsc?.transform ?? {}), + react: { + ...(config?.jsc?.transform?.react ?? {}), + runtime: 'automatic', + importSource: 'preact', + pragma: 'h', + pragmaFrag: 'Fragment', + development: isDevelopment, + }, + }, + }, + }; +}; diff --git a/code/presets/react-webpack/package.json b/code/presets/react-webpack/package.json index c7be54baa76e..f6ef0324349e 100644 --- a/code/presets/react-webpack/package.json +++ b/code/presets/react-webpack/package.json @@ -71,7 +71,7 @@ "dependencies": { "@babel/preset-flow": "^7.22.15", "@babel/preset-react": "^7.22.15", - "@pmmmwh/react-refresh-webpack-plugin": "^0.5.5", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11", "@storybook/core-webpack": "workspace:*", "@storybook/docs-tools": "workspace:*", "@storybook/node-logger": "workspace:*", @@ -83,7 +83,7 @@ "fs-extra": "^11.1.0", "magic-string": "^0.30.5", "react-docgen": "^7.0.0", - "react-refresh": "^0.11.0", + "react-refresh": "^0.14.0", "semver": "^7.3.7", "webpack": "5" }, diff --git a/code/presets/react-webpack/src/framework-preset-react.ts b/code/presets/react-webpack/src/framework-preset-react.ts index e913a32b5332..65da5b9825aa 100644 --- a/code/presets/react-webpack/src/framework-preset-react.ts +++ b/code/presets/react-webpack/src/framework-preset-react.ts @@ -83,3 +83,26 @@ export const webpackFinal: StorybookConfig['webpackFinal'] = async (config, opti ], }; }; + +export const swc: StorybookConfig['swc'] = async (config, options) => { + const isDevelopment = options.configType !== 'PRODUCTION'; + + if (!(await applyFastRefresh(options))) { + return config; + } + + return { + ...config, + jsc: { + ...(config?.jsc ?? {}), + transform: { + ...(config?.jsc?.transform ?? {}), + react: { + ...(config?.jsc?.transform?.react ?? {}), + development: isDevelopment, + refresh: isDevelopment, + }, + }, + }, + }; +}; From c714f972d91fa99169051e873c1e4e126db04901 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Fri, 10 Nov 2023 14:47:38 +0100 Subject: [PATCH 2/3] Load swc preset options for swc loader --- .../src/preview/iframe-webpack.config.ts | 2 +- .../builder-webpack5/src/preview/loaders.ts | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/code/builders/builder-webpack5/src/preview/iframe-webpack.config.ts b/code/builders/builder-webpack5/src/preview/iframe-webpack.config.ts index af8a0c874896..8542375d1b9d 100644 --- a/code/builders/builder-webpack5/src/preview/iframe-webpack.config.ts +++ b/code/builders/builder-webpack5/src/preview/iframe-webpack.config.ts @@ -322,7 +322,7 @@ export default async ( }, }, builderOptions.useSWC - ? createSWCLoader(Object.keys(virtualModuleMapping)) + ? await createSWCLoader(Object.keys(virtualModuleMapping), options) : createBabelLoader(babelOptions, typescriptOptions, Object.keys(virtualModuleMapping)), { test: /\.md$/, diff --git a/code/builders/builder-webpack5/src/preview/loaders.ts b/code/builders/builder-webpack5/src/preview/loaders.ts index a458cf8cdaea..26943cd90e06 100644 --- a/code/builders/builder-webpack5/src/preview/loaders.ts +++ b/code/builders/builder-webpack5/src/preview/loaders.ts @@ -1,7 +1,8 @@ import { getProjectRoot } from '@storybook/core-common'; -import type { Options } from '@swc/core'; +import type { Options as SwcOptions } from '@swc/core'; import { dedent } from 'ts-dedent'; import { logger } from '@storybook/node-logger'; +import type { Options } from '@storybook/types'; import type { TypescriptOptions } from '../types'; export const createBabelLoader = ( @@ -22,14 +23,24 @@ export const createBabelLoader = ( }; }; -export const createSWCLoader = (excludes: string[] = []) => { +export const createSWCLoader = async (excludes: string[] = [], options: Options) => { logger.warn(dedent` The SWC loader is an experimental feature and may change or even be removed at any time. `); - const config: Options = { + const swc = await options.presets.apply('swc', {}, options); + const typescriptOptions = await options.presets.apply<{ skipCompiler?: boolean }>( + 'typescript', + {}, + options + ); + + const config: SwcOptions = { + ...swc, jsc: { + ...(swc.jsc ?? {}), parser: { + ...(swc.jsc?.parser ?? {}), syntax: 'typescript', tsx: true, dynamicImport: true, @@ -37,7 +48,7 @@ export const createSWCLoader = (excludes: string[] = []) => { }, }; return { - test: /\.(mjs|cjs|tsx?|jsx?)$/, + test: typescriptOptions.skipCompiler ? /\.(mjs|cjs|jsx?)$/ : /\.(mjs|cjs|tsx?|jsx?)$/, use: [ { loader: require.resolve('swc-loader'), From 731281e5e5331fc0cb41510bf27f02be91c5dd12 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Fri, 10 Nov 2023 16:02:40 +0100 Subject: [PATCH 3/3] Update yarn.lock --- code/yarn.lock | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/code/yarn.lock b/code/yarn.lock index e6edb2eb9d61..d18a4c18a885 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -4464,7 +4464,7 @@ __metadata: languageName: node linkType: hard -"@pmmmwh/react-refresh-webpack-plugin@npm:^0.5.1, @pmmmwh/react-refresh-webpack-plugin@npm:^0.5.5": +"@pmmmwh/react-refresh-webpack-plugin@npm:^0.5.1, @pmmmwh/react-refresh-webpack-plugin@npm:^0.5.11": version: 0.5.11 resolution: "@pmmmwh/react-refresh-webpack-plugin@npm:0.5.11" dependencies: @@ -7060,7 +7060,7 @@ __metadata: dependencies: "@babel/preset-flow": "npm:^7.22.15" "@babel/preset-react": "npm:^7.22.15" - "@pmmmwh/react-refresh-webpack-plugin": "npm:^0.5.5" + "@pmmmwh/react-refresh-webpack-plugin": "npm:^0.5.11" "@storybook/core-webpack": "workspace:*" "@storybook/docs-tools": "workspace:*" "@storybook/node-logger": "workspace:*" @@ -7072,7 +7072,7 @@ __metadata: fs-extra: "npm:^11.1.0" magic-string: "npm:^0.30.5" react-docgen: "npm:^7.0.0" - react-refresh: "npm:^0.11.0" + react-refresh: "npm:^0.14.0" semver: "npm:^7.3.7" typescript: "npm:~4.9.3" webpack: "npm:5" @@ -25635,13 +25635,6 @@ __metadata: languageName: node linkType: hard -"react-refresh@npm:^0.11.0": - version: 0.11.0 - resolution: "react-refresh@npm:0.11.0" - checksum: cbb5616c7ba670bbd2f37ddadcdfefa66e727ea188e89733ccb8184d3b874631104b0bc016d5676a7ade4d9c79100b99b46b6ed10cd117ab5d1ddcbf8653a9f2 - languageName: node - linkType: hard - "react-refresh@npm:^0.14.0": version: 0.14.0 resolution: "react-refresh@npm:0.14.0"