Using Command Line Tools to Aid Development — Part 3 (Linting)

Living The Dream
10 min readDec 10, 2019
Photo by averie woodard on Unsplash

Part 3 of this series takes us away from quick text processing to a bigger concept.

What is linting?

No, don’t print your code off and stick it in the tumble dryer.

Taken from Wiki, the definition of software linting is as follows:

Lint, or a linter, is a tool that analyzes source code to flag programming errors, bugs, stylistic errors, and suspicious constructs.[1] The term originates from a Unix utility that examined C language source code.[2]

So the idea behind it is fairly basic to understand, its a tool you can use to analyse your source code (before compilation/interpretation) to enforce good coding practice, find potential bugs, correct/flag styling issues etc.

When the first lint program was created it was all about coding in C or similar compiled languages. Nowadays the pool of different languages & paradigms is much larger — so what if you want to lint your Node.js server source? or your Python Machine Learning model? Or the new crate you want to publish in Rust?

Luckily, most modern languages all have a de-facto linter packing their own language specific rulesets and I will first explain a bit about how linters usually work then I will show an example from a very popular language & a list of the most popular linters for most modern day languages.

A little wooly history

Photo by Guillaume LORAIN on Unsplash

Linting was first introduced in the 1980s (by Stephen C. Johnson) for the original Unix operating system, so the concept is an old one. Now after some time the C/C++ compilers caught up by introducing lint-like rules into the compiling stage to help people with less need for the external tool. (See end of article!)

Linters had to get more creative as languages evolved at an extreme rate, by the time we made it to 2000 (& beyond!) they started introducing the functionality we see today that even help you name variables & indent your code properly. When interpreted languages such as Perl, Python, Ruby etc became very popular there was even more need to ‘lint’ source code because the compiling stage was no longer needed before execution — meaning that you would skip the part of the development process that tells you about possible warnings/errors that will occur during runtime.

Despite this many developers today do still not know about the many command-line based linting tools that exist for 99% of languages. Even more so some developers have their code linted automatically by build processes, packers (Webpack is a lovable offender) and such without even realising what they do in the background. So whichever camp your in, you should know about this stuff.

How does it work?

A real quick breakdown of how linting works:

  • It opens each code file one-by-one.
  • It goes down the file usually line-by-line. (It also parses language specific stuff like indents, functions, code blocks, loops etc for multi-line applicable rules)
  • Each line gets parsed and checked against ‘rulesets’
  • The rulesets are configurable to your liking/standards and usually are stored in configuration/markup files that contributors come up with containing all sorts of rules such as code styling, naming conventions, unused variables, runtime errors etc.
  • If a line matches a rule, it is logged by the linter with its file/line location and the rule it triggered.
  • After all the code you specified or the whole project has been ‘linted’, the results are displayed to you usually then & there with the required information to go find every rule or style break and fix them up — alternatively you can let certain implementations loose and allow auto-fixing in-place or in new file copies.
  • Results can be made into reports for large projects or codebases that need extensive review usually in common formats like HTML, XML & JSON.
  • Most implementations will support a variety of in-code or in-project/global configuration for ignoring the odd line or the entire project with certain warnings or errors, useful for the exceptions to the rules.

This should be enough of an explanation for you to understand the language specific example I’ll get into now, it's not necessary to get into the depths of how they really crawl the code as that can change per language as well depending on if its compile or “onsave” methods parsing the files.

Lets get linty

For the first example I’ve chosen the most popular language at this point in time — Javascript. Everyone knows Javascript and now it has server side capabilities and frameworks for UI such as React.js, Angular.js and Vue.js it has become unstoppably popular among front & back & full stack developers in recent years.

Some of these modern JS frameworks actually enforce code linting without you really noticing unless you know what it is — we will do an example using the current most popular JS linter which is ESLint.

ESLint is an open source project originally created by Nicholas C. Zakas in June 2013. Its goal is to provide a pluggable linting utility for JavaScript.

ESLint is used by frameworks such as React.js to automatically keep your code in check, it has full support for the JSX variant to help even further.

First off you better have it installed, its readily available on NPM and its best to have it globally installed so you can easily use it to check any code in any project.

Locally (Recommended):

livethedream$ npm install eslint --save-dev

# or

livethedream$ yarn add eslint --dev

Globally:

livethedream$ npm install -g eslint
/usr/local/bin/eslint -> /usr/local/lib/node_modules/eslint/bin/eslint.js
+ eslint@5.16.0
added 117 packages from 70 contributors in 8.917s
livethedream$

Once that has completed, you should be able to run it and see its options.

livethedream$ eslint --help
eslint [options] file.js [file.js] [dir]
...lots of options here...

It will print out its full set of command line options, but that only scratches the surface of this modular wonder of enforced code perfection. It is mainly configured by the use of .eslintrc files either in the directory or globally depending on how you installed it earlier in the process.

If you installed it locally as a development dependency then you can run it anytime using npx:

livethedream$ npx eslint --init
^ This creates your local .eslintrc file.
livethedream$ npx eslint file.js
^ This runs the linter on the specified file.
livethedream$ eslint --init
^ Everything after the command is the same global or not, you can just remove npx prefixing if you have it globally installed.

When you run eslint init, it asks you a set of questions & criteria to specify and then it generates your file, 90% of cases you will only need to do this once and never dig into the files themselves unless you have to customise one of the very popular existing rulesets.

eslint — init

After the ‘core’ ruleset always included by default, the other three main rulesets for ESLint are Airbnb, Standard and Google, both maintained to a high standard at the time of writing, your in capable hands with all of them and of course you can modify the rulesets or ignore certain rules and tweak until your heart’s content. They are designed to drop on top of the default set to create the final base rules, focusing more on style & convention than actual errors.

ESLint and most Linters worth their Wool (I know, lame) support .eslintignore files that you can use in the exact same fashion as .gitignore to specify list of files/directories to not be parsed under & in the directory the ignore file is placed.

Right, you’ve installed & initialised your environment, setup your npm project for linting and presumably you’ve got some code to be parsed, lets see what happens if we purposely wind up ESLint.

Let’s take a look at a simple example of it in action, using the Google ruleset and Node as the target environment. I made a quick file lintme.js:

const lintMePlease = () => {
console.log('LINT ME');
}

If you just paste this in your browser’s Javascript console, it would cleanly be accepted in any modern browser that supports arrow functions and const (ES6+ ECMA spec) — but linters don’t just fix errors in code, they also enforce styling & convention based on the rulesets which by design can be rather strict.

Lets run ESLint and see the output, I generated an image showing the output so I could show how ESLint formats & colours its output in your console to make it easier to read and get the jist of the results.

livethedream$ npx eslint lintme.js
Linters usually format their output as best as possible in the terminal, or even generate output files in formats such as HTML, XML & JSON.

It is best to run Linters without fixing on your first runs to correct everything you want to correct manually first, to set ignores for any specific use cases you can’t change and the like — then you can take advantage of the fix option to in-place edit the code as it goes fixing what it can from its rulesets.

livethedream$ npx eslint --help
Fixing problems:
--fix Automatically fix problems
--fix-dry-run Automatically fix problems without
saving the changes to the file system
--fix-type Array Specify the types of fixes to apply (problem, suggestion, layout)
livethedream$ npx eslint --fix lintme.js
lintme.js
1:7 error 'lintMePlease' is assigned a value but never used no-unused-vars
✖ 1 problem (1 error, 0 warnings)
livethedream$

It automatically fixed the semicolon error which is an easy one of course, but it has to leave the no-unused-vars error because it cannot make up logic or code only refactor, optimise & style existing code.

Using Ignores

Let’s see how we can use ignores to make the file pass the final check of the file, there is a massive list of all the usual code level problems in the default ESLint ruleset (separate to the selected 3rd party Github repository rules you chose during init) page here. It denotes which can be fixed automatically and which require manual modification, sometimes though you just need to turn off one rule for one line, block, file or folder of a project, which is where ignores come in.

ESLint and most popular linters allow certain comments or directives to disable XYZ for N lines or files etc, this is our new file now using eslint-disable-next-line to ensure it now passes along with its already in-place fixed semicolon.

// eslint-disable-next-line
const lintMePlease = () => {
console.log('LINT ME');
};

This results in no output, which for linting is good news meaning it passed all its checks and fixed anything it was configured too by default!

I know this guide concentrates solely on ESLint in the examples, but I created it as more of a general guide that had to be kept short and so I chose the Linter that was most popular installed on people’s editors on the current most popular language (Javascript) to target the largest amount of people — while keeping in mind that all the concepts explained and the examples all follow the basic principles of how these programs execute and how they are operated. With documentation you will be able to adapt the theory to any language/linting combination.

Getting Linty in your editor

Sublime Text Editor | Photo by Ilya Pavlov on Unsplash

I have recently converted fully to Visual Studio Code for my editor and it has fantastic support in its plugins library for all the primary linters for each language you could think of. Once I had ESLint installed globally by npm it was only a couple of clicks to get VSCode running all .js files through ESLint upon save using the .eslintrc files in workspace folders as well, allowing for the typical customisation without limits.

Other editors like Sublime Text & Atom also have good support for popular language/linter combinations through their own community supported plugin & package systems — you’d be lucky to find a language & linter that you can’t automatically setup in your preferred environment in just a few minutes.

Make sure your editor’s formatting package is prepared to work alongside the re-formatting carried out by the Linter, a common combination is ESLint and Prettier formatter on VSCode.

Getting Linty at compile-time

With modern compilers such as the standard Rust & Go compilers or modern C++ compilers (especially commercial ones) include linting in the functionality of the compiler — they also can help catch errors, memory leaks or other possible runtime issues.

It's not unheard of to run code through a 3rd party strict ruleset first to enforce your own higher level guidelines and then compile-time rulesets as standard with the compilation process.

Getting Pipe-Linty

As a quick bonus, I will cover how you can easily pipe from any command straight into ESLint and have it fix in-place before returning the code to the stdout output (so it can be piped on further instead of being written to a file), ESLint specifies the use of the following options to achieve it, creating a pipe’d JSON object reporting the findings and new code:

livethedream$ cat lintme.js | eslint --stdin --fix-dry-run --format=json | python -m json.tool
[
{
"errorCount": 1,
"filePath": "<text>",
"fixableErrorCount": 0,
"fixableWarningCount": 0,
"messages": [
{
"column": 7,
"endColumn": 19,
"endLine": 1,
"line": 1,
"message": "'lintMePlease' is assigned a value but never used.",
"nodeType": "Identifier",
"ruleId": "no-unused-vars",
"severity": 2
}
],
"output": "const lintMePlease = () => {\n console.log('LINT ME');\n};\n",
"warningCount": 0
}
]

Linted List

Here is a handy list I’ve made up of each popular modern language and its associated most popular linter(s) (up to 3):

Thank you for listening to all my ranting, I hope this new part of my old series will help at least someone out there! Feel free to provide feedback or email me at root @ livingthedream.fun — I am currently working fulltime as a lead developer again but I wish to get back into article writing more often and I am openly looking for a Medium Publication if anyone sees this and thinks I’d be a good fit.

Happy Linting!

--

--

Living The Dream

Software/Web Development with data science, multiple disciplines and cynicism thrown in.