Improving the Ember DX, Part 2: Changing Our Toolbelt

By: Sergio Arbeo
Man standing on mountain peak

In the previous post, Improving Your DX on Ember, Part 1: Quick Wins, we saw some quick best practices for improving our DX in Ember. In this post, we will replace a few add-ons with other tools. Specifically, we will be removing ember-cli-eslint, ember-cli-stylelint and ember-cli-template-lint while keeping our project completely linted.

Adding Husky

The husky project lets your team share and version your git hooks. We will be using two hooks:

  1. pre-commit, where we will lint and fix all staged files.
  2. pre-push, where we will prevent the push if our linters fail.

Adding husky is as easy as installing any other npm dependency: do npm install husky --save-dev.

Husky can store its configuration in the same package.json, but I prefer it in its own file; and usually, I prefer a js file. Create the following .huskyrc.js with the content:

/* eslint-env node */

module.exports = {
  hooks: {
    "pre-commit": "lint-staged",
    "pre-push": "npm run lint"
  }
};

In this configuration, we are asking husky to run lint-staged before committing any change, and npm run lint before pushing. We don’t need to do ./node_modules/.bin/lint-staged in husky, it will find the executable in our project.

Talking about lint-staged, let’s install that now.

Install Lint-Staged

lint-staged let us run commands on staged files. We will be using it to fix linting errors in files before committing.

Install it with npm install lint-staged --save-dev, and create a lint-staged.config.js file:

/* eslint-env node */

module.exports = {
  "**/*.js": ["eslint --fix", "git add"],

  "app/styles/**/*.{scss,css}": ["stylelint --fix", "git add"],

  "app/templates/**/*.hbs": "ember-template-lint"
};

With this configuration, we are asking lint-staged to run eslint --fix and git add on any JavaScript file in our project. This will check those JS files and fix them before they are committed. Notice that for executing several commands, we don’t need to use && between them and can instead use an array of strings.

The file also contains similar configuration for linting CSS and SCSS files with stylelint, and templates with ember-template-lint.

Preventing jsconfig.json from Changing

As stated in the Part 1 post, the extension Ember CLI in Visual Studio Code overwrites jsconfig.json every time VSCode is launched. A configuration option to prevent this was added in September 2018, but, as the time of this writing, it has not been released.

With our new setup, reversing these changes is a simple task. A new rule that only matches jsconfig.json and that reverts the changes would make our lint-staged.config.js file look like this:

/* eslint-env node */

module.exports = {
  "jsconfig.json": [
    "git reset HEAD", // Remove from staged area
    "git checkout --" // Revert changes
  ],

  "**/*.js": ["eslint --fix", "git add"],

  "app/styles/**/*.{scss,css}": ["stylelint --fix", "git add"],

  "app/templates/**/*.hbs": "ember-template-lint"
};

If you need to make any changes to your jsconfig.json file, you can still commit them using the --no-verify option of git commit, which would prevent the hooks from running.

Updating Scripts

So far, we have set up husky and lint-staged. There is one thing left: define the npm script lint. For this, we will use concurrently, which let us run several npm scripts concurrently rather than serially like the && shell operator would do (npm run lint.hbs && npm run lint.js && npm run lint.styles). This can reduce script run time significantly, especially in big applications.

First, install it with npm install concurrently --save-dev. Then add the following lint script to your package.json:

{
  "scripts": {
    "lint": "node_modules/.bin/concurrently \"npm:lint.*\""
  }
}

This script runs all tasks in scripts that start with lint.. Let’s define them!

{
  "scripts": {
    "lint.hbs": "node_modules/.bin/ember-template-lint app/templates/**/*",
    "lint.js": "node_modules/.bin/eslint app config scripts server/index.js server/mocks tests *.js",
    "lint.styles": "./node_modules/.bin/stylelint app/styles/**/*.scss"
  }
}

Given we are writing scripts in npm, we do need to prepend the node_modules/.bin/ path to our executables.

One more piece to change in scripts: run the lint script as part of your CI process. If you don’t have a specific script, it might be a good time to add one:

{
  "scripts": {
    "test.ci": "npm run lint && npm run test"
  }
}

If you are adding a new one, don’t forget to update your CI configuration to run it.

That’s it! We are done almost entirely with set up. There is only one thing left.

Uninstall Dependencies

We no longer need ember-cli-eslint, ember-cli-stylelint nor ember-cli-template-lint. These addons run the linters during tests, but do increase start up time in a noticeable way in big applications. Let’s just get rid of them:

npm uninstall ember-cli-eslint ember-cli-stylelint ember-cli-template-lint --save-dev

After removing these, we might not have eslint, stylelint, and ember-template-lint in our project. Let’s add these tools back:

npm install eslint stylelint ember-template-lint --save-dev

So that’s it!

Recap

In part II we have removed ember-cli-eslint, ember-cli-stylelint, and ember-cli-template-lint. Instead, we are using eslint, stylelint, and ember-template-lint without losing any coverage. This also removes some weight from our ember server command.

Furthermore, we have setup husky and lint-staged so we can autofix our linting errors whenever possible before committing the code, and we won’t be able to push if our linters error.

What’s Next?

For the next and final part, we will be announcing something we’ve been working on lately that will improve the quality of your software code. Stay tuned.

DockYard is a digital product agency offering custom software, mobile, and web application development consulting. We provide exceptional professional services in strategy, user experience, design, and full stack engineering using Ember.js and Elixir. With staff nationwide, we’ve got consultants in key markets across the United States, including San Francisco, San Diego, Phoenix, Houston, Detroit, Miami, Pittsburgh, Baltimore, Boston, and New York.