如何让Rollup在Jest测试中使用模拟文件系统?
问题
为Rollup插件编写测试时,使用memfs模拟内存文件系统,但运行测试报错RollupError: Could not resolve entry module "src/index.js"。直接用fs模块能读取到文件内容,但Rollup似乎没使用模拟的文件系统。测试代码如下:
import { describe, expect, jest, test } from "@jest/globals"; import memfs from "memfs"; import { InputOptions, OutputAsset, OutputChunk, OutputOptions, rollup, } from "rollup"; import { externalAssets } from "../src"; jest.mock("fs", () => memfs.fs); jest.mock("fs/promises", () => memfs.fs.promises); async function build(inputOptions: InputOptions, outputOptions: OutputOptions) { const bundle = await rollup(inputOptions); try { const { output } = await bundle.generate(outputOptions); const chunks = output.filter( (file): file is OutputChunk => file.type === "chunk" ); const assets = output.filter( (file): file is OutputAsset => file.type === "asset" ); return { chunks, assets }; } finally { await bundle.close(); } } describe("produces correct import paths", () => { test("chunk and asset in same dir", async () => { memfs.vol.fromJSON({ "assets/text.txt": "Lorem Ipsum is simply dummy text", "src/index.js": ` import txt from "../assets/text.txt"; console.log(txt); `, }); const { chunks } = await build( { input: "src/index.js", plugins: [externalAssets("assets/*")], }, { assetFileNames: "[name][extname]", file: "index.js", } ); expect(chunks).toHaveLength(1); expect(chunks[0].code).toMatchSnapshot(); }); });
解决方案
Rollup 默认不会直接依赖 Node.js 的 fs 模块处理文件,而是有自己的文件系统抽象层,单纯 mock fs 和 fs/promises 无法让 Rollup 切换到内存文件系统,需要通过 Rollup 插件机制替换它的文件读取逻辑,有两种可行方案:
方案一:使用@rollup/plugin-virtual注入虚拟文件
如果测试用的文件内容固定,可以直接用该插件在 Rollup 配置中注入虚拟文件,无需依赖 memfs:
import virtual from '@rollup/plugin-virtual'; // 在 build 的 inputOptions 中添加插件 plugins: [ virtual({ 'src/index.js': ` import txt from "../assets/text.txt"; console.log(txt); `, 'assets/text.txt': 'Lorem Ipsum is simply dummy text' }), externalAssets("assets/*") ]
方案二:自定义插件适配memfs
编写一个轻量插件,拦截 Rollup 的文件解析和读取钩子,用 memfs 处理文件操作:
function memfsRollupPlugin() { return { name: 'memfs-plugin', async resolveId(source, importer) { const resolvedPath = require('path').resolve( importer ? require('path').dirname(importer) : process.cwd(), source ); // 告知Rollup该路径由当前插件处理 if (memfs.vol.existsSync(resolvedPath)) { return resolvedPath; } return null; // 其他路径交给默认逻辑处理 }, async load(id) { // 用memfs读取文件内容 if (memfs.vol.existsSync(id)) { return memfs.vol.readFileSync(id, 'utf8'); } return null; } }; } // 在测试的inputOptions中添加该插件 plugins: [memfsRollupPlugin(), externalAssets("assets/*")]
注意事项
- 确保
memfs.vol.fromJSON()的调用在build函数执行前完成,保证内存文件系统已初始化 - 若使用TypeScript,可给自定义插件添加类型声明,或用
// @ts-ignore临时忽略类型检查
内容的提问来源于stack exchange,提问作者soufiane yakoubi
相关产品推荐
相关产品推荐

