"Yo Swiz, why aren't there any generated assets on preprod?"

Preprod is production-but-not. As the last step before deploying to production, it acts just like the real deal. Real data, real users, real configuration, real everything except payments. Because that would be rude.

"Errr … what do you mean there's no assets?"

"Well, we ran out of environments, so I'm testing on preprod. It's like Webpack doesn't output anything, but it works on localhost and all our other environments."

O.o

ERROR in tutor_onboarding.js from UglifyJs
SyntaxError: Unexpected token: name (defaultHeaders) [./~/better-fetch/index.js:2,0]

That’s funny … UglifyJS throws an error on the first line of my better-fetch library. Why would it do that? ?

“We don’t minify our code on localhost, do we? What about on staging?”

“Yeah, only prod and preprod”

Great, at least we know how nobody noticed this in over a month. It only happens on servers configured for production.

Thank the-god-of-grizzled-veterans for preprod! Error happens, deploy goes through, assets are empty, nothing works. Imagine how absolutely fantastic this would be to discover when deploying to real production ?

Here’s the line that broke UglifyJS:

let defaultHeaders = {};

I built a fetch() wrapper in ES6. Fuck me, right? Not like it’s an ES6+ only API or anything like that…

Hint: it is. It’s ES6 only. You’d think building a wrapper in ES6 without compiling before npm publish would work, right?

Nope. Not if a project that imports your library minifies their assets.

How to package your ES6 library so anyone can use it

3.73 months ago, I thought packaging a library was hard. Turns out it’s not so bad at all.

You have to add Babel, the ES6 -> ES5 compiler1, configure it, update your new main entry, and add a build script to npm scripts. That makes it easier to run.

These 7 lines are all it takes: diff on Github

You have to install babel-cli and babel-preset-latest:

$ npm install --save-dev babel-cli babel-preset-latest

And configure Babel:

// package.json
 
"babel": {
    "presets": ["latest"]
}

Configuring in package.json is easiest. 17 characters is not a good enough reason to add another config file to your project.

You should update your main entry as well. It tells npm which file to use when somebody uses require('your-library') or import YourThing from 'your-library'.

// package.json
 
"main": "lib/index.js"

Many projects use lib/ for their compiled files. dist/ is another popular choice.

To improve your quality of life, you should add a build script. Also in package.json:

// package.json
"scripts": {
    "build": "babel src -d lib"
}

Now you can use npm run build to run babel, read files from src/, and output the compiled versions in lib/. Library fixed. Crisis solved. \o/

Further quality of life improvement

While heroically saving the day with the above 7 lines of configuration, I found another way to improve my quality of life: automatic version bumping and GitHub pushing.

Add these 3 lines to package.json:

// package.json
"scripts": {
    "preversion": "",
    "version": "npm run build && git add -A lib",
    "postversion": "git push && git push --tags && rm -rf build/temp",
}

You can use npm version X where X is one of patch, minor, major, etc. to bump your library version number. That always works.

These three lines make it better. preversion runs a command before updating the version number. If the command fails, the version doesn't change. Usually these are tests. In my case, it's empty because I'm a bad person.

version runs a command when the version updates. This is a great spot to compile your library and add the compiled files to git.

postversion runs after the version updates. And this is a great spot to push to GitHub and perform any build cleanup.

Now you can update your library then run npm version patch to bump the version, build the lib, and push to github. npm publish announces the fix to the world.

Time to congratulate yourself and grab a beverage. ?

Lesson Learned

UglifyJS doesn't support ES6. They've been working on it since at least March of 2014. As of December 6th, 2016, it's still not ready. Turns out this is a hard problem.

Most real world projects use UglifyJS to minify their JavaScript.

You have to distribute a compiled version of your JavaScript libraries no matter what they do. Such is life.


  1. Yes, Babel is technically a transpiler

Learned something new? 💌

Join 8,400+ people becoming better Frontend Engineers!

Here's the deal: leave your email and I'll send you an Interactive ES6 Cheatsheet 📖 right away.  After that you'll get an email once a week with my writings about React, JavaScript,  and life as an engineer.


You should follow me on twitter, here.