Socket
Socket
Sign inDemoInstall

eslint

Package Overview
Dependencies
Maintainers
1
Versions
369
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

eslint - npm Package Compare versions

Comparing version 0.18.0 to 0.19.0

lib/rules/no-continue.js

2

conf/eslint.json

@@ -22,2 +22,3 @@ {

"no-constant-condition": 2,
"no-continue": 0,
"no-control-regex": 2,

@@ -143,2 +144,3 @@ "no-debugger": 2,

"operator-assignment": [0, "always"],
"operator-linebreak": 0,
"padded-blocks": 0,

@@ -145,0 +147,0 @@ "quote-props": 0,

@@ -41,2 +41,3 @@ /**

* @property {boolean} reset True disables all default rules and environments.
* @property {boolean|object} baseConfig Base config object. False disables all default rules and environments.
* @property {boolean} ignore False disables use of .eslintignore.

@@ -73,2 +74,3 @@ * @property {string[]} rulePaths An array of directories to load custom rules from.

reset: false,
baseConfig: require(path.resolve(__dirname, "..", "conf", "eslint.json")),
rulePaths: [],

@@ -75,0 +77,0 @@ useEslintrc: true,

@@ -219,2 +219,3 @@ /**

var useConfig;
options = options || {};

@@ -226,4 +227,12 @@

this.baseConfig = options.reset ? { rules: {} } :
require(path.resolve(__dirname, "..", "conf", "eslint.json"));
if (options.reset || options.baseConfig === false) {
// If `options.reset` is truthy or `options.baseConfig` is set to `false`,
// disable all default rules and environments
this.baseConfig = { rules: {} };
} else {
// If `options.baseConfig` is an object, just use it,
// otherwise use default base config from `conf/eslint.json`
this.baseConfig = options.baseConfig ||
require(path.resolve(__dirname, "..", "conf", "eslint.json"));
}

@@ -230,0 +239,0 @@ this.baseConfig.format = options.format;

21

lib/eslint.js

@@ -27,15 +27,6 @@ /**

// Have to modify estraverse until TryStatement.handlers is removed
estraverse.VisitorKeys.TryStatement = ["block", "handlers", "finalizer"];
// TODO: Remove when estraverse is updated
estraverse.Syntax.Super = "Super";
estraverse.VisitorKeys.Super = [];
// TODO: Remove when estraverse is updated
estraverse.Syntax.ExportNamedDeclaration = "ExportNamedDeclaration";
estraverse.Syntax.ExportDefaultDeclaration = "ExportDefaultDeclaration";
estraverse.Syntax.ExportAllDeclaration = "ExportAllDeclaration";
estraverse.Syntax.ExportSpecifier = "ExportSpecifier";
estraverse.VisitorKeys.ExportNamedDeclaration = ["declaration", "specifies", "source"];
estraverse.VisitorKeys.ExportAllDeclaration = ["source"];
estraverse.VisitorKeys.ExportDefaultDeclaration = ["declaration"];
estraverse.VisitorKeys.ExportNamedDeclaration = ["declaration", "specifiers", "source"];
estraverse.VisitorKeys.ExportSpecifier = ["exported", "local"];
/**

@@ -980,3 +971,7 @@ * Parses a list of "name:boolean_value" or/and "name" options divided by comma or

if (scope) {
return scope;
if (scope.type === "function-expression-name") {
return scope.childScopes[0];
} else {
return scope;
}
}

@@ -983,0 +978,0 @@

@@ -111,3 +111,2 @@ /**

function declareClass(node) {
pushScope();

@@ -117,2 +116,4 @@ if (node.id) {

}
pushScope();
}

@@ -164,2 +165,10 @@

case "AssignmentPattern":
declareByNodeType(node.left);
break;
case "RestElement":
declareByNodeType(node.argument);
break;
// no default

@@ -172,2 +181,8 @@ }

/**
* Adds declarations of the function parameters and pushes the scope
* @param {ASTNode} node The node containing declarations.
* @returns {void}
* @private
*/
function functionHandler(node) {

@@ -180,3 +195,2 @@ pushScope();

declare(node.id ? [node.id.name] : []);
declare(node.rest ? [node.rest.name] : []);

@@ -186,2 +200,24 @@ declare(["arguments"]);

/**
* Adds declaration of the function name in its parent scope then process the function
* @param {ASTNode} node The node containing declarations.
* @returns {void}
* @private
*/
function functionDeclarationHandler(node) {
declare(node.id ? [node.id.name] : []);
functionHandler(node);
}
/**
* Process function declarations and declares its name in its own scope
* @param {ASTNode} node The node containing declarations.
* @returns {void}
* @private
*/
function functionExpressionHandler(node) {
functionHandler(node);
declare(node.id ? [node.id.name] : []);
}
function variableDeclarationHandler(node) {

@@ -238,3 +274,3 @@ node.declarations.forEach(function(declaration) {

"FunctionDeclaration": functionHandler,
"FunctionDeclaration": functionDeclarationHandler,
"FunctionDeclaration:exit": popScope,

@@ -251,5 +287,6 @@

"FunctionExpression": functionHandler,
"FunctionExpression": functionExpressionHandler,
"FunctionExpression:exit": popScope,
// Arrow functions cannot have names
"ArrowFunctionExpression": functionHandler,

@@ -256,0 +293,0 @@ "ArrowFunctionExpression:exit": popScope,

@@ -59,2 +59,7 @@ /**

// "never" check properties
if (properties === "never") {
return;
}
// Always report underscored object names

@@ -61,0 +66,0 @@ if (node.parent.object.type === "Identifier" &&

@@ -66,2 +66,11 @@ /**

/**
* Checks whether a node is contained on a single line.
* @param {ASTNode} node AST Node being evaluated.
* @returns {boolean} True if the node is a single line.
*/
function isSingleLine(node) {
return (node.loc.end.line === node.loc.start.line);
}
//------------------------------------------------------------------------------

@@ -179,2 +188,26 @@ // Rule Definition

/**
* Creates groups of properties.
* @param {ASTNode} node ObjectExpression node being evaluated.
* @returns {ASTNode[]} Groups of property AST node lists.
*/
function createGroups(node) {
if (node.properties.length === 1) {
return node.properties;
}
return node.properties.reduce(function(groups, property) {
var currentGroup = last(groups),
prev = last(currentGroup);
if (!prev || continuesPropertyGroup(prev, property)) {
currentGroup.push(property);
} else {
groups.push([property]);
}
return groups;
}, [[]]);
}
/**
* Verifies correct vertical alignment of a group of properties.

@@ -184,3 +217,3 @@ * @param {ASTNode[]} properties List of Property AST nodes.

*/
function verifyAlignment(properties) {
function verifyGroupAlignment(properties) {
var length = properties.length,

@@ -214,2 +247,43 @@ widths = properties.map(getKeyWidth), // Width of keys, including quotes

/**
* Verifies vertical alignment, taking into account groups of properties.
* @param {ASTNode} node ObjectExpression node being evaluated.
* @returns {void}
*/
function verifyAlignment(node) {
createGroups(node).forEach(function(group) {
verifyGroupAlignment(group);
});
}
/**
* Verifies spacing of property conforms to specified options.
* @param {ASTNode} node Property node being evaluated.
* @returns {void}
*/
function verifySpacing(node) {
var whitespace = getPropertyWhitespace(node);
if (whitespace) { // Object literal getters/setters lack colons
report(node, "key", whitespace.beforeColon, beforeColon);
report(node, "value", whitespace.afterColon, afterColon);
}
}
/**
* Verifies spacing of each property in a list.
* @param {ASTNode[]} properties List of Property AST nodes.
* @returns {void}
*/
function verifyListSpacing(properties) {
var length = properties.length;
for (var i = 0; i < length; i++) {
verifySpacing(properties[i]);
}
}
//--------------------------------------------------------------------------
// Public API
//--------------------------------------------------------------------------
if (align) { // Verify vertical alignment

@@ -219,16 +293,7 @@

"ObjectExpression": function(node) {
node.properties.reduce(function(groups, property) {
var currentGroup = last(groups),
prev = last(currentGroup);
if (!prev || continuesPropertyGroup(prev, property)) {
currentGroup.push(property);
} else {
groups.push([property]);
}
return groups;
}, [[]]).forEach(function(group) {
verifyAlignment(group);
});
if (isSingleLine(node)) {
verifyListSpacing(node.properties);
} else {
verifyAlignment(node);
}
}

@@ -241,7 +306,3 @@ };

"Property": function (node) {
var whitespace = getPropertyWhitespace(node);
if (whitespace) { // Object literal getters/setters lack colons
report(node, "key", whitespace.beforeColon, beforeColon);
report(node, "value", whitespace.afterColon, afterColon);
}
verifySpacing(node);
}

@@ -248,0 +309,0 @@ };

@@ -5,2 +5,3 @@ /**

* @copyright 2015 Gopal Venkatesan. All rights reserved.
* @copyright 2015 Casey Visco. All rights reserved.
*/

@@ -15,33 +16,77 @@

module.exports = function(context) {
var mode = context.options[0],
validator; // regex to validate the rule
if (mode === "never") {
validator = /\S(?:\r?\n)?\S/;
} else { // assume mode === "always"
validator = /\S(?:\r?\n){2,}\S?/;
mode = "always";
var ALWAYS_MESSAGE = "Expected blank line after variable declarations.",
NEVER_MESSAGE = "Unexpected blank line after variable declarations.";
// Default `mode` to "always". This means that invalid options will also
// be treated as "always" and the only special case is "never"
var mode = context.options[0] === "never" ? "never" : "always";
// Cache line numbers of comments for faster lookup
var comments = context.getAllComments().map(function (token) {
return token.loc.start.line;
});
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
/**
* Determine if provided keyword is a variable declaration
* @private
* @param {String} keyword - keyword to test
* @returns {Boolean} True if `keyword` is a type of var
*/
function isVar(keyword) {
return keyword === "var" || keyword === "let" || keyword === "const";
}
return {
"VariableDeclaration:exit": function(node) {
var lastToken = context.getLastToken(node),
nextToken = context.getTokenAfter(node),
// peek few characters beyond the last token (typically the semi-colon)
sourceLines = context.getSource(lastToken, 0, 3);
/**
* Checks that a blank line exists after a variable declaration when mode is
* set to "always", or checks that there is no blank line when mode is set
* to "never"
* @private
* @param {ASTNode} node - `VariableDeclaration` node to test
* @returns {void}
*/
function checkForBlankLine(node) {
var lastToken = context.getLastToken(node),
nextToken = context.getTokenAfter(node),
nextLineNum = lastToken.loc.end.line + 1,
noNextLineToken,
hasNextLineComment;
// Some coding styles like Google uses multiple `var` statements.
// So if the next token is a `var` statement don't do anything.
if (nextToken && nextToken.type === "Keyword" &&
(nextToken.value === "var" || nextToken.value === "let" || nextToken.value === "const")) {
return;
}
// Ignore if there is no following statement
if (!nextToken) {
return;
}
// Next statement is not a `var`
if (sourceLines && !sourceLines.match(validator)) {
context.report(node, "Newline is " + mode + " expected after a \"var\" statement.",
{ identifier: node.name });
}
// Some coding styles use multiple `var` statements, so do nothing if
// the next token is a `var` statement.
if (nextToken.type === "Keyword" && isVar(nextToken.value)) {
return;
}
// Next statement is not a `var`...
noNextLineToken = nextToken.loc.start.line > nextLineNum;
hasNextLineComment = comments.indexOf(nextLineNum) >= 0;
if (mode === "never" && noNextLineToken && !hasNextLineComment) {
context.report(node, NEVER_MESSAGE, { identifier: node.name });
}
if (mode === "always" && (!noNextLineToken || hasNextLineComment)) {
context.report(node, ALWAYS_MESSAGE, { identifier: node.name });
}
}
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
"VariableDeclaration": checkForBlankLine
};
};

@@ -19,5 +19,3 @@ /**

var parent = node.parent,
parentType = parent.type,
comments,
i;
parentType = parent.type;

@@ -34,8 +32,5 @@ // if the body is not empty, we can just return immediately

// any other block is only allowed to be empty, if it contains an empty comment
comments = context.getComments(node).trailing;
for (i = 0; i < comments.length; i++) {
if (comments[i].value.trim() === "empty") {
return;
}
// any other block is only allowed to be empty, if it contains a comment
if (context.getComments(node).trailing.length > 0) {
return;
}

@@ -42,0 +37,0 @@

@@ -40,5 +40,7 @@ /**

// "arguments" is a special case that has no identifiers (#1759)
variable.identifiers.length > 0
variable.identifiers.length > 0 &&
// function names are exempt
variable.defs.length && variable.defs[0].type !== "FunctionName"
) {
context.report(variable.identifiers[0], "{{a}} is already declared in the upper scope.", {a: variable.name});

@@ -67,5 +69,6 @@ }

"FunctionDeclaration": checkForShadows,
"FunctionExpression": checkForShadows
"FunctionExpression": checkForShadows,
"ArrowFunctionExpression": checkForShadows
};
};

@@ -13,3 +13,3 @@ /**

var escope = require("escope");
// none!

@@ -63,30 +63,7 @@ //------------------------------------------------------------------------------

var NOT_DEFINED_MESSAGE = "'{{name}}' is not defined.",
READ_ONLY_MESSAGE = "'{{name}}' is read only.";
var NOT_DEFINED_MESSAGE = "\"{{name}}\" is not defined.",
READ_ONLY_MESSAGE = "\"{{name}}\" is read only.";
// TODO: Remove once escope is updated
var hackedImports = [];
/**
* Temporary function to fix escope issue. Remove once we upgrade to
* escope 3.x.
* @param {ASTNode} node The import specifier node.
* @returns {void}
* @private
*/
function fixImport(node) {
var scope = context.getScope(),
variable = new escope.Variable(node.local.name, scope);
variable.defs.push(node);
scope.variables.push(variable);
hackedImports.push(variable);
}
return {
// TODO: Remove once escope is updated
"ImportSpecifier": fixImport,
"ImportNamespaceSpecifier": fixImport,
"ImportDefaultSpecifier": fixImport,
"Program:exit": function(/*node*/) {

@@ -104,7 +81,2 @@

// hack until https://github.com/eslint/eslint/issues/1968 is properly fixed
if (name === "super") {
return;
}
if (!variable) {

@@ -117,6 +89,2 @@ context.report(ref.identifier, NOT_DEFINED_MESSAGE, { name: name });

// TODO: Remove once escope is updated
globalScope.variables = globalScope.variables.filter(function (variable) {
return hackedImports.indexOf(variable) < 0;
});
}

@@ -123,0 +91,0 @@

@@ -38,3 +38,3 @@ /**

if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier)) {
context.report(node, "Unexpected dangling '_' in '" + identifier + "'.");
context.report(node, "Unexpected dangling \"_\" in \"" + identifier + "\".");
}

@@ -49,3 +49,3 @@ }

!isSpecialCaseIdentifierInVariableExpression(identifier)) {
context.report(node, "Unexpected dangling '_' in '" + identifier + "'.");
context.report(node, "Unexpected dangling \"_\" in \"" + identifier + "\".");
}

@@ -59,3 +59,3 @@ }

!isSpecialCaseIdentifierForMemberExpression(identifier)) {
context.report(node, "Unexpected dangling '_' in '" + identifier + "'.");
context.report(node, "Unexpected dangling \"_\" in \"" + identifier + "\".");
}

@@ -62,0 +62,0 @@ }

@@ -16,2 +16,5 @@ /**

var ALWAYS_MESSAGE = "Block must be padded by blank lines.",
NEVER_MESSAGE = "Block must not be padded by blank lines.";
/**

@@ -22,3 +25,3 @@ * Checks if the given non empty block node has a blank line before its first child node.

*/
function isNonEmptyBlockTopPadded(node) {
function isBlockTopPadded(node) {
var blockStart = node.loc.start.line,

@@ -42,6 +45,6 @@ first = node.body[0],

*/
function isNonEmptyBlockBottomPadded(node) {
function isBlockBottomPadded(node) {
var blockEnd = node.loc.end.line,
last = node.body[node.body.length - 1],
lastLine = last.loc.end.line,
lastLine = context.getLastToken(last).loc.end.line,
expectedLastLine = blockEnd - 2,

@@ -58,20 +61,2 @@ trailingComments = context.getComments(last).trailing;

/**
* Checks if the given non empty block node starts AND ends with a blank line.
* @param {ASTNode} node The AST node of a BlockStatement.
* @returns {boolean} Whether or not the block starts and ends with a blank line.
*/
function isNonEmptyBlockPadded(node) {
return isNonEmptyBlockTopPadded(node) && isNonEmptyBlockBottomPadded(node);
}
/**
* Checks if the given non empty block node starts OR ends with a blank line.
* @param {ASTNode} node The AST node of a BlockStatement.
* @returns {boolean} Whether or not the block starts and ends with a blank line.
*/
function hasNonEmptyBlockExtraPadding(node) {
return isNonEmptyBlockTopPadded(node) || isNonEmptyBlockBottomPadded(node);
}
/**
* Checks the given BlockStatement node to be padded if the block is not empty.

@@ -83,10 +68,22 @@ * @param {ASTNode} node The AST node of a BlockStatement.

if (node.body.length > 0) {
var blockHasTopPadding = isBlockTopPadded(node),
blockHasBottomPadding = isBlockBottomPadded(node);
if (requirePadding) {
if (!isNonEmptyBlockPadded(node)) {
context.report(node, "Block must be padded by blank lines.");
if (!blockHasTopPadding) {
context.report(node, ALWAYS_MESSAGE);
}
if (!blockHasBottomPadding) {
context.report(node, node.loc.end, ALWAYS_MESSAGE);
}
} else {
if (hasNonEmptyBlockExtraPadding(node)) {
context.report(node, "Block must not be padded by blank lines.");
if (blockHasTopPadding) {
context.report(node, NEVER_MESSAGE);
}
if (blockHasBottomPadding) {
context.report(node, node.loc.end, NEVER_MESSAGE);
}
}

@@ -93,0 +90,0 @@ }

@@ -55,3 +55,3 @@ /**

if (!(key.type === "Literal" && typeof key.value === "string")) {
if (!node.method && !(key.type === "Literal" && typeof key.value === "string")) {
context.report(node, "Unquoted property `{{key}}` found.", {

@@ -58,0 +58,0 @@ key: key.name || key.value

@@ -12,3 +12,3 @@ /**

var OPT_OUT_PATTERN = /[\[\(\/\+\-]/;
var OPT_OUT_PATTERN = /[\[\(\/\+\-]/; // One of [(/+-

@@ -22,2 +22,21 @@ var always = context.options[0] !== "never";

/**
* Reports a semicolon error with appropriate location and message.
* @param {ASTNode} node The node with an extra or missing semicolon.
* @returns {void}
*/
function report(node) {
var message = always ? "Missing semicolon." : "Extra semicolon.";
context.report(node, context.getLastToken(node).loc.end, message);
}
/**
* 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:

@@ -27,12 +46,22 @@ * - next token is on a new line and is not one of the opt-out tokens

* @param {Token} lastToken last token of current node.
* @param {Token} nextToken next token after current node.
* @returns {boolean} whether the semicolon is unnecessary.
*/
function isUnnecessarySemicolon(lastToken, nextToken) {
function isUnnecessarySemicolon(lastToken) {
var isDivider, isOptOutToken, lastTokenLine, nextToken, nextTokenLine;
var lastTokenLine = lastToken.loc.end.line,
nextTokenLine = nextToken && nextToken.loc.start.line,
isOptOutToken = nextToken && OPT_OUT_PATTERN.test(nextToken.value),
isDivider = nextToken && (nextToken.value === "}" || nextToken.value === ";");
if (!isSemicolon(lastToken)) {
return false;
}
nextToken = context.getTokenAfter(lastToken);
if (!nextToken) {
return true;
}
lastTokenLine = lastToken.loc.end.line;
nextTokenLine = nextToken.loc.start.line;
isOptOutToken = OPT_OUT_PATTERN.test(nextToken.value);
isDivider = (nextToken.value === "}" || nextToken.value === ";");
return (lastTokenLine !== nextTokenLine && !isOptOutToken) || isDivider;

@@ -47,15 +76,11 @@ }

function checkForSemicolon(node) {
var lastToken = context.getLastToken(node),
nextToken = context.getTokenAfter(node);
var lastToken = context.getLastToken(node);
if (always) {
if (lastToken.type !== "Punctuator" || lastToken.value !== ";") {
context.report(node, lastToken.loc.end, "Missing semicolon.");
if (!isSemicolon(lastToken)) {
report(node);
}
} else {
if (lastToken.type === "Punctuator" &&
lastToken.value === ";" &&
isUnnecessarySemicolon(lastToken, nextToken)
) {
context.report(node, node.loc.end, "Extra semicolon.");
if (isUnnecessarySemicolon(lastToken)) {
report(node);
}

@@ -71,3 +96,2 @@ }

function checkForSemicolonForVariableDeclaration(node) {
var ancestors = context.getAncestors(),

@@ -97,15 +121,13 @@ parentIndex = ancestors.length - 1,

"ContinueStatement": checkForSemicolon,
"EmptyStatement": function (node) {
var lastToken, nextToken;
if (!always) {
lastToken = context.getLastToken(node);
nextToken = context.getTokenAfter(node) || context.getLastToken(node);
if (isUnnecessarySemicolon(lastToken, nextToken)) {
context.report(node, "Extra semicolon.");
}
"ImportDeclaration": checkForSemicolon,
"ExportAllDeclaration": checkForSemicolon,
"ExportNamedDeclaration": function (node) {
if (!node.declaration) {
checkForSemicolon(node);
}
},
"ExportDefaultDeclaration": function (node) {
if (!/(?:Class|Function)Declaration/.test(node.declaration.type)) {
checkForSemicolon(node);
}
}

@@ -112,0 +134,0 @@ };

@@ -40,4 +40,4 @@ /**

missingSpaceClosers = [],
rejectedSpaceOpeners = [" ", "\\n", "\\r"],
rejectedSpaceClosers = [" ", "\\n", "\\r"],
rejectedSpaceOpeners = ["\\s"],
rejectedSpaceClosers = ["\\s"],
missingSpaceCheck,

@@ -102,4 +102,4 @@ rejectedSpaceCheck;

function getAlwaysChecks(opts) {
var missingSpaceOpeners = [" ", "\\)", "\\r", "\\n"],
missingSpaceClosers = [" ", "\\(", "\\r", "\\n"],
var missingSpaceOpeners = ["\\s", "\\)"],
missingSpaceClosers = ["\\s", "\\("],
rejectedSpaceOpeners = [],

@@ -106,0 +106,0 @@ rejectedSpaceClosers = [],

{
"name": "eslint",
"version": "0.18.0",
"version": "0.19.0",
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",

@@ -42,4 +42,4 @@ "description": "An AST-based pattern checker for JavaScript.",

"escape-string-regexp": "^1.0.2",
"escope": "2.0.6",
"espree": "^1.12.0",
"escope": "^3.0.0",
"espree": "^2.0.1",
"estraverse": "^2.0.0",

@@ -70,2 +70,3 @@ "estraverse-fb": "^1.3.1",

"jsonlint": "^1.6.2",
"markdownlint": "^0.0.3",
"mocha": "^2.1.0",

@@ -72,0 +73,0 @@ "mocha-phantomjs": "^3.5.0",

@@ -9,3 +9,3 @@ [![NPM version][npm-image]][npm-url]

[Website](http://eslint.org) | [Configuring](http://eslint.org/docs/user-guide/configuring) | [Rules](http://eslint.org/docs/user-guide/rules) | [Contributing](http://eslint.org/docs/developer-guide/contributing.html) | [Twitter](https://twitter.com/geteslint) | [Mailing List](https://groups.google.com/group/eslint)
[Website](http://eslint.org) | [Configuring](http://eslint.org/docs/user-guide/configuring) | [Rules](http://eslint.org/docs/rules/) | [Contributing](http://eslint.org/docs/developer-guide/contributing) | [Twitter](https://twitter.com/geteslint) | [Mailing List](https://groups.google.com/group/eslint)

@@ -12,0 +12,0 @@ ESLint is a tool for identifying and reporting on patterns found in ECMAScript/JavaScript code. In many ways, it is similar to JSLint and JSHint with a few exceptions:

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc