Build a custom Javascript linter in 5 minutes
source link: https://blog.sylver.dev/build-a-custom-javascript-linter-in-5-minutes
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Build a custom Javascript linter in 5 minutes
Creating a custom linter can be a great way to enforce coding standards and detect code smells. In this tutorial, we'll use Sylver, a source code query engine to build a custom Javascript linter in just a few lines of code.
Sylver's main interface is a REPL console, in which we can load the source code of our project to query it using a SQL-like query language called SYLQ
. Once we'll have authored SYLQ
queries expressing our linting rules, we'll be able to save them into a ruleset that can be run like a traditional linter.
Installation
If sylver --version
doesn't output a version number >= 0.1.9
, go to https://sylver.dev to download a fresh copy of the software.
Starting the REPL
Starting the REPL is as simple as invoking the following command at the root of your project:
sylver query --files="src/**/*.js" --spec=https://github.com/sylver-dev/javascript.git#javascript.yaml
The REPL can be exited by pressing Ctrl+C
or typing :quit
at the prompt.
We can now execute SYLQ
queries by typing the code of the query, followed by a ;
.
For instance: to retrieve all the method definitions (denoted by the node type MethodDefinition):
match MethodDefinition;
The results of the query will be formatted as follow:
[...]
$0 [MethodDefinition src/store/createArticles.js:36:5-38:5]
$1 [MethodDefinition src/store/createArticles.js:39:5-41:5]
$2 [MethodDefinition src/store/createArticles.js:42:5-59:5]
$3 [MethodDefinition src/store/createArticles.js:60:5-77:5]
$4 [MethodDefinition src/store/createArticles.js:78:5-83:5]
$5 [MethodDefinition src/store/createArticles.js:84:5-89:5]
[...]
The code of a given method definition can be displayed by typing :print
followed by the node alias (for instance: :print $3
). The parse tree can be displayed using the :print_ast
command (for instance: :print_ast $3
).
Rule1: use of the ==
operator
For our first rule, we'd like to detect uses of the unsafe ==
operator for checking equality.
The first step is to get familiar with the tree structure of Javascript's binary expressions, so let's print a BinaryExpression
node along with its AST:
λ> match BinaryExpression;
[...]
$43 [BinaryExpression src/pages/Article/Comments.js:7:31-7:77]
[...]
λ> :print $43
currentUser.username == comment.author.username
λ> :print_ast $43
BinaryExpression {
. ● left: MemberExpression {
. . ● object: Identifier { currentUser }
. . ● property: Identifier { username }
. }
. ● operator: EqEq { == }
. ● right: MemberExpression {
. . ● object: MemberExpression {
. . . ● object: Identifier { comment }
. . . ● property: Identifier { author }
. . }
. . ● property: Identifier { username }
. }
}
It appears that the nodes violating our rule are the BinaryExpression
nodes
for which the operator
field contains an EqEq
node.
This can be easily expressed in SYLQ
:
match BinaryExpression(operator: EqEq);
Rule2: functions with too many parameters
For our second linting rule, we'd like to identify functions that have more than 6 parameters.
Here is the relevant part of the parse tree of a Function
node:
Function {
. ● async: AsyncModifier { async }
. ● name: Identifier { send }
. ● parameters: FormalParameters {
. . ● params: List {
. . . FormalParameter {
. . . . ● value: Identifier { method }
. . . }
. . . FormalParameter {
. . . . ● value: Identifier { url }
. . . }
. . . FormalParameter {
. . . . ● value: Identifier { data }
. . . }
. . . FormalParameter {
. . . . ● value: Identifier { resKey }
. . . }
. . }
. }
. ● body: StatementBlock {
[...]
Function parameters are represented by FormalParameters
nodes with a params
field containing the actual function parameters. In our query, the condition
regarding the length of the params
list can be specified in a when
clause, as follows:
match f@FormalParameters when f.params.length > 6;
Rule3: JSX 'img' elements without an 'alt' attribute
For our last rule, we'd like to identify <img>
elements that miss the alt
attribute. img
elements are self-closing, so we'll start by looking at the parse tree of a JsxSelfClosingElement
node:
λ> match JsxSelfClosingElement;
[...]
$73 [JsxSelfClosingElement src/pages/Article/Comments.js:21:11-21:55]
[...]
λ> :print $73
<img src={image} class="comment-author-img"/>
λ> :print_ast $73
JsxSelfClosingElement {
. ● name: Identifier { img }
. ● attribute: List {
. . JsxAttribute {
. . . ● name: Identifier { src }
. . . ● value: JsxExpression {
. . . . Identifier { image }
. . . }
. . }
. . JsxAttribute {
. . . ● name: Identifier { class }
. . . ● value: String { "comment-author-img" }
. . }
. }
}
In order to find the img
elements that have no JsxAttribute with named alt
in their attribute
list, we can use a list quantifying expression, as illustrated in the following query:
match j@JsxSelfClosingElement(name: "img")
when no j.attribute match JsxAttribute(name: "alt");
Creating the ruleset
The following ruleset uses our linting rules:
id: customLinter
language: "https://github.com/sylver-dev/javascript.git#javascript.yaml"
rules:
- id: unsafeEq
message: equality comparison with `==` operator
severity: warning
query: "match BinaryExpression(operator: EqEq)"
- id: tooManyParams
message: function has too many parameters
severity: info
note: According to our style guide, functions should have less than 6 parameters.
query: match f@FormalParameters when f.params.length > 6
- id: missingAlt
message: <img> tags should have an "alt" attribute
severity: error
query:
"match j@JsxSelfClosingElement(name: 'img')
when no j.attribute match JsxAttribute(name: 'alt')"
Assuming that it is stored in a file called custom_linter.yaml
at the root of our project, we can run it with the following command:
sylver ruleset run --files="src/**/*.js" --rulesets=custom_linter.yaml
Getting updates
For more informations about new features and/or cool SYLQ
one-liners, connect with Sylver on Twitter or Discord!
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK