Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Force opt-out of HMR: Just always reload the page. #9133

Open
aspcartman opened this issue Jul 15, 2023 · 5 comments
Open

Force opt-out of HMR: Just always reload the page. #9133

aspcartman opened this issue Jul 15, 2023 · 5 comments

Comments

@aspcartman
Copy link

🙋 feature request

Mention a possible force opt-out of HRM in documentation, that would guarantee a classic dev-server behavior: any file change -> page reload.

🤔 Expected Behavior

According to the official documentation page, HMR is not active until module.hot.accept is explicitly called. That reads as "ok if I do not put the provided snippet to my code it will just work as usual - reload the whole page on any change".

😯 Current Behavior

  • Upon a code change the parcel serve reports on rebuild, but the page is not reloaded.
  • Adding --no-hmr flag doesn't help.
  • Adding the mentioned in the doc snippet to the entrypoint with the window.location.reload() callback in the accept() call leds to reloads on some files change, but most - still don't work.
  • None of the hacky solutions googled in ~8h help.

💁 Possible Solution

Mention on the same documentation page a force-opt-out. I figured it by is possible to:

// @ts-ignore
if (module.hot) {
    window.addEventListener('parcelhmraccept', () => {
        window.location.reload()
    });
}

Yet the parcelhmraccept is not a public api symbol and might change in the future - not clear if ok to be mentioned in the docs.
A better alternative might be an explicit module.hot.forcePageReloads() api.
Even more better - make --no-hmr force reload page on any file change that triggered the rebuild.

🔦 Context

The real project I'm working on is a Preact single-page app. During my investigation I've found that:

  1. @parcel/transformer-react-refresh-wrap does wrap preact components - seems to be an undesired behavior with unclear consequences. All components in the project ends up wrapped in the dev bundle.
  2. The AntDesign library used in the project accesses the module.hot at will - unclear if that 'opts-in' HRM, as the current documentation states. I don't see any 'opt-in' logic in the parcel output bundle though.
  3. (Opinionated) The stack-overflow and github issues seem to contain lots of questions on the topic, and the only close-to-working solution (with the callback mentioned) doesn't nail it.
  4. As mentioned, --no-hmr does something unobvious, where the obvious seems to be "disable hot module reload, reload the whole page instead".

Reloading the page upon a code change is an expected default behaviour of a development web server and it's quite possible that people facing such an issue during the adopt process of Parcel might stop their trial just there.

BTW: Should I fill a bug report on @parcel/transformer-react-refresh-wrap ?

@r0hin
Copy link

r0hin commented Jul 15, 2023

+1, hot reload / HMR has many problems – including sometimes crashing the entire page (#8615).

@danieltroger
Copy link
Contributor

danieltroger commented Jul 19, 2023

+1

We have hacked this together the following way in our code because parcel doesn't reload the page for CSS changes when making a library build (#6506). It works because parcel always calls console.clear on HMR. You'll have to adapt it for your use-case

if (process.env.NODE_ENV === "development") {
    console.clear = new Proxy(console.clear, {
      apply(target, this_arg, arg_list) {
        if (
          new Error().stack
            ?.split("\n")
            .filter(Boolean)
            .some(s => s.includes("http://localhost:1234/PARCEL-DEV-URL.js"))
        ) {
          /* eslint-disable no-console */
          console.log("CSS change detected through hack, reloading");
          location.reload();
        }
        return target.apply(this_arg, arg_list);
      },
    });
  }

@devongovett
Copy link
Member

devongovett commented Jul 19, 2023

Reloading the page is the default, but some frameworks provide a built in HMR handler. React Refresh is one of those. If you want to opt out of that, you can remove the plugin by reconfiguring the pipeline for JS/TS files in your .parcelrc:

{
  "extends": "@parcel/config-default",
  "transformers": {
    "*.{js,jsx,ts,tsx}": ["@parcel/transformer-js"]
  }
}

This overrides the default pipeline which includes @parcel/transformer-react-refresh-wrap. Without that, React Refresh won't be loaded and no HMR handlers will be registered so it should fall back to reloading the page.

@bjornhanson
Copy link

bjornhanson commented Sep 7, 2023

When using Parcel 2 with React, will it only try to use React's Fast Refresh, or will it fall back to a page reload? I'm updating from Parcel 1 (where it will reload the page on changes) to Parcel 2 and the CSS hot reloading works, but nothing updates or reloads when making changes to React components and I'm not sure why.

It's possible the app is not fully compatible with Fast Refresh, but changing everything just for that is out of the scope of what I'm doing right now. I'm fine with page reloads for now, but nothing is happening. When I update a React component, the only thing I see happen in the browser is the console getting cleared.

Update: I'm not sure getting the app fully compatible with Fast Refresh is even possible right now. According to the Parcel tips for using Fast Refresh, you can only use it with function components, but hooks do not cover all use cases, and we have a top-level component that can't be switched yet. Does this mean Parcel won't do any automatic reloading when our React components change?

@SrBrahma
Copy link

Reloading the page is the default, but some frameworks provide a built in HMR handler. React Refresh is one of those. If you want to opt out of that, you can remove the plugin by reconfiguring the pipeline for JS/TS files in your .parcelrc:

{
  "extends": "@parcel/config-default",
  "transformers": {
    "*.{js,jsx,ts,tsx}": ["@parcel/transformer-js"]
  }
}

This overrides the default pipeline which includes @parcel/transformer-react-refresh-wrap. Without that, React Refresh won't be loaded and no HMR handlers will be registered so it should fall back to reloading the page.

image

This is the @parcel/config-default transformer.

I believe that instead of just ["@parcel/transformer-js"] we want "*.{js,mjs,jsm,jsx,es6,cjs,ts,tsx}": [ "@parcel/transformer-babel", "@parcel/transformer-js", ]? (to have the -babel at first)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants