Me!

TJ VanToll

Linting JavaScript in NativeScript Apps

| Comments

One of the great things about NativeScript is you can use the JavaScript tools you already know to help build your native iOS and Android apps. In my case I recently added two tools I was already familiar with—JSHint and JSCS—to my NativeScript apps to automate linting. In this article I’ll show you how to do it too.

Setting up Gulp

There are a number of tools you can use to automate tasks in NativeScript apps—including Gulp, Grunt, and even npm—but I’m a fan of Gulp so that’s what I’ll be using in this article. First, if you don’t have Gulp installed globally you’ll need to grab it from npm:

npm install -g gulp

Next, to start using Gulp in your NativeScript projects you need to make sure each project has a package.json file in its root:

.
├── app
│   └── ...
├── package.json ────────────── here
└── platforms
    └── ...

If your project doesn’t already have a package.json file, run npm init in the project’s root and npm will help you build one.

After that, install Gulp locally along with its JSHint and JSCS packages:

npm install gulp gulp-jshint gulp-jscs --save-dev

The --save-dev flag tells npm to remember these dependencies in your newly created package.json file. If you open your project’s package.json you should now see a "devDependencies" key at the bottom that looks something like this:

{
  ...
  "devDependencies": {
    "gulp": "^3.8.11",
    "gulp-jscs": "^1.6.0",
    "gulp-jshint": "^1.10.0"
  }
}

With the installation out the way, now it’s time to write the code that uses these tools.

Writing the task

To write a Gulp task that runs JSHint and JSCS you’ll need to create a gulpfile.js file in your project’s root:

.
├── app
│   └── ...
├── gulpfile.js ────────────── here
├── package.json
└── platforms
    └── ...

Paste the following code into your newly created gulpfile.js:

var gulp = require("gulp");
var jscs = require("gulp-jscs");
var jshint = require("gulp-jshint");

var filesToLint = [
    "app/**/*.js",

    // Exclude node modules from linting
    "!app/node_modules/**/*.js",

    // Exclude NativeScript modules from linting
    "!app/tns_modules/**/*.js"
];

gulp.task("jscs", function() {
    gulp.src(filesToLint)
        .pipe(jscs());
});

gulp.task("jshint", function() {
    return gulp.src(filesToLint)
        .pipe(jshint())
        .pipe(jshint.reporter());
});

gulp.task("lint", ["jshint", "jscs"]);

This code defines three Gulp tasks: jscs, jshint, and lint. As you might expect, gulp jscs runs JSCS, gulp jshint runs JSHint, and gulp lint runs both.

The filesToLint array specifies which files the linters should hit. By default this is set to lint all JavaScript files in the app directory—excluding any npm modules you have in the node_modules folder, and any NativeScript modules in tns_modules folder—but you may wish to customize this depending on how you’ve structured your app.

With the gulpfile.js file in place the last thing you need to do is add a few configuration files.

Configuring JSHint and JSCS

JSHint and JSCS each have comprehensive sets of options for specifying exactly how they should lint your code. To specify these options, head back to the root of your project and create two files: .jshintrc and .jscsrc:

.
├── app
│   └── ...
├── gulpfile.js
├── package.json
├── platforms
│   └── ...
├── .jshintrc ────────────── here
└── .jscsrc ────────────── here

The contents of these configuration files will depend on your personal coding preferences. I’ll share my preferences if you’d like to use them as a starting point, and you can look over JSHint’s option docs and JSCS’s option docs to configure them to your liking.

Here’s my .jshintrc:

{
  "boss": true,
  "curly": true,
  "esnext": true,
  "eqeqeq": true,
  "eqnull": true,
  "expr": true,
  "immed": true,
  "noarg": true,
  "quotmark": "double",
  "smarttabs": true,
  "trailing": true,
  "unused": true
}

And here’s my .jscsrc:

{
  "disallowMixedSpacesAndTabs": true,
  "disallowMultipleLineBreaks": true,
  "disallowMultipleSpaces": true,
  "disallowMultipleVarDecl": true,
  "disallowNamedUnassignedFunctions": true,
  "disallowNewlineBeforeBlockStatements": true,
  "disallowSpacesInCallExpression": true,
  "disallowSpacesInFunctionDeclaration": {
    "beforeOpeningRoundBrace": true
  },
  "disallowTrailingWhitespace": true,
  "esnext": true,
  "requireCommaBeforeLineBreak": true,
  "requireCurlyBraces": ["if", "else", "for", "while", "try", "catch"],
  "requireSemicolons": true,
  "requireSpaceBetweenArguments": true,
  "requireSpacesInConditionalExpression": true,
  "requireSpacesInForStatement": true,
  "requireSpacesInsideObjectBrackets": "all",
  "validateIndentation": "\t",
  "validateQuoteMarks": "\""
}

After you have these files in place head back to your terminal and run gulp lint. If all went well you should see something like this:

$ gulp lint
[12:06:49] Using gulpfile /path/to/my-project/gulpfile.js
[12:06:49] Starting 'jshint'...
[12:06:49] Starting 'jscs'...
[12:06:49] Finished 'jscs' after 84 ms
[12:06:49] Finished 'jshint' after 305 ms
[12:06:49] Starting 'lint'...
[12:06:49] Finished 'lint' after 13 μs

If you head back into your app and create a problem, such as changing var to vax, gulp lint should now point out the error:

$ gulp lint
[12:09:24] Using gulpfile /path/to/my-project/gulpfile.js
[12:09:24] Starting 'jshint'...
[12:09:24] Starting 'jscs'...
[12:09:24] Finished 'jscs' after 70 ms
[12:09:24] Finished 'jshint' after 255 ms
[12:09:24] Starting 'lint'...
[12:09:24] Finished 'lint' after 6.09 μs

events.js:72
        throw er; // Unhandled 'error' event
              ^
Error: Unexpected identifier at my-file.js :
     2 |
     3 |exports.loaded = function(args) {
     4 | vax page = args.object;
--------------^
     5 | page.bindingContext = viewModel;
     6 |};

Wrapping up

That’s it! If you have any other questions about this setup let me know in the comments. If you’re looking for an example of this setup in a real app, check out JustMeme, which is available on GitHub as well as the iOS app store.

Comments