Reading List

The most recent articles from a list of feeds I subscribe to.

Vite with Laravel: Auto-refresh Blade views

We’re up and running, but there’s another Laravel-specific quality of life improvement we can make: auto-refreshing when a Blade file changes.

To do this, we’ll write a simple Blade plugin right inside the Vite configuration.

I didn’t write this plugin myself, this is an except from innocenzi/laravel-vite.

The plugin listens to changes in .blade.php files, and does a full reload when they change.

// vite.config.js
export default ({ command }) => ({
base: command === 'serve' ? '' : '/build/',
outDir: 'public/build',
publicDir: 'fake_dir_so_nothing_gets_copied',
build: {
manifest: true,
rollupOptions: {
input: 'resources/js/app.js',
},
},
 plugins: [
 {
 name: 'blade',
 handleHotUpdate({ file, server }) {
 if (file.endsWith('.blade.php')) {
 server.ws.send({
 type: 'full-reload',
 path: '*',
 });
 }
 },
 }
 ],
});

Vite with Laravel

I’ve had an eye on Vite for a while. With a stable release out the door (2.0, as 1.0 never left the release candidate stage) it seemed like a good time to give it a shot.

Vite is a frontend build tool like webpack. Instead of bundling development assets, Vite serves native ES modules transpiled with esbuild from the dev server. This means there’s a lot less bundling to do, and results in a very fast developer experience. For production builds, Vite uses Rollup to bundle the assets.

If you want to delve deeper into Vite’s background, check out Why Vite in their docs. Now, let’s dive into Laravel.

After adding Vite to a JavaScript-heavy Laravel project we’re working on, we saw:

  • Faster dev server startupnpm run hot went from 15s to <1s
  • Faster production buildsnpm run production went from from 18s to 15s
  • A lot less config & dependencies — Coming from Webpack (without Laravel Mix) the PR for Vite counts +577 −3,367 lines
  • Additional develop experience improvements — Auto-refresh when Blade files change

This post dives into a basic Vite setup for Laravel. There are also follow-up posts for Blade, Tailwind, Vue, React, TypeScript, and Inertia.

Before getting started, consider not configuring this yourself. innocenzi/laravel-vite is an off the shelf solution to add Vite to your Laravel application. If you prefer full ownership over your build tools (like me), or want to learn more about the inner workings, do carry on.

Installation

First, clean up Laravel’s default package.json by getting rid of all Laravel Mix dependencies.

 {
"private": true,
"scripts": {
- "dev": "npm run development",
- "development": "mix",
- "watch": "mix watch",
- "watch-poll": "mix watch -- --watch-options-poll=1000",
- "hot": "mix watch --hot",
- "prod": "npm run production",
- "production": "mix --production"
 },
"devDependencies": {
"axios": "^0.21",
- "laravel-mix": "^6.0.6",
 "lodash": "^4.17.19",
- "postcss": "^8.1.14"
 }
}

Next, install Vite.

npm i vite --dev

Back in package.json, add the necessary scripts.

 {
"private": true,
"scripts": {
+ "dev": "vite",
+ "production": "vite build"
 },
"devDependencies": {
"axios": "^0.21",
"lodash": "^4.17.19",
"vite": "^2.1.0"
}
}

Vite configuration

Create a vite.config.js file in the project root. Here’s the basic configuration for a Laravel app.

// vite.config.js
export default ({ command }) => ({
base: command === 'serve' ? '' : '/build/',
publicDir: 'fake_dir_so_nothing_gets_copied',
build: {
manifest: true,
outDir: 'public/build',
rollupOptions: {
input: 'resources/js/app.js',
},
},
});

build.rollupOptions.input should point to the main input of our app. Important! This should only be a script. Stylesheets should be imported from the script, otherwise they won’t appear in manifest.json.

build.manifest generates a manifest.json file our Laravel app will read to discover the asset file names.

