mirror of https://github.com/lukechilds/node.git
Browse Source
Update ESLint and configuration to version 4.0.0. Backport-PR-URL: https://github.com/nodejs/node/pull/14340 PR-URL: https://github.com/nodejs/node/pull/13645 Reviewed-By: Teddy Katz <teddy.katz@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Refael Ackermann <refack@gmail.com> Reviewed-By: Sam Roberts <vieuxtech@gmail.com> Reviewed-By: Alexey Orlenko <eaglexrlnk@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>v6.x
Rich Trott
8 years ago
committed by
Myles Borins
1031 changed files with 36736 additions and 42098 deletions
File diff suppressed because it is too large
@ -0,0 +1,15 @@ |
|||||
|
{ |
||||
|
"type": "object", |
||||
|
"properties": { |
||||
|
"root": { "type": "boolean" }, |
||||
|
"globals": { "type": ["object"] }, |
||||
|
"parser": { "type": ["string", "null"] }, |
||||
|
"env": { "type": "object" }, |
||||
|
"plugins": { "type": ["array"] }, |
||||
|
"settings": { "type": "object" }, |
||||
|
"extends": { "type": ["string", "array"] }, |
||||
|
"rules": { "type": "object" }, |
||||
|
"parserOptions": { "type": "object" } |
||||
|
}, |
||||
|
"additionalProperties": false |
||||
|
} |
@ -0,0 +1,33 @@ |
|||||
|
/** |
||||
|
* @fileoverview Default config options |
||||
|
* @author Teddy Katz |
||||
|
*/ |
||||
|
|
||||
|
"use strict"; |
||||
|
|
||||
|
/** |
||||
|
* Freezes an object and all its nested properties |
||||
|
* @param {Object} obj The object to deeply freeze |
||||
|
* @returns {Object} `obj` after freezing it |
||||
|
*/ |
||||
|
function deepFreeze(obj) { |
||||
|
if (obj === null || typeof obj !== "object") { |
||||
|
return obj; |
||||
|
} |
||||
|
|
||||
|
Object.keys(obj).map(key => obj[key]).forEach(deepFreeze); |
||||
|
return Object.freeze(obj); |
||||
|
} |
||||
|
|
||||
|
module.exports = deepFreeze({ |
||||
|
env: {}, |
||||
|
globals: {}, |
||||
|
rules: {}, |
||||
|
settings: {}, |
||||
|
parser: "espree", |
||||
|
parserOptions: { |
||||
|
ecmaVersion: 5, |
||||
|
sourceType: "script", |
||||
|
ecmaFeatures: {} |
||||
|
} |
||||
|
}); |
@ -0,0 +1,235 @@ |
|||||
|
/** |
||||
|
* @fileoverview Rule to enforce linebreaks after open and before close array brackets |
||||
|
* @author Jan Peer Stöcklmair <https://github.com/JPeer264>
|
||||
|
*/ |
||||
|
|
||||
|
"use strict"; |
||||
|
|
||||
|
const astUtils = require("../ast-utils"); |
||||
|
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
// Rule Definition
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
|
||||
|
module.exports = { |
||||
|
meta: { |
||||
|
docs: { |
||||
|
description: "enforce linebreaks after opening and before closing array brackets", |
||||
|
category: "Stylistic Issues", |
||||
|
recommended: false |
||||
|
}, |
||||
|
fixable: "whitespace", |
||||
|
schema: [ |
||||
|
{ |
||||
|
oneOf: [ |
||||
|
{ |
||||
|
enum: ["always", "never"] |
||||
|
}, |
||||
|
{ |
||||
|
type: "object", |
||||
|
properties: { |
||||
|
multiline: { |
||||
|
type: "boolean" |
||||
|
}, |
||||
|
minItems: { |
||||
|
type: ["integer", "null"], |
||||
|
minimum: 0 |
||||
|
} |
||||
|
}, |
||||
|
additionalProperties: false |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
|
||||
|
create(context) { |
||||
|
const sourceCode = context.getSourceCode(); |
||||
|
|
||||
|
|
||||
|
//----------------------------------------------------------------------
|
||||
|
// Helpers
|
||||
|
//----------------------------------------------------------------------
|
||||
|
|
||||
|
/** |
||||
|
* Normalizes a given option value. |
||||
|
* |
||||
|
* @param {string|Object|undefined} option - An option value to parse. |
||||
|
* @returns {{multiline: boolean, minItems: number}} Normalized option object. |
||||
|
*/ |
||||
|
function normalizeOptionValue(option) { |
||||
|
let multiline = false; |
||||
|
let minItems = 0; |
||||
|
|
||||
|
if (option) { |
||||
|
if (option === "always" || option.minItems === 0) { |
||||
|
minItems = 0; |
||||
|
} else if (option === "never") { |
||||
|
minItems = Number.POSITIVE_INFINITY; |
||||
|
} else { |
||||
|
multiline = Boolean(option.multiline); |
||||
|
minItems = option.minItems || Number.POSITIVE_INFINITY; |
||||
|
} |
||||
|
} else { |
||||
|
multiline = true; |
||||
|
minItems = Number.POSITIVE_INFINITY; |
||||
|
} |
||||
|
|
||||
|
return { multiline, minItems }; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Normalizes a given option value. |
||||
|
* |
||||
|
* @param {string|Object|undefined} options - An option value to parse. |
||||
|
* @returns {{ArrayExpression: {multiline: boolean, minItems: number}, ArrayPattern: {multiline: boolean, minItems: number}}} Normalized option object. |
||||
|
*/ |
||||
|
function normalizeOptions(options) { |
||||
|
const value = normalizeOptionValue(options); |
||||
|
|
||||
|
return { ArrayExpression: value, ArrayPattern: value }; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Reports that there shouldn't be a linebreak after the first token |
||||
|
* @param {ASTNode} node - The node to report in the event of an error. |
||||
|
* @param {Token} token - The token to use for the report. |
||||
|
* @returns {void} |
||||
|
*/ |
||||
|
function reportNoBeginningLinebreak(node, token) { |
||||
|
context.report({ |
||||
|
node, |
||||
|
loc: token.loc, |
||||
|
message: "There should be no linebreak after '['.", |
||||
|
fix(fixer) { |
||||
|
const nextToken = sourceCode.getTokenAfter(token, { includeComments: true }); |
||||
|
|
||||
|
if (astUtils.isCommentToken(nextToken)) { |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
return fixer.removeRange([token.range[1], nextToken.range[0]]); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Reports that there shouldn't be a linebreak before the last token |
||||
|
* @param {ASTNode} node - The node to report in the event of an error. |
||||
|
* @param {Token} token - The token to use for the report. |
||||
|
* @returns {void} |
||||
|
*/ |
||||
|
function reportNoEndingLinebreak(node, token) { |
||||
|
context.report({ |
||||
|
node, |
||||
|
loc: token.loc, |
||||
|
message: "There should be no linebreak before ']'.", |
||||
|
fix(fixer) { |
||||
|
const previousToken = sourceCode.getTokenBefore(token, { includeComments: true }); |
||||
|
|
||||
|
if (astUtils.isCommentToken(previousToken)) { |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
return fixer.removeRange([previousToken.range[1], token.range[0]]); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Reports that there should be a linebreak after the first token |
||||
|
* @param {ASTNode} node - The node to report in the event of an error. |
||||
|
* @param {Token} token - The token to use for the report. |
||||
|
* @returns {void} |
||||
|
*/ |
||||
|
function reportRequiredBeginningLinebreak(node, token) { |
||||
|
context.report({ |
||||
|
node, |
||||
|
loc: token.loc, |
||||
|
message: "A linebreak is required after '['.", |
||||
|
fix(fixer) { |
||||
|
return fixer.insertTextAfter(token, "\n"); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Reports that there should be a linebreak before the last token |
||||
|
* @param {ASTNode} node - The node to report in the event of an error. |
||||
|
* @param {Token} token - The token to use for the report. |
||||
|
* @returns {void} |
||||
|
*/ |
||||
|
function reportRequiredEndingLinebreak(node, token) { |
||||
|
context.report({ |
||||
|
node, |
||||
|
loc: token.loc, |
||||
|
message: "A linebreak is required before ']'.", |
||||
|
fix(fixer) { |
||||
|
return fixer.insertTextBefore(token, "\n"); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Reports a given node if it violated this rule. |
||||
|
* |
||||
|
* @param {ASTNode} node - A node to check. This is an ObjectExpression node or an ObjectPattern node. |
||||
|
* @param {{multiline: boolean, minItems: number}} options - An option object. |
||||
|
* @returns {void} |
||||
|
*/ |
||||
|
function check(node) { |
||||
|
const elements = node.elements; |
||||
|
const normalizedOptions = normalizeOptions(context.options[0]); |
||||
|
const options = normalizedOptions[node.type]; |
||||
|
const openBracket = sourceCode.getFirstToken(node); |
||||
|
const closeBracket = sourceCode.getLastToken(node); |
||||
|
const firstIncComment = sourceCode.getTokenAfter(openBracket, { includeComments: true }); |
||||
|
const lastIncComment = sourceCode.getTokenBefore(closeBracket, { includeComments: true }); |
||||
|
const first = sourceCode.getTokenAfter(openBracket); |
||||
|
const last = sourceCode.getTokenBefore(closeBracket); |
||||
|
|
||||
|
const needsLinebreaks = ( |
||||
|
elements.length >= options.minItems || |
||||
|
( |
||||
|
options.multiline && |
||||
|
elements.length > 0 && |
||||
|
firstIncComment.loc.start.line !== lastIncComment.loc.end.line |
||||
|
) |
||||
|
); |
||||
|
|
||||
|
/* |
||||
|
* Use tokens or comments to check multiline or not. |
||||
|
* But use only tokens to check whether linebreaks are needed. |
||||
|
* This allows: |
||||
|
* var arr = [ // eslint-disable-line foo
|
||||
|
* 'a' |
||||
|
* ] |
||||
|
*/ |
||||
|
|
||||
|
if (needsLinebreaks) { |
||||
|
if (astUtils.isTokenOnSameLine(openBracket, first)) { |
||||
|
reportRequiredBeginningLinebreak(node, openBracket); |
||||
|
} |
||||
|
if (astUtils.isTokenOnSameLine(last, closeBracket)) { |
||||
|
reportRequiredEndingLinebreak(node, closeBracket); |
||||
|
} |
||||
|
} else { |
||||
|
if (!astUtils.isTokenOnSameLine(openBracket, first)) { |
||||
|
reportNoBeginningLinebreak(node, openBracket); |
||||
|
} |
||||
|
if (!astUtils.isTokenOnSameLine(last, closeBracket)) { |
||||
|
reportNoEndingLinebreak(node, closeBracket); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//----------------------------------------------------------------------
|
||||
|
// Public
|
||||
|
//----------------------------------------------------------------------
|
||||
|
|
||||
|
return { |
||||
|
ArrayPattern: check, |
||||
|
ArrayExpression: check |
||||
|
}; |
||||
|
} |
||||
|
}; |
@ -0,0 +1,230 @@ |
|||||
|
/** |
||||
|
* @fileoverview Rule to enforce line breaks after each array element |
||||
|
* @author Jan Peer Stöcklmair <https://github.com/JPeer264>
|
||||
|
*/ |
||||
|
|
||||
|
"use strict"; |
||||
|
|
||||
|
const astUtils = require("../ast-utils"); |
||||
|
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
// Rule Definition
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
|
||||
|
module.exports = { |
||||
|
meta: { |
||||
|
docs: { |
||||
|
description: "enforce line breaks after each array element", |
||||
|
category: "Stylistic Issues", |
||||
|
recommended: false |
||||
|
}, |
||||
|
fixable: "whitespace", |
||||
|
schema: [ |
||||
|
{ |
||||
|
oneOf: [ |
||||
|
{ |
||||
|
enum: ["always", "never"] |
||||
|
}, |
||||
|
{ |
||||
|
type: "object", |
||||
|
properties: { |
||||
|
multiline: { |
||||
|
type: "boolean" |
||||
|
}, |
||||
|
minItems: { |
||||
|
type: ["integer", "null"], |
||||
|
minimum: 0 |
||||
|
} |
||||
|
}, |
||||
|
additionalProperties: false |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
|
||||
|
create(context) { |
||||
|
const sourceCode = context.getSourceCode(); |
||||
|
|
||||
|
//----------------------------------------------------------------------
|
||||
|
// Helpers
|
||||
|
//----------------------------------------------------------------------
|
||||
|
|
||||
|
/** |
||||
|
* Normalizes a given option value. |
||||
|
* |
||||
|
* @param {string|Object|undefined} option - An option value to parse. |
||||
|
* @returns {{multiline: boolean, minItems: number}} Normalized option object. |
||||
|
*/ |
||||
|
function normalizeOptionValue(option) { |
||||
|
let multiline = false; |
||||
|
let minItems; |
||||
|
|
||||
|
option = option || "always"; |
||||
|
|
||||
|
if (option === "always" || option.minItems === 0) { |
||||
|
minItems = 0; |
||||
|
} else if (option === "never") { |
||||
|
minItems = Number.POSITIVE_INFINITY; |
||||
|
} else { |
||||
|
multiline = Boolean(option.multiline); |
||||
|
minItems = option.minItems || Number.POSITIVE_INFINITY; |
||||
|
} |
||||
|
|
||||
|
return { multiline, minItems }; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Normalizes a given option value. |
||||
|
* |
||||
|
* @param {string|Object|undefined} options - An option value to parse. |
||||
|
* @returns {{ArrayExpression: {multiline: boolean, minItems: number}, ArrayPattern: {multiline: boolean, minItems: number}}} Normalized option object. |
||||
|
*/ |
||||
|
function normalizeOptions(options) { |
||||
|
const value = normalizeOptionValue(options); |
||||
|
|
||||
|
return { ArrayExpression: value, ArrayPattern: value }; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Reports that there shouldn't be a line break after the first token |
||||
|
* @param {Token} token - The token to use for the report. |
||||
|
* @returns {void} |
||||
|
*/ |
||||
|
function reportNoLineBreak(token) { |
||||
|
const tokenBefore = sourceCode.getTokenBefore(token, { includeComments: true }); |
||||
|
|
||||
|
context.report({ |
||||
|
loc: { |
||||
|
start: tokenBefore.loc.end, |
||||
|
end: token.loc.start |
||||
|
}, |
||||
|
message: "There should be no linebreak here.", |
||||
|
fix(fixer) { |
||||
|
if (astUtils.isCommentToken(tokenBefore)) { |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
if (!astUtils.isTokenOnSameLine(tokenBefore, token)) { |
||||
|
return fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], " "); |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
* This will check if the comma is on the same line as the next element |
||||
|
* Following array: |
||||
|
* [ |
||||
|
* 1 |
||||
|
* , 2 |
||||
|
* , 3 |
||||
|
* ] |
||||
|
* |
||||
|
* will be fixed to: |
||||
|
* [ |
||||
|
* 1, 2, 3 |
||||
|
* ] |
||||
|
*/ |
||||
|
const twoTokensBefore = sourceCode.getTokenBefore(tokenBefore, { includeComments: true }); |
||||
|
|
||||
|
if (astUtils.isCommentToken(twoTokensBefore)) { |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
return fixer.replaceTextRange([twoTokensBefore.range[1], tokenBefore.range[0]], ""); |
||||
|
|
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Reports that there should be a line break after the first token |
||||
|
* @param {Token} token - The token to use for the report. |
||||
|
* @returns {void} |
||||
|
*/ |
||||
|
function reportRequiredLineBreak(token) { |
||||
|
const tokenBefore = sourceCode.getTokenBefore(token, { includeComments: true }); |
||||
|
|
||||
|
context.report({ |
||||
|
loc: { |
||||
|
start: tokenBefore.loc.end, |
||||
|
end: token.loc.start |
||||
|
}, |
||||
|
message: "There should be a linebreak after this element.", |
||||
|
fix(fixer) { |
||||
|
return fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], "\n"); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Reports a given node if it violated this rule. |
||||
|
* |
||||
|
* @param {ASTNode} node - A node to check. This is an ObjectExpression node or an ObjectPattern node. |
||||
|
* @param {{multiline: boolean, minItems: number}} options - An option object. |
||||
|
* @returns {void} |
||||
|
*/ |
||||
|
function check(node) { |
||||
|
const elements = node.elements; |
||||
|
const normalizedOptions = normalizeOptions(context.options[0]); |
||||
|
const options = normalizedOptions[node.type]; |
||||
|
|
||||
|
let elementBreak = false; |
||||
|
|
||||
|
/* |
||||
|
* MULTILINE: true |
||||
|
* loop through every element and check |
||||
|
* if at least one element has linebreaks inside |
||||
|
* this ensures that following is not valid (due to elements are on the same line): |
||||
|
* |
||||
|
* [ |
||||
|
* 1, |
||||
|
* 2, |
||||
|
* 3 |
||||
|
* ] |
||||
|
*/ |
||||
|
if (options.multiline) { |
||||
|
elementBreak = elements |
||||
|
.filter(element => element !== null) |
||||
|
.some(element => element.loc.start.line !== element.loc.end.line); |
||||
|
} |
||||
|
|
||||
|
const needsLinebreaks = ( |
||||
|
elements.length >= options.minItems || |
||||
|
( |
||||
|
options.multiline && |
||||
|
elementBreak |
||||
|
) |
||||
|
); |
||||
|
|
||||
|
elements.forEach((element, i) => { |
||||
|
const previousElement = elements[i - 1]; |
||||
|
|
||||
|
if (i === 0 || element === null || previousElement === null) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
const commaToken = sourceCode.getFirstTokenBetween(previousElement, element, astUtils.isCommaToken); |
||||
|
const lastTokenOfPreviousElement = sourceCode.getTokenBefore(commaToken); |
||||
|
const firstTokenOfCurrentElement = sourceCode.getTokenAfter(commaToken); |
||||
|
|
||||
|
if (needsLinebreaks) { |
||||
|
if (astUtils.isTokenOnSameLine(lastTokenOfPreviousElement, firstTokenOfCurrentElement)) { |
||||
|
reportRequiredLineBreak(firstTokenOfCurrentElement); |
||||
|
} |
||||
|
} else { |
||||
|
if (!astUtils.isTokenOnSameLine(lastTokenOfPreviousElement, firstTokenOfCurrentElement)) { |
||||
|
reportNoLineBreak(firstTokenOfCurrentElement); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
//----------------------------------------------------------------------
|
||||
|
// Public
|
||||
|
//----------------------------------------------------------------------
|
||||
|
|
||||
|
return { |
||||
|
ArrayPattern: check, |
||||
|
ArrayExpression: check |
||||
|
}; |
||||
|
} |
||||
|
}; |
@ -0,0 +1,105 @@ |
|||||
|
/** |
||||
|
* @fileoverview enforce "for" loop update clause moving the counter in the right direction.(for-direction) |
||||
|
* @author Aladdin-ADD<hh_2013@foxmail.com> |
||||
|
*/ |
||||
|
|
||||
|
"use strict"; |
||||
|
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
// Rule Definition
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
|
||||
|
module.exports = { |
||||
|
meta: { |
||||
|
docs: { |
||||
|
description: "enforce \"for\" loop update clause moving the counter in the right direction.", |
||||
|
category: "Possible Errors", |
||||
|
recommended: false |
||||
|
}, |
||||
|
fixable: null, |
||||
|
schema: [] |
||||
|
}, |
||||
|
|
||||
|
create(context) { |
||||
|
|
||||
|
/** |
||||
|
* report an error. |
||||
|
* @param {ASTNode} node the node to report. |
||||
|
* @returns {void} |
||||
|
*/ |
||||
|
function report(node) { |
||||
|
context.report({ |
||||
|
node, |
||||
|
message: "The update clause in this loop moves the variable in the wrong direction." |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* check UpdateExpression add/sub the counter |
||||
|
* @param {ASTNode} update UpdateExpression to check |
||||
|
* @param {string} counter variable name to check |
||||
|
* @returns {int} if add return 1, if sub return -1, if nochange, return 0 |
||||
|
*/ |
||||
|
function getUpdateDirection(update, counter) { |
||||
|
if (update.argument.type === "Identifier" && update.argument.name === counter) { |
||||
|
if (update.operator === "++") { |
||||
|
return 1; |
||||
|
} |
||||
|
if (update.operator === "--") { |
||||
|
return -1; |
||||
|
} |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* check AssignmentExpression add/sub the counter |
||||
|
* @param {ASTNode} update AssignmentExpression to check |
||||
|
* @param {string} counter variable name to check |
||||
|
* @returns {int} if add return 1, if sub return -1, if nochange, return 0 |
||||
|
*/ |
||||
|
function getAssignmentDirection(update, counter) { |
||||
|
if (update.left.name === counter) { |
||||
|
if (update.operator === "+=") { |
||||
|
return 1; |
||||
|
} |
||||
|
if (update.operator === "-=") { |
||||
|
return -1; |
||||
|
} |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
return { |
||||
|
ForStatement(node) { |
||||
|
|
||||
|
if (node.test && node.test.type === "BinaryExpression" && node.test.left.type === "Identifier" && node.update) { |
||||
|
const counter = node.test.left.name; |
||||
|
const operator = node.test.operator; |
||||
|
const update = node.update; |
||||
|
|
||||
|
if (operator === "<" || operator === "<=") { |
||||
|
|
||||
|
// report error if update sub the counter (--, -=)
|
||||
|
if (update.type === "UpdateExpression" && getUpdateDirection(update, counter) < 0) { |
||||
|
report(node); |
||||
|
} |
||||
|
|
||||
|
if (update.type === "AssignmentExpression" && getAssignmentDirection(update, counter) < 0) { |
||||
|
report(node); |
||||
|
} |
||||
|
} else if (operator === ">" || operator === ">=") { |
||||
|
|
||||
|
// report error if update add the counter (++, +=)
|
||||
|
if (update.type === "UpdateExpression" && getUpdateDirection(update, counter) > 0) { |
||||
|
report(node); |
||||
|
} |
||||
|
|
||||
|
if (update.type === "AssignmentExpression" && getAssignmentDirection(update, counter) > 0) { |
||||
|
report(node); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
}; |
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,37 @@ |
|||||
|
/** |
||||
|
* @fileoverview disallow use of the Buffer() constructor |
||||
|
* @author Teddy Katz |
||||
|
*/ |
||||
|
"use strict"; |
||||
|
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
// Rule Definition
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
|
||||
|
module.exports = { |
||||
|
meta: { |
||||
|
docs: { |
||||
|
description: "disallow use of the Buffer() constructor", |
||||
|
category: "Node.js and CommonJS", |
||||
|
recommended: false |
||||
|
}, |
||||
|
schema: [] |
||||
|
}, |
||||
|
|
||||
|
create(context) { |
||||
|
|
||||
|
//----------------------------------------------------------------------
|
||||
|
// Public
|
||||
|
//----------------------------------------------------------------------
|
||||
|
|
||||
|
return { |
||||
|
"CallExpression[callee.name='Buffer'], NewExpression[callee.name='Buffer']"(node) { |
||||
|
context.report({ |
||||
|
node, |
||||
|
message: "{{example}} is deprecated. Use Buffer.from(), Buffer.alloc(), or Buffer.allocUnsafe() instead.", |
||||
|
data: { example: node.type === "CallExpression" ? "Buffer()" : "new Buffer()" } |
||||
|
}); |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
}; |
@ -0,0 +1,587 @@ |
|||||
|
/** |
||||
|
* @fileoverview Rule to require or disallow newlines between statements |
||||
|
* @author Toru Nagashima |
||||
|
*/ |
||||
|
|
||||
|
"use strict"; |
||||
|
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
// Requirements
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
|
||||
|
const astUtils = require("../ast-utils"); |
||||
|
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
// Helpers
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
|
||||
|
const LT = `[${Array.from(astUtils.LINEBREAKS).join("")}]`; |
||||
|
const PADDING_LINE_SEQUENCE = new RegExp( |
||||
|
String.raw`^(\s*?${LT})\s*${LT}(\s*;?)$` |
||||
|
); |
||||
|
const CJS_EXPORT = /^(?:module\s*\.\s*)?exports(?:\s*\.|\s*\[|$)/; |
||||
|
const CJS_IMPORT = /^require\(/; |
||||
|
|
||||
|
/** |
||||
|
* Creates tester which check if a node starts with specific keyword. |
||||
|
* |
||||
|
* @param {string} keyword The keyword to test. |
||||
|
* @returns {Object} the created tester. |
||||
|
* @private |
||||
|
*/ |
||||
|
function newKeywordTester(keyword) { |
||||
|
return { |
||||
|
test: (node, sourceCode) => |
||||
|
sourceCode.getFirstToken(node).value === keyword |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Creates tester which check if a node is specific type. |
||||
|
* |
||||
|
* @param {string} type The node type to test. |
||||
|
* @returns {Object} the created tester. |
||||
|
* @private |
||||
|
*/ |
||||
|
function newNodeTypeTester(type) { |
||||
|
return { |
||||
|
test: node => |
||||
|
node.type === type |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Checks the given node is an expression statement of IIFE. |
||||
|
* |
||||
|
* @param {ASTNode} node The node to check. |
||||
|
* @returns {boolean} `true` if the node is an expression statement of IIFE. |
||||
|
* @private |
||||
|
*/ |
||||
|
function isIIFEStatement(node) { |
||||
|
if (node.type === "ExpressionStatement") { |
||||
|
let call = node.expression; |
||||
|
|
||||
|
if (call.type === "UnaryExpression") { |
||||
|
call = call.argument; |
||||
|
} |
||||
|
return call.type === "CallExpression" && astUtils.isFunction(call.callee); |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Checks whether the given node is a block-like statement. |
||||
|
* This checks the last token of the node is the closing brace of a block. |
||||
|
* |
||||
|
* @param {SourceCode} sourceCode The source code to get tokens. |
||||
|
* @param {ASTNode} node The node to check. |
||||
|
* @returns {boolean} `true` if the node is a block-like statement. |
||||
|
* @private |
||||
|
*/ |
||||
|
function isBlockLikeStatement(sourceCode, node) { |
||||
|
|
||||
|
// do-while with a block is a block-like statement.
|
||||
|
if (node.type === "DoWhileStatement" && node.body.type === "BlockStatement") { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
// IIFE is a block-like statement specially from
|
||||
|
// JSCS#disallowPaddingNewLinesAfterBlocks.
|
||||
|
if (isIIFEStatement(node)) { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
// Checks the last token is a closing brace of blocks.
|
||||
|
const lastToken = sourceCode.getLastToken(node, astUtils.isNotSemicolonToken); |
||||
|
const belongingNode = astUtils.isClosingBraceToken(lastToken) |
||||
|
? sourceCode.getNodeByRangeIndex(lastToken.range[0]) |
||||
|
: null; |
||||
|
|
||||
|
return Boolean(belongingNode) && ( |
||||
|
belongingNode.type === "BlockStatement" || |
||||
|
belongingNode.type === "SwitchStatement" |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Check whether the given node is a directive or not. |
||||
|
* @param {ASTNode} node The node to check. |
||||
|
* @param {SourceCode} sourceCode The source code object to get tokens. |
||||
|
* @returns {boolean} `true` if the node is a directive. |
||||
|
*/ |
||||
|
function isDirective(node, sourceCode) { |
||||
|
return ( |
||||
|
node.type === "ExpressionStatement" && |
||||
|
( |
||||
|
node.parent.type === "Program" || |
||||
|
( |
||||
|
node.parent.type === "BlockStatement" && |
||||
|
astUtils.isFunction(node.parent.parent) |
||||
|
) |
||||
|
) && |
||||
|
node.expression.type === "Literal" && |
||||
|
typeof node.expression.value === "string" && |
||||
|
!astUtils.isParenthesised(sourceCode, node.expression) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Check whether the given node is a part of directive prologue or not. |
||||
|
* @param {ASTNode} node The node to check. |
||||
|
* @param {SourceCode} sourceCode The source code object to get tokens. |
||||
|
* @returns {boolean} `true` if the node is a part of directive prologue. |
||||
|
*/ |
||||
|
function isDirectivePrologue(node, sourceCode) { |
||||
|
if (isDirective(node, sourceCode)) { |
||||
|
for (const sibling of node.parent.body) { |
||||
|
if (sibling === node) { |
||||
|
break; |
||||
|
} |
||||
|
if (!isDirective(sibling, sourceCode)) { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Gets the actual last token. |
||||
|
* |
||||
|
* If a semicolon is semicolon-less style's semicolon, this ignores it. |
||||
|
* For example: |
||||
|
* |
||||
|
* foo() |
||||
|
* ;[1, 2, 3].forEach(bar) |
||||
|
* |
||||
|
* @param {SourceCode} sourceCode The source code to get tokens. |
||||
|
* @param {ASTNode} node The node to get. |
||||
|
* @returns {Token} The actual last token. |
||||
|
* @private |
||||
|
*/ |
||||
|
function getActualLastToken(sourceCode, node) { |
||||
|
const semiToken = sourceCode.getLastToken(node); |
||||
|
const prevToken = sourceCode.getTokenBefore(semiToken); |
||||
|
const nextToken = sourceCode.getTokenAfter(semiToken); |
||||
|
const isSemicolonLessStyle = Boolean( |
||||
|
prevToken && |
||||
|
nextToken && |
||||
|
prevToken.range[0] >= node.range[0] && |
||||
|
astUtils.isSemicolonToken(semiToken) && |
||||
|
semiToken.loc.start.line !== prevToken.loc.end.line && |
||||
|
semiToken.loc.end.line === nextToken.loc.start.line |
||||
|
); |
||||
|
|
||||
|
return isSemicolonLessStyle ? prevToken : semiToken; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* This returns the concatenation of the first 2 captured strings. |
||||
|
* @param {string} _ Unused. Whole matched string. |
||||
|
* @param {string} trailingSpaces The trailing spaces of the first line. |
||||
|
* @param {string} indentSpaces The indentation spaces of the last line. |
||||
|
* @returns {string} The concatenation of trailingSpaces and indentSpaces. |
||||
|
* @private |
||||
|
*/ |
||||
|
function replacerToRemovePaddingLines(_, trailingSpaces, indentSpaces) { |
||||
|
return trailingSpaces + indentSpaces; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Check and report statements for `any` configuration. |
||||
|
* It does nothing. |
||||
|
* |
||||
|
* @returns {void} |
||||
|
* @private |
||||
|
*/ |
||||
|
function verifyForAny() { |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Check and report statements for `never` configuration. |
||||
|
* This autofix removes blank lines between the given 2 statements. |
||||
|
* However, if comments exist between 2 blank lines, it does not remove those |
||||
|
* blank lines automatically. |
||||
|
* |
||||
|
* @param {RuleContext} context The rule context to report. |
||||
|
* @param {ASTNode} prevNode The previous node to check. |
||||
|
* @param {ASTNode} nextNode The next node to check. |
||||
|
* @param {Array<Token[]>} paddingLines The array of token pairs that blank |
||||
|
* lines exist between the pair. |
||||
|
* @returns {void} |
||||
|
* @private |
||||
|
*/ |
||||
|
function verifyForNever(context, prevNode, nextNode, paddingLines) { |
||||
|
if (paddingLines.length === 0) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
context.report({ |
||||
|
node: nextNode, |
||||
|
message: "Unexpected blank line before this statement.", |
||||
|
fix(fixer) { |
||||
|
if (paddingLines.length >= 2) { |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
const prevToken = paddingLines[0][0]; |
||||
|
const nextToken = paddingLines[0][1]; |
||||
|
const start = prevToken.range[1]; |
||||
|
const end = nextToken.range[0]; |
||||
|
const text = context.getSourceCode().text |
||||
|
.slice(start, end) |
||||
|
.replace(PADDING_LINE_SEQUENCE, replacerToRemovePaddingLines); |
||||
|
|
||||
|
return fixer.replaceTextRange([start, end], text); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Check and report statements for `always` configuration. |
||||
|
* This autofix inserts a blank line between the given 2 statements. |
||||
|
* If the `prevNode` has trailing comments, it inserts a blank line after the |
||||
|
* trailing comments. |
||||
|
* |
||||
|
* @param {RuleContext} context The rule context to report. |
||||
|
* @param {ASTNode} prevNode The previous node to check. |
||||
|
* @param {ASTNode} nextNode The next node to check. |
||||
|
* @param {Array<Token[]>} paddingLines The array of token pairs that blank |
||||
|
* lines exist between the pair. |
||||
|
* @returns {void} |
||||
|
* @private |
||||
|
*/ |
||||
|
function verifyForAlways(context, prevNode, nextNode, paddingLines) { |
||||
|
if (paddingLines.length > 0) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
context.report({ |
||||
|
node: nextNode, |
||||
|
message: "Expected blank line before this statement.", |
||||
|
fix(fixer) { |
||||
|
const sourceCode = context.getSourceCode(); |
||||
|
let prevToken = getActualLastToken(sourceCode, prevNode); |
||||
|
const nextToken = sourceCode.getFirstTokenBetween( |
||||
|
prevToken, |
||||
|
nextNode, |
||||
|
{ |
||||
|
includeComments: true, |
||||
|
|
||||
|
/** |
||||
|
* Skip the trailing comments of the previous node. |
||||
|
* This inserts a blank line after the last trailing comment. |
||||
|
* |
||||
|
* For example: |
||||
|
* |
||||
|
* foo(); // trailing comment.
|
||||
|
* // comment.
|
||||
|
* bar(); |
||||
|
* |
||||
|
* Get fixed to: |
||||
|
* |
||||
|
* foo(); // trailing comment.
|
||||
|
* |
||||
|
* // comment.
|
||||
|
* bar(); |
||||
|
* |
||||
|
* @param {Token} token The token to check. |
||||
|
* @returns {boolean} `true` if the token is not a trailing comment. |
||||
|
* @private |
||||
|
*/ |
||||
|
filter(token) { |
||||
|
if (astUtils.isTokenOnSameLine(prevToken, token)) { |
||||
|
prevToken = token; |
||||
|
return false; |
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
) || nextNode; |
||||
|
const insertText = astUtils.isTokenOnSameLine(prevToken, nextToken) |
||||
|
? "\n\n" |
||||
|
: "\n"; |
||||
|
|
||||
|
return fixer.insertTextAfter(prevToken, insertText); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Types of blank lines. |
||||
|
* `any`, `never`, and `always` are defined. |
||||
|
* Those have `verify` method to check and report statements. |
||||
|
* @private |
||||
|
*/ |
||||
|
const PaddingTypes = { |
||||
|
any: { verify: verifyForAny }, |
||||
|
never: { verify: verifyForNever }, |
||||
|
always: { verify: verifyForAlways } |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* Types of statements. |
||||
|
* Those have `test` method to check it matches to the given statement. |
||||
|
* @private |
||||
|
*/ |
||||
|
const StatementTypes = { |
||||
|
"*": { test: () => true }, |
||||
|
"block-like": { |
||||
|
test: (node, sourceCode) => isBlockLikeStatement(sourceCode, node) |
||||
|
}, |
||||
|
"cjs-export": { |
||||
|
test: (node, sourceCode) => |
||||
|
node.type === "ExpressionStatement" && |
||||
|
node.expression.type === "AssignmentExpression" && |
||||
|
CJS_EXPORT.test(sourceCode.getText(node.expression.left)) |
||||
|
}, |
||||
|
"cjs-import": { |
||||
|
test: (node, sourceCode) => |
||||
|
node.type === "VariableDeclaration" && |
||||
|
node.declarations.length > 0 && |
||||
|
Boolean(node.declarations[0].init) && |
||||
|
CJS_IMPORT.test(sourceCode.getText(node.declarations[0].init)) |
||||
|
}, |
||||
|
directive: { |
||||
|
test: isDirectivePrologue |
||||
|
}, |
||||
|
expression: { |
||||
|
test: (node, sourceCode) => |
||||
|
node.type === "ExpressionStatement" && |
||||
|
!isDirectivePrologue(node, sourceCode) |
||||
|
}, |
||||
|
"multiline-block-like": { |
||||
|
test: (node, sourceCode) => |
||||
|
node.loc.start.line !== node.loc.end.line && |
||||
|
isBlockLikeStatement(sourceCode, node) |
||||
|
}, |
||||
|
|
||||
|
block: newNodeTypeTester("BlockStatement"), |
||||
|
empty: newNodeTypeTester("EmptyStatement"), |
||||
|
|
||||
|
break: newKeywordTester("break"), |
||||
|
case: newKeywordTester("case"), |
||||
|
class: newKeywordTester("class"), |
||||
|
const: newKeywordTester("const"), |
||||
|
continue: newKeywordTester("continue"), |
||||
|
debugger: newKeywordTester("debugger"), |
||||
|
default: newKeywordTester("default"), |
||||
|
do: newKeywordTester("do"), |
||||
|
export: newKeywordTester("export"), |
||||
|
for: newKeywordTester("for"), |
||||
|
function: newKeywordTester("function"), |
||||
|
if: newKeywordTester("if"), |
||||
|
import: newKeywordTester("import"), |
||||
|
let: newKeywordTester("let"), |
||||
|
return: newKeywordTester("return"), |
||||
|
switch: newKeywordTester("switch"), |
||||
|
throw: newKeywordTester("throw"), |
||||
|
try: newKeywordTester("try"), |
||||
|
var: newKeywordTester("var"), |
||||
|
while: newKeywordTester("while"), |
||||
|
with: newKeywordTester("with") |
||||
|
}; |
||||
|
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
// Rule Definition
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
|
||||
|
module.exports = { |
||||
|
meta: { |
||||
|
docs: { |
||||
|
description: "require or disallow padding lines between statements", |
||||
|
category: "Stylistic Issues", |
||||
|
recommended: false |
||||
|
}, |
||||
|
fixable: "whitespace", |
||||
|
schema: { |
||||
|
definitions: { |
||||
|
paddingType: { |
||||
|
enum: Object.keys(PaddingTypes) |
||||
|
}, |
||||
|
statementType: { |
||||
|
anyOf: [ |
||||
|
{ enum: Object.keys(StatementTypes) }, |
||||
|
{ |
||||
|
type: "array", |
||||
|
items: { enum: Object.keys(StatementTypes) }, |
||||
|
minItems: 1, |
||||
|
uniqueItems: true, |
||||
|
additionalItems: false |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
}, |
||||
|
type: "array", |
||||
|
items: { |
||||
|
type: "object", |
||||
|
properties: { |
||||
|
blankLine: { $ref: "#/definitions/paddingType" }, |
||||
|
prev: { $ref: "#/definitions/statementType" }, |
||||
|
next: { $ref: "#/definitions/statementType" } |
||||
|
}, |
||||
|
additionalProperties: false, |
||||
|
required: ["blankLine", "prev", "next"] |
||||
|
}, |
||||
|
additionalItems: false |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
create(context) { |
||||
|
const sourceCode = context.getSourceCode(); |
||||
|
const configureList = context.options || []; |
||||
|
let scopeInfo = null; |
||||
|
|
||||
|
/** |
||||
|
* Processes to enter to new scope. |
||||
|
* This manages the current previous statement. |
||||
|
* @returns {void} |
||||
|
* @private |
||||
|
*/ |
||||
|
function enterScope() { |
||||
|
scopeInfo = { |
||||
|
upper: scopeInfo, |
||||
|
prevNode: null |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Processes to exit from the current scope. |
||||
|
* @returns {void} |
||||
|
* @private |
||||
|
*/ |
||||
|
function exitScope() { |
||||
|
scopeInfo = scopeInfo.upper; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Checks whether the given node matches the given type. |
||||
|
* |
||||
|
* @param {ASTNode} node The statement node to check. |
||||
|
* @param {string|string[]} type The statement type to check. |
||||
|
* @returns {boolean} `true` if the statement node matched the type. |
||||
|
* @private |
||||
|
*/ |
||||
|
function match(node, type) { |
||||
|
while (node.type === "LabeledStatement") { |
||||
|
node = node.body; |
||||
|
} |
||||
|
if (Array.isArray(type)) { |
||||
|
return type.some(match.bind(null, node)); |
||||
|
} |
||||
|
return StatementTypes[type].test(node, sourceCode); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Finds the last matched configure from configureList. |
||||
|
* |
||||
|
* @param {ASTNode} prevNode The previous statement to match. |
||||
|
* @param {ASTNode} nextNode The current statement to match. |
||||
|
* @returns {Object} The tester of the last matched configure. |
||||
|
* @private |
||||
|
*/ |
||||
|
function getPaddingType(prevNode, nextNode) { |
||||
|
for (let i = configureList.length - 1; i >= 0; --i) { |
||||
|
const configure = configureList[i]; |
||||
|
const matched = |
||||
|
match(prevNode, configure.prev) && |
||||
|
match(nextNode, configure.next); |
||||
|
|
||||
|
if (matched) { |
||||
|
return PaddingTypes[configure.blankLine]; |
||||
|
} |
||||
|
} |
||||
|
return PaddingTypes.any; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Gets padding line sequences between the given 2 statements. |
||||
|
* Comments are separators of the padding line sequences. |
||||
|
* |
||||
|
* @param {ASTNode} prevNode The previous statement to count. |
||||
|
* @param {ASTNode} nextNode The current statement to count. |
||||
|
* @returns {Array<Token[]>} The array of token pairs. |
||||
|
* @private |
||||
|
*/ |
||||
|
function getPaddingLineSequences(prevNode, nextNode) { |
||||
|
const pairs = []; |
||||
|
let prevToken = getActualLastToken(sourceCode, prevNode); |
||||
|
|
||||
|
if (nextNode.loc.start.line - prevToken.loc.end.line >= 2) { |
||||
|
do { |
||||
|
const token = sourceCode.getTokenAfter( |
||||
|
prevToken, |
||||
|
{ includeComments: true } |
||||
|
); |
||||
|
|
||||
|
if (token.loc.start.line - prevToken.loc.end.line >= 2) { |
||||
|
pairs.push([prevToken, token]); |
||||
|
} |
||||
|
prevToken = token; |
||||
|
|
||||
|
} while (prevToken.range[0] < nextNode.range[0]); |
||||
|
} |
||||
|
|
||||
|
return pairs; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Verify padding lines between the given node and the previous node. |
||||
|
* |
||||
|
* @param {ASTNode} node The node to verify. |
||||
|
* @returns {void} |
||||
|
* @private |
||||
|
*/ |
||||
|
function verify(node) { |
||||
|
const parentType = node.parent.type; |
||||
|
const validParent = |
||||
|
astUtils.STATEMENT_LIST_PARENTS.has(parentType) || |
||||
|
parentType === "SwitchStatement"; |
||||
|
|
||||
|
if (!validParent) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Save this node as the current previous statement.
|
||||
|
const prevNode = scopeInfo.prevNode; |
||||
|
|
||||
|
// Verify.
|
||||
|
if (prevNode) { |
||||
|
const type = getPaddingType(prevNode, node); |
||||
|
const paddingLines = getPaddingLineSequences(prevNode, node); |
||||
|
|
||||
|
type.verify(context, prevNode, node, paddingLines); |
||||
|
} |
||||
|
|
||||
|
scopeInfo.prevNode = node; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Verify padding lines between the given node and the previous node. |
||||
|
* Then process to enter to new scope. |
||||
|
* |
||||
|
* @param {ASTNode} node The node to verify. |
||||
|
* @returns {void} |
||||
|
* @private |
||||
|
*/ |
||||
|
function verifyThenEnterScope(node) { |
||||
|
verify(node); |
||||
|
enterScope(); |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
Program: enterScope, |
||||
|
BlockStatement: enterScope, |
||||
|
SwitchStatement: enterScope, |
||||
|
"Program:exit": exitScope, |
||||
|
"BlockStatement:exit": exitScope, |
||||
|
"SwitchStatement:exit": exitScope, |
||||
|
|
||||
|
":statement": verify, |
||||
|
|
||||
|
SwitchCase: verifyThenEnterScope, |
||||
|
"SwitchCase:exit": exitScope |
||||
|
}; |
||||
|
} |
||||
|
}; |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue