craftcms / nitro

Speedy local dev environment for @craftcms.
https://getnitro.sh
MIT License
178 stars 24 forks source link

Recommend setup for front end complexities like HMR #20

Closed mattstein closed 3 years ago

mattstein commented 4 years ago

This may be a non-issue, but my DDEV/Docker setup has been tricky at times with front end testing (Codeception browser tests, Jest) and webpack (specifically HMR), so even if it’s a documentation issue (!) it’ll be good to have some best practices and recommendations around how those things should work with a project running in a Nitro machine.

ademers commented 4 years ago

I haven't yet managed to get Codeception in browser testing working with Homestead https://discordapp.com/channels/456442477667418113/585661264433250304/629048027709046785 This appears to be easily doable in Valet. See https://craftquest.io/courses/testing-with-codeception/10255 It would be nice if this was easily doable with Nitro.

jasonmccallister commented 3 years ago

With Nitro 2, we have a new setup and should make sure we update the documentation on how to use HMR and webpack.

ademers commented 3 years ago

I've been struggling to get any kind of HMR, BrowserSync working with the Nitro 2 beta, so very much looking forward to seeing some documentation soon. This is the only thing keeping me from switching from Homestead to Nitro. 🙏

khalwat commented 3 years ago

imo a good approach would be this basically: https://nystudio107.com/blog/run-your-node-js-apps-buildchains-via-docker

The approach I took was to have the container mount the dir, and you do

make docker

and then

make npm xxxx

so you use NPM scripts as an avenue to run whatever you want, e.g.

make npm install

or

make npm run dev

or

make npm run build

obv. you can substitute nitro for make here since you control that.

This is a containerized webpack 5 setup: https://github.com/nystudio107/annotated-webpack-config/tree/webpack-5

jasonmccallister commented 3 years ago

We do support have support for nitro npm <command> with Nitro 2.

By default, it uses the upstream node:<version>-alpine image from Docker Hub and places the current directory into the container. We are most likely going to add support for defining the node version per site in a future release and support something like nvm.

However, as of right now there are three ways to run npm commands:

On the Host Machine If you are running npm outside of the container on your host machine, you can use the host.docker.internal:<webpack-port> to reach the webpack server.

Using the nitro npm <arg> Command If you are running the npm from the nitro npm command that will create a new container from the current working directory and pass your args to the node:<version>-alpine container which does not expose any ports.

Using nitro ssh --root and Installing npm The last option is to run nitro ssh --root and manually install npm using apk add npm. If you ran the webpack server on port 3000 in the same container as the site, you can access the webpack dev server using localhost:3000 from the site, since they are running in the same container.

Definitely have some ideas to make this easier in the future, but those are some options now.

khalwat commented 3 years ago

Yeah we were trying the nitro npm <arg> command, which works fine for running things in the container, but WDS needs a port exposed for HMR to work.

Is there a way to manually expose a port?

jasonmccallister commented 3 years ago

A running container can't edit the exposed ports or environment variables without creating a new container. The node image does not expose a port in the Dockerfile so we might need to make our own image or support proxying a default port to each of the sites... We have some ideas but will nail the specifics down soon!

kevinmu17 commented 3 years ago

On the Host Machine If you are running npm outside of the container on your host machine, you can use the host.docker.internal:<webpack-port> to reach the webpack server.

I'm trying to understand this but i can't seem to find how this can be done.

I have my project folder, did nitro add followed by npm run hot. This is with webpack&HMR. But i'm getting all sorts of errors like: net::ERR_CERT_AUTHORITY_INVALID or net::ERR_CONNECTION_REFUSED. Is this because of the PORT issue?

andrewmenich commented 3 years ago

On the Host Machine If you are running npm outside of the container on your host machine, you can use the host.docker.internal:<webpack-port> to reach the webpack server.

I'm trying to understand this but i can't seem to find how this can be done.

I have my project folder, did nitro add followed by npm run hot. This is with webpack&HMR. But i'm getting all sorts of errors like: net::ERR_CERT_AUTHORITY_INVALID or net::ERR_CONNECTION_REFUSED. Is this because of the PORT issue?

By default, the webpack dev-server runs on http://, unless specified otherwise with the https config option. Are you accessing your dev site via https://? That would explain the errors you mentioned above.

Regarding your first question, host.docker.internal:<webpack-port> is what you would use in your template or asset plugin (like Twigpack or Asset Rev). This is because localhost on your host machine is not the same localhost in the Docker container where your site is running. host.docker.internal is how Docker communicates to your host machine.

kevinmu17 commented 3 years ago

By default, the webpack dev-server runs on http://, unless specified otherwise with the https config option. Are you accessing your dev site via https://? That would explain the errors you mentioned above.

If https config option is off in webpack I get: net::ERR_SSL_PROTOCOL_ERROR If https config option is on in webpack i get: net::ERR_CERT_AUTHORITY_INVALID

I did run this Nitro command: https://craftcms.com/docs/nitro/2.x/commands.html#trust

Regarding your first question, host.docker.internal:<webpack-port> is what you would use in your template or asset plugin (like Twigpack or Asset Rev). This is because localhost on your host machine is not the same localhost in the Docker container where your site is running. host.docker.internal is how Docker communicates to your host machine.

I am using Asset Rev. There was a little documentation in the config

 * For hot module reloading with the Webpack Dev Server, this expects a few values
 * to be set in the `.env` file:
 *
 * ```
 * WEBPACK_SERVER_URL="http://localhost:8080"
 * USE_WEBPACK_DEV_SERVER=1 # set to 0 (zero) to turn off
 * ```
 *
 * The base asset url will be based on `WEBPACK_SERVER_URL` if:
 *
 * 1. The Craft environment is "dev" (`ENVIRONMENT` in `.env`)
 * 2. `USE_WEBPACK_DEV_SERVER` is set to `1`
 *
 * Otherwise it will be based on `DEFAULT_SITE_URL`

So here is where it starts to feel like magic for me... in the .ENV file i have set these env variables:

USE_WEBPACK_DEV_SERVER=1
WEBPACK_SERVER_URL="http://host.docker.internal:8080"

Is this how it 'should' work? i feel so dumb :D I took the craft starter-blog as an example project: https://github.com/craftcms/starter-blog/blob/main/config/assetrev.php but this is before nitro 2 so I think this needs an update.

jasonmccallister commented 3 years ago

@mattstein I beleive with version 2.0.7 and these docs we can mark this as completed? Should we maybe include a tutorial on how to setup webpack with Nitro specifically?

mattstein commented 3 years ago

@jasonmccallister I’d call this completed. The example on that page you linked to really is all you’d need to change with webpack’s configuration to have HMR working in a simple webpack setup like the starter blog project uses.

I’m guessing the more confusing part will be addressing common combinations of things like Craft + Asset Rev + Laravel Mix or variations where you’ll need to tell a plugin about a base URL and a webpack project or wrapper about the port it should use. If that’s the case, we can publish Knowledge Base articles to address whatever turns out to be most common.

tl;dr Docs are accurate, setups differ wildly.

khalwat commented 3 years ago

@mattstein you spelled "Twigpack" wrong :)

ademers commented 3 years ago

Craft + Laravel Mix + Twigpack! 🚀

mattstein commented 3 years ago

Closing now that Nitro exposes ports making this possible. If anybody’d like to chime in over here with whatever they commonly use for front end development with Craft, please do! Will address further documentation separately.

kevinmu17 commented 3 years ago

@mattstein I beleive with version 2.0.7 and these docs we can mark this as completed?

Somehow Depending on your front end build setup, you may also need to provide your site with a base URL for the node server. In this case, it would mean changing http://localhost:8080 to http://starterblog.nitro:3000 this works for the front-end, but when trying to access /admin it gives a net::ERR_ABORTED 404 (Not Found) on every craft cms resource (cpresources)

Should we maybe include a tutorial on how to setup webpack with Nitro specifically?

Yes please! :)

mattstein commented 3 years ago

@kevinmu17 It sounds like you changed Craft’s site URL, which is different.

If you’re using webpack-dev-server (or a project that wraps it), you’re using webpack to run a separate, live process with a different hostname and/or port specifically for serving built JS+CSS. The browser can talk to that freely if it’s running directly on your host machine, or via port 3000/3001 if you’re running it in a Nitro container and you’ve configured that server to use port 3000 or 3001. That last part is what the example on this page mentions, and that’s all that needs changing if your setup looks like the simple webpack configuration on the starter blog.

If you’re using a Craft plugin like Asset Rev, Twigpack, or Mix that needs to be aware of the webpack-dev-server base URL, that’s the thing you may need to update on the Craft side separately from your site and control panel URLs. But again, if you need to update anything at all there it will depend on what you’re using.

Please consider sharing whatever your setup involves in this issue so we can consider doing a writeup.

Edit: I’m starting to think I mis-diagnosed that, but your front end’s webpack URLs definitely shouldn’t have anything to do with the web/cpresources/ directory and details about the rest of your setup are going to be important.

kevinmu17 commented 3 years ago

and that’s all that needs changing if your setup looks like the simple webpack configuration on the starter blog.

Well, i just tried that with the starter blog but with no working results.

system info:

  1. nitro version 2.0.7
  2. NPM version 7.5.6
  3. NODE version 14.15.1

git clone https://github.com/craftcms/starter-blog.git .

rm -rf .git

cp .env.example .env

composer install

./craft setup

nitro add

npm install

npm run hot

This doesn't work right away, so apply changes from the docs (webpack.config.js)

devServer: {
port: 3000,
}

(.env) DEFAULT_SITE_URL="http://test.nitro"

Now you get the messages that your WDS is running and live reloading is enabled... But it's not working. Change the index.js file with a simple console.log, it's doing a reload but it's not making any changes so you need to refresh manually.

Now a few weird things are going on here:

  1. First save [HMR] Cannot find update. Need to do a full reload! + [HMR] Error: Aborted because ./src/index.js is not accepted Update propagation: ./src/index.js
  2. Second save after edit: main.js:1554 GET http://test.nitro/assets/dist/main.7e5118b4fb7b5b5e42e0.hot-update.json 404 (Not Found)

Now that second one is weird, because when i check the folder this file should be, it contains a hot file with a complete different name (main.61aa72230d4a1aa55685.hot-update.js)

I'm totally clueless here, am I doing something wrong here?

mattstein commented 3 years ago

@kevinmu17 It doesn’t look like you’re running npm install or npm run hot within the container (after nitro ssh).

If you’re doing this all from your host machine (option 1 in the docs), the starter blog example should work as-is without changing the devServer port.

That said, I’m having an issue right now with the node-sass binding with Nitro 2.0.7, node 14.16.0, and 6.14.11 that definitely wasn’t a problem when I was testing for the docs update—where HMR was working like you’d expect. Looking into that and will follow up when I figure out what’s wrong.

kevinmu17 commented 3 years ago

FYI: i've update node-sass with sass 1.32.8 because of deprecation sass page also see NPM

I've managed to get it all working now!

twigpack.php config:

'server' => [
    'manifestPath' => '@webroot/assets/dist',
    'publicPath' => '/',
],
// webpack-dev-server config
'devServer' => [
    'manifestPath' => 'http://host.docker.internal:8080/assets/dist',
    'publicPath' => 'http://test.nitro:8080/',
],

webpack.config.js:

devServer: {
    contentBase: path.resolve(__dirname, 'public_html/assets/dist'),
    publicPath: '/assets/dist',
    writeToDisk: true,
    disableHostCheck: true,
    inline: true,
    hot: true,
    host: '0.0.0.0',
    port: 8080,
},

Hope this helps more people :D

mattstein commented 3 years ago

@kevinmu17 It turns out I was close and missed setting my own DEFAULT_SITE_URL to http://starterblog.nitro (not https)—so we can ignore this comment.

But I had to tell webpack to use 0.0.0.0 instead of localhost so port 3000 would be available from any interface.

It also still seems like you’re running your npm commands from your host machine and not the Nitro container—and the only way http://test.nitro:8080/ could possibly be working for you is if you’re on Linux where Docker is not running in a VM. (Because port 8080 shouldn’t be exposed on your host machine otherwise.) If you’re not on Linux, everything I think I know about the universe is probably wrong.

mattstein commented 3 years ago

So from the top, the starter blog should work—HMR included—following these steps:

  1. nitro create craftcms/starter-blog starterblog.nitro.
  2. nitro craft install [follow prompts]
  3. Edit .env and set DEFAULT_SITE_URL to http://starterblog.nitro.
  4. Edit webpack.config.js and
    • set config.devServer.port to 3000
    • add config.devServer.host and set it to '0.0.0.0'
  5. nitro ssh
  6. npm install
  7. npm run hot

Webpack should run successfully within the container, and you should be able to visit http://starterblog.nitro in your browser and see [WDS] Hot Module Replacement enabled. in the console.

You should also be able to edit src/styles/main.scss and add something like background-color: red to the html style properties and watch it update in the browser without a refresh.

The 0.0.0.0 host part is the critical omission from the docs, assuming others are successful here as well. Sorry for the confusion I’ve added here!

kevinmu17 commented 3 years ago

Hi Matt, thanks for clarifications!

I followed your steps 1-1. So when I am running hot with nitro ssh I'm getting this error:

GET http://starterblog.nitro:3000/sockjs-node/info?t=1618307531972 net::ERR_CONNECTION_REFUSED sockjs.js:1609
VM100:37 [WDS] Disconnected!

I'm on MacOS 11.2.3

When doing npm run hot outside of nitro, it kinda works, but not how it's supposed to. The browser refreshes on every save no matter what I do.

mattstein commented 3 years ago

That’s definitely not what should be happening, @kevinmu17. I’ve watched a few others run through these instructions successfully and I’m not sure what we’re missing.

It’s also weird that you can access test.nitro:8080 from your macOS host machine, because that’s the site container’s port that shouldn’t be available from the host machine unless you’re on Linux.

So unless you’ve got something unrelated to Nitro running on your host machine, nitro portcheck should show 8080 being available:

❯ nitro portcheck 8080
Port 8080 is available!

Meaning any requests to test.nitro:8080 should fail.

DM me on Discord if you’re up for trying a screen share to figure out what’s going on with your setup.

kevinmu17 commented 3 years ago

Update: it now works as it should. Nitro ssh is now only working. I think this issue was tied to Docker. Now only port 3000 is available!

I'm still stuck on configuring a properly functioning HTTPS connection with Webpack Dev Sever. Nitro trust creates a valid certificate and the site can be reached with https (shows valid ssl) but the WDS is giving me errors https://SITE.nitro:3000/sockjs-node/info?t=1627458266619 net::ERR_SSL_PROTOCOL_ERROR Any ideas?