build.outDir determines where the final build will en up. In a Laravel app, this must be in the public directory. I recommend a subdirectory like public/build to make it easier to .gitignore.

Because we modified the outDir, we need to configure base. This ensures all of the path references in the files Vite generates will also point to /build/. On the development server (command === 'serve'), assets are served from http://localhost:3000. Because outDir isn’t used on the development server, we shouldn’t override base.

All files in publicDir get copied over to the outDir on build. This is how Vite deals with static assets in a SPA. Since we already have Laravel’s public directory, this isn’t relevant to us. Unfortunately there’s no way to disable this, so we’ll provide a fake path so nothing gets copied.

Before moving on to the Laravel configuration, we need to make some changes to the default app.js file.

  • As mentioned, CSS should be imported from JavaScript
  • Replace all require statements with import, as Vite requires us to use ES modules
  • Import Vite’s dynamic import polyfill
// resources/js/app.js
import 'vite/dynamic-import-polyfill';
import '../css/app.css';
import './bootstrap';

The bootstrap.js file also has some requires that need to be reworked.

+ import _ from 'lodash';
+ import axios from 'axios';
+
- window._ = require('lodash');
+ window._ = _;

/**
* We'll load the axios HTTP library which allows us to easily issue requests
* to our Laravel back-end. This library automatically handles sending the
* CSRF token as a header based on the value of the "XSRF" token cookie.
*/
- window.axios = require('axios');
+ window.axios = axios;

window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
/**
* Echo exposes an expressive API for subscribing to channels and listening
* for events that are broadcast by Laravel. Echo and event broadcasting
* allows your team to easily build robust real-time web applications.
*/
// import Echo from 'laravel-echo';
+ // import Pusher from 'pusher-js';

- // window.Pusher = require('pusher-js');
+ // window.Pusher = Pusher;

// window.Echo = new Echo({
// broadcaster: 'pusher',
// key: process.env.MIX_PUSHER_APP_KEY,
// cluster: process.env.MIX_PUSHER_APP_CLUSTER,
// forceTLS: true
// });

All configured!

If we run npm run dev, the dev server should start at http://localhost:3000. If we run npm run production a bundle should generate in public/build.

Laravel configuration

With the build pipeline up and running, it’s time to load the assets in the application.

First the dev server. When we run npm run dev, Vite spins up a dev server with hot module replacement enabled on localhost:3000.

To load our app, load Vite’s runtime, then create a module script tag that points to our entry filename on localhost:3000.

<script type="module" src="http://localhost:3000/@vite/client"></script>
<script type="module" src="http://localhost:3000/resources/js/app.js"></script>

When we run npm run production we need to find the exact asset path in the manifest, generated at public/build/manifest.json.

{
"resources/js/app.js": {
"file": "assets/app.e8ed53e0.js",
"src": "resources/js/app.js",
"isEntry": true,
"imports": [
"_vendor.0c0b30aa.js"
],
"css": [
"assets/app.1ce589ef.css"
]
},
"_vendor.0c0b30aa.js": {
"file": "assets/vendor.0c0b30aa.js"
}
}

This requires a few more steps:

  • Read the manifest.json file
  • Extract the script location and render a script element
  • Extract the stylesheet location and render a link element
@php
$manifest = json_decode(file_get_contents(public_path('build/manifest.json')), true);
@endphp
<script type="module" src="/build/{{ $manifest['resources/js/app.js']['file'] }}"></script>
<link rel="stylesheet" href="/build/{{ $manifest['resources/js/app.js']['css'][0] }}">

Don’t forget to prefix the path with the base path!

To ensure the right assets are loaded in every environment, combine the previous snippets with an @production directive.

