Storybook 层级分隔符的废弃与自动迁移:eslint-plugin-storybook 的 hierarchy-separator 规则详解
导读
Storybook 从 6.0 起移除了侧边栏中可自定义的层级分隔符,组件(story kind)的分组路径现在统一使用 / 且不可配置。eslint-plugin-storybook 中的 storybook/hierarchy-separator 规则专门用于检查 CSF 元数据(meta)的 title 中仍在使用已被废弃的 | 分隔符的写法,帮助开发者在升级 Storybook 后保持故事侧边栏分组正确、目录结构干净。读完本文,你将掌握该规则的触发条件、配置与覆盖方法,并学会使用官方 codemod 一键批量迁移历史代码。
背景:为什么分隔符只能使用 /
在 Storybook 的早期版本中,开发者可以通过 title 字段控制故事在侧边栏中的分组(hierarchy)。当时不仅支持 /,还支持用 | 表示故事"根"分组、用 . 表示路径层级,这些分隔符甚至可以在配置中自定义。从 Storybook 5.3 开始,官方着手"简化层级分隔符"(simplified hierarchy separators),将规则收敛为单一分隔符 /;到 6.0 正式移除了自定义分隔符能力,仅保留 / 且不可配置。相关演进记录可查看仓库根目录的 MIGRATION.md。
因此,如果你的存量代码中仍有类似 title: 'Components|Forms/Input' 的写法,| 不会被识别为分组边界,故事可能被折叠成一个带特殊字符的奇怪名字,或者分组与预期完全不符。你需要把这类分隔符全部改为 /。
规则做什么:扫描 meta.title 中的废弃分隔符
storybook/hierarchy-separator 规则所做的工作非常聚焦:它会解析每个 story 文件默认导出的 meta 对象,读取其中的 title 字符串字面量,并检查其中是否包含废弃的 | 字符。一旦发现,即以警告(warn)级别上报,并同时提供一个可自动修复(--fix)的方案与一个修复建议(suggestion)。
该规则对如下 不正确 的代码报错:
export default {
title: 'Components|Forms/Input', // '|' 是已废弃的层级分隔符
component: Input,
};
正确写法应统一使用 /:
export default {
title: 'Components/Forms/Input', // 全部使用 '/' 分隔层级
component: Input,
};
从 规则实现 的源码可以看到核心逻辑:规则仅当 metaTitle.includes('|') 时触发上报,上报信息包含原始 title(Deprecated hierarchy separator in title property: {{metaTitle}});修复函数 fix 会把字符串中的全部 | 替换为 /(metaTitle.replace(/\|/g, '/')),并作为 suggestion(Use correct separators)一并提供。
触发与不触发:从测试用例看边界
规则只扫描 默认导出(export default)的 meta 对象 中的 title。参考 规则测试用例:
- 直接内联对象的写法会触发检测,例如
export default { title: 'Examples|Components/Button' }; - meta 先赋值给变量再默认导出同样会被检测,例如:
const meta = { title: 'Examples|Components/Button' } export default meta - 不会误报的情况包括:
export default { title: 'Examples.Components' }(注意,title中仅含.不会被本规则拦截)、标准的/分隔标题title: 'Examples/Components/Button'、带类型断言的写法title: '...' as ComponentMeta<typeof Button>,以及 meta 使用了展开运算符的export default { ...props }(此时规则无法确定title的真实值)。
需要特别说明:规则文档提到 | 或 . 都建议改为 /,但从测试可见,本规则的实际检测范围聚焦在废弃根分隔符 | 上,单独使用 . 作为路径的标题不会触发该规则的告警,迁移时请自行留意这类写法。
规则在哪些配置集中默认开启
该规则被包含在以下 6 个官方推荐配置集中,且默认均为 warn(警告级别):
| 配置集(eslintrc 风格) | 配置集(flat config 风格) |
|---|---|
csf |
flat/csf |
recommended |
flat/recommended |
csf-strict |
flat/csf-strict |
你可以在 csf 配置源文件 与 recommended 配置源文件 中看到 'storybook/hierarchy-separator': 'warn' 的默认声明。也就是说,只要你在项目里继承了 plugin:storybook/recommended(eslintrc 风格)或 storybook.configs['flat/recommended'](flat config 风格),该规则就会自动只作用于你的 *.stories.* / *.story.* 故事文件。
安装与配置
安装插件
eslint-plugin-storybook 无需额外安装 Storybook 即可使用(插件自带所需依赖,适合在 monorepo 共享 ESLint 预设中引用):
npm install eslint-plugin-storybook --save-dev
# 或
yarn add eslint-plugin-storybook --dev
通过 extends 启用(eslintrc 风格)
在你的 .eslintrc.* 中继承推荐配置,插件会自动只应用于符合 *.stories.* 或 *.story.* 模式的文件:
{
"extends": ["plugin:storybook/recommended"]
}
通过 flat config 启用(ESLint v9 / v8.57+)
import storybook from 'eslint-plugin-storybook';
export default [
// 添加其他通用规则集,如 js.configs.recommended ...
...storybook.configs['flat/recommended'],
];
单独启用、提级或关闭
如果你未继承包含它的推荐配置,或想调整严重级别,可以在覆盖(overrides)中单独设置,注意只对故事文件生效:
eslintrc 风格:
{
"overrides": [
{
"files": ["**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)"],
"rules": {
"storybook/hierarchy-separator": "error" // 也可设为 "off" 关闭
}
}
]
}
flat config 风格:
import storybook from 'eslint-plugin-storybook';
export default [
...storybook.configs['flat/recommended'],
{
files: ['**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)'],
rules: {
// 提级为 error(阻止构建/提交),或置 "off" 关闭
'storybook/hierarchy-separator': 'error',
},
},
];
更多配置方式(包括与 oxlint 配合使用的示例)参见 eslint-plugin-storybook README。
自动修复与 codemod 批量迁移
ESLint --fix 自动修复
由于规则标记了 fixable: 'code',你可以直接在故事文件上运行 ESLint 自动修复,把 | 全部替换为 /:
npx eslint "src/**/*.stories.{js,ts,jsx,tsx}" --fix
官方 codemod 一键迁移
如果你的历史代码较多,更推荐使用官方提供的 upgrade-hierarchy-separators codemod,在项目根目录运行即可批量转换所有故事文件:
npx storybook@latest migrate upgrade-hierarchy-separators --glob="*/**/*.stories.@(tsx|jsx|ts|js)"
该 codemod 属于 @storybook/codemod 工具集(源码位于 code/lib/codemod),其针对 CSF 的转换效果可参考 upgrade-hierarchy-separators 的测试快照,例如把 title: 'Foo|Bar/baz/whatever' 转换为 title: 'Foo/Bar/baz/whatever'。对于使用了旧版 storiesOf API 的文件同样适用。需要留意的是,官方 MIGRATION 指南明确指出:codemod 无法处理 .mdx 组件,MDX 文档中的层级标题需要手工修改(参见 MIGRATION.md)。
迁移后的小贴士:关于 Roots 显示
历史上使用 | 的另一个常见原因是希望得到"根(roots)"分组效果。Storybook 6.0 起默认在侧边栏为顶层分组显示可折叠的 root 入口;如果你不想要这种效果,可以在 .storybook/manager.js 中关闭:
import { addons } from '@storybook/addons';
addons.setConfig({
showRoots: false,
});
也就是说,迁移时不必依赖 | 去制造根分组——/ 的第一段会自动成为 root 层级,是否展示可由 showRoots 控制。
小结与延伸阅读
storybook/hierarchy-separator 是一条面向迁移场景的轻量规则:它帮你发现仍在使用已废弃 | 分隔符的 CSF title,配合 --fix 或官方 upgrade-hierarchy-separators codemod 即可把全部代码迁移到单一 / 分隔符的新范式上,避免侧边栏分组错乱。
进一步阅读当前仓库中的相关资料:
- 规则文档:本规则的完整说明;
- 规则实现源码:检测与自动修复逻辑;
- 规则测试用例:触发与不触发场景清单;
- eslint-plugin-storybook README:安装、flat config 与 oxlint 用法;
- codemod 说明:
upgrade-hierarchy-separators的用法与适用范围; - MIGRATION.md:Storybook 6.0 移除层级分隔符的完整迁移背景与
showRoots说明。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00