@@ -7,27 +7,58 @@ const rootDir = process.cwd()
77const config : StorybookConfig = {
88 stories : [ '../src/**/*.mdx' , '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)' ] ,
99 addons : [
10- '@storybook/addon-links' , // 推荐加上:支持故事间跳转
11- '@storybook/addon-a11y' , // 保持
12- '@storybook/addon-docs' , // 如果你用了 MDX3,可以去掉(essentials 已包含)
10+ '@storybook/addon-links' ,
11+ // '@storybook/addon-a11y',
12+ '@storybook/addon-docs' ,
1313 ] ,
1414 docs : { } ,
1515 framework : {
1616 name : '@storybook/react-vite' ,
1717 options : { } ,
1818 } ,
1919 viteFinal : async ( viteConfig ) => {
20- // Storybook 会合并项目的 Vite 配置;如果把应用的 HTML input 带进来,
21- // 可能导致 storybook-static/index.html 变成主应用模板(从而 manager 资源加载失败)。
20+ // Storybook 会合并项目的 Vite 配置。
21+ // 需要剔除「仅用于主应用 build」的选项/插件,否则可能:
22+ // 1) 覆盖 Storybook 的入口(index.html 变成主应用模板)
23+ // 2) 触发压缩/打包等后置插件,导致 Storybook build 失败(产物不完整,iframe.html/index.json 404)
2224 const cfg : any = viteConfig
23- if ( cfg ?. build ?. rollupOptions ?. input ) {
24- delete cfg . build . rollupOptions . input
25+
26+ // build 相关:只移除「主应用的 index.html input」,保留 Storybook 自己的 iframe 输入。
27+ const inputLooksLikeAppIndex = ( input : unknown ) : boolean => {
28+ const isIndexHtml = ( v : unknown ) => {
29+ if ( typeof v !== 'string' ) return false
30+ const normalized = v . replace ( / \\ / g, '/' ) . toLowerCase ( )
31+ return normalized . endsWith ( '/index.html' ) || normalized === 'index.html'
32+ }
33+ if ( isIndexHtml ( input ) ) return true
34+ if ( Array . isArray ( input ) ) return input . some ( isIndexHtml )
35+ if ( input && typeof input === 'object' ) {
36+ return Object . values ( input as Record < string , unknown > ) . some ( isIndexHtml )
37+ }
38+ return false
2539 }
26- if ( cfg ?. build ?. outDir ) {
27- delete cfg . build . outDir
40+
41+ if ( cfg ?. build ?. rollupOptions ?. input && inputLooksLikeAppIndex ( cfg . build . rollupOptions . input ) ) {
42+ delete cfg . build . rollupOptions . input
2843 }
44+ if ( cfg ?. build ?. outDir ) delete cfg . build . outDir
2945
30- return mergeConfig ( viteConfig , {
46+ // 插件相关:过滤掉主应用专用插件(压缩/zip/sentry/analyze 等)。
47+ const filteredPlugins = Array . isArray ( cfg ?. plugins )
48+ ? cfg . plugins . filter ( ( p : any ) => {
49+ const name : string = String ( p ?. name || '' )
50+ if ( ! name ) return true
51+ if ( name === 'vite-plugin-compression' ) return false
52+ if ( name === 'zip-after-build' ) return false
53+ if ( name . includes ( 'sentry' ) ) return false
54+ if ( name . includes ( 'visualizer' ) ) return false
55+ return true
56+ } )
57+ : cfg ?. plugins
58+
59+ const merged = mergeConfig ( viteConfig , {
60+ // 避免 Vite 默认拾取项目根目录的 index.html(会把 storybook-static/index.html 覆盖成主应用 loader)
61+ root : path . resolve ( rootDir , '.storybook' ) ,
3162 resolve : {
3263 alias : {
3364 '@' : path . resolve ( rootDir , 'src' ) ,
@@ -60,8 +91,17 @@ const config: StorybookConfig = {
6091
6192 build : {
6293 chunkSizeWarningLimit : 2000 ,
94+ // 强制 preview 产物落在 storybook-static(否则会被主应用 outDir 污染,导致 iframe.html 缺失 -> 404)
95+ outDir : path . resolve ( rootDir , 'storybook-static' ) ,
96+ // manager 会负责清理 outputDir;这里避免 vite 误删 manager 文件
97+ emptyOutDir : false ,
6398 } ,
6499 } )
100+
101+ // mergeConfig 会合并 plugins 数组,这里强制替换为过滤后的插件列表。
102+ ; ( merged as any ) . plugins = filteredPlugins
103+
104+ return merged
65105 } ,
66106}
67107
0 commit comments