@production
@php
$manifest = json_decode(file_get_contents(public_path('build/manifest.json')), true);
@endphp
<script type="module" src="/build/{{ $manifest['resources/js/app.js']['file'] }}"></script>
<link rel="stylesheet" href="/build/{{ $manifest['resources/js/app.js']['css'][0] }}">
@else
<script type="module" src="http://localhost:3000/@vite/client"></script>
<script type="module" src="http://localhost:3000/resources/js/app.js"></script>
@endproduction

Better DX for non-frontend developers

The above setup forces you to run npm run dev and watch on local. However, often backend developers working on the application don’t need to watch the assets for changes.

Devs can run npm run production to generate assets once, but the application will still try to load assets from locahost:3000. The production check we have in place isn’t enough.

To work around this, we can ping localhost:3000. It it connects, we know the dev server is running and we can render the hot scripts.

First, let’s extract the code we had written in our Blade template to a helper function. Next, we’ll use Laravel Http facade to ping localhost:3000. If it connects, we know the dev server is running.

<?php
use Exception;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\HtmlString;
function vite_assets(): HtmlString
{
$devServerIsRunning = false;
if (app()->environment('local')) {
try {
Http::get("http://localhost:3000");
$devServerIsRunning = true;
} catch (Exception) {
}
}
if ($devServerIsRunning) {
return new HtmlString(<<<HTML
<script type="module" src="http://localhost:3000/@vite/client"></script>
<script type="module" src="http://localhost:3000/resources/js/app.js"></script>
HTML);
}
$manifest = json_decode(file_get_contents(
public_path('build/manifest.json')
), true);
return new HtmlString(<<<HTML
<script type="module" src="/build/{{ $manifest['resources/js/app.js']['file'] }}"></script>
<link rel="stylesheet" href="/build/{{ $manifest['resources/js/app.js']['css'][0] }}">
HTML);
}

Finally, echo the assets in our application’s layout template.

{{ vite_assets() }}

Ready to go! Vite is set up in our Laravel application. The next step is to add additional configuration based on the tooling we’ll use.

How to do a case sensitive file rename in git on macOS

Mac is case insensitive, Linux isn’t. This has caused me trouble in the past after deploying my code to an Ubuntu server.

If you rename a file on Mac, git won’t pick up any changes if you only change the case.

mv app.js App.js

macOS displays the newly-cased filename, but git doesn’t see any changes.

In the past I’ve worked around this by doing two commits: one to change it to a different filename entirely, and then another to what it should be. app.jsappp.jsApp.js 🤦‍♂️

Turns out there’s an easier way: git mv. If you use the git mv command to rename the file, git picks it up correctly.

git mv app.js App.js

That’s it. Happy committing!

Self-deprecating comments

Henrik Nyh suggests to make comments more resilient to change with double-entry bookkeeping.

- $timeoutMs = 1000; // Equals 1 second
+ $timeoutMs = 1000; // 1000ms equals 1 second

Whether the discrepancy is caught immediately by the author, or in review, or by another developer far down the line, it will be explicitly clear that the comment was not intended for the current value.

This lowers the odds that a comment will get out of sync, especially useful in configuration files.

For more context head to Henrik’s blog, The Pug Automatic.

Back at it! Process discovery & a newsletterroll

Hi,

In the beginning of 2021 I decided to tone things down: low blogging activity, not too much open source follow-up, and most importantly a break from Twitter—which I wrote about. With so much to learn and keep up with, an occasional digital fast triggers a necessary reboot.

Process discovery & communication: things I learned at an event storming workshop

Last month I attended an event storming workshop at DDDEU. It was given by the creator of the method, Alberto Brandolini.

In short, event storming is a collection of methods to discover concepts and processes in a business. The main tool: Post-Its.

Since this was remote workshop, Post-Its were replaced by Miro, a realtime collaboration board.

Here are a few things I learned that reach beyond event storming.

When you’re trying to discover how something works, it’s better to have something wrong on paper than having nothing. You’re going to make mistakes in the discovery process anyway. Having something on paper will make it explicit, and will ensure you keep track of it in other places even though when not perfect. It is better to be prescriptive and wrong, than vague and right. If it’s not there, it’s not actionable.

