Node fs读取文件报ENOENT错误:路径存在但无法访问
问题排查:fs.readFileSync 找不到已存在的文件
问题背景
我在项目中使用Node.js的fs模块编写了读取文件的代码,该代码属于Container类的SetComponent方法。当用vitest调用Container.SetComponent()时,抛出文件不存在的错误,但实际文件确实存在。尝试添加.tsx后缀、使用ts-node和tsx运行均未解决问题。
关键代码片段
SetComponent方法中读取文件的核心代码:
const files: FileSystemTree = { "Component.tsx": { file: { contents: fs.readFileSync( `../../apps/components/${name.toLowerCase()}/${name.charAt(0).toUpperCase()}${name.slice(1)}.tsx` ) } } };
完整Container类代码
import {WebContainer, FileSystemTree} from "@webcontainer/api"; import fs from "fs"; export default class Container { private mContainer!: WebContainer; constructor() { this.CreateContainer(); this.ScaffoldViteApp(); this.StartDevServer(); } private CreateContainer = async () => { try { this.mContainer = await WebContainer.boot(); } catch (err) { throw new Error(JSON.stringify(err)); } return this.mContainer; }; private ScaffoldViteApp = async () => { try { this.mContainer.spawn("npm", [ "create", "vite", "container-example -- --template react-swc-ts" ]); } catch (err) { throw new Error(JSON.stringify(err)); } return this.mContainer; }; private StartDevServer = async () => { try { this.mContainer.spawn("npm", ["run", "dev"]); } catch (err) { throw new Error(JSON.stringify(err)); } return this.mContainer; }; public SetComponent(name: string) { const files: FileSystemTree = { "Component.tsx": { file: { contents: fs.readFileSync( `../../apps/components/${name.toLowerCase()}/${name.charAt(0).toUpperCase()}${name.slice(1)}.tsx` ) } } }; console.log(fs.readFileSync( `../../apps/components/${name.toLowerCase()}/${name.charAt(0).toUpperCase()}${name.slice(1)}` )) try { this.mContainer.fs.mkdir("components"); } catch (err) { throw new Error(JSON.stringify(err)); } try { this.mContainer.mount(files, {mountPoint: "components"}); } catch (err) { throw new Error(JSON.stringify(err)); } return this.mContainer; } public GetIframe() { return this.mContainer.on("server-ready", (port, url) => { return url; }); } public WriteComponent(name: string, content: string) { const files: FileSystemTree = { "Component.tsx": { file: { contents: content } } }; try { this.mContainer.mount(files, {mountPoint: "components"}); } catch (err) { throw new Error(JSON.stringify(err)); } return this.mContainer; } }
错误信息
Error: ENOENT: no such file or directory, open '../../apps/components/example/Example'
项目结构
. ├── README.md ├── apps │ ├── components │ │ ├── example │ │ │ ├── Example.tsx │ │ │ ├── stories │ │ │ │ └── Example.stories.tsx │ │ │ └── tests │ │ │ └── example.spec.tsx │ │ └── project.json │ ├── docs │ │ ├── README.md │ │ ├── astro.config.mjs │ │ ├── node_modules │ │ │ ├── @astrojs │ │ │ │ ├── check -> ../../../../node_modules/.pnpm/@astrojs+check@0.7.0_prettier@3.2.5_typescript@5.4.5/node_modules/@astrojs/check │ │ │ │ └── starlight -> ../../../../node_modules/.pnpm/@astrojs+starlight@0.23.2_astro@4.9.2_@types+node@20.12.13_terser@5.31.0_typescript@5.4.5_/node_modules/@astrojs/starlight │ │ │ ├── astro -> ../../../node_modules/.pnpm/astro@4.9.2_@types+node@20.12.13_terser@5.31.0_typescript@5.4.5/node_modules/astro │ │ │ ├── sharp -> ../../../node_modules/.pnpm/sharp@0.32.6/node_modules/sharp │ │ │ └── typescript -> ../../../node_modules/.pnpm/typescript@5.4.5/node_modules/typescript │ │ ├── package.json │ │ ├── project.json │ │ ├── public │ │ │ └── favicon.svg │ │ └── src │ │ ├── assets │ │ │ └── houston.webp │ │ ├── content │ │ │ ├── config.ts │ │ │ └── docs │ │ │ ├── components │ │ │ │ └── example.mdx │ │ │ ├── guides │ │ │ │ └── example.md │ │ │ └── index.mdx │ │ └── env.d.ts │ └── storybook │ ├── node_modules │ ├── package.json │ ├── project.json │ ├── tsconfig.json │ └── tsconfig.storybook.json ├── docs │ ├── assets │ │ ├── highlight.css │ │ ├── icons.js │ │ ├── icons.svg │ │ ├── main.js │ │ ├── navigation.js │ │ ├── search.js │ │ └── style.css │ ├── functions │ │ └── default.html │ ├── index.html │ ├── modules.html │ └── types │ └── Props.html ├── eslint.config.js ├── index.css ├── nx.json ├── package.json ├── packages │ ├── docs-sandbox │ │ ├── createContainer.ts │ │ ├── project.json │ │ └── tests │ │ └── createContainer.spec.ts │ ├── etech-ui-dev │ │ └── project.json │ └── etech-ui-utils │ └── project.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── postcss.config.cjs ├── reactDocgen.ts ├── tailwind.config.js ├── tests │ └── setup.ts ├── tsconfig.astro.json ├── tsconfig.json ├── tsconfig.node.json ├── tsconfig.react.json ├── types │ └── eslint-plugin-react.d.ts └── vite.config.ts
解决方案
1. 修复console.log中的路径缺失后缀
错误信息里的路径../../apps/components/example/Example缺少.tsx后缀,这是直接触发报错的原因。修改SetComponent方法中的console.log代码:
console.log(fs.readFileSync( `../../apps/components/${name.toLowerCase()}/${name.charAt(0).toUpperCase()}${name.slice(1)}.tsx` ))
2. 使用绝对路径避免工作目录问题
vitest运行时的工作目录可能和预期不一致(比如从packages/docs-sandbox/tests目录执行),相对路径会失效。改用绝对路径:
- 先导入path模块:
import path from "path"; - 修改fs.readFileSync的路径为绝对路径:
const componentPath = path.resolve( __dirname, `../../apps/components/${name.toLowerCase()}/${name.charAt(0).toUpperCase()}${name.slice(1)}.tsx` ); // 替换两处fs.readFileSync的路径为componentPath
如果是ES模块环境,也可以用import.meta.url替代__dirname:
const componentPath = new URL( `../../apps/components/${name.toLowerCase()}/${name.charAt(0).toUpperCase()}${name.slice(1)}.tsx`, import.meta.url ).pathname;
3. 修复异步方法调用的时序问题
Container构造函数中直接调用异步方法(CreateContainer、ScaffoldViteApp、StartDevServer)但未使用await,会导致mContainer还未初始化就调用SetComponent,引发潜在问题。建议重构为异步初始化:
export default class Container { private mContainer!: WebContainer; private constructor() {} public static async init() { const container = new Container(); await container.CreateContainer(); await container.ScaffoldViteApp(); await container.StartDevServer(); return container; } // ...其他方法保持不变 } // 使用时: const container = await Container.init(); container.SetComponent("example");
内容的提问来源于stack exchange,提问作者Ethan
相关产品推荐
相关产品推荐

