parcel-bundler / parcel

The zero configuration build tool for the web. ๐Ÿ“ฆ๐Ÿš€
https://parceljs.org
MIT License
43.34k stars 2.25k forks source link

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

Open aspcartman opened 1 year ago

aspcartman commented 1 year ago

๐Ÿ™‹ 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

๐Ÿ’ 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 commented 1 year ago

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

danieltroger commented 1 year ago

+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 (https://github.com/parcel-bundler/parcel/issues/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 commented 1 year ago

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 commented 11 months ago

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 commented 6 months ago

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)

github-actions[bot] commented 6 days ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs.