首页
/ Storybook ESLint 规则 `csf-component` 深入指南:为 CSF Meta 补全 component 属性

Storybook ESLint 规则 `csf-component` 深入指南:为 CSF Meta 补全 component 属性

2026-09-06 18:09:55作者:郁楠烈Hubert

导读

本指南围绕 Storybook 官方 ESLint 插件(eslint-plugin-storybook)中的 storybook/csf-component 规则展开。该规则要求你在每个 CSF(Component Story Format)文件的默认导出 meta 中声明 component 属性。本文将说明规则为什么存在、会报告哪些代码模式、与 csf / csf-strict 配置的关系、何时应当关闭它,并结合插件源码剖析其真实检查逻辑与解析边界,帮助你在自己的 Storybook 项目中正确地启用和运用这条规则。

规则概览

csf-componenteslint-plugin-storybook 提供的一条静态检查规则,其核心理念是:每一个 CSF 文件默认导出的 meta 对象都应该显式声明 component 属性,把该文件组织下的所有 story 与某一个具体组件明确绑定。

从插件的规则注册表可以确认该规则的默认行为(src/rules/csf-component.ts):

  • 规则类型suggestion,默认警告级别 severity: 'warn'
  • 错误信息Missing component property.(内部 messageIdmissingComponentProperty);
  • 无任何可配置选项schema: []),启用即按默认行为工作;
  • 所属分类csfCategoryId.CSF)。

出现在哪些推荐配置中

该规则被下列四个预置配置自动包含:

配置名称 面向 ESLint 版本/风格
csf ESLint v8 及更早的 .eslintrc.* 风格
flat/csf ESLint v9 的 flat config 风格
csf-strict .eslintrc.* 风格下的 CSF 严格模式
flat/csf-strict flat config 风格下的 CSF 严格模式

也就是说,只要你在项目中启用了 plugin:storybook/csf(或 flat/csfcsf-strictflat/csf-strict),这条规则就会自动生效。可以对比参考 README 中的规则总表,例如 storybook/default-exportsstorybook/hierarchy-separatorstorybook/no-redundant-story-name 同样同时出现在 csfcsf-strict 中,而 csf-strict 还会额外启用 storybook/no-stories-ofstorybook/no-title-property-in-meta 等更严格的约束。

提示:storybook/csf-component 并不包含在默认的 plugin:storybook/recommended 配置中,需要显式开启 csf / csf-strict 系列配置才会被加载。

为什么应当设置 component 属性

在 CSF 语法中,component 属性虽然是可选的,但显式设置它能解锁 Storybook 的一系列核心能力,这也是规则存在的根本原因:

  1. 自动生成 Props 表格文档(prop table):在大多数框架下,Storybook 会根据 component 指向的组件,通过 docgen 机制提取其属性、类型、默认值、描述等,在 Docs 页面上自动渲染成参数表格。这依赖工具链对该组件的类型信息或源码注释进行分析,但起点必须是 meta 中声明的 component

  2. 自动生成 Controls(动态编辑面板):基于上述自动推断出的 argTypes,Storybook 的 Controls 插件才能为每个参数渲染合适的编辑控件(如文本输入、颜色选择器、下拉框等),让你在 UI 上实时改参、即时预览。

  3. CSF3 下无需 render 函数的默认渲染:在 CSF3 中,一旦声明了 component,即使某条 story 没有显式定义 render/args 之外的渲染逻辑,Storybook 也能用合理的默认方式渲染该组件;而缺少 component 时,通常必须为 story 显式提供 render 函数,否则无法得知要渲染什么。

  4. 可读性与可维护性:把「这个文件的故事属于哪个组件」写到默认导出里,使组件与故事文件的对应关系一目了然,也方便工具做导航分组与代码跳转。

从源码层面看,component 缺失时规则会基于 AST 直接报告(见下文「实现原理」),并不会去验证 component 的值是否是一个真实存在的组件——它只负责督促你养成声明该属性的习惯,真正"解码"组件类型、生成文档的工作由 Storybook 运行时与框架的 docgen 工具完成。

规则细节:正确与错误的示例

会被报告的“不正确”代码

缺少 component 的默认导出都会触发 Missing component property.

export default {
  title: 'Button',
};
// 即便使用 Meta<T> 类型标注也不能豁免
export default {
  title: 'Button',
} as ComponentMeta<typeof Button>;

被判定为“正确”的代码

显式声明了 component 即可通过检查:

import { Button } from './Button';

export default {
  title: 'Button',
  component: Button,
};

用上 TypeScript 的类型标注同样合法:

import type { Meta } from '@storybook/react';
import { Button } from './Button';

export default {
  title: 'Button',
  component: Button,
} satisfies Meta<typeof Button>;

对应的测试用例可以从 src/rules/csf-component.test.ts 中看到:valid 一组收录了内联对象与 as ComponentMeta<typeof Button> 两种写法;invalid 一组则覆盖了纯内联、as 断言、以及把 meta 先赋给变量再默认导出(const meta = {...}; export default meta)三种缺失场景,均报告在 ExportDefaultDeclaration 节点上。

实现原理:规则是如何检查的

深入阅读 src/rules/csf-component.ts 可以看清它的完整工作流:

  1. 监听 AST 中的 ExportDefaultDeclaration 节点;
  2. 调用 getMetaObjectExpression(node, context) 提取默认导出背后的 meta 对象表达式;
  3. 在 meta 对象的 properties 中查找键名为 component 的属性,跳过展开元素(spread element);
  4. 若找不到,就在该节点上报告 missingComponentProperty

其中几个实现细节非常关键:

meta 解析支持多种写法

getMetaObjectExpressionsrc/utils/index.ts)会依次处理这些情况,因此规则能够理解多样化的 meta 书写方式:

  • 直接对象字面量export default { title: 'Button', component: Button }
  • 标识符引用 + 变量声明解析:遇到 export default meta 这种形式时,通过 scope 找到变量定义(const meta = ...)再取其初始化表达式,所以上面「间接导出」的写法同样会被检查;
  • TS 类型断言与 satisfies:遇到 asTSAsExpression)或 satisfiesTSSatisfiesExpression)包裹的表达式时,会先剥掉外层类型节点再取内部对象;
  • 最终只有解析结果是 ObjectExpression 时才会继续,否则规则直接跳过(例如导出的是函数或其他非对象形式,就不适用本规则)。

Spread 元素会被忽略

检查 component 属性时使用了 !isSpreadElement(property) 的守卫(src/rules/csf-component.ts,其类型判断定义见 src/utils/ast.ts)。也就是说,如果 meta 是通过 ...baseMeta 展开合并而来的,即使展开的源头里其实含有 component,静态分析层面也不会把展开元素计入——这与 ESLint 不做跨文件/跨作用域数据流分析的定位一致。如果你确实使用了 spread 合并 meta 的写法,这条规则可能会误报,请参考下面的「何时不使用本规则」。

兼容 ESLint v8 与 v9

在解析 export default meta 这类标识符引用时,代码特意兼容了 ESLint v8 的 context.getScope() 与 v9 的 sourceCode.getScope(node) 两套 API(src/utils/index.ts),因此该规则在 .eslintrc.* 与 flat config 两种模式下都能正常工作。

启用方式与配置示例

通过 csf 配置启用(.eslintrc.*)

在项目根目录的 .eslintrc.*extends 对应的配置即可,插件前缀 eslint-plugin- 可以省略:

{
  "extends": ["plugin:storybook/csf"]
}

如果你希望同时得到更严格的 CSF 检查(例如禁止 storiesOf、禁止在 meta 中写 title),可以使用:

{
  "extends": ["plugin:storybook/csf-strict"]
}

插件会自动把规则仅应用于 *.stories.*(推荐)或 *.story.* 文件,无需额外配置匹配。

通过 flat/csf 配置启用(eslint.config.js)

ESLint v9(或 v8.57+ 使用 flat config)下,在 eslint.config.js / eslint.config.mjs 中展开对应配置对象:

import storybook from 'eslint-plugin-storybook';

export default [
  // ...其他通用 ruleset,例如 js.configs.recommended
  ...storybook.configs['flat/csf'],
];

对应严格模式则替换为:

  ...storybook.configs['flat/csf-strict'],

flat/csf-strict 的命名规则对象可以在 src/configs/flat/csf-strict.ts 中核对,插件的所有配置入口集中在 src/index.ts

单独覆写规则强度

csf / csf-strict 已开启的前提下,你可以针对自己的 stories 文件把警告升级为错误或直接关闭。以 .eslintrc.* 为例:

{
  "overrides": [
    {
      "files": ["**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)"],
      "rules": {
        "storybook/csf-component": "error",
        "storybook/csf-component": "off"
      }
    }
  ]
}

flat config 下则在对应文件匹配块中写入 rules 即可,具体写法可参考 README 的覆写示例

安装插件后,建议在 .eslintignore 中追加 !.storybook,让插件也能检查 .storybook 目录下的配置文件(例如校验 addon 名称拼写)。

当你想用 MDX 书写故事时需要注意

需要注意:eslint-plugin-storybook 本身不支持 MDX 文件(参见 README 的 MDX Support 一节)。因此 csf-component 只作用于基于 JS/TS 的 CSF 文件。如果你使用 .mdx 形式的 CSF(即 stories 与 docs 都写在 MDX 中),该规则不会生效,需要依赖 Docs 内文档块(如 <Story />)或 docgen 的其他约定来获得组件信息。

何时不使用本规则(When Not To Use It)

虽然 eslint-plugin-storybook 鼓励每个 CSF 文件都清晰地对应当前文件中的一个组件,但 Storybook 本身允许你按任何方式组织你的内容。以下几种情况你可能并不需要(甚至不应该)开启 csf-component

  • 一个 CSF 文件聚合多个组件或混编 docs:例如把若干个紧密相关的小组件、或纯文档型页面(docs-only)放在同一个文件中统一叙事;
  • 存在无组件的抽象故事层:比如某些组合式 story 只是把子故事组织到一起,本身并不直接渲染一个独立组件;
  • 大量依赖 render 函数 + 手动 argTypes 的团队约定:如果团队所有 story 都显式提供了 render 并手写了完整 argTypes,那么 component 带来的自动推断增益有限;
  • 使用 spread 合并 meta:如上一节所述,规则无法穿透 ...spread 静态识别 component,此类代码会被误报为缺失。

面对这些组织方式,正确做法是关闭或降级这条规则(见上文覆写配置),而非强行改造自己的文件结构来迎合检查器。文档对此也给出了同样的取舍建议:组织方式不同时,本规则可能并不适用于你的项目。

关联阅读与延伸

围绕这条规则涉及的核心能力,值得继续探索的仓库文档与源码包括:

小结

storybook/csf-component 是一条轻量但收益明确的规则:它通过 AST 静态检查督促你在每个 CSF 文件的 meta 中声明 component,从而让 Storybook 的 prop 文档、动态 Controls 与 CSF3 默认渲染能力真正运转起来。它随 csf / csf-strict(含 flat 变体)配置自动启用,支持直接对象、变量引用、as/satisfies 类型标注等多种 meta 写法,但对 spread 展开与 MDX 文件无能为力——理解这些边界,能让你在享受自动化收益的同时,为多组件聚合、docs-only 等特殊组织方式做出合理的规则取舍。

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