Skip to content
This repository was archived by the owner on Apr 8, 2020. It is now read-only.

Support Webpack HMR with non-root base URLs #488

Closed
SteveSandersonMS opened this issue Dec 5, 2016 · 4 comments
Closed

Support Webpack HMR with non-root base URLs #488

SteveSandersonMS opened this issue Dec 5, 2016 · 4 comments

Comments

@SteveSandersonMS
Copy link
Member

Since Webpack dev middleware is a dev-time-only (not production) feature, this will only affect people whose development environment is set up to host their site at a non-root URL (e.g., http://localhost:port/somedir). This is a very unusual configuration, and in fact is quite hard to set up even on its own as you most likely need some external reverse proxy (e.g., nginx) on your development machine to make that happen. Very few people will do this.

Now, for people who do that, HMR won't work out-of-the-box right now, because the Webpack configuration doesn't know what directory you're hosting your app inside.

The PathBase information doesn't exist until runtime inside the context of a specific HTTP request where PathBase has been populated by UsePathBaseMiddleware. This is tricky for HMR, because the HMR's endpoint (e.g., /__webpack_hmr) is compiled into your .js bundle at build time.

The best solution I have in mind is:

  • Instruct developers that, if they plan to deploy to a non-root URL, then they have to configure publicPath values in their Webpack config accordingly. The publicPath values must truly reflect the URLs that browsers need to make requests to (this is the case even for regular production deployments; it's not specific to HMR).
    • At dev time, having correctly-prefixed publicPath values (e.g., /vdir/dist/) will automatically configure the client-side code to fetch HMR updates (e.g., blah123123.hot-update.json) from under that URL root
    • However, on its own, it would break the HMR proxying, because the current .NET-side proxying code would then start listening for requests under /vdir/vdir/dist.
  • To fix that latter problem, add a further option on WebpackDevMiddlewareOptions: BaseUrl. Amend the proxying code to automatically strip off that prefix from the publicPath values that it adds proxy listeners for.
  • Also, the .NET-side HMR code can add the configured BaseUrl value as a prefix onto the hotModuleReplacementEndpointUrl that it passes to the Node code. Then the client will correctly attempt to connect to (e.g.) /vdir/__webpack_hmr instead of just /__webpack_hmr.

So, it's not going to make things automatic for developers, but:

  • It's inevitable that they have to tweak publicPath values in Webpack config manually, because that's a compile-time item, and must actually reflect real URLs so that things like images/CSS/etc can actually be loaded from their true complete URLs in production
  • It's (almost) inevitable that some specific BaseUrl must also be configured, because otherwise there wouldn't necessarily be a single specific one we can use when controlling the HMR endpoint location. The only way to avoid this would be if we delayed WebpackDevMiddleware compilation until requests actually started, and then obtained the PathBase from the incoming requests.
@KraigWalker
Copy link

This is a great description of the HMR problem, one which I've hit when creating multiple entry points in the Web App I'm working on.

This is a very unusual configuration, and in fact is quite hard to set up even on its own as you most likely need some external reverse proxy (e.g., nginx) on your development machine to make that happen. Very few people will do this.

Funnily enough we're actually just using an MVC Controller to serve a different entry point. Our scenario is that we have a big and heavy dashboard UI which is only seen by administrators, and there's a public facing UI which shares some common UI components built with React, but actually uses the more lightweight Preact library instead.

I think the common justification for not tackling this is that it's just easier to create a different app entirely, but because UI components are shared between the public and private sides, there's actually quite a lot of complexity in keeping that in sync while still being able to make ad-hoc changes rapidly.

@SteveSandersonMS
Copy link
Member Author

This is now implemented and will take effect as of the next release. It turned out to be possible to make this work without requiring any extra configuration from the developer thanks to Webpack's dynamicPublicPath feature.

@Psvensso
Copy link

Psvensso commented Jan 3, 2018

@SteveSandersonMS
Would you please reconsider adding the WebpackDevMiddlewareOptions: BaseUrl back?The We still have situations with the /vdir/vdir/dist problem.

We have, for whatever reason, our client side code and static resources on our own IIS site under example.contoso.net/subservice/static

The html page requiring the app and script are on the example.contoso.net/subservice/ endpoint. E.g. it loads <script src="example.contoso.net/subservice/static/dist/spa-bundle.js"></script>

This works ofcourse when were using the <base href="example.contoso.net/subservice/static/" /> but using <base> can create all possible kinds of issues since it sets all relative paths to the "sub sub" static endpoint. In our case it actually also redirects the React router to the wrong paths since its using relative paths from the subservice url.

This perhaps is a little bit strange scenario for most but there must be more cases where one would like to specify the endpoints without changing the proxy. (as described above).

Or are we missing something, can the be done in current setup?

Edit

I have done some testing with a RelativeBasePath setting and it works fine. Unsure how to test all other scenarios without a test rigg so i just created a gist. Perhaps it can be used to discuss the matter further.

The Webpack middleware simply takes in the setting and passes to the proxy The only real change is in the proxy where it adds and removes the RelativeBasePath to the proxy call, as you described in the description above..

Full gist

@andlju
Copy link

andlju commented Apr 16, 2018

Allow me to 👍 the request by @Psvensso. We're trying to set up a microfrontend-style environment where a single page has the ability to include more than one SPA-app (that each run under different base paths). That brakes the <base href=""> mechanism since that is a one-per-page tag.

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

No branches or pull requests

4 participants