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

feat: compatible with webpack5 addons #132

Merged
merged 1 commit into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,28 @@ The `configType` variable will be either `"DEVELOPMENT"` or `"PRODUCTION"`.
The function should return the updated Rsbuild configuration.
### Using customized addon
Since Rspack is compatible with Webpack 5, storybook-builder-rsbuild can also use Webpack 5 addons. The configuration for ⁠`webpackAddons` is identical to that of Storybook's [addons](https://storybook.js.org/docs/api/main-config/main-config-addons). However, Storybook's addons only support the Vite builder and Webpack 5 builder by default. If you want to use Webpack 5 addons in storybook-builder-rsbuild, please add them to ⁠webpackAddons. For example, using the [`@storybook/addon-coverage`](https://github.com/storybookjs/addon-coverage) addon:
```js
const config: StorybookConfig = {
// --snip--
webpackAddons: [
{
name: '@storybook/addon-coverage',
options: {
istanbul: {
include: ['src/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
exclude: [],
},
},
},
],
// --snip--
}
```
## FAQ
### How to Storybook to a subdirectory / subpath?
Expand Down
1 change: 0 additions & 1 deletion packages/builder-rsbuild/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@
"@types/fs-extra": "^11.0.4",
"@types/node": "^18.0.0",
"@types/pretty-hrtime": "^1.0.3",
"add": "^2.0.6",
"pretty-hrtime": "^1.0.3",
"slash": "^5.1.0",
"storybook": "8.4.0-alpha.0",
Expand Down
57 changes: 53 additions & 4 deletions packages/builder-rsbuild/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ import express from 'express'
import fs from 'fs-extra'
import prettyTime from 'pretty-hrtime'
import { corePath } from 'storybook/core-path'
import { getPresets, resolveAddonName } from 'storybook/internal/common'
import { WebpackInvocationError } from 'storybook/internal/server-errors'
import type { Options } from 'storybook/internal/types'
import type {
Options,
Preset,
StorybookConfigRaw,
} from 'storybook/internal/types'
import rsbuildConfig, {
type RsbuildBuilderOptions,
} from './preview/iframe-rsbuild.config'
Expand Down Expand Up @@ -43,10 +48,53 @@ export const executor = {
},
}

