How Do We Even JS? (The Components of Nylas's Javascript Stack)
Juan
Posted on December 13, 2017
Javascript fatigue has been extremely prevalent over the past few years, making it really difficult to choose from the enormous pool of tooling and framework options without high levels of anxiety.
Fortunately, it’s 2017 already, and smarter people than I have already made a lot of these choices.
While the primary codebase at Nylas is written in Python, we’ve been building with Javascript for a while, and we’ve grown with it; we have a large codebase where you can find Coffeescript, plain old ES5, ES6, React, JSX, CJSX (I know, WTF?), along with many flavors of Flux, Redux and observable-based architectures.
Needless to say, we were excited to start a new greenfield project - our new Nylas Dashboard for our API customers - and try out all of the latest tools and practices the React community has been shifting towards.
In this post we’ll go over the choices we’ve made for starting a new web project in 2017.
TL;DR: We bit the bullet and used a bunch of tools built by Facebook. (Yay for their new MIT license for React!)
node
Use latest node because, why not?
install nvm
nvm install 8
nvm use 8
✌️
yarn
Before even starting, we had to choose a dependency manager. We’ve always used npm, which works great, but decided to go with yarn for a few reasons:
- Lockfiles for consistent installs across machines
- Seems faster
- Output has emoji ✨
- We can just run yarn instead of npm install
- Dependencies are saved to package.json by default without having to add the
--save
flag
The killer feature, IMO, is running custom scripts without having to preface them with run. Say you have the following script in your package.json:
// Your package.json
"scripts": {
"win": "node ./scripts/win.js",
},
To run that script with npm, you’d have to type: npm run win
. But with yarn, you just need to type: yarn win
. It feels so nice.
(Bear in mind, npm v5 was recently released with many of the goodies that yarn provides, like lockfiles, better perf, and a nicer CLI. You might want to use it, too!)
create-react-app
We’ve been writing React for a while, so we wanted to continue using it in our projects. However, starting a React project that uses modern Javascript from scratch is nontrivial because it requires A LOT of configuration:
- webpack (or browserify, or rollup, or brunch, or…) to generate builds to ship to the browser. That in itself requires lots of configuration.
- babel to transpile your code. It also requires a lot of configuration. Plus, knowing what Javascript features you are using in your code that need to be transpiled so they can run in the browsers you want to support, making sure you polyfill the right things depending on browsers you are targeting (e.g. Promise, fetch), and more that we probably haven’t realized yet.
- eslint to lint your code
- More that I’ve probably forgotten
- Then tying it all together!
BUT, as I mentioned, it’s 2017 already, and thank the GODS create-react-app exists. With a single command, you can scaffold a React application that comes preconfigured with webpack, babel, eslint, a dev environment that’s ready to go with automatic reloading, scripts to generate optimized production builds, etc.
yarn global add create-react-app
create-react-app dope-af
# ✨~magic~✨
cd dope-af
yarn
yarn start # Start development server
yarn build # Create optimized production build
Additionally, it comes with an excellent User Guide with information on pretty much anything you might want to do.
You can get pretty far with just create-react-app , but if you reach a point where you need to modify the configuration that comes out of the box, you can run the eject command to manually manage the configuration.
There’s a couple of things that I found are missing from the default configuration, like #2310, but luckily the community is really active, and I believe these will be included without having to eject.
jest
create-react-app comes out of the box with jest configured as its test runner. In this post we won’t cover how we write tests using jest, but suffice to say, it’s dope AF. This talk convinced me.
eslint
With create-react-app, eslint comes preconfigured with sensible defaults. If you want to extend it, you just need to write a custom .eslintrc file:
// .eslintrc
{
"extends": "react-app",
"rules": [
"prefer-const": "warn" // custom rules
]
}
Your code will be linted by default when you run yarn start or yarn build, but you can also add a custom script:
yarn add --dev eslint@3.19.0 # Same version that create-react-app uses
// Your package.json
"scripts": {
"lint": "eslint ./src ./scripts",
},
flow
flow is a static type checker for Javascript. There are numerous arguments for and against a static type checker, but after we grew an immense Javascript codebase here at Nylas, the value of types became more and more apparent, especially for core abstractions and highly used modules in a codebase.
One good thing is that flow supports gradual typing, which means that type annotations are not required everywhere. That means that if you’re prototyping or writing something you know is likely to change, you don’t need to use types, but if you’re writing a core module in your codebase, you can lock it down with types.
Types can be beneficial because:
- they give us more confidence when making changes or refactors
- they serve as documentation (which wont become stale, unlike comments)
- they prevent a lot of unnecessary unit tests that we usually end up writing just to check types
create-react-app does not enable flow by default, but, of course, the user guide specifies how to do it.
There’s one extra thing we did when configuring flow, and it was changing it to report type errors as eslint errors:
yarn add --dev eslint-plugin-flowtype-errors
// Your .eslintrc
{
"extends": [
"react-app"
],
"plugins": [
"flowtype-errors"
],
"rules": {
"flowtype-errors/show-errors": 2,
"prefer-const": "warn"
}
}
That’s useful because if you already have eslint configured in your editor you won’t need an extra editor integration for flow. It also applies for other tools that you’ve already configured with eslint. E.g., we use Phabricator at Nylas for code review, and we’ve already integrated it with eslint, so we didn’t have to write a new integration for flow.
prettier
prettier is a Javascript code formatter. What that means is that it’s a program that takes your javascript code as input and then outputs that same code, but formatted. The code will be formatted in a standard way it so it all “looks” the same—i.e., uses the same rules for spacing, where to place newlines, and so on.
It’s great because:
- You won’t spend time manually formatting code as you write it. You can just write the ugliest code imaginable, and as long as it’s valid code, prettier will automagically make it, well, prettier.
- You have a fast and automated way of formatting code, so all of the code in your repo will look the same, which makes it way easier to read and follow.
- You won’t get into fights about how code should be formatted since it’s all just offloaded to the tool, regardless of how people want to write it.
create-react-app doesn’t have a section on prettier, but that’s why we’re here! Here’s how we configured it:
yarn add --dev prettier eslint-config-prettier eslint-plugin-prettier
// Your package.json
"scripts": {
"prettier": "prettier --single-quote --trailing-comma es5 --write \"!(build)/**/*.js\"",
},
// Your .eslintrc
{
"extends": [
"react-app",
"prettier",
"prettier/flowtype",
"prettier/react"
],
"plugins": [
"flowtype-errors",
"prettier"
],
"rules": {
"flowtype-errors/show-errors": 2,
"prettier/prettier": ["error", {
"singleQuote": true,
"trailingComma": "es5"
}],
"prefer-const": "warn"
}
}
There’s a few things going on here, so let’s flesh those out:
- We defined a custom prettier script: yarn prettier. When run, it will read any code that is not inside the build/ directory and write it back correctly formatted. It also includes some basic configuration options for prettier.
- We wanted eslint to report any formatting errors detected by prettier. To do that, we added eslint-plugin-prettier and enabled it in our plugins and rules section:
"plugins": [
"flowtype-errors",
"prettier"
],
"rules": {
"flowtype-errors/show-errors": 2,
"prettier/prettier": ["error", {
"singleQuote": true,
"trailingComma": "es5"
}],
"prefer-const": "warn"
}
One thing to note is that we had to duplicate our prettier configuration in this file (😢) and in our package.json because prettier doesn’t currently support configuration files.
Finally, eslint includes several formatting rules itself, but given that we are using prettier, we don’t want eslint to complain about formatting rules that prettier will handle. To achieve this, we added eslint-config-prettier, which will disable any eslint rules that affect formatting. In order to enable it, we added these extra extensions:
"extends": [
"react-app",
"prettier",
"prettier/flowtype",
"prettier/react"
],
And that’s it! Our basic prettier workflow works in the following way:
- Write code
- Try to submit a diff
- Our diff tool reports prettier errors via eslint
- Run yarn prettier
- Submit diff! 🎉
You can also have a tighter integration by adding prettier to your editor so it formats your code as you type or as you save your file, so you don’t ever have to explicitly run yarn prettier. However, editor integrations are a topic for another post.
And that’s how we JS here at Nylas! (Or at least how we’re trying.)
Finally, I wanted to call out and thank everyone who has spent time building and contributing to these awesome tools, which, in my humble opinion, are making Javascript development in 2017 a lot more fun.
How do you do JS at your company? Leave a comment below.
This post was originally published on the Nylas Engineering Blog.
Posted on December 13, 2017
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.