Skip to content
This repository has been archived by the owner on Aug 4, 2020. It is now read-only.

Add babel semi #121

Merged
merged 2 commits into from
Feb 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ original ones as well!).
"babel/object-curly-spacing": 1,
"babel/no-await-in-loop": 1,
"babel/flow-object-type": 1,
"babel/no-invalid-this": 1
"babel/no-invalid-this": 1,
"babel/semi": 1
}
}
```
Expand All @@ -45,6 +46,7 @@ Each rule corresponds to a core `eslint` rule, and has the same options.
- `babel/new-cap`: Ignores capitalized decorators (`@Decorator`)
- `babel/object-curly-spacing`: doesn't complain about `export x from "mod";` or `export * as x from "mod";` (🛠 )
- `babel/no-invalid-this`: doesn't fail when inside class properties (`class A { a = this.b; }`)
- `babel/semi`: Includes class properties (🛠 )

The following rules are not in `eslint`, but are relevant only to syntax that is not specified by
the current JavaScript standard or supported by `eslint`.
Expand Down
2 changes: 2 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module.exports = {
'flow-object-type': require('./rules/flow-object-type'),
'func-params-comma-dangle': require('./rules/func-params-comma-dangle'),
'no-invalid-this': require('./rules/no-invalid-this'),
'semi': require('./rules/semi'),
},
rulesConfig: {
'generator-star-spacing': 0,
Expand All @@ -24,5 +25,6 @@ module.exports = {
'flow-object-type': 0,
'func-params-comma-dangle': 0,
'no-invalid-this': 0,
'semi': 0,
}
};
225 changes: 225 additions & 0 deletions rules/semi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
/**
* @fileoverview Rule to flag missing semicolons.
* @author Nicholas C. Zakas
*/
"use strict";

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = {
meta: {
docs: {
description: "require or disallow semicolons instead of ASI",
category: "Stylistic Issues",
recommended: false
},

fixable: "code",

schema: {
anyOf: [
{
type: "array",
items: [
{
enum: ["never"]
}
],
minItems: 0,
maxItems: 1
},
{
type: "array",
items: [
{
enum: ["always"]
},
{
type: "object",
properties: {
omitLastInOneLineBlock: { type: "boolean" }
},
additionalProperties: false
}
],
minItems: 0,
maxItems: 2
}
]
}
},

create(context) {

const OPT_OUT_PATTERN = /^[-[(/+`]/; // One of [(/+-`
const options = context.options[1];
const never = context.options[0] === "never",
exceptOneLine = options && options.omitLastInOneLineBlock === true,
sourceCode = context.getSourceCode();

//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------

/**
* Reports a semicolon error with appropriate location and message.
* @param {ASTNode} node The node with an extra or missing semicolon.
* @param {boolean} missing True if the semicolon is missing.
* @returns {void}
*/
function report(node, missing) {
const lastToken = sourceCode.getLastToken(node);
let message,
fix,
loc = lastToken.loc;

if (!missing) {
message = "Missing semicolon.";
loc = loc.end;
fix = function(fixer) {
return fixer.insertTextAfter(lastToken, ";");
};
} else {
message = "Extra semicolon.";
loc = loc.start;
fix = function(fixer) {
return fixer.remove(lastToken);
};
}

context.report({
node,
loc,
message,
fix
});

}

/**
* Checks whether a token is a semicolon punctuator.
* @param {Token} token The token.
* @returns {boolean} True if token is a semicolon punctuator.
*/
function isSemicolon(token) {
return (token.type === "Punctuator" && token.value === ";");
}

/**
* Check if a semicolon is unnecessary, only true if:
* - next token is on a new line and is not one of the opt-out tokens
* - next token is a valid statement divider
* @param {Token} lastToken last token of current node.
* @returns {boolean} whether the semicolon is unnecessary.
*/
function isUnnecessarySemicolon(lastToken) {
if (!isSemicolon(lastToken)) {
return false;
}

const nextToken = sourceCode.getTokenAfter(lastToken);

if (!nextToken) {
return true;
}

const lastTokenLine = lastToken.loc.end.line;
const nextTokenLine = nextToken.loc.start.line;
const isOptOutToken = OPT_OUT_PATTERN.test(nextToken.value) && nextToken.value !== "++" && nextToken.value !== "--";
const isDivider = (nextToken.value === "}" || nextToken.value === ";");

return (lastTokenLine !== nextTokenLine && !isOptOutToken) || isDivider;
}

/**
* Checks a node to see if it's in a one-liner block statement.
* @param {ASTNode} node The node to check.
* @returns {boolean} whether the node is in a one-liner block statement.
*/
function isOneLinerBlock(node) {
const nextToken = sourceCode.getTokenAfter(node);

if (!nextToken || nextToken.value !== "}") {
return false;
}

const parent = node.parent;

return parent && parent.type === "BlockStatement" &&
parent.loc.start.line === parent.loc.end.line;
}

/**
* Checks a node to see if it's followed by a semicolon.
* @param {ASTNode} node The node to check.
* @returns {void}
*/
function checkForSemicolon(node) {
const lastToken = sourceCode.getLastToken(node);

if (never) {
if (isUnnecessarySemicolon(lastToken)) {
report(node, true);
}
} else {
if (!isSemicolon(lastToken)) {
if (!exceptOneLine || !isOneLinerBlock(node)) {
report(node);
}
} else {
if (exceptOneLine && isOneLinerBlock(node)) {
report(node, true);
}
}
}
}

/**
* Checks to see if there's a semicolon after a variable declaration.
* @param {ASTNode} node The node to check.
* @returns {void}
*/
function checkForSemicolonForVariableDeclaration(node) {
const ancestors = context.getAncestors(),
parentIndex = ancestors.length - 1,
parent = ancestors[parentIndex];

if ((parent.type !== "ForStatement" || parent.init !== node) &&
(!/^For(?:In|Of)Statement/.test(parent.type) || parent.left !== node)
) {
checkForSemicolon(node);
}
}

//--------------------------------------------------------------------------
// Public API
//--------------------------------------------------------------------------

return {
VariableDeclaration: checkForSemicolonForVariableDeclaration,
ExpressionStatement: checkForSemicolon,
ReturnStatement: checkForSemicolon,
ThrowStatement: checkForSemicolon,
DoWhileStatement: checkForSemicolon,
DebuggerStatement: checkForSemicolon,
BreakStatement: checkForSemicolon,
ContinueStatement: checkForSemicolon,
ImportDeclaration: checkForSemicolon,
ExportAllDeclaration: checkForSemicolon,
ClassProperty: checkForSemicolon,
ExportNamedDeclaration(node) {
if (!node.declaration) {
checkForSemicolon(node);
}
},
ExportDefaultDeclaration(node) {
if (!/(?:Class|Function)Declaration/.test(node.declaration.type)) {
checkForSemicolon(node);
}
}
};

}
};
Loading