vite icon indicating copy to clipboard operation
vite copied to clipboard

Fails to hmr accept virtual modules

Open Janpot opened this issue 1 year ago • 3 comments

Describe the bug

I'm trying to hot reload a virtual module, but it doesn't seem to be able to accept it:

// server.mjs
import { createServer } from "vite";

async function main() {
  const devServer = await createServer({
    clearScreen: false,
    plugins: [
      {
        name: "my-plugin",
        async resolveId(id) {
          if (id === "virtual:my-plugin:foo") {
            return "\0virtual:my-plugin:foo";
          }
          return null;
        },
        async load(id) {
          if (id === "\0virtual:my-plugin:foo") {
            return `export default Math.random();`;
          }
          return null;
        },
      },
    ],
  });

  await devServer.listen(3000);
  devServer.printUrls();

  setInterval(() => {
    const mod = devServer.moduleGraph.getModuleById("\0virtual:my-plugin:foo");
    if (mod) {
      console.log("reloading virtual module");
      devServer.reloadModule(mod);
    }
  }, 2000);
}

main();
// bar.js
export default Math.random();
// main.js
import foo from "virtual:my-plugin:foo";
import bar from "./bar";

console.log("loading foo ", foo);
console.log("loading bar ", bar);

if (import.meta.hot) {
  import.meta.hot.accept("virtual:my-plugin:foo", (newFoo) => {
    console.log(`hmr accepting ${newFoo.default} from "virtual:my-plugin:foo"`);
  });

  import.meta.hot.accept("./bar", (newBar) => {
    console.log(`hmr accepting ${newBar.default} from "./bar"`);
  });
}

Reproduction

https://github.com/Janpot/vitejs-virtual-hmr

Steps to reproduce

  1. run yarn && yarn dev
  2. open browser to http://localhost:3000
  3. open console
  4. observe the page reloading when the interval fires
  5. try the same but for the real module: const mod = devServer.moduleGraph.getModuleById(path.resolve("./src/bar.js"));
  6. observe the module being hot accepted

I tried using /@id/__x00__virtual:my-plugin:foo, but that doesn't work neither

System Info

System:
    OS: macOS 13.3.1
    CPU: (8) arm64 Apple M1
    Memory: 101.66 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 16.20.0 - ~/.nvm/versions/node/v16.20.0/bin/node
    Yarn: 1.22.19 - ~/.nvm/versions/node/v16.20.0/bin/yarn
    npm: 8.19.4 - ~/.nvm/versions/node/v16.20.0/bin/npm
  Browsers:
    Chrome: 112.0.5615.121
    Firefox: 111.0.1
    Safari: 16.4
  npmPackages:
    vite: ^4.3.0-beta.2 => 4.3.0-beta.8

Used Package Manager

yarn

Logs

No response

Validations

Janpot avatar Apr 19 '23 14:04 Janpot

Re-opening as fix is reverted in https://github.com/vitejs/vite/pull/13734

bluwy avatar Aug 03 '23 07:08 bluwy

I think this should still be open? @bluwy ?

patricklx avatar Feb 23 '24 19:02 patricklx

I believe I've run into a similar issue, a virtual module has broken my build https://github.com/vitejs/vite/discussions/16142

grctest avatar Apr 14 '24 10:04 grctest

Please ignore everything I wrote below

I turns out that in my Vite config I got confused between invalidating the server module, which leads to the transitive reloading that I observed below, and actively pushing an HMR update with moduleGraph.reloadModule(mod).

If I push an HMR everything works as expected.

I think I've run into this, or a very similar issue.

I'm using unplugin-vue-router to generate a virtual routes module.

My problem is that when I save a Vue SFC component, the routes are invalidated, which reloads the module that creates the router, then there are multiple routers cached in various places in the application in things quickly break down.

I have used import.meta.hot.accept("__vue-router/auto-routes", newRoutes => ...) to create a HMR boundary and manually handle the route table change without actually reloading the module and creating a new router.

This almost works, but the following seems to be a bug:

  • hot.accept("__vue-router/auto-routes", ...) partially works: my module is not reloaded anymore. If I remove this line or change the module name, my module is reloaded again. So the HMR boundary seems to work.
  • Yet the callback is not called. I've put debugger and various console.log(..) in the callback and they're never hit. This means that I cannot manually process the new module changes.

EDIT: one observation that might be useful is that the __vue-router/auto-routes is not reloaded in front-end (looking at >Network tab), which explains why the callback is not called.

Before, the SFC would reload my router.ts with a modified time, which itself reloads the auto-routes with a modified time. After, as router.ts is not reloading when auto-routes changes anymore, its timestamp is not modified. So SFC doesn't actually reload it. And nothing fetches/reloads auto-routes.~~

jods4 avatar Sep 06 '24 16:09 jods4