React JSX 输出策略
问题背景
React 组件库经常涉及 JSX 输出策略。JSX 可以被转换成 JS 调用,也可以保留为 JSX 源语法。Rslib 对这两种方式的支持取决于 bundle 模式。
关键点是:bundleless 可以保留 JSX,bundle 不允许保留 JSX。
默认行为
Rslib React 模板默认使用 @rsbuild/plugin-react,并在 tsconfig 中设置:
这意味着默认会转换 JSX,使用 React automatic runtime:
输出大致是:
所以 bundleless 默认不是 preserve JSX。它只是允许 preserve JSX。
Preserve JSX
如果用户配置:
则输出可以继续保留 JSX:
这在库场景中有时有意义。比如用户希望发布更接近源码的 JSX 产物,让下游框架、编译器或 React Compiler 再处理。
为什么 bundleless 可以 preserve
Bundleless 的目标是保留模块结构。每个源码文件仍单独输出,消费者或下游 bundler 继续处理这些文件是合理的。
如果一个库明确选择发布 JSX 产物,它通常会在 package exports、文档或构建约定中说明消费者需要编译 JSX。Rslib bundleless 不阻止这个选择。
为什么 bundle 模式禁止 preserve
Bundle 模式的目标是生成可直接消费的 JS bundle。bundle 中保留 JSX 会带来问题:
- JSX 不是浏览器或 Node 可直接执行的语法。
- Rspack bundle runtime、chunk、scope hoisting 等逻辑不以保留 JSX 为目标。
- 用户可能以为 bundle 可直接运行,但运行时语法错误。
- UMD/IIFE 这类最终产物更不应包含 JSX。
因此 core 中 BundlePlugin 会检查:
如果命中,就报错并退出。
关键源码
源码位置:
packages/core/src/config.ts:793
测试位置:
tests/integration/preserve-jsx
测试覆盖了:
- bundleless 下 JSX 保留。
- bundle 下 preserve JSX 报错。
- preserve JSX 和其他 bundle environment 共存时不误伤。
坏产物示例
如果 bundle 模式允许 preserve JSX,可能产生:
这个文件不是有效的普通 JS。消费者如果不再经过 JSX 编译,就会失败。
而 bundleless 用户看到这种输出是有心理预期的,因为他们主动配置了 preserve runtime。
和 React 组件库的关系
这不是“React 包名特判”,而是 JSX 语法策略。它主要影响 React,是因为 React 组件库最常见 JSX preserve 需求。
Rslib 并没有写“如果 import React 就怎样”。它看的是 Rspack parser 的 JSX preserve 状态。
修改风险
如果改这块逻辑,要检查:
tests/integration/preserve-jsx。- React 模板默认输出。
- bundleless + preserve。
- bundle + preserve 报错。
- 多 environment 中一个 preserve bundleless、一个普通 bundle 的组合。
不要因为某个场景希望发布 JSX bundle 就直接移除限制。那会让普通 bundle 产物的可运行性变得不可靠。