You can’t validate something without a conversation. I’m a proponent of async, written communication. Written communication ensures the writer has given something enough thought to write it down, and gives the reader time to think and process it in their own pace. However, written communication should often be reviewed together to validate it, as there’s always more room for interpretation than both the writer and reader expect. Write, read, validate, improve.

When you’re stuck on a problem, try to solve it in reverse. One of the exercises in the workshop was to create a timeline of the Cinderella story. Getting started is easy, but it gets harder as you approach the middle of the story. This is often the case, processes tend to be thicker in the middle. When you’re stuck, tell the story in reverse. After that, it’s easier to uncover the rest because you have gaps to fill.

Newsletterroll

Like a blogroll, but for newsletters and harder on the tongue.

The creator economy is growing fast, and newsletters are a key part of it. In the past few months, there’s been an influx of new, quality newsletters paid and free.

Newsletters have become my main source of reading and discovering. Here a list of the ones I (almost) always digest.

Dev, design, and related topics

Alfonso de la Rocha
Dev — A weekly newsletter about various programming-related topics, language and ecosystem independent.
Developer Avocados
Technical writing & speaking — I’m not a developer advocate, but Developer Avocados shares a lot of good resources about having a technical blog.
Murze
Dev — An every two weeks newsletter with links about Laravel, PHP, and JavaScript.
Ryan Singer
Product design — Ryan Singer is Basecamp’s head of product design and the author of Shape Up.
Tiny React Email
React — Three links to keep up with the React ecosystem sent out once a week. Short and sweet.
UX Collective
Design — Links and inspiration about UX & visual design.

Productivity, business, and more

Every
Productivity & more — I’m a paid subscriber of Every, which is a bundke of newsletters about productivity, business, finance, and various other topics.
Farnam Street
Productivity & self-improvement — Weekly inspiration about productivity, mental models, and more.
Forte Labs
Productivity & self-improvement — Articles about productivity and personal knowledge management.
Morning Brew
News — A daily newsletter with bite sized articles about business & finance. I don’t read it every day, but skim through often.
Monday Medley by Nat Eliason
Productivity, self-improvement & more — I started following Nat when I got into Roam, and his newsletter is full of diverse, interesting links and thoughts.
Publisher Weekly
Writing & publishing — I enjoy following up on changes in the self-publishing world change as a bystander.

Cooking

A Newsletter by Alison Roman
Cooking — I love Alison Roman’s recipes and writing style.
The Perfect Loaf
Bread baking — My favorite resource for (sourdough) bread recipes.

More from the blog

There have been 2 posts on the blog since my previous newsletter.

macOS settings I always customize
When I set up a new Mac, there are a few things that make it immediately feel “off” to me. These are the small System Preferences tweaks I make that my muscle memory relies on.
Twitter break
Around the end of January, I decided to take a break from Twitter for an undetermined amount of time. Twitter was my main source of distraction, and I wanted to find out how much it affects my productivity.

More from the web

Links from across the web I learned from, or simply enjoyed reading.

Theory of Constraints 101
The best introduction to the theory of constraints I’ve come upon so far. After reading this, I started seeing this pattern all over the place.
Why does it take so long to build software?
The difference between essential and accidental complexity.
The Alameda-Weehawken Burrito Tunnel
A classic satire article by Maciej Cegłowski from Pinboard.

Smalltalk

Late to the party, but I had this queued up for my January newsletter that never happened. Here are my favorite albums released in 2020.

On the horizon

Back to writing! Dive deeper into event storming. Maybe release my first stable PHP package in months (years?). Finally get a haircut since barber shops reopened. Continue reading Moby’s memoir, Porcelain. Enjoy coffee brewed with the Chemex I got for my birthday. Go over budget buying records again.

Until the next issue,

Seb