Skip to content

Latest commit

 

History

History
 
 

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 

README.md

Tooling Recommendations for JavaScript Projects

This is an overview of tools you may use in your project and (rather opinionated) guide to choosing the right set of options.

tl;dr

It Is Dangerous To Go Alone, Take One Of These

Package Managers

When it comes to package management in JavaScript, there is npm (which isn’t an acronym for “node package manager”) and then there are others. You will use npm to install all Node-based tools, including other package managers. However, it is perfectly okay to use npm for front-end dependencies (i.e. for browsers) – it is even officially endorsed.

npm packages are “CommonJS modules:” every module requires dependencies and exports stuff to be consumed by other modules. Thus your code has an obvious dependency graph and you don't need to pollute global namespace with your functions. The only disadvantage of npm is that you need to bundle your modules to be consumed by browsers, even while you are developing the application. There are more details in separate section.

Another popular package manager specifically for front-end projects is Bower. It is strictly a dependency manager: it will just download your dependencies to bower_components directory and the rest is up to you; usually you will place <script> into your HTML files for every dependency you want to use. This is fine for very small projects, but it can get out of hand very quickly.

  • Dependencies between packages are not explicit, you need to manage them yourself,
  • it won't help you to split your code into multiple modules,
  • since there is no dependency graph, you will usually resort to passing dependencies through the global space and/or by modifying global objects (popular with jQuery plugins),
  • for code minification and concatenation you will end-up with some crazy magic comments.

On the other hand, Bower does not require you to bundle your code and does not assume anything about your modules, which gives you more options (usually to do some very messy stuff). It is still better than manually downloading JS files and placing it into your repository, but only marginally. If you have only a few dependencies or you want to avoid compile step, you may as well use libraries from CDN.

Recomended Reading

Module Bundlers

Once you decide to divide your code into neat modules or tap into npm’s huge registry, you will need to decide how to deliver your code for browser.

Node.js supports CommonJS by default, so your server-side code will work out of the box. However, browsers don't understand require or exports in your modules. This is where module bundler comes to play: it will load your entry file, traverse your dependency graph and spits out a single JS file, bundle, ready to be served to browsers.

Our favourite bundler is Browserify; it’s very simple to use, can be nicely extended and plays well with existing Node ecosystem. While developing project, you may want to use Watchify, which monitors changes in your files and rebuilds the bundle for you.

Recently Webpack gained traction as more complete solution. It handles all assets, including CSS files and images. Webpack takes a “batteries-included” approach: it has more closed ecosystem and it is less composable, but handles common use cases more easily. Perhaps the most controversial feature of Webpack is use of “loadersdirectly within the code which breaks interoperability with other CommonJS modules and can lead to duplication of code, since you need to repeat your loaders all over the place. The recommended way is to specify your configuration in webpack.config.js file outside of your code.

The choice of bundler depends on your preferences and project needs. Browserify is a great choice for small and JavaScript-only projects, it is easy to tinker with and compose with others and its sharper focus on JS bundling can be also advantage. Webpack seems to be a favourite solution for React projects and it lets you do more with your assets with less hassle, like inlining images to CSS or combining CSS with JavaScript components.

Recommended Readings

Build Tools

Now that you have some tools in your toolbelt, you surely need a task runner to manage are your build needs! Choose yours: Grunt, Gulp, Broccoli? Or how about Mimosa, Gobble, Brunch, Jake, Cake, Gear.js or Fly?

Well, you don't need any of these. Use npm scripts.

Probably the main promise of these build tools is an easy composability; you can combine your transpiler with code minification, copy assets, publish packages etc. from one place. No need to hunt for commands with long, complicated arguments.

However module bundlers do most of that for us already; in the end, your fancy task runner is just a verbose way to pass configuration options to Browserify or Webpack.

Put commands you run often into "scripts" section of your package.json, for example:

...
"scripts": {
  "test": "tap test/*.js",
  "build": "npm run build:js && npm run build:css",
  "build:js": "browserify index.js -o dist/index.js",
  "build:css": "cp index.css dist/index.css",
  "watch:js": "watchify index.js -o dist/index.js"
  ...
}

You can run these scripts with npm run <script_name>, e.g. npm run build.

If you need something complex the bundler won't do for you, like copy arbitrary files to dist/ directory, just write a shell script.

The only advantage of task runners is a portability: the same task will work on Mac OS X, Linux, and Windows (hopefully…). Your Bash scripts won't run on Windows out of the box. The solution is to write your scripts in JavaScript as well. ShellJS provides a familiarity of *nix commands with a portability of JavaScript, use it.

It's a Unix system, you know this!

Put your custom scripts into script/ folder. Name them in package.json:

"scripts": {
  "something-complicated": "node script/something-complicated.js",
  ...
}

If you need to run some tasks in parallel, for example a development server and a modules bundler, try npm-run-all.

Recommended Readings

Transpilers

If you think there's too many JavaScript task runners, just take a look at the list of languages that compile to JS. But most of them don't matter.

One problem with many of these languages is that they require you to learn a new syntax in addition to JavaScript; it is a leaky abstraction and you might end up debugging the resulting JavaScript code. Furthermore the syntax of these languages tends to be very opinionated and your team members might have quite different preferences. And most of these options lack a proper tooling, like linter or static analysis tools.

