How to setup a new node project

To create a new node project requires the same set of boring preliminary tasks. This document will describe hot to setup a new node project using es6, eslit and mocha.

Preliminary

It is expected that you have already installed the bare minimum which is:

The first step is to create the package.json file of your project. To do so, just run :

npm init

and provide reasonable responses. It will always be possible to update the package.json file later.

Directory structure

I am using es6 so the javascript sources won’t go in the usual lib directory.

The directories for the project are then:

Once transpiled into es5, the generated javascript files will go in the lib directory.

If you are not sure about es6, check this page to see all the goodies you would be missing.

babel

As said previously, the sources will be written in es6 and babel is required to transpile them to es5.

It is recommended to first install babel globally:

npm install -g babel`

A couple of modules are required to mame babel work with the other parts of your setup (like eslint and gulp):

npm install --save-dev babel babel-core babel-eslint babel-preset-es2015`

eslint

You should never write more than 2 lines of javascript without a linter. My favorite one is eslint.

npm install --save-dev eslint eslint-config-airbnb eslint-plugin-babel`

If you are using sublime text, it is required to install eslint locally so that sublime can do linting automatically for you.

The next step is to have a .eslintrc file where you define the rules for your project.

Here is my .eslintrc file:

{
  "parser": "babel-eslint",
  "rules": {
    "quotes": [
      2,
      "single"
    ],
    "linebreak-style": [
      2,
      "unix"
    ],
    "no-unused-vars": [2, {
      "vars": "all",
      "args": "after-used",
      "argsIgnorePattern": "^_",
      "varsIgnorePattern": "^_"
    }],
    "semi": [
      2,
      "always"
    ],
    "no-console" : 0,
    "id-length": [2, {
      "min": 2,
      "exceptions": ["x","v","T"],
    }],
  },
  "env": {
    "es6": true,
    "node": true,
    "mocha" : true,
  },
  "ecmaFeatures": {
    "classes": true,
    "modules": true,
  },
  "plugins": [
  ],
  "extends": "airbnb/base"
}

The interesting feature with eslint is that you can reuse rules – I am using the rules recommended by AirBnb using the extends feature of eslint.

Eslint is a very powerful tool and will deserve its own post to describe all the benefits it can add to your project.

gulp

Even though you can add tasks in your package.json -using the scripts feature- , to run mocha, eslint etc… it can become really quickly out of control and not very convenient if you want to run those tasks on multiple platforms.

You need a task runner.

I am using gulp, but grunt would work to.

You install gulp globally using:

npm install -g gulp

You install gulp with plugins you should use using:

npm install --save-dev gulp gulp-babel gulp-eslint gulp-help gulp-mocha

You need a gulp file to define your tasks . This file is a regular javascript file so you may want to write them in es6, right?

To do so:

{
  "presets": ["es2015"]
}

(More information here)

If you get an error like this:

[07:20:05] Requiring external module babel-core/register
gulpfile.babel.js:1
(function (exports, require, module, __filename, __dirname) { import _gulp fro
                                                              ^^^^^^

That do means that you have forgotten to create a proper .babelrc file.

gulp-help

Among all the gulp plugins you want to use, you should consider gulp-help.

Over time, your gulp file will grow and it may become difficult to remember the tasks you have defined: gulp-help adds a default help task and you can easily add a a custome help message to your gulp tasks:

✗ gulp
[08:16:08] Requiring external module babel-core/register
[08:16:58] Using gulpfile ~/work/graphql-mysql/gulpfile.babel.js
[08:16:58] Starting 'help'...

Usage
  gulp [TASK] [OPTIONS...]

Available tasks
  babel    generate es5 files in lib directory
  clean    remove generated files in lib directory
  default  [help]
  help     Display this help text.
  lib      generate the es5 library in lib [clean, babel]
  lint     run eslint on all the source files
  mocha    run the unit tests using mocha
  watch    watcher task to generate es5 files

[08:16:58] Finished 'help' after 2.07 ms
[08:16:58] Starting 'default'...
[08:16:58] Finished 'default' after 11 μs

There are 2 steps to follow to use gulp-help.

You must override gulp itself:

import _gulp from 'gulp';
import gulpHelp from 'gulp-help';

const gulp = gulpHelp(_gulp);

You then use gulp as usual, except that you can add an optional string - the help message - as the second parameter:

gulp.task('babel', 'generate es5 files in lib directory', () => {
  return gulp.src('src/**/*.js')
    .pipe(babel({ optional: ['runtime'] }))
    .pipe(gulp.dest('lib/'));
});

mocha

To use mocha for your unit tests, but it is pretty straightforward :

install --save-dev mocha expect

and then, in your test directory you can write your unit tests:

import expect from 'expect';
import { schema } from '../../src';

describe('Schema', () => {
  describe('#getSchema()', () => {
    it('should find the tables', () => {
      const dbDschema = schema();
      expect(dbDschema).toExist();
    });
  });
});

Nothing fancy here, but the following gulp file allows you to easily run those tests.

The mocha tasks will use the babel transpiler in order to be able to also write your unit tests in es6!

Example of a gulpfile.babel.js

To finish this post, here is an example of a gulpfile.babel.js file:

import _gulp from 'gulp';
import gulpHelp from 'gulp-help';
import babel from 'gulp-babel';
import eslint from 'gulp-eslint';
import del from 'del';
import mocha from 'gulp-mocha';

const gulp = gulpHelp(_gulp);

gulp.task('clean', 'remove generated files in lib directory', () => {
  return del([
    'lib/**/*',
  ]);
});

gulp.task('babel', 'generate es5 files in lib directory', () => {
  return gulp.src('src/**/*.js')
    .pipe(babel({ optional: ['runtime'] }))
    .pipe(gulp.dest('lib/'));
});

gulp.task('watch', 'watcher task to generate es5 files', () => {
  gulp.watch('*.js', ['babel']);
});

gulp.task('mocha', 'run the unit tests using mocha', () => {
  return gulp.src(['test/**/*.test.js'])
      .pipe(mocha({
        compilers: {
          js: babel,
        },
      }));
});

gulp.task('lib', 'generate the es5 library in lib', ['clean', 'babel']);

gulp.task('lint', 'run eslint on all the source files', () => {
  // Be sure to return the stream from the task;
  // Otherwise, the task may end before the stream has finished.
  return gulp.src(['src/**/*.js'])
    // eslint() attaches the lint output to the "eslint" property
    // of the file object so it can be used by other modules.
    .pipe(eslint())
    // eslint.format() outputs the lint results to the console.
    // Alternatively use eslint.formatEach() (see Docs).
    .pipe(eslint.format())
    // To have the process exit with an error code (1) on
    // lint error, return the stream and pipe to failAfterError last.
    .pipe(eslint.failAfterError());
});

// Default Task
gulp.task('default', ['help']);