Est. reading time: 6 minutes

Snowpack 🏔

Yesterday I finally found some time to get my hands on the new Snowpack release. For people who have not come across Snowpack yet, it's "a lightning-fast frontend build tool, designed for the modern web". I had my eye on it for a while and with the newest release, they promised a bunch of build goodness that I wanted to try. Fast startup/build/rebuild times, out-of-the-box support for things like CSS modules, the dynamic duo TypeScript & React (including HMR) and much more, so I wanted to see what it's all about. If you're interested you can find all the nitty-gritty details right here.

Time to upgrade

I already had an "old" Snowpack v2 project lying around where I also introduced Emotion for styling and Jest for testing. When you look at the docs for Snowpack guides you'll find that with regards to updating from v2:

Snowpack v3.0 was mainly designed around new features, and therefore didn’t have many major breaking changes introduced. However, there are some changes to be aware of:

...

Sounds good 👍

So I grabbed the latest versions of @snowpack/app-scripts-react and snowpack from npm and spun up the dev server. Right after that, the computer said no! Got a good old 404 when searching for my index.js file, the one linked in the public HTML file. Well turns out they renamed the default output directory from _dist_ to simply dist. Ok, simple enough - quick update and that error was taken care of.

Right after that the Emotion setup I had working with v2 did not really like v3 it seemed. I always have some issues when it comes to installing Emotion because they have a couple of hundred ways of actually doing it 😅 it's great that you have support for any situation you can possibly think of but it's also kinda confusing when you want to know what's the right way for your specific context. For example, should I use the @emotion/core or @emotion/react package? Do I need both? What about the babel plugins or presets? And macros? What's all this about the new JSX runtimes?

In the end, it always is a bit of guesswork and trial and error but until now it worked out 🤷‍♂️

In this case, I switched from @emotion/core to @emotion/react, updated to the latest @emotion/babel-preset-css-prop and changed my babel.config.json to look like the one mentioned in the docs:

{
"extends": "@snowpack/app-scripts-react/babel.config.json",
"presets": [
[
"@emotion/babel-preset-css-prop",
{
"autoLabel": "dev-only",
"labelFormat": "[local]"
}
]
]
}

Having done that the only thing left was to tell Typescript about the css prop by adding

{
"compilerOptions": {
...
"types": [
"snowpack-env",
"@emotion/react/types/css-prop"
]
}
}

to my tsconfig.json and that type error was also gone. Finally, the dev server was happy and I was greeted with a spinning react logo.

I believe you Jest

After making it work locally I was ready to push to the repo but was quickly brought back to a halt by my git test hooks. How dare they prevent me from pushing broken things to main? Well, it was not really broken it was just Jest being Jest and requiring some extra steps to understand JSX (or rather TSX).

Snowpack also comes with it's own version of create-react-app (CRA) which is called - yeah, you guessed it right - create-snowpack-app (CSA). It also sets you up with a jest.config.js

module.exports = {
...require('@snowpack/app-scripts-react/jest.config.js')(),
};

and a jest.setup.js. At least it did in v2.

// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom/extend-expect';

You can also view them of course to see exactly what's going on under the hood. So I was able to see that we needed some custom transforms for Jest to be able to do its thing. In the end, it wasn't that big of a deal and after a couple of minutes and a bit of trial and error (again) I ended up with a working config.

module.exports = {
...require('@snowpack/app-scripts-react/jest.config.js')(),
transform: {
'^.+\\.(js|jsx|ts|tsx)$': 'ts-jest',
'^.+\\.svg$': '<rootDir>/jest/svgTransform.js',
'^.+\\.css$': '<rootDir>/jest/cssTransform.js',
},
};

I'm using ts-jest to transform all the .ts(x) things first so that Jest can read it and we're good to go. In my case, I was also including some .css and .svg files for which I needed to tell Jest how to handle them. The <file-ending>Transform.js files you see up there are just simple mocks that don't do anything special - but Jest needs to know what to do to not choke on them.

Something else that's needed is some slight changes to your tsconfig.json again.

{
"compilerOptions": {
...
"allowJs": true,
"esModuleInterop": true,
"jsx": "react",
}
}

allowJS to allow javascript files to be compiled, otherwise Jest will choke on your jest.setup.js. esModuleInterop will help with some import issues and jsx set to react will transform JSX to React.createElement.

Done and done

So after a bit of work, the whole upgrade from Snowpack v2 to v3 was done and Emotion and Jest were also happy with the result. What I can say for now is that the whole thing is really, really fast. HMR is a dream to work with and the freedom to customize what you want without having to eject is something that I wish was available everywhere. Another big thing I have not touched yet is Streaming Imports. It's the ability to "skip the npm install step entirely and just fetch the relevant, pre-built package code on-demand via ESM import". Looking forward to trying it out soon 😁