react-router icon indicating copy to clipboard operation
react-router copied to clipboard

ENOENT error whenever I change a route module's exports

Open rossipedia opened this issue 7 months ago • 6 comments

I'm using React Router as a...

framework, SSR-enabled

Reproduction

Unfortunately I have not been able to create a minimal reproduction. The only place I see this is in our (Docker) internal web monorepro. I'd be more than happy to get on a Zoom call or similar with somebody to demonstrate the issue.

System Info

System:
    OS: macOS 15.4.1
    CPU: (12) arm64 Apple M2 Max
    Memory: 4.35 GB / 64.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 22.15.0 - ~/.local/state/fnm_multishells/61607_1747190035669/bin/node
    npm: 11.3.0 - ~/.local/state/fnm_multishells/61607_1747190035669/bin/npm
    pnpm: 10.7.1 - ~/.local/state/fnm_multishells/61607_1747190035669/bin/pnpm
    bun: 1.2.5 - ~/.bun/bin/bun
  Browsers:
    Brave Browser: 111.1.49.132
    Chrome: 136.0.7103.93
    Edge: 136.0.3240.64
    Safari: 18.4

Used Package Manager

yarn

Expected Behavior

I should be able to change a route module's exports (default, loader, action, clientAction, `clientLoader, etc…) and HMR should work and I should be able to reload the page in my browser without error.

Actual Behavior

When I add/remove one of the React Router route module exports, my dev server reloads, but I see this error in terminal output, and my express error handler is always triggered (this is the error handler that is added after the React Router handler, and is used as a "catch-all" final error handler when things go catastrophically wrong).

11:13:16 AM [vite] Pre-transform error: ENOENT: no such file or directory, open './build/client/.vite/manifest.json'
11:13:16 AM [vite] Error when evaluating SSR module virtual:react-router/server-build:
|- Error: ENOENT: no such file or directory, open './build/client/.vite/manifest.json'

This error gets "stuck" and I have to kill the process with SIGINT (Ctrl-C) and restart the process to get things working again.

rossipedia avatar May 14 '25 15:05 rossipedia

What does your vite.config.ts or react-router.config.ts (if you have one) look like?

Based on what you've said, you've got a custom Express server instead of using the @react-router/dev server. Does your setup look significantly different from this?

const viteDevServer =
  process.env.NODE_ENV === 'production'
    ? undefined
    : await import('vite').then(vite =>
        vite.createServer({
          server: { middlewareMode: true, hmr: { server }, allowedHosts: true },
          appType: 'custom'
        })
      )

const reactRouterHandler = createRequestHandler({
  build: viteDevServer
    ? () => viteDevServer.ssrLoadModule('virtual:react-router/server-build')
    : await import('./build/server/index.js')
})

timdorr avatar May 14 '25 16:05 timdorr

Yeah that's basically what we have, although we do some things to wrap the loaded route modules (instrumentation and such) before handing the ServerBuild off to createRequestHandler()

I can try removing those enhancers and seeing if I can still reproduce the issue.

react-router.config.ts is effectively this:

export default {
  serverModuleFormat: 'esm',
  future: {
    unstable_middleware: true,
    unstable_optimizeDeps: true,
  },
  ssr: true,
};

vite.config.ts is a bit complicated, but the plugins part looks like this:

    plugins: [
      envOnlyMacros(),
      viteCommonjs(),
      mdx({ remarkPlugins: [remarkFrontmatter, remarkMdxFrontmatter] }),
      tsconfigPaths({ loose: true }),
      ...(process.env['NODE_ENV'] !== 'test'
        ? [reactRouter(), safeRoutes({ outDir: 'types' })]
        : []),
      cjsInterop({
        dependencies: [
          '@mui/system/**',
          'attr-accept',
          'uuid',
          // Prod build only
          ...(process.env['NODE_ENV'] === 'production'
            ? ['safe-stringify', 'indent-string']
            : []),
        ],
      }),
    ],

I might be able to trim that down, but we're still on MUI 6, which has rather severely broken packaging, which is why we need the viteCommonjs() and cjsInterop() plugins.

We've had to apply the workaround that @markdalgleish wrote about here as well.

rossipedia avatar May 14 '25 17:05 rossipedia

Note: I've had conversations on Discord with @brophdawg11 and @pcattori about this, it was requested that I make a GH issue so that it doesn't slip through the cracks.

rossipedia avatar May 14 '25 18:05 rossipedia

To align with our new Open Governance model, we are now requiring that all issues have a minimal and runnable reproduction. To that end, we're doing some housekeeping in the repo to clean up existing issues that do not have a valid reproduction. This should get us down to a more manageable number of issues and allow us to be more responsive to existing and newly filed issues.

We're using a Github actions script to identify issues without a reproduction by looking for a StackBlitz, CodeSandbox, or Github link in the issue body. This won't be perfect, so if this issue has a reproduction on another platform, please comment back on here and we can re-open the issue. Similarly, if there's a reproduction buried in a comment, please move the link into the description and comment back. Please tag @brophdawg11 or @brookslybrand in your comment so we get a notification as well 🙂.

If this issue did not have a reproduction but is still valid, or if you wish to start with a fresh issue, please create a new issue with a fresh reproduction against v7 and link to this issue in the new description.

If this a feature request, please open a new Proposal Discussion in React Router, and if it gets enough community support it can be considered for implementation.

If you have any questions you can always reach out on Discord. Thanks again for providing feedback and helping us make React Router even better!

github-actions[bot] avatar Jun 25 '25 19:06 github-actions[bot]

Reopening based on @rossipedia's last comment

brookslybrand avatar Jun 25 '25 19:06 brookslybrand

Hey there, I'm experiencing the same issue since we made the move from Remix to RR7. Our app also uses an Express server, and its config follows closely the Epic Stack's one

On my side, I see this error each time I add/remove a new file, and more generally, when doing filesystem changes.

I dug a bit more to try to identify a potential cause and found out that it comes from this part -> https://github.com/remix-run/react-router/blob/422186644b4af7e0fae2934871ec9b0cddb5c757/packages/react-router-dev/vite/plugin.ts#L2038

When serving my app for the first time, viteCommand = "serve", but when I do a fs change, it triggers a vite [react-router] Config changed. and now viteCommand = "build".

I'll continue digging, but I hope this info will be helpful to anyone.

EDIT:

I think I found the culprit, and it comes from the safe-routes vite plugin, which you're also using @rossipedia. When the vite config changes and gets reloaded, this line is called: https://github.com/yesmeck/safe-routes/blob/main/src/vite.ts#L45

It changes the viteConfigEnv.command to "build" while we're in dev mode and then triggers the issue. When I remove this plugin, I don't have the error anymore. I'll open an issue on the safe-routes repo about this.

lildesert avatar Jul 16 '25 10:07 lildesert