export const rsbuild = async (_: unknown, options: RsbuildBuilderOptions) => {
const isObject = (val: unknown): val is Record<string, any> =>
val != null && typeof val === 'object' && Array.isArray(val) === false

function nonNullables<T>(value: T): value is NonNullable<T> {
return value !== undefined
}

const rsbuild = async (_: unknown, options: RsbuildBuilderOptions) => {
const { presets } = options
let defaultConfig = await rsbuildConfig(options)
// #region webpack addons
const webpackAddons =
await presets.apply<StorybookConfigRaw['addons']>('webpackAddons')
const resolvedWebpackAddons = (webpackAddons ?? [])
.map((preset: Preset) => {
const options = isObject(preset) ? preset.options || undefined : undefined
const name = isObject(preset) ? preset.name : preset
// Taken fromm https://github.com/storybookjs/storybook/blob/f3b15ce1f28daac195e7698c075be7790f8172f1/code/core/src/common/presets.ts#L198.
return resolveAddonName(options.configDir, name, options)
})
.filter(nonNullables)
const { apply } = await getPresets(resolvedWebpackAddons, options)
const webpackAddonsConfig: rsbuildReal.Rspack.Configuration = await apply(
'webpackFinal',
// TODO: using empty webpack config as base for now. It's better to using the composed rspack
// config in `iframe-rsbuild.config.ts` as base config. But when `tools.rspack` is an async function,
// the following `tools.rspack` raise an ` Promises are not supported` error.
{
output: {},
module: {},
plugins: [],
resolve: {},
devServer: {},
optimization: {},
performance: {},
externals: {},
experiments: {},
node: {},
stats: {},
entry: {},
},
options,
)
// #endregion

let defaultConfig = await rsbuildConfig(options, webpackAddonsConfig)
const shimsConfig = await applyReactShims(defaultConfig, options)

defaultConfig = mergeRsbuildConfig(
defaultConfig,
shimsConfig,
Expand All @@ -58,13 +106,14 @@ export const rsbuild = async (_: unknown, options: RsbuildBuilderOptions) => {
options,
)

return mergeRsbuildConfig(finalDefaultConfig)
return finalDefaultConfig
}

export const getConfig: RsbuildBuilder['getConfig'] = async (options) => {
const { presets } = options
const typescriptOptions = await presets.apply('typescript', {}, options)
const frameworkOptions = await presets.apply<any>('frameworkOptions')

return rsbuild({}, {
...options,
typescriptOptions,
Expand Down
10 changes: 5 additions & 5 deletions packages/builder-rsbuild/src/preview/iframe-rsbuild.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { dirname, join, resolve } from 'node:path'
import { loadConfig, mergeRsbuildConfig } from '@rsbuild/core'
import type { RsbuildConfig } from '@rsbuild/core'
import type { RsbuildConfig, Rspack } from '@rsbuild/core'
import { pluginTypeCheck } from '@rsbuild/plugin-type-check'
import { webpack as docsWebpack } from '@storybook/addon-docs/dist/preset'
// @ts-expect-error (I removed this on purpose, because it's incorrect)
Expand Down Expand Up @@ -56,6 +56,7 @@ export type RsbuildBuilderOptions = Options & {

export default async (
options: RsbuildBuilderOptions,
extraWebpackConfig?: Rspack.Configuration,
): Promise<RsbuildConfig> => {
const { rsbuildConfigPath, addonDocs } =
await getBuilderOptions<BuilderOptions>(options)
Expand Down Expand Up @@ -199,7 +200,7 @@ export default async (
? 'static/media/[name].[contenthash:8][ext]'
: 'static/media/[path][name][ext]'

const merged = mergeRsbuildConfig(contentFromConfig, {
const rsbuildConfig = mergeRsbuildConfig(contentFromConfig, {
output: {
cleanDistPath: false,
assetPrefix: '/',
Expand Down Expand Up @@ -354,8 +355,7 @@ export default async (
// as it's a built-in logic for Storybook's official webpack and Vite builder.
// we should remove this once we merge this into Storybook's repository
// by defining builder plugin in @storybook/addon-docs/preset's source code

return mergeConfig(config, appliedDocsWebpack)
return mergeConfig(config, extraWebpackConfig, appliedDocsWebpack)
},
htmlPlugin: {
filename: 'iframe.html',
Expand Down Expand Up @@ -391,5 +391,5 @@ export default async (
},
})

return merged
return rsbuildConfig
}
2 changes: 2 additions & 0 deletions packages/builder-rsbuild/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
Builder,
BuilderResult as BuilderResultBase,
Options,
StorybookConfigRaw,
TypescriptOptions as TypeScriptOptionsBase,
} from 'storybook/internal/types'
import type { Stats } from './index'
Expand Down Expand Up @@ -32,6 +33,7 @@ export type RsbuildFinal = (

export type StorybookConfigRsbuild = {
rsbuildFinal?: RsbuildFinal
webpackAddons?: StorybookConfigRaw['addons']
}

export type BuilderOptions = {
Expand Down
1 change: 0 additions & 1 deletion packages/react-rsbuild/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@
"@rsbuild/core": "1.0.8",
"@storybook/types": "8.4.0-alpha.0",
"@types/resolve": "^1.20.6",
"add": "^2.0.6",
"react": "18.3.1",
"react-dom": "18.3.1",
"storybook": "8.4.0-alpha.0",
Expand Down
1 change: 0 additions & 1 deletion packages/react-rsbuild/src/preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ export const rsbuildFinal: StorybookConfig['rsbuildFinal'] = async (

export const core: PresetProperty<'core'> = async (config, options) => {
const framework = await options.presets.apply('framework')

return {
...config,
builder: {
Expand Down
Loading