FredKSchott / snowpack

ESM-powered frontend build tool. Instant, lightweight, unbundled development. ✌️
https://www.snowpack.dev
MIT License
19.48k stars 921 forks source link

[FEATURE] Using Snowpack with HMR etc. in a brownfield application that owns its HTML code. #2816

Open eirslett-visma opened 3 years ago

eirslett-visma commented 3 years ago

The problem that you want to solve.

Using Snowpack with HMR etc. in a brownfield application that owns its HTML code.

I'm working on a legacy Java/JSP application and would like to try using Snowpack to build assets. The Java app has an index.jspfile that generates HTML code, which is different from "vanilla Snowpack", where Snowpack rewrites its index.html files on-the-fly.

We would like the Java application to use assets managed by Snowpack, using a simple <script type="module" src="http://localhost:8081/snowpack/blabla.js from the Java application's index.jsp file (Assuming the Java app runs on port 8080, and Snowpack runs as a sidecar on port 8081). However, this breaks hot module reloading. Snowpack's HMR is based on injecting code into index.html, which is why it doesn't work properly when only including the bundle directy.

Your take on the correct solution to problem.

Instead of dynamically changing Snowpack's index.html files on-the-fly, Snowpack could provide a simple/generated http://localhost:8081/_snowpack/dev.js script, which would include all of Snowpack's development boilerplate (HMR setup, react-fast-refresh etc.) When Snowpack is used for greenfield projects, and Snowpack "controls" its index.html, it would work similarly - its only dynamic HTML file change would be to inject a <script> tag which would load this dev.js script.

Are you willing to submit a pull request to implement this change?

Yes, if this sounds like a good solution.

AlexJMohr commented 3 years ago

I have gotten most of the way there with my Flask/Python application, which owns the HTML, similar to your situation. I built the snowpack app exactly like: https://www.snowpack.dev/tutorials/react

Running the snowpack build in watch mode with hot module replacement enabled:

snowpack build --watch --hmr

In my Flask template:

<html>
<head>
    <!-- ... -->
    <script>window.HMR_WEBSOCKET_URL = 'ws://localhost:12321'</script> <!-- 12321 is the default port -->
    <script type="module" src="/build/_snowpack/hmr-client.js"></script>
    <script type="module" src="/build/_snowpack/hmr-error-overlay.js"></script>
</head>
<body>
    <div id="root"></div> <!-- React app root -->
    <script type="module" src="/build/index.js"></script>
</body>
</html>

In index.jsx:

// ... react app code ...

if (import.meta.hot) {
    import.meta.hot.accept();
}

The only problem is updating a module refreshes the entire page, rather than just replacing the module. It looks like this is because import.meta.hot is undefined in production builds, i.e. snowpack build, even when using the --hmr flag.

eirslett-visma commented 3 years ago

@AlexJMohr Thanks! I tried something similar, and it worked in my Java app. What I did, was copy that snippet, and also copy the entire payload that Snowpack injects into the HTML code, for hot-reloading React components. Even if it technically works, it's not an optimal solution. Any changes in the injected code that is supposed to be served by Snowpack, would no longer be kept up-to-date, since the "old" version is copy-pasted into the Java app.

melissamcewen commented 3 years ago

Have ya'll tried the upcoming version? It may be able to do this: https://github.com/snowpackjs/snowpack/issues/1935