为 Remotion 切换传统 Babel 转译:@remotion/babel-loader 与 replaceLoadersWithBabel 全解析
【免费下载链接】remotion🎥 Make videos programmatically with React项目地址: https://gitcode.com/GitHub_Trending/re/remotion
Remotion 默认使用 esbuild-loader(Webpack)或 SWC(Rspack)进行转译以获得更快的构建速度,但在遇到某些依赖 Babel 插件体系的场景时,需要通过兼容包@remotion/babel-loader提供的replaceLoadersWithBabel()将打包器的 JavaScript/TypeScript loader 替换回 Babel。读完本文,你将掌握该包的安装方式、在remotion.config.ts与 Node.jsbundle()API 两种场景下的完整配置写法,并能从源码层面理解 loader 替换的实现细节、默认转译目标的差异,以及仓库中现有的集成测试如何验证替换生效。
一、@remotion/babel-loader 是什么
@remotion/babel-loader是 Remotion 仓库中的一个独立子包(见 packages/babel-loader/package.json),其官方描述就是 "Babel loader for Remotion"。它的作用单一而明确:向 Remotion 的打包流程注入 webpack 风格的babel-loader规则,替换掉默认的 esbuild/SWC 转译。
从包定义可以看到几个关键事实:
- 包版本随 Remotion 主版本发布,当前仓库中为
4.0.520(见 packages/babel-loader/package.json); - 它以
@remotion/bundler为 peerDependency,导出函数replaceLoadersWithBabel()接收的类型正是@remotion/bundler中的BundlerConfiguration; - 包自身直接声明了 Babel 全家桶依赖:
@babel/core@7.29.6、@babel/preset-env@7.23.2、@babel/preset-react@7.14.5、@babel/preset-typescript@7.23.2、babel-loader@8.2.2、react-refresh@0.18.0与webpack@5.105.0(见 packages/babel-loader/package.json)。
官方文档对它的定位是"兼容性包"(compatibility package),并明确建议:"一般情况下不应需要这样做,我们鼓励你报告默认转译器的问题"(见 packages/docs/docs/legacy-babel-loader.mdx)。换句话说,这是一条为特殊场景保留的退路,而非推荐的常规配置。
二、安装:--save-exact 与版本对齐
README(packages/babel-loader/README.md)给出的安装命令是:
npm install @remotion/babel-loader --save-exact这里有两个要点:
- 必须用精确版本(
--save-exact)。README 强调:安装任何remotion/@remotion/*包时,所有相关包版本必须对齐到同一版本,需要去掉版本号前的^字符。这是因为 Remotion 各包(bundler、renderer、cli 等)之间通过内部协议协作,版本不一致会直接导致运行时报错。 - 官方文档在示例中另外要求用户自行安装 Babel 侧依赖,以便兼容用户自己项目里的 Babel 生态:
# npm npm i babel-loader @babel/preset-env @babel/preset-react # pnpm pnpm i babel-loader @babel/preset-env @babel/preset-react # yarn yarn add babel-loader @babel/preset-env @babel/preset-react三种包管理器的命令等价(见 packages/docs/docs/legacy-babel-loader.mdx)。
三、场景一:在 remotion.config.ts 中替换 loader
Remotion 的打包器覆盖采用 reducer 风格:你收到默认配置对象,返回修改后的配置对象。官方文档给出的最小可用示例(packages/docs/docs/legacy-babel-loader.mdx):
import { Config } from "@remotion/cli/config"; import { replaceLoadersWithBabel } from "@remotion/babel-loader"; Config.overrideBundlerConfig((currentConfiguration) => { return replaceLoadersWithBabel(currentConfiguration); });这段配置放在remotion.config.ts中即可对 Studio、渲染等所有走配置文件的路径生效。
replaceLoadersWithBabel 到底做了什么
阅读源码 packages/babel-loader/src/index.ts 可以确认其完整行为:
import type {BundlerConfiguration} from '@remotion/bundler'; const envPreset = [ require.resolve('@babel/preset-env'), { targets: { chrome: '85', }, }, ] as const; export const replaceLoadersWithBabel = < Configuration extends BundlerConfiguration, >( conf: Configuration, ): Configuration => { return { ...conf, module: { ...conf.module, rules: (conf.module?.rules ?? []).map((rule) => { // ... 仅重写匹配 .tsx / .jsx 的规则 }), }, }; };逐条拆解它的实现逻辑:
- 只动脚本规则,其余规则原样保留。函数遍历
conf.module.rules,逐条检查rule.test?.toString():包含.tsx的规则被替换为 TypeScript/TSX 规则,包含.jsx的规则被替换为 JavaScript/JSX 规则,其余规则(如 CSS、字体、媒体文件规则以及 Rspack 的'...'占位符)一律不动(见 packages/babel-loader/src/index.ts)。 - TS/TSX 规则(
.tsx?)使用babel-loader,注入的 presets/plugins 为:@babel/preset-env,targets固定为chrome: '85'——这与 Remotion 渲染所依赖的 Chromium 环境对齐;@babel/preset-react,runtime: 'automatic',即不依赖手动import React的自动 JSX 运行时;@babel/preset-typescript,isTSX: true, allExtensions: true,让.ts与.tsx都按 TSX 语义解析;- plugins:
@babel/plugin-proposal-class-properties,并且在conf.mode === 'development'时额外注入react-refresh/babel,为 Studio 的快速刷新提供 Babel 侧支持(见 packages/babel-loader/src/index.ts)。
- JS/JSX 规则(
.jsx?)使用同一套babel-loader与preset-env+preset-react(automatic),plugins 仅保留 class properties,不包含 TypeScript preset(见 packages/babel-loader/src/index.ts)。 - 源码中有一条注释值得注意:"All modules that use require.resolve need to be added to cli/src/load-config -> external array"(packages/babel-loader/src/index.ts),即 loader 内部用
require.resolve锁定的每个模块,CLI 在加载配置文件时都必须将其标记为 external,避免被提前打包。从源码结构看,这是该包能在remotion.config.ts这种"被 CLI 自身打包加载"的场景中运行的前提。
与默认配置的对比
要理解替换的差异,可以参考默认配置。在 packages/bundler/src/webpack-config.ts 中,Webpack 路径默认使用 esbuild-loader,目标同样是 Chrome 85:
const esbuildLoaderOptions: LoaderOptions = { target: 'chrome85', loader: 'tsx', implementation: esbuild, remotionRoot, };其中.tsx?规则在 development 模式下还会追加fast-refresh/loader.js(见 packages/bundler/src/webpack-config.ts)。replaceLoadersWithBabel的 TSX 规则刻意将react-refresh/babel放在use数组中保持与 fast-refresh loader 相同的相对顺序(源码注释 "Keep the order to match babel-loader",见 packages/bundler/src/webpack-config.ts),保证替换后热刷新行为一致。此外,bundlerOverride是在基础配置构造完成、webpackOverride之前统一应用的(见 packages/bundler/src/webpack-config.ts),这解释了为什么 reducer 风格"收到默认配置、返回修改后配置"是官方推荐写法。
值得注意的是,Webpack 与 Rspack 两条路径共用同一个bundlerOverride:Rspack 侧默认使用内置的builtin:swc-loader,replaceLoadersWithBabel通过相同的规则字符串匹配逻辑对其生效,因此该包对两种 bundler 都是"可移植"(portable)的——这也是集成测试用 "portable overrides" 命名的原因。
四、场景二:通过 Node.js API bundle() 传覆盖函数
Node.js API 不读取remotion.config.ts,因此覆盖函数必须直接传入。官方文档示例(packages/docs/docs/legacy-babel-loader.mdx):
import { bundle } from "@remotion/bundler"; import { replaceLoadersWithBabel } from "@remotion/babel-loader"; await bundle({ entryPoint: require.resolve("./src/index.ts"), bundlerOverride: (config) => replaceLoadersWithBabel(config), });文档同时指出:若要把bundle()生成的目录部署到 Lambda,应将其传给@remotion/lambda的deploySiteFromBundle()(该函数实现在 packages/lambda/src/api/deploy-site-from-bundle.ts)。
五、真实用法参考:组合其他 override
Remotion 的示例工程展示了一个更完整的组合写法。在 packages/example/src/webpack-override.mjs 中,replaceLoadersWithBabel与 SCSS、Skia、Tailwind 的 enable 函数以及自定义 MDX loader 规则嵌套组合:
/** @type {import('@remotion/bundler').BundlerOverrideFn} */ export const bundlerOverride = (currentConfiguration) => { const replaced = (() => { if (WEBPACK_OR_ESBUILD === 'webpack') { const {replaceLoadersWithBabel} = require(/* @remotion/babel-loader */); return replaceLoadersWithBabel(currentConfiguration); } return currentConfiguration; })(); return enableScss( enableSkia( enableTailwind({ ...replaced, module: { ...replaced.module, rules: [ ...(replaced.module?.rules ?? []), {test: /\.mdx?$/, use: [{loader: '@mdx-js/loader', options: {}}]}, ], }, resolve: { ...replaced.resolve, alias: { ...replaced.resolve.alias, lib: path.join(process.cwd(), 'src', 'lib'), }, }, }), ), ); };这个例子说明了 override 组合的两条惯例:
- 每个
enable*函数和replaceLoadersWithBabel都遵循"展开原配置 → 局部修改 → 返回"的 reducer 模式,可以任意嵌套; - 追加自定义规则时保留原有
rules数组(...(replaced.module?.rules ?? [])),避免覆盖掉 Babel 规则或 CSS 规则。
六、集成测试:如何验证替换真正生效
仓库中的集成测试 packages/it-tests/src/bundle/rspack-portable-overrides.test.ts 专门验证了 Babel 替换与 SCSS、Tailwind v3 的组合(测试名为 "SCSS, Tailwind v3, and Babel helpers work through a shared Rspack override"):
const bundlerOverride: BundlerOverrideFn = (configuration) => { const withHelpers = replaceLoadersWithBabel( enableScss( enableTailwind(configuration, { configLocation: path.join(fixtureDirectory, 'tailwind.config.cjs'), }), ), ); // ...收集 tsx 规则中实际生效的 loader 列表 return withHelpers; };断言部分(packages/it-tests/src/bundle/rspack-portable-overrides.test.ts):
expect(scriptLoaders[0]).toContain('babel-loader'); expect(scriptLoaders).not.toContain('builtin:swc-loader'); expect(result).toContain('BABEL_LOADER_SENTINEL');即:.tsx规则的第一个 loader 必须是babel-loader、不能残留 Rspack 的builtin:swc-loader,且打包产物包含 fixture 中定义的哨兵字符串。这正是判断"替换是否成功"的可复现验证方式——检查最终 rule 的use链与产物内容。
七、适用前提与限制小结
- 适用前提:使用 Remotion 4.x(本仓库版本为
4.0.520),且所有remotion与@remotion/*包版本严格对齐; - 生效范围:
Config.overrideBundlerConfig作用于走配置文件的所有路径;Node.js API 必须显式传bundlerOverride; - 对 Rspack 同样有效:
replaceLoadersWithBabel匹配的是规则字符串而非 Webpack 专属 loader,测试证明其在 Rspack(SWC)下也能把脚本规则替换为 Babel; - 开发模式差异:仅
mode === 'development'时注入react-refresh/babel,production 构建不会包含该插件; - 官方立场:该包是兼容性退路,遇到默认 esbuild/SWC 转译问题时,官方建议优先提 issue 反馈而非切换到 Babel。
八、关键文件索引
| 内容 | 路径 |
|---|---|
| 包 README(安装说明) | packages/babel-loader/README.md |
核心实现replaceLoadersWithBabel() | packages/babel-loader/src/index.ts |
| 包依赖与版本 | packages/babel-loader/package.json |
| 官方文档(legacy-babel) | packages/docs/docs/legacy-babel-loader.mdx |
| 默认 Webpack 配置与 esbuild-loader | packages/bundler/src/webpack-config.ts |
| 组合 override 示例 | packages/example/src/webpack-override.mjs |
| Babel 替换集成测试 | packages/it-tests/src/bundle/rspack-portable-overrides.test.ts |
| Lambda 部署入口 | packages/lambda/src/api/deploy-site-from-bundle.ts |
【免费下载链接】remotion🎥 Make videos programmatically with React项目地址: https://gitcode.com/GitHub_Trending/re/remotion
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考