Instead of hunting for syntactic sugar for JavaScript, focus on JavaScript and its new features, especially ES6. Use Babel to transpile ES6 code for backward compatibility with today browsers. Eventually these features will gain native support and we can move to transpiling ES7.

As usual, there are exceptions to these rules. Perhaps you have a project in other language and you want to share some parts of existing codebase with browser or just reduce the mental overhead of switching between languages. Or your project has specific needs. If you are adventurous, we recommend checking out these languages which transpile to JavaScript:

  • LiveScript – An indirect descendant of CoffeeScript with nice functional programming features.
  • Elm – Purely functional language similar to Haskell with direct support for GUI programming, time travelling debugger and other goodies.
  • Opal – Ruby to JavaScript compiler.
  • ClojureScript – Clojure to JavaScript compiler.

Recommended Readings

Type Checking

Special case of transpilers are type-check extensions for JavaScript. Type checking can help in a larger code base and it can also remove need for some kinds of tests we usually do to make up for the lack of strict typing. If you'd like to incorporate type annotations, check out these projects:

  • Flow – Type checks are optional, you can add annotations gradually to your project, plus the checker can infer some type checks on its own. No official support for Windows yet.
  • TypeScript – Adds interfaces and classes to the language. Supports external type definitions which is useful for working with external JavaScript code.

Live Reloading

Front-end development incorporates a lot of visual checking. Save your changes, switch to browser, click refresh, check the result, repeat. To reduce clicking on refresh button, you may want to use some live reload solution.

  • Browsersync watches HTML, JS, and CSS files and reloads the browser when there is a change. It can also act as proxy for non-JavaScript projects (i.e. Java or Ruby server-side sites) and synchronize your interaction (so you can test on multiple devices at once!).
  • beefy is a development server for Browserify with live reloading.
  • webpack-dev-server is a similar solution for Webpack.

Hot Module Replacement

Most live reload servers can inject CSS without reloading the whole page. What if you could do the same thing with your JavaScript code? Ideally your application would maintain exactly the same state and only replace the modules which were changed? This would be very hard without knowing where the state is located in your application. But if your code is stateless and you keep your state contained, hot module replacement may be easy to implement for you. React makes this more feasible since components have an explicit state.

Check out these solutions:

Minification

Code minification can improve load speed by removing comments and whitespace from your code, rename variables to shorter names, eliminate dead code etc. It should be a part of your build process before deploying code to production.

Use code minifier for the module bundler of your choice. Minifyify for Browserify or minimize option for Webpack. Both of them use UglifyJS2 for minification, which you can also use on its own.

Closure Compiler may give you better results if you annotate the code for it. But unless you properly document all your code (or you use some type checker), it is probably not worth it.

Linters

ESLint is our favourite solution. It is extensible, configurable, and supports JSX and ES6. We have linting rules for it. Do not accept substitutes!

Avoid JSCS, it checks only your code style. It will make your life hell if you miss some extra newline, but it won't point out actual errors in your code.

Testing

Consider writing your tests with Tape. It is extremely simple, works both in Node and in Browser and goes well with any package bundler (especially Browserify). It is the easiest option if you would like to run your tests against various browsers with testling-ci. Tape produces TAP output which can be further used with some nice summarizers. For headless testing with Tape check out Prova.

If Tape doesn't work for you, you will have to do some decisions. There are many testing frameworks, libraries and test runners for JavaScript. They are very similar in features and style, but each takes a bit different approach. Some of them are browser-only and some of them are very opinionated about your testing needs.

For a rough idea, there are:

  • Assertion Libraries, need to be paired with framework or runner, provide helper functions for assertions and mocking:
    • Power Assert – acts like a standard assert, but adds extremely descriptive error messages; it works through code transformation with plugins for bundlers and Babel,
    • Chai – supports various assertion styles,
    • Sinon.js – mocking, stubbing, and test spies,
    • Should.js – supports “BDD” syntax should(x).be…
    • Expect.js – like should, but with expect(x)
  • Testing Frameworks, cover unit testing:
    • Mocha – runner and “outer shell” for tests; bring your own assertion library,
    • Jasmine – similar to Mocha, but with own assertions library,
    • QUnit – XUnit-style framework, originally for jQuery,
    • Unit.js – Still kinda new, but with interesting features and focused on interoperability with runners and other frameworks.
  • Test Runners: Make some aspects of testing easier, like running with headless browsers, automatic mocking and code injection, …:
    • Jest – Built on top of Jasmine, mocks dependencies and DOM, runs tests in parallel,
    • Karma – Browser-only testing which launches (headless) browser against integrated test server,
    • Intern – Buzzword-heavy, name-dropping runner with support for both unit tests (Node and browser) and headless functional tests. Uses Chai for assertions by default.

The favourite combination seems to be Mocha, Chai and Karma, but make your own research. However, keep things simple; tests don’t provide real functionality and you should not spend too much thinking about your test stack.

Functional Testing

Functional tests simulate user interaction and it’s a world of its own. Most projects build upon Selenium WebDriver, which lets you automate pretty much any browser. You can automate desktop browsers or special headless browsers like PhantomJS and SlimerJS.

Given the indirection of WebDriver protocol, you can write functional tests in most languages with a proper driver. The following projects run under Node, so you can automate interaction with JavaScript.

You may also want to check out projects with support for PhantomJS.

Recommended Readings

Hosted Services

If you publish your project as open-source on GitHub or in the npm registry, consider using these useful services: