Storybook 的 `no-renderer-packages` 规则:阻止在 Stories 中直接导入渲染器包
no-renderer-packages 是 Storybook 官方 ESLint 插件 eslint-plugin-storybook 内置的一条问题级(problem)规则,它禁止在 *.stories.* / *.story.* 文件中直接导入 @storybook/react、@storybook/vue3、@storybook/web-components 这类渲染器(renderer)基础包,并引导你改而使用与具体构建工具(Vite、Webpack 等)或框架(Next.js、SvelteKit)绑定的框架包(framework package)。本篇文章将基于该规则当前仓库中的文档、源码与测试,讲解规则背景、命中行为、包名替换映射、配置方法及适用范围。
规则背景:为什么不能直接导入渲染器包
Storybook 的包结构分为两层:
- 渲染器包(renderer packages),如
@storybook/react、@storybook/vue3,只提供"把某个 UI 框架渲染进 Storybook 画布"的核心能力,本身不感知构建工具; - 框架包(framework packages),如
@storybook/react-vite、@storybook/react-webpack5,在渲染器之上绑定具体的构建工具或上层框架,负责解析、加载并针对该构建环境进行优化。
在 Stories 中直接导入渲染器包,通常会绕过框架层提供的集成与优化,导致模块解析、装饰器注入或构建行为与项目实际使用的 Storybook 配置不一致。这条规则因此被纳入插件默认的 recommended 与 flat/recommended 配置中,随规则文档、规则实现与测试用例一并位于 code/lib/eslint-plugin 目录。
规则报告内容与完整替换映射
当检测到导入语句的包名恰好命中渲染器包清单时,规则会报告 Do not import renderer package "{{rendererPackage}}" directly. Use a framework package instead (e.g. {{suggestions}}).,其中 suggestions 会被拼接成逗号分隔的候选框架包列表。
规则文档只列出了部分映射,而源码 no-renderer-packages.ts 中的 rendererToFrameworks 表更为完整,是判断替换建议的真实依据:
| 渲染器包 | 建议的框架包 |
|---|---|
@storybook/html |
@storybook/html-vite、@storybook/html-webpack5 |
@storybook/preact |
@storybook/preact-vite、@storybook/preact-webpack5 |
@storybook/react |
@storybook/nextjs、@storybook/react-vite、@storybook/nextjs-vite、@storybook/react-webpack5、@storybook/react-native-web-vite |
@storybook/server |
@storybook/server-webpack5 |
@storybook/svelte |
@storybook/svelte-vite、@storybook/svelte-webpack5、@storybook/sveltekit |
@storybook/vue3 |
@storybook/vue3-vite、@storybook/vue3-webpack5 |
@storybook/web-components |
@storybook/web-components-vite、@storybook/web-components-webpack5 |
规则针对的渲染器包类型(RendererPackage)恰好在仓库中有对应的独立渲染器实现目录,例如 code/renderers/react、code/renderers/vue3、code/renderers/svelte、code/renderers/web-components 等,可据此印证"渲染器层"的划分确实存在于项目结构中。
错误与正确示例
以下写法不正确 —— 直接从渲染器包导入:
// Don't import renderer packages directly
import { something } from '@storybook/react';
import { something } from '@storybook/vue3';
import { something } from '@storybook/web-components';
以下写法正确 —— 按构建工具选择对应框架包:
// Do use the appropriate framework package for your build tool
import { something } from '@storybook/react-vite'; // For Vite
import { something } from '@storybook/vue3-vite'; // For Vite
import { something } from '@storybook/web-components-vite'; // For Vite
import { something } from '@storybook/nextjs'; // For Next.js
规则实现:一个纯粹的 ImportDeclaration 检查器
规则实现非常精简,关键逻辑集中在 src/rules/no-renderer-packages.ts:
- 只监听 AST 节点类型
ImportDeclaration; - 读取
node.source.value取得被导入的包名字符串; - 若该字符串以
in命中rendererToFrameworks映射表,即从表中取出替换建议并context.report(...); - 元信息将规则类型声明为
problem、默认严重度为error,且schema: []表明该规则不接受任何自定义选项,行为完全由内置映射表决定。
规则通过 create-storybook-rule.ts 中基于 @typescript-eslint/utils 的 RuleCreator(docsUrl) 构建,文档链接会自动拼接,报错信息指向本规则文档。所有命中与不命中的场景都由测试文件 no-renderer-packages.test.ts 覆盖。
测试验证:哪些导入会命中
测试用例对规则行为给出了明确边界:
视为合法的导入(valid)
@storybook/react-vite、@storybook/vue3-webpack5、@storybook/web-components-vite等框架包导入;- 非 Storybook 的普通导入,例如
import React from 'react'与import { something } from 'some-other-package'。
判定为错误的导入(invalid)
import { something } from '@storybook/react',期望报错并给出完整建议串:@storybook/nextjs, @storybook/react-vite, @storybook/nextjs-vite, @storybook/react-webpack5, @storybook/react-native-web-vite;import { something } from '@storybook/vue3',期望建议@storybook/vue3-vite, @storybook/vue3-webpack5;import { something } from '@storybook/web-components',期望建议@storybook/web-components-vite, @storybook/web-components-webpack5。
测试还断言报告节点类型必须为 ImportDeclaration(AST_NODE_TYPES.ImportDeclaration),说明规则只针对静态导入语句生效,不涉及 require() 动态加载等场景。
启用该规则:两种 ESLint 配置方式
本规则随默认配置自动开启。从源码可以看出:
- 传统
.eslintrc的recommended配置(src/configs/recommended.ts)中,storybook/no-renderer-packages被设为error,作用于**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)与**/*.story.@(ts|tsx|js|jsx|mjs|cjs)两类文件; - flat config 的
flat/recommended(src/configs/flat/recommended.ts)以同样方式开启,规则元信息也通过categories: [CategoryId.RECOMMENDED]声明其归属(见 constants.ts)。
因此,你不需要单独配置即可获得该规则,只需在项目中启用插件配置:
// .eslintrc
{
"extends": ["plugin:storybook/recommended"]
}
或使用 flat config:
import storybook from 'eslint-plugin-storybook';
export default [
...storybook.configs['flat/recommended'],
];
如果你使用 .storybook 目录下的配置文件并希望插件同时检查其中的 addon 配置,需在 .eslintignore 中加入 !.storybook(flat config 中则使用 globalIgnores(['!.storybook'], ...))。更完整的安装步骤(含 ESLint 与插件版本对应关系)参见 code/lib/eslint-plugin/README.md。在仓库本体的 oxlint 示例配置 中,storybook/no-renderer-packages: "error" 同样被显式开启。
什么时候应该关闭它
如果你确实有特殊需求,需要直接在 Stories 中使用渲染器包,可以关闭该规则:
// .eslintrc overrides
{
"overrides": [
{
"files": ["**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)"],
"rules": {
"storybook/no-renderer-packages": "off"
}
}
]
}
不过,规则文档与实现都建议保留默认开启:框架包针对你的构建工具做了优化,能提供与开发环境更好的集成。需要警惕的是,仓库内各框架的渲染器实现往往同时暴露多种写法,若脱离对应框架包直接引用渲染器,容易在 Vite/Webpack/Next.js 等不同环境下产生不一致的模块解析行为。
小结
no-renderer-packages 通过一张硬编码的"渲染器 → 框架包"映射表,在编译前用 ESLint 层面拦截掉 Stories 中对渲染器基础包的直连导入,并给出可一键照抄的候选替换清单。理解这张映射表(完整版见源码 rendererToFrameworks)与它的启用范围(recommended / flat/recommended、仅针对 stories 文件),就能让你的组件测试与文档代码始终保持与所选框架包一致的集成方式。
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 StartedRust0627
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