首页
/ Storybook CLI 实战指南:从 `npx storybook init` 到 add / upgrade / migrate 命令的源码级解析

Storybook CLI 实战指南:从 `npx storybook init` 到 add / upgrade / migrate 命令的源码级解析

2026-09-06 17:47:12作者:侯霆垣

本文以 @storybook/cli 包(仓库路径 code/lib/cli-storybook)的 README 为主体,完整讲解 Storybook CLI 的安装、addon 管理、版本升级与 codemod 迁移能力,并结合 src/bin/run.tssrc/add.tssrc/upgrade.ts 等源码,帮助你在项目中熟练使用 initaddupgrademigrate 等命令,同时理解 storybook/componentsstorybook/theming 等核心 API 子导出的实际归属。

一、用 CLI 把 Storybook 引入项目

Storybook CLI(Command Line Interface)是把 Storybook 添加到你的项目中最简单的方式。按照 README 的标准流程:

cd my-app
npx storybook@latest init

init 命令会在你的项目中完成框架检测、依赖安装和 .storybook/ 配置目录的初始化。从 src/bin/run.tscommand('init') 的注册代码可以看到,它实际支持一组丰富的选项:

选项 说明
-f, --force 强制添加 Storybook
-s, --skip-install 跳过依赖安装
--package-manager <type> 强制指定安装依赖所用的包管理器
--use-pnp 为 Yarn 2+ 启用 PnP 模式(源码中标注计划在 SB11 移除)
-p, --parser <babel | babylon | flow | ts | tsx> 指定 jscodeshift 使用的解析器
-t, --type <type> 为特定项目类型添加 Storybook
-y, --yes 对所有交互提示自动回答 yes
-b, --builder <webpack5 | vite> 指定构建器
-l, --linkable 准备可 link 的安装(贡献者辅助选项)
--dev / --no-dev 初始化完成后是否启动开发服务器(默认不启动)

入口与 Node 版本门禁

CLI 的构建入口由 build-config.ts 声明,打包 src/bin/index.ts 为 Node 产物;发布产物中 bin 字段指向 ./dist/bin/index.js(见 package.json)。而 src/bin/index.ts 在真正执行前有一道版本门禁:

if (!isNodeVersionSupported(major, minor, patch)) {
  logger.error(dedent`To run Storybook, you need Node.js version ${MIN_SUPPORTED_NODE_DESCRIPTION}.
    You are currently running Node.js ${process.version}. Please upgrade your Node.js installation.`);
  process.exit(1);
}

即如果当前 Node 版本低于 storybook/internal/common 导出的最低支持版本,命令会直接报错退出——遇到此类提示时升级 Node 即可。

全局选项

run.ts 中的 command() 工厂为每个子命令统一挂载了以下全局选项,任何子命令(addupgradedoctor 等)均可使用:

  • --disable-telemetry:关闭遥测数据上报(读取 STORYBOOK_DISABLE_TELEMETRY 环境变量);
  • --debug:输出 debug 级别日志;
  • --enable-crash-reports:允许将崩溃报告发送到遥测数据中;
  • --logfile [path]:运行结束后把调试日志写入文件(默认 debug-storybook.log);
  • --loglevel <trace | debug | info | warn | error | silent>:定义日志级别,默认 info

另外值得一提的是容错细节:当用户输入不存在的命令时,CLI 会用 leven 库计算与已有命令的编辑距离(小于 3 视为匹配),给出 “Did you mean xxx?” 的拼写建议,这一点在 run.ts 末尾的 program.on('command:*') 钩子中实现。README 中提示的 -h / --help 同样适用于列出所有命令。

二、命令全景:README 之外还有哪些命令

README 列出的核心命令是 initaddinfoupgrademigrate。对照 run.ts 的源码,当前 CLI 实际注册的完整命令集还包括:

  • remove <addon>:从 Storybook 中移除 addon(README 未提及,但源码中已实现);
  • automigrate [fixId]:检查不兼容项或迁移并应用修复,是 upgrade 内部自动迁移能力的独立入口;
  • doctor:检查已知问题并给出建议或修复(实现见 src/doctor/index.ts);
  • sandbox [filterValue](别名 repro):从模板集合创建沙盒项目,可选 --no-init 创建未初始化 Storybook 的模板(实现见 src/sandbox.ts);
  • link <repo-url-or-directory>:从 URL 或本地目录拉取复现工程并 link、运行 Storybook,支持 --local--no-start

三、add:安装 addon 并自动注册到 main 配置

npx storybook@latest add <addon> 会完成两件事:安装 addon 包,并把它注册进 main.tsaddons 数组。源码 src/add.ts 中的关键逻辑:

export const getVersionSpecifier = (addon: string) => {
  const groups = /^(@{0,1}[^@]+)(?:@(.+))?$/.exec(addon);
  if (groups) {
    return [groups[1], groups[2]] as const;
  }
  return [addon, undefined] as const;
};

也就是说 @storybook/addon-vitest@9.0.1 会被拆分为包名和版本;若省略版本说明符且目标是 Storybook 官方 addon,则会自动使用与你当前 Storybook 安装版本一致的版本说明符(源码注释中明确:If there is no version specifier and it's a Storybook addon, it will try to use the version specifier matching your current Storybook install version)。

实现层面还有两个判断函数:

  • checkInstalled:遍历 main 配置中已有的 addons 条目(兼容字符串与 { name } 对象两种写法),检查是否已注册,避免重复添加;
  • isCoreAddon:通过 Object.hasOwn(versions, addonName) 判断该 addon 是否属于 Storybook 官方发布物集合。

add 支持的主要选项(来自 run.ts):

storybook add <addon>
  --package-manager <type>   # 强制指定包管理器
  -c, --config-dir <dir>    # Storybook 配置目录
  --skip-install            # 跳过依赖安装
  -s --skip-postinstall     # 跳过包相关的 postinstall 配置修改
  -y --yes                  # 跳过用户交互提示
  --skip-doctor             # 跳过 doctor 检查

安装完成后,postinstallAddonsrc/postinstallAddon.ts)会处理包特有的 postinstall 配置修改。整个 add 动作被 withTelemetry('add', ...) 包裹,即默认会记录一次遥测事件,可用全局 --disable-telemetry 关闭。

四、upgrade:升级 Storybook 到目标版本

npx storybook@latest upgrade 用于把项目中的 Storybook 相关包升级到指定版本(不带参数时默认升级到当前 CLI 携带的版本,即 package.json 中的 version,当前仓库快照为 10.6.0-beta.1)。

run.ts 中注册的选项包括:

选项 说明
--features <list> 升级期间要启用的实验特性开关(逗号分隔),参数解析时会先经 resolveRequestedFeatures 校验合法性
-f, --force 强制执行升级,跳过 autoblocker 检查
-n, --dry-run 只检查是否可升级,不实际安装
-s, --skip-check 跳过 postinstall 的版本检查与自动迁移检查
--skip-automigrations 完全跳过自动迁移,只更新包版本并安装
-c, --config-dir <dir...> 支持传入多个配置目录(monorepo 场景)

src/upgrade.ts 的实现可以看到几个值得了解的机制:

  • 版本解析getStorybookVersion 用正则 /(@storybook\/[^@]+)@(\S+)/ 从包管理器输出中解析每个 @storybook/* 包及其版本;
  • 废弃包提示deprecatedPackages 列表维护了旧版本中的废弃包(如 @storybook/addon-notes@storybook/addon-info 等),升级时会提示并附 MIGRATION 文档锚点链接;
  • autoblocker 机制src/autoblock/ 目录下实现了 Node 版本、大版本、实验 addon(如 @storybook/addon-test)等阻断检查,processAutoblockerResults 会汇总这些结果;--force 即用于跳过这些检查;
  • 多项目支持upgrade 通过 src/util.tsgetProjects 收集多个 Storybook 项目(monorepo),再调用 runAutomigrationssrc/automigrate/multi-project.ts)执行自动迁移。

自动迁移本身由 src/automigrate/fixes/ 下的一批 codemod 组成,覆盖诸如 addon 更名(@storybook/addon-a11y / addon-test 合并、@storybook/addon-mcp)、framework 包更名(@storybook/react@storybook/react-vite 一类迁移)、main.tsfeatures/docs 配置项清理等,每个 fix 都配有对应的 .test.ts 用例,这也是 automigrate 命令可独立运行的基础。

五、migrate:手动运行 codemod 迁移

migrate 命令用于对源码文件运行特定的 codemod 迁移(如 CSF 2 → CSF 3 工厂写法迁移)。其实现非常薄——src/migrate.ts 完全委托给 @storybook/codemod 包:

import { listCodemods, runCodemod } from '@storybook/codemod';

export async function migrate(migration: any, { glob, dryRun, list, rename, parser }: CLIOptions) {
  if (list) {
    listCodemods().forEach((key: any) => logger.log(key));
  } else if (migration) {
    await runCodemod(migration, { glob, dryRun, logger, rename, parser });
  } else {
    throw new Error('Migrate: please specify a migration name or --list');
  }
}

即:不带迁移名会报错;-l, --list 列出所有可用迁移;否则调用底层 jscodeshift 执行器(@storybook/codemod 基于 jscodeshift,见 package.json 的依赖声明)。

常用选项:

storybook migrate <migration>
  -l --list                      # 列出可用迁移
  -g --glob <glob>               # 应用迁移的文件匹配模式,默认 **/*.js
  -p --parser <babel|babylon|flow|ts|tsx>  # jscodeshift 解析器
  -c, --config-dir <dir>        # Storybook 配置目录
  -n --dry-run                  # 仅校验迁移存在并展示将处理的文件
  -r --rename <from-to>         # codemod 应用后重命名文件后缀,如 ".js:.ts"

六、info:为 bug 报告收集环境信息

README 提到 info 命令用于打印系统信息以便提交 bug 报告。从 run.tscommand('info') 的实现看,它基于 envinfo 收集:系统(OS / CPU / Shell)、二进制(Node / Yarn / npm / pnpm)、浏览器(Chrome / Edge / Firefox / Safari),以及项目内和全局安装的 {@storybook/*, *storybook*, sb, chromatic} 相关包,并高亮标记当前生效的包管理器。把这份输出贴进 issue,是 Storybook 维护者排查版本/环境问题的重要依据。

七、Core APIs:README 提到的 storybook/* 子导出

README 的 “Core APIs” 部分说明了一批可供 addon 作者使用的子导出。它们的实际归属在 storybook 核心包(code/core/package.json)的 exports 字段中:

storybook/components

面向 addon 作者的一组 UI 组件,用于保证 addon 与 Storybook 界面一致的观感、减少样板代码。从当前仓库源码结构看,组件实现在 code/core/src/components 目录,package.json 中对应导出路径为 ./internal/components,即内部子路径下提供,供 addon 生态引用。

storybook/theming

暴露主题相关工具函数,帮助组件自动适配当前主题(深浅色切换等)。实现位于 code/core/src/theming,在 code/core/package.json 的 exports 中注册为公开的 ./theming(另有 ./theming/create 子路径)。

storybook/preview-api

包含 preview iframe 内可用的 API(如 addonsparameterstypes 等运行时能力),实现位于 code/core/src/preview-api,exports 中注册为 ./preview-api

storybook/manager-api

包含 manager iframe 内可用的 API(面板注册、store 等),实现位于 code/core/src/manager-api,exports 中注册为 ./manager-api

storybook/types

暴露 Storybook 各处使用的 TypeScript 接口,包括 StorybookConfig 等配置类型与 addon 相关类型,实现位于 code/core/src/types。从当前快照的 exports 结构看,它通过 ./internal/types 提供(README 文档中以 storybook/types 的语义对外描述)。

这组子导出构成了 addon 开发的基础面:用 components 写 UI、用 theming 适配主题、在 preview-api / manager-api 中访问各自 iframe 的运行时能力、用 types 保证类型安全。

八、小结:CLI 与仓库内相关文件的对应关系

能力 实现位置
命令注册与全局选项 code/lib/cli-storybook/src/bin/run.ts
Node 版本检查入口 code/lib/cli-storybook/src/bin/index.ts
add 命令实现 code/lib/cli-storybook/src/add.ts
upgrade 命令实现 code/lib/cli-storybook/src/upgrade.ts
自动迁移 codemod 集合 code/lib/cli-storybook/src/automigrate/
migrate 命令实现 code/lib/cli-storybook/src/migrate.ts
doctor 检查 code/lib/cli-storybook/src/doctor/index.ts
包定义与依赖 code/lib/cli-storybook/package.json
核心子导出(theming/preview-api/manager-api 等) code/core/package.json

日常使用时记住三条主线即可:初始化用 npx storybook@latest init(配合 --package-manager--skip-install 等选项),日常增删 addon 用 add / remove,跨大版本演进用 upgrade(内部自动串联 autoblock 检查与 automigrate),而需要手动执行特定 codemod 时再用 migrate(可用 -l 查看可用迁移清单)

登录后查看全文
热门项目推荐
相关项目推荐