Hot Module Reloading - live update while coding

I found a simple way to enable HMR with Livewire.

  1. Start a watch process to monitor file savings
  2. Send the filename to the browser via a socket server
  3. Add javascript in the browser to listen for the websocket, and emit an event
  4. Add event listener to the Livewire component
  5. Start the watch process

Part 1 and 2:
a) Install node-watch and ws from node:

yarn add node-watch
yarn add ws

b) Create a file called watch.js

const watch = require('node-watch');

const WebSocket = require('ws');

const wss = new WebSocket.Server({port: 8080});

watch([
  './resources/views/livewire',
  './app/Http/Livewire'
], {recursive: true}, (evt, name) => {
  console.log('%s changed.', name);
  wss.clients.forEach((client) => {
    if (client.readyState === WebSocket.OPEN) {
      client.send(name);
    }
  });
});

Part 3: Add the following to you app.blade.php

@if( config('app.debug') )
    <script type="text/javascript">
      if ("WebSocket" in window) {
        // Let us open a web socket
        var ws = new WebSocket("ws://localhost:8080/");

        ws.onopen = function () {
          console.log("HMR open");
        };
        ws.onmessage = function (evt) {
          var received_msg = evt.data;
          console.log("Message is received...", received_msg);
          window.dispatchEvent(new CustomEvent('hmr', {detail: received_msg}));
          window.livewire.emit('hmrupdated' + (received_msg));
        };

        ws.onclose = function () {
          console.log("HRM Connection is closed...");
        };
      } else {
        console.log("HMR WebSocket NOT supported by your Browser!");
      }
    </script>
@endif

Part 4:

Make your Livewire components inherit from a BaseComponent, and add an event listener:

<?php

namespace App\Http\Livewire;

use Illuminate\Support\Str;
use Livewire\Component;

class BaseComponent extends Component
{

    protected function getEventsAndHandlers()
    {
        $eventsAndhandlers = collect($this->getListeners())
            ->mapWithKeys(function ($value, $key) {
                $key = is_numeric($key) ? $value : $key;

                return [$key => $value];
            })->toArray();
        if(config('app.debug')){
            // Hot module reloading

            $nameBladeFile = Str::of($this->getName())
                ->replace('.','/')
                ->append('.blade.php')
                ->prepend('resources/views/livewire/')
            ;
            $eventsAndhandlers['hmrupdated'.$nameBladeFile] = '$refresh';
            $nameClass = Str::of(static::class)
                    ->replaceFirst('App', 'app')
                    ->replace('\\', '/') . ".php";
            $eventsAndhandlers['hmrupdated'.$nameClass] = '$refresh';
        }
        return $eventsAndhandlers;
    }
}

Part 5: Start watching your files:

node watch.js

Then the browser should be able to autoupdate the changed components.

1 Like