You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js应用本地可读取MDX文件,部署至Vercel/Netlify时遇ENOENT错误

解决Next.js部署Vercel/Netlify时MDX文件ENOENT找不到的问题

问题场景

本地环境可正常读取src/app/bounties/content/bounties目录下的MDX文件,但部署到Vercel或Netlify时,出现ENOENT错误,提示目标文件不存在。例如访问路由/bounties/dynamic-sidebar时,报错找不到src/app/bounties/content/bounties/dynamic-sidebar.mdx。

原代码示例

import { type MDXRemoteSerializeResult } from "next-mdx-remote";
import { serialize } from "next-mdx-remote/serialize";
import { promises as fs } from "fs";
import { MdxContent } from "../../mdx-content";
import BountiesBreadcrumb from "@/app/components/bountiesBreadcrumb";
import path from "path";

type Frontmatter = {
  title: string;
  date: string;
};

type Post<TFrontmatter> = {
  serialized: MDXRemoteSerializeResult;
  frontmatter: TFrontmatter;
};

interface IBountyProps {
  params: {
    slug: string;
  };
}

async function getPost(filepath: string): Promise<Post<Frontmatter>> {
  const raw = await fs.readFile(filepath, "utf-8");

  const serialized = await serialize(raw, {
    parseFrontmatter: true,
  });

  const frontmatter = serialized.frontmatter as Frontmatter;

  return {
    frontmatter,
    serialized,
  };
}

export default async function Bounty({ params }: IBountyProps) {
  const filePath = path.join(
    "src",
    "app",
    "bounties",
    "content",
    "bounties",
    `${params.slug}.mdx`
  );

  const { serialized, frontmatter } = await getPost(filePath);

  return (
    <div className="text-zinc-400 p-16 border-r border-zinc-800">
      <BountiesBreadcrumb />

      <div className="py-16 max-w-4xl">
        <h1 className="text-zinc-300 font-bold text-4xl">
          {frontmatter.title}
        </h1>

        <span className="text-zinc-400 text-md mt-8">
          Published at {frontmatter.date}
        </span>

        <div className="mt-8">
          <MdxContent source={serialized} />
        </div>
      </div>
    </div>
  );
}

错误原因

部署时Next.js生产构建会将src目录代码打包到输出目录,此时直接使用相对路径src/app/...不再有效——部署环境的工作目录并非项目根目录,导致路径解析错误。

解决方案

使用process.cwd()获取项目根目录作为路径起始点,确保本地和部署环境都能正确定位文件,同时增加文件存在检查提升调试体验。

修改后的代码

import { type MDXRemoteSerializeResult } from "next-mdx-remote";
import { serialize } from "next-mdx-remote/serialize";
import { promises as fs } from "fs";
import { MdxContent } from "../../mdx-content";
import BountiesBreadcrumb from "@/app/components/bountiesBreadcrumb";
import path from "path";

type Frontmatter = {
  title: string;
  date: string;
};

type Post<TFrontmatter> = {
  serialized: MDXRemoteSerializeResult;
  frontmatter: TFrontmatter;
};

interface IBountyProps {
  params: {
    slug: string;
  };
}

async function getPost(filepath: string): Promise<Post<Frontmatter>> {
  // 提前检查文件是否存在
  try {
    await fs.access(filepath);
  } catch (err) {
    throw new Error(`目标文件不存在: ${filepath}`);
  }

  const raw = await fs.readFile(filepath, "utf-8");

  const serialized = await serialize(raw, {
    parseFrontmatter: true,
  });

  const frontmatter = serialized.frontmatter as Frontmatter;

  return {
    frontmatter,
    serialized,
  };
}

export default async function Bounty({ params }: IBountyProps) {
  // 基于项目根目录拼接文件路径
  const filePath = path.join(
    process.cwd(),
    "src",
    "app",
    "bounties",
    "content",
    "bounties",
    `${params.slug}.mdx`
  );

  const { serialized, frontmatter } = await getPost(filePath);

  return (
    <div className="text-zinc-400 p-16 border-r border-zinc-800">
      <BountiesBreadcrumb />

      <div className="py-16 max-w-4xl">
        <h1 className="text-zinc-300 font-bold text-4xl">
          {frontmatter.title}
        </h1>

        <span className="text-zinc-400 text-md mt-8">
          Published at {frontmatter.date}
        </span>

        <div className="mt-8">
          <MdxContent source={serialized} />
        </div>
      </div>
    </div>
  );
}

关键说明

  • process.cwd()返回当前Node.js进程的工作目录,本地开发和部署环境中均指向项目根目录,确保路径解析一致。
  • fs.access()提前检查文件存在性,避免模糊的ENOENT错误,抛出更明确的调试信息。

内容的提问来源于stack exchange,提问作者Duca

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 13:07:35