The PET Stack - Part I

We are living in weird times right now. The Corona Virus is shutting down many parts
of public life world-wide. Today is the first weekend of a two-weeks-long
government-mandated stay-at-home period in the German state of Bavaria. Time to find
something productive to do from home. I have worked on 8+ web apps in the
past year. Today I want to share my favorite way of making them with you.

There are many tooling choices to make, when starting a new project. Which
language or JavaScript flavor to use? How to do CSS? And how do we package
our application?
The JavaScript ecosystem offers lots of options, so many in-fact it can be quite
overwhelming at times. I have tried out and have worked with many tools, but I
couldn't be happier about the combination of tools, that I have found for myself
right now, which I have started calling the PET Stack. It's ParcelJS +
Elm + TailwindCSS.
I will describe how to use each of them in more detail in this post and throughout
the next couple of posts.

P is for Parcel

Bundlers package your app typically into an HTML, a JS and a CSS file at build
time into something browsers can run efficiently.
So most likely, if you're building apps for the browser, you're gonna need a bundler.
You're gonna want to have a dev-server with hot-reloading to preview your
progress and you're gonna want small and optimized bundles for production.
Parcel can do all of that for you in a super simple way. It has out-of-the-box
support for many different languages and packages HTML, CSS, JavaScript and
luckily among others: Elm.

Here's a typical config file for Parcel:

# emptiness ... tumbleweed rolling along ...

Ok, I admit, the joke was not great, but it's true Parcel has great defaults
and does not even need any configuration in most cases. Let's see how to set up
a simple build with live reloading and transpiling JavaScript with babel.

Project setup

First, we'll set up a new project and install Parcel so it can be built.
If you're following along, create a new folder and do the following in a terminal.

npm init -y
npm i -D parcel-bundler

Now, open up package.json in your editor of choice and add a couple of scripts.

This way we're telling parcel to run a dev server on npm start.
Running npm run build will produce a production build. Both commands reference
a src/index.html file, which we will create in a second. Parcel will
automatically take care of injecting necessary hot reloading runtimes for
development and performing minification for production.

Next, let's add some actual content.

Adding a basic structure

We're gonna need three files for now and will put them into a src directory
under the project root, like this:

.
|-src
|-index.html
|-style.css
|-index.js

Let's add index.html with a basic structure. It is used as Parcel's entrypoint
in our npm commands. Note how we reference the other two files here.

<!DOCTYPE HTML><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1"><title>Trying out Parcel</title><!-- we are referencing our stylesheet here--><linkrel="stylesheet"href="./style.css"></head><body><!-- we will need this div later --><divid="app"></div><!-- we are referencing our js file here--><scriptsrc="./index.js"></script></body></html>

Now we need a basic stylesheet. Nothing special yet, we just wanna get our
first build working.

Finally, run npm start in your terminal and you should see parcel building
everything and firing up a dev-server on http://localhost:1234. Open your
browser and make sure you're seeing this:

That's what our web app looks like at the moment.

What just happened?

Wow, that was quick. How does Parcel know what to do? Parcel uses a set of
great defaults and a little info it got from us, to give us what we need. But
let's go through what happened step-by-step.

We call Parcel through npm start, where we told Parcel to use src/index.html as the entrypoint.

Parcel grabs index.html and finds a link to style.css. We are not doing
any CSS processing at the moment but Parcel will just include the CSS as is.

Parcel also finds a script tag which points to ./index.js and also includes it in the final
bundle. If you peek around in the browser dev tools, you'll see that the JS file has been
transpiled down to ES5. That's because Parcel uses babel by default and applies
the preset-env to it. If you add a .babelrc you can configure Babel's behavior
if you need more transforms than that.

Parcel swaps out the src and href attributes in the final html output and
replaces them with links to the final JS and CSS file.
Parcel also injects hot module replacement runtimes and source maps into the
output build.
Try to change any one of the files we added, you should see the change immediately in the
browser. Wohoo, hot-module-replacement for free 🥳.

We got a lot of what you would want from a bundler with zero-configuration from Parcel.
Now, let's look at the production build. npm run build. This time we can
find the production assets inside the dist/ folder. Open them up and you'll
find that they all have been minified without us having to do anything for it.
Parcel even takes care of adding a hash like src.3e2ab70.js
to the files, which is great for setting long-term caching headers. (Side note:
I found this post by Jake Archibald about http-caching
best practices very good.)

Adding Elm to the mix

So far we built our HTML, JS and CSS. But I prefer to build apps with
a functional language with strong type safety.
If you have never heard about Elm and if I had to describe it,
I would say, Elm is how I want programming to feel.

This is a tiny Elm program that simply renders a <h1> tag with some text to the screen.
Last thing we need to do is initialize our Elm app from JavaScript. For this
we open src/index.js and replace the contents with the following.

Parcel will automatically compile our Elm code to JavaScript and import it.
The second line with then initialize the Elm app into out app container.
Now, run npm start again. Our Elm code is running correctly if you see this:

Elm says Hello.

Recap

We saw that Parcel let's us build our app with basically zero-configuration.
We checked out development and production builds. We added an Elm asset,
which was also immediately built by Parcel and the resulting JS was bundled up
into our build output. Compared to other tools like Webpack, we
had the benefit of not having to write a config file or having to install and
configure a bunch of plugins.
So overall, we pieced everything together with Parcel pretty quickly and easily.

Some more noteworthy things about Parcel:

Parcel also has support for Webmanifest, Service-Worker, JSON, Web Assembly,
environment variables and a ton more, as shown on the docs page.

Throughout the rest of this series, I'll continue to show how to build an
application with Parcel, Elm and TailwindCSS. In Part II, we'll add some Elm code to make
interesting things happen on the screen, while letting the compiler make sure
we stay clear of bugs and bad practices.