preactjs/prefresh

prefresh/vite: runtimeCode is not defined

cyyynthia opened this issue · 5 comments

While working on a project of mine bundled with Vite using the preact preset, I've encountered strange errors which seemed to be from prefresh/vite.

Upon closer inspection of the error message and the lib's code, I've found that in the vite/src/index.js file, line 24 there is indeed a reference to a runtimeCode variable, which indeed is not define anywhere, leading to an unpleasant error :(

Oh waw, that is completely my mistake 😅 fix incoming!

Updated to 2.2.0, it seems like the issue is gone for npm (and probably yarn) users, but a it seems to create an issue with pnpm now, although minor (at least I think). pnpm has a strict policy on imports and projects can only import packages that are directly mentioned in the package.json. Indirect dependencies are not accessible.

Because the import is injected into the main script, vite attemps to find prefresh/vite, which is not a direct dependency of mine and therefore fails to import (it's a dependency of preact/preset-vite). A quick fix is to add prefresh/vite to my package.json, but it then defeats the purpose of the preset which is supposed to handle everything for you in my opinion.

Hmm, yes this issue is solved when you're using @prefresh/vite directly through https://github.com/JoviDeCroock/prefresh/blob/main/packages/vite/src/runtime.js, no clue how we'd do something like that for the preset though

CC @marvinhagemeister

no clue how we'd do something like that for the preset though

After looking into it a bit today, it seems like it will be hard to fix (at least with the current state of node & esm). I've managed to get some progress on getting it to load when using the preset and pnpm (I'm mentioning the preset specifically since it applies to my case, but the issue will happen in any scenario where prefresh isn't a direct dependency of a project).

The "fix" is relatively simple as it consists of using require.resolve to resolve the fullpath of the lib within the transform logic, passing this fullpath to Rollup's resolve method, and use the result as the thing we're importing.

Better than words, some code:

// file: /packages/vite/src/index.js

// note: because Rollup's resolve method is async, the transform method must also become async
const prefreshRuntime = await this.resolve(require.resolve('@prefresh/vite/runtime'))
const prefreshUtils = await this.resolve(require.resolve('@prefresh/vite/utils'))

const prelude = `
  ${'import'} ${JSON.stringify(prefreshRuntime.id)};
  ${'import'} { flushUpdates } from ${JSON.stringify(prefreshUtils.id)};
  ...
`

While it managed to get Vite to successfully lookup the file, this causes 2 problems:

  • require.resolve does not work in es modules, and import.meta.resolve is still experimental
  • This causes the .js file to be loaded instead of the .mjs one, which causes some errors as Vite expects an es module (Uncaught SyntaxError: The requested module '[...]/@prefresh/vite/utils/index.js' does not provide an export named 'flushUpdates')

For now, other than telling pnpm users to install @prefresh/vite along with the preset, I unfortunately don't think there's a magic solution yet :(

Thank you so much for looking into this @cyyynthia and for the in-depth explanation. This is much appreciated