tools: update ESLint to v6.5.1

PR-URL: https://github.com/nodejs/node/pull/29785
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Yongsheng Zhang <zyszys98@gmail.com>
Reviewed-By: Beth Griggs <Bethany.Griggs@uk.ibm.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: David Carlier <devnexen@gmail.com>
This commit is contained in:
Rich Trott 2019-09-30 21:09:47 -07:00
parent a67b73b9ae
commit 88ef086e39
37 changed files with 603 additions and 156 deletions

16
tools/node_modules/eslint/README.md generated vendored
View File

@ -120,7 +120,7 @@ Yes, ESLint natively supports parsing JSX syntax (this must be enabled in [confi
### What ECMAScript versions does ESLint support?
ESLint has full support for ECMAScript 3, 5 (default), 2015, 2016, 2017, and 2018. You can set your desired ECMAScript syntax (and other settings, like global variables or your target environments) through [configuration](https://eslint.org/docs/user-guide/configuring).
ESLint has full support for ECMAScript 3, 5 (default), 2015, 2016, 2017, 2018, and 2019. You can set your desired ECMAScript syntax (and other settings, like global variables or your target environments) through [configuration](https://eslint.org/docs/user-guide/configuring).
### What about experimental features?
@ -210,11 +210,6 @@ Toru Nagashima
Gyandeep Singh
</a>
</td><td align="center" valign="top" width="11%">
<a href="https://github.com/kaicataldo">
<img src="https://github.com/kaicataldo.png?s=75" width="75" height="75"><br />
Kai Cataldo
</a>
</td><td align="center" valign="top" width="11%">
<a href="https://github.com/not-an-aardvark">
<img src="https://github.com/not-an-aardvark.png?s=75" width="75" height="75"><br />
Teddy Katz
@ -241,6 +236,11 @@ The people who review and implement new features.
The people who review and fix bugs and help triage issues.
<table><tbody><tr><td align="center" valign="top" width="11%">
<a href="https://github.com/kaicataldo">
<img src="https://github.com/kaicataldo.png?s=75" width="75" height="75"><br />
Kai Cataldo
</a>
</td><td align="center" valign="top" width="11%">
<a href="https://github.com/g-plane">
<img src="https://github.com/g-plane.png?s=75" width="75" height="75"><br />
Pig Fang
@ -262,9 +262,9 @@ The following companies, organizations, and individuals support ESLint's ongoing
<!-- NOTE: This section is autogenerated. Do not manually edit.-->
<!--sponsorsstart-->
<h3>Gold Sponsors</h3>
<p><a href="https://www.shopify.com"><img src="https://images.opencollective.com/shopify/eeb91aa/logo.png" alt="Shopify" height="96"></a> <a href="http://engineering.salesforce.com"><img src="https://images.opencollective.com/salesforce/d1b37c4/logo.png" alt="Salesforce" height="96"></a> <a href="https://badoo.com/team?utm_source=eslint"><img src="https://images.opencollective.com/badoo/2826a3b/logo.png" alt="Badoo" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a> <a href="https://code.facebook.com/projects/"><img src="https://images.opencollective.com/fbopensource/fbb8a5b/logo.png" alt="Facebook Open Source" height="96"></a></p><h3>Silver Sponsors</h3>
<p><a href="https://www.shopify.com"><img src="https://images.opencollective.com/shopify/eeb91aa/logo.png" alt="Shopify" height="96"></a> <a href="http://engineering.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://badoo.com/team?utm_source=eslint"><img src="https://images.opencollective.com/badoo/2826a3b/logo.png" alt="Badoo" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a> <a href="https://opensource.facebook.com"><img src="https://images.opencollective.com/fbopensource/fbb8a5b/logo.png" alt="Facebook Open Source" height="96"></a></p><h3>Silver Sponsors</h3>
<p><a href="https://www.ampproject.org/"><img src="https://images.opencollective.com/amp/c8a3b25/logo.png" alt="AMP Project" height="64"></a></p><h3>Bronze Sponsors</h3>
<p><a href="https://discordapp.com"><img src="https://images.opencollective.com/discordapp/7e3d9a9/logo.png" alt="Discord" height="32"></a> <a href="https://moneypug.co.uk/"><img src="https://images.opencollective.com/moneypug/45f8d53/logo.png" alt="MONEYPUG" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/0b37d14/logo.png" alt="Free Icons by Icons8" height="32"></a> <a href="https://uxplanet.org/top-ui-ux-design-agencies-user-experience-firms-8c54697e290"><img src="https://images.opencollective.com/ui-ux-design-agencies/cae5dfe/logo.png" alt="UI UX Design Agencies" height="32"></a> <a href="https://clay.global"><img src="https://images.opencollective.com/clayglobal/2468f34/logo.png" alt="clay" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS Server" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://tekhattan.com"><img src="https://images.opencollective.com/tekhattan/bc73c28/logo.png" alt="TekHattan" height="32"></a> <a href="https://www.marfeel.com/"><img src="https://images.opencollective.com/marfeel/4b88e30/logo.png" alt="Marfeel" height="32"></a> <a href="http://www.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" height="32"></a> <a href="https://jsheroes.io/"><img src="https://images.opencollective.com/jsheroes1/9fedf0b/logo.png" alt="JSHeroes " height="32"></a></p>
<p><a href="https://discordapp.com"><img src="https://images.opencollective.com/discordapp/7e3d9a9/logo.png" alt="Discord" height="32"></a> <a href="https://mixpanel.com"><img src="https://images.opencollective.com/mixpanel/cd682f7/logo.png" alt="Mixpanel" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/0b37d14/logo.png" alt="Free Icons by Icons8" height="32"></a> <a href="https://uxplanet.org/top-ui-ux-design-agencies-user-experience-firms-8c54697e290"><img src="https://images.opencollective.com/ui-ux-design-agencies/cae5dfe/logo.png" alt="UI UX Design Agencies" height="32"></a> <a href="https://clay.global"><img src="https://images.opencollective.com/clayglobal/2468f34/logo.png" alt="clay" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS Server" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://tekhattan.com"><img src="https://images.opencollective.com/tekhattan/bc73c28/logo.png" alt="TekHattan" height="32"></a> <a href="https://www.marfeel.com/"><img src="https://images.opencollective.com/marfeel/4b88e30/logo.png" alt="Marfeel" height="32"></a> <a href="http://www.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" height="32"></a> <a href="https://jsheroes.io/"><img src="https://images.opencollective.com/jsheroes1/9fedf0b/logo.png" alt="JSHeroes " height="32"></a></p>
<!--sponsorsend-->
## <a name="technology-sponsors"></a>Technology Sponsors

View File

@ -16,9 +16,9 @@ require("v8-compile-cache");
// Helpers
//------------------------------------------------------------------------------
const useStdIn = (process.argv.indexOf("--stdin") > -1),
init = (process.argv.indexOf("--init") > -1),
debug = (process.argv.indexOf("--debug") > -1);
const useStdIn = process.argv.includes("--stdin"),
init = process.argv.includes("--init"),
debug = process.argv.includes("--debug");
// must do this initialization *before* other requires in order to work
if (debug) {
@ -30,9 +30,9 @@ if (debug) {
//------------------------------------------------------------------------------
// now we can safely include the other modules that use debug
const cli = require("../lib/cli"),
path = require("path"),
fs = require("fs");
const path = require("path"),
fs = require("fs"),
cli = require("../lib/cli");
//------------------------------------------------------------------------------
// Execution
@ -50,7 +50,6 @@ process.once("uncaughtException", err => {
console.error("\nOops! Something went wrong! :(");
console.error(`\nESLint: ${pkg.version}.\n\n${template(err.messageData || {})}`);
} else {
console.error(err.stack);
}
@ -60,13 +59,40 @@ process.once("uncaughtException", err => {
if (useStdIn) {
/*
* Note: `process.stdin.fd` is not used here due to https://github.com/nodejs/node/issues/7439.
* Accessing the `process.stdin` property seems to modify the behavior of file descriptor 0, resulting
* in an error when stdin is piped in asynchronously.
* Note: See
* - https://github.com/nodejs/node/blob/master/doc/api/process.md#processstdin
* - https://github.com/nodejs/node/blob/master/doc/api/process.md#a-note-on-process-io
* - https://lists.gnu.org/archive/html/bug-gnu-emacs/2016-01/msg00419.html
* - https://github.com/nodejs/node/issues/7439 (historical)
*
* On Windows using `fs.readFileSync(STDIN_FILE_DESCRIPTOR, "utf8")` seems
* to read 4096 bytes before blocking and never drains to read further data.
*
* The investigation on the Emacs thread indicates:
*
* > Emacs on MS-Windows uses pipes to communicate with subprocesses; a
* > pipe on Windows has a 4K buffer. So as soon as Emacs writes more than
* > 4096 bytes to the pipe, the pipe becomes full, and Emacs then waits for
* > the subprocess to read its end of the pipe, at which time Emacs will
* > write the rest of the stuff.
*
* Using the nodejs code example for reading from stdin.
*/
const STDIN_FILE_DESCRIPTOR = 0;
let contents = "",
chunk = "";
process.exitCode = cli.execute(process.argv, fs.readFileSync(STDIN_FILE_DESCRIPTOR, "utf8"));
process.stdin.setEncoding("utf8");
process.stdin.on("readable", () => {
// Use a loop to make sure we read all available data.
while ((chunk = process.stdin.read()) !== null) {
contents += chunk;
}
});
process.stdin.on("end", () => {
process.exitCode = cli.execute(process.argv, contents, "utf8");
});
} else if (init) {
const configInit = require("../lib/init/config-initializer");

View File

@ -85,8 +85,8 @@ const validFixTypes = new Set(["problem", "suggestion", "layout"]);
* @property {number} warningCount Number of warnings for the result.
* @property {number} fixableErrorCount Number of fixable errors for the result.
* @property {number} fixableWarningCount Number of fixable warnings for the result.
* @property {string=} [source] The source code of the file that was linted.
* @property {string=} [output] The source code of the file that was linted, with as many fixes applied as possible.
* @property {string} [source] The source code of the file that was linted.
* @property {string} [output] The source code of the file that was linted, with as many fixes applied as possible.
*/
/**
@ -329,7 +329,6 @@ function getRule(ruleId, configArrays) {
/**
* Collect used deprecated rules.
* @param {ConfigArray[]} usedConfigArrays The config arrays which were used.
* @param {Map<string, Object>} ruleMap The rule definitions which were used (built-ins).
* @returns {IterableIterator<DeprecatedRuleInfo>} Used deprecated rules.
*/
function *iterateRuleDeprecationWarnings(usedConfigArrays) {
@ -530,7 +529,6 @@ class CLIEngine {
/**
* Creates a new instance of the core CLI engine.
* @param {CLIEngineOptions} providedOptions The options for this instance.
* @constructor
*/
constructor(providedOptions) {
const options = Object.assign(

View File

@ -78,7 +78,7 @@ function mergeDefaultOptions(options) {
return Object.assign({}, DEFAULT_OPTIONS, options);
}
/* eslint-disable valid-jsdoc */
/* eslint-disable jsdoc/check-param-names, jsdoc/require-param */
/**
* Normalize the path separators in a given string.
* On Windows environment, this replaces `\` by `/`.
@ -89,7 +89,7 @@ function mergeDefaultOptions(options) {
const normalizePathSeps = path.sep === "/"
? (str => str)
: ((seps, str) => str.replace(seps, "/")).bind(null, new RegExp(`\\${path.sep}`, "gu"));
/* eslint-enable valid-jsdoc */
/* eslint-enable jsdoc/check-param-names, jsdoc/require-param */
/**
* Converts a glob pattern to a new glob pattern relative to a different directory
@ -298,7 +298,7 @@ class IgnoredPaths {
/**
* read ignore filepath
* @param {string} filePath, file to add to ig
* @param {string} filePath file to add to ig
* @returns {Array} raw ignore rules
*/
readIgnoreFile(filePath) {

View File

@ -47,7 +47,6 @@ class LintResultCache {
/**
* Creates a new LintResultCache instance.
* @constructor
* @param {string} cacheFileLocation The cache file location.
* configuration lookup by file path).
*/

25
tools/node_modules/eslint/lib/cli.js generated vendored
View File

@ -20,7 +20,8 @@ const fs = require("fs"),
mkdirp = require("mkdirp"),
{ CLIEngine } = require("./cli-engine"),
options = require("./options"),
log = require("./shared/logging");
log = require("./shared/logging"),
RuntimeInfo = require("./shared/runtime-info");
const debug = require("debug")("eslint:cli");
@ -159,13 +160,18 @@ const cli = {
}
const files = currentOptions._;
const useStdin = typeof text === "string";
if (currentOptions.version) { // version from package.json
log.info(`v${require("../package.json").version}`);
if (currentOptions.version) {
log.info(RuntimeInfo.version());
} else if (currentOptions.envInfo) {
try {
log.info(RuntimeInfo.environment());
return 0;
} catch (err) {
log.error(err.message);
return 2;
}
} else if (currentOptions.printConfig) {
if (files.length) {
log.error("The --print-config option must be used with exactly one file name.");
@ -177,17 +183,13 @@ const cli = {
}
const engine = new CLIEngine(translateOptions(currentOptions));
const fileConfig = engine.getConfigForFile(currentOptions.printConfig);
log.info(JSON.stringify(fileConfig, null, " "));
return 0;
} else if (currentOptions.help || (!files.length && !useStdin)) {
log.info(options.generateHelp());
} else {
debug(`Running on ${useStdin ? "text" : "files"}`);
if (currentOptions.fix && currentOptions.fixDryRun) {
@ -227,9 +229,8 @@ const cli = {
return (report.errorCount || tooManyWarnings) ? 1 : 0;
}
return 2;
}
return 0;

View File

@ -99,7 +99,7 @@ function groupByProperty(objects) {
* Configs may also have one or more additional elements to specify rule
* configuration or options.
*
* @typedef {array|number} ruleConfig
* @typedef {Array|number} ruleConfig
* @param {number} 0 The rule's severity (0, 1, 2).
*/
@ -185,7 +185,7 @@ class RuleConfigSet {
/**
* Stored valid rule configurations for this instance
* @type {array}
* @type {Array}
*/
this.ruleConfigs = configs || [];
}

View File

@ -163,7 +163,7 @@ function checkDevDeps(packages) {
/**
* Check whether package.json is found in current path.
*
* @param {string=} startDir Starting directory
* @param {string} [startDir] Starting directory
* @returns {boolean} Whether a package.json is found in current path.
*/
function checkPackageJson(startDir) {

View File

@ -21,7 +21,7 @@ const debug = require("debug")("eslint:code-path");
* @returns {string} Id of the segment.
*/
/* istanbul ignore next */
function getId(segment) { // eslint-disable-line require-jsdoc
function getId(segment) { // eslint-disable-line jsdoc/require-jsdoc
return segment.id + (segment.reachable ? "" : "!");
}

View File

@ -472,7 +472,6 @@ function normalizeFilename(filename) {
return index === -1 ? filename : parts.slice(index).join(path.sep);
}
// eslint-disable-next-line valid-jsdoc
/**
* Normalizes the possible options for `linter.verify` and `linter.verifyAndFix` to a
* consistent shape.

View File

@ -224,6 +224,12 @@ module.exports = optionator({
default: "false",
description: "Run config initialization wizard"
},
{
option: "env-info",
type: "Boolean",
default: "false",
description: "Output execution environment information"
},
{
option: "debug",
type: "Boolean",

View File

@ -130,7 +130,7 @@ function freezeDeeply(x) {
*/
function sanitize(text) {
return text.replace(
/[\u0000-\u001f]/gu, // eslint-disable-line no-control-regex
/[\u0000-\u0009|\u000b-\u001a]/gu, // eslint-disable-line no-control-regex
c => `\\u${c.codePointAt(0).toString(16).padStart(4, "0")}`
);
}
@ -177,7 +177,6 @@ class RuleTester {
/**
* Creates a new instance of RuleTester.
* @param {Object} [testerConfig] Optional, extra configuration for the tester
* @constructor
*/
constructor(testerConfig) {

View File

@ -59,7 +59,7 @@ const DEFAULTS = {
* @param {string} which Either "line" or "block".
* @returns {Object} The normalized options.
*/
function getNormalizedOptions(rawOptions = {}, which) {
function getNormalizedOptions(rawOptions, which) {
return Object.assign({}, DEFAULTS, rawOptions[which] || rawOptions);
}
@ -70,7 +70,7 @@ function getNormalizedOptions(rawOptions = {}, which) {
* @returns {Object} An object with "Line" and "Block" keys and corresponding
* normalized options objects.
*/
function getAllNormalizedOptions(rawOptions) {
function getAllNormalizedOptions(rawOptions = {}) {
return {
Line: getNormalizedOptions(rawOptions, "line"),
Block: getNormalizedOptions(rawOptions, "block")

View File

@ -303,7 +303,7 @@ module.exports = {
* @param {int} needed Expected indentation character count
* @param {int} gottenSpaces Indentation space count in the actual node/code
* @param {int} gottenTabs Indentation tab count in the actual node/code
* @param {Object=} loc Error line and column location
* @param {Object} [loc] Error line and column location
* @param {boolean} isLastNodeCheck Is the error for last node check
* @returns {void}
*/

View File

@ -81,6 +81,9 @@ const KNOWN_NODES = new Set([
"WhileStatement",
"WithStatement",
"YieldExpression",
"JSXFragment",
"JSXOpeningFragment",
"JSXClosingFragment",
"JSXIdentifier",
"JSXNamespacedName",
"JSXMemberExpression",
@ -1453,6 +1456,31 @@ module.exports = {
offsets.setDesiredOffsets(node.name.range, firstToken, 1);
},
JSXFragment(node) {
const firstOpeningToken = sourceCode.getFirstToken(node.openingFragment);
const firstClosingToken = sourceCode.getFirstToken(node.closingFragment);
addElementListIndent(node.children, firstOpeningToken, firstClosingToken, 1);
},
JSXOpeningFragment(node) {
const firstToken = sourceCode.getFirstToken(node);
const closingToken = sourceCode.getLastToken(node);
offsets.setDesiredOffsets(node.range, firstToken, 1);
offsets.matchOffsetOf(firstToken, closingToken);
},
JSXClosingFragment(node) {
const firstToken = sourceCode.getFirstToken(node);
const slashToken = sourceCode.getLastToken(node, { skip: 1 });
const closingToken = sourceCode.getLastToken(node);
const tokenToMatch = astUtils.isTokenOnSameLine(slashToken, closingToken) ? slashToken : closingToken;
offsets.setDesiredOffsets(node.range, firstToken, 1);
offsets.matchOffsetOf(firstToken, tokenToMatch);
},
JSXExpressionContainer(node) {
const openingCurly = sourceCode.getFirstToken(node);
const closingCurly = sourceCode.getLastToken(node);

View File

@ -65,7 +65,11 @@ module.exports = {
const lastToken = sourceCode.getLastToken(node);
const hasLastParen = lastToken && astUtils.isClosingParenToken(lastToken);
const hasParens = hasLastParen && astUtils.isOpeningParenToken(sourceCode.getTokenBefore(lastToken));
// `hasParens` is true only if the new expression ends with its own parens, e.g., new new foo() does not end with its own parens
const hasParens = hasLastParen &&
astUtils.isOpeningParenToken(sourceCode.getTokenBefore(lastToken)) &&
node.callee.range[1] < node.range[1];
if (always) {
if (!hasParens) {

View File

@ -40,6 +40,7 @@ module.exports = {
},
create(context) {
const sourceCode = context.getSourceCode();
let scopeInfo = null;
/**
@ -71,8 +72,13 @@ module.exports = {
return null;
}
const firstTokenToRemove = context.getSourceCode()
const firstTokenToRemove = sourceCode
.getFirstTokenBetween(node.parent.object, node.parent.property, astUtils.isNotClosingParenToken);
const lastTokenToRemove = sourceCode.getLastToken(node.parent.parent);
if (sourceCode.commentsExistBetween(firstTokenToRemove, lastTokenToRemove)) {
return null;
}
return fixer.removeRange([firstTokenToRemove.range[0], node.parent.parent.range[1]]);
}

View File

@ -116,7 +116,15 @@ module.exports = {
node: labelNode,
messageId: "unexpected",
data: labelNode,
fix: fixer => fixer.removeRange([sourceCode.getFirstToken(node).range[1], labelNode.range[1]])
fix(fixer) {
const breakOrContinueToken = sourceCode.getFirstToken(node);
if (sourceCode.commentsExistBetween(breakOrContinueToken, labelNode)) {
return null;
}
return fixer.removeRange([breakOrContinueToken.range[1], labelNode.range[1]]);
}
});
}
return;

View File

@ -205,7 +205,14 @@ module.exports = {
const lastToken = sourceCode.getLastToken(newExpression);
const penultimateToken = sourceCode.getTokenBefore(lastToken);
return newExpression.arguments.length > 0 || astUtils.isOpeningParenToken(penultimateToken) && astUtils.isClosingParenToken(lastToken);
return newExpression.arguments.length > 0 ||
(
// The expression should end with its own parens, e.g., new new foo() is not a new expression with parens
astUtils.isOpeningParenToken(penultimateToken) &&
astUtils.isClosingParenToken(lastToken) &&
newExpression.callee.range[1] < newExpression.range[1]
);
}
/**
@ -338,7 +345,7 @@ module.exports = {
function finishReport() {
context.report({
node,
loc: leftParenToken.loc.start,
loc: leftParenToken.loc,
messageId: "unexpected",
fix(fixer) {
const parenthesizedSource = sourceCode.text.slice(leftParenToken.range[1], rightParenToken.range[0]);
@ -887,6 +894,12 @@ module.exports = {
report(node.object);
}
if (nodeObjHasExcessParens &&
node.object.type === "NewExpression" &&
isNewExpressionWithParens(node.object)) {
report(node.object);
}
if (node.computed && hasExcessParens(node.property)) {
report(node.property);
}

View File

@ -5,7 +5,29 @@
"use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const astUtils = require("./utils/ast-utils");
const regexpp = require("regexpp");
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
const regExpParser = new regexpp.RegExpParser();
const DOUBLE_SPACE = / {2}/u;
/**
* Check if node is a string
* @param {ASTNode} node node to evaluate
* @returns {boolean} True if its a string
* @private
*/
function isString(node) {
return node && node.type === "Literal" && typeof node.value === "string";
}
//------------------------------------------------------------------------------
// Rule Definition
@ -27,40 +49,70 @@ module.exports = {
},
create(context) {
const sourceCode = context.getSourceCode();
/**
* Validate regular expressions
* @param {ASTNode} node node to validate
* @param {string} value regular expression to validate
* @param {number} valueStart The start location of the regex/string literal. It will always be the case that
* `sourceCode.getText().slice(valueStart, valueStart + value.length) === value`
* Validate regular expression
*
* @param {ASTNode} nodeToReport Node to report.
* @param {string} pattern Regular expression pattern to validate.
* @param {string} rawPattern Raw representation of the pattern in the source code.
* @param {number} rawPatternStartRange Start range of the pattern in the source code.
* @param {string} flags Regular expression flags.
* @returns {void}
* @private
*/
function checkRegex(node, value, valueStart) {
const multipleSpacesRegex = /( {2,})( [+*{?]|[^+*{?]|$)/u,
regexResults = multipleSpacesRegex.exec(value);
function checkRegex(nodeToReport, pattern, rawPattern, rawPatternStartRange, flags) {
if (regexResults !== null) {
const count = regexResults[1].length;
// Skip if there are no consecutive spaces in the source code, to avoid reporting e.g., RegExp(' \ ').
if (!DOUBLE_SPACE.test(rawPattern)) {
return;
}
context.report({
node,
message: "Spaces are hard to count. Use {{{count}}}.",
data: { count },
fix(fixer) {
return fixer.replaceTextRange(
[valueStart + regexResults.index, valueStart + regexResults.index + count],
` {${count}}`
);
}
});
const characterClassNodes = [];
let regExpAST;
/*
* TODO: (platinumazure) Fix message to use rule message
* substitution when api.report is fixed in lib/eslint.js.
*/
try {
regExpAST = regExpParser.parsePattern(pattern, 0, pattern.length, flags.includes("u"));
} catch (e) {
// Ignore regular expressions with syntax errors
return;
}
regexpp.visitRegExpAST(regExpAST, {
onCharacterClassEnter(ccNode) {
characterClassNodes.push(ccNode);
}
});
const spacesPattern = /( {2,})(?: [+*{?]|[^+*{?]|$)/gu;
let match;
while ((match = spacesPattern.exec(pattern))) {
const { 1: { length }, index } = match;
// Report only consecutive spaces that are not in character classes.
if (
characterClassNodes.every(({ start, end }) => index < start || end <= index)
) {
context.report({
node: nodeToReport,
message: "Spaces are hard to count. Use {{{length}}}.",
data: { length },
fix(fixer) {
if (pattern !== rawPattern) {
return null;
}
return fixer.replaceTextRange(
[rawPatternStartRange + index, rawPatternStartRange + index + length],
` {${length}}`
);
}
});
// Report only the first occurence of consecutive spaces
return;
}
}
}
@ -71,25 +123,22 @@ module.exports = {
* @private
*/
function checkLiteral(node) {
const token = sourceCode.getFirstToken(node),
nodeType = token.type,
nodeValue = token.value;
if (node.regex) {
const pattern = node.regex.pattern;
const rawPattern = node.raw.slice(1, node.raw.lastIndexOf("/"));
const rawPatternStartRange = node.range[0] + 1;
const flags = node.regex.flags;
if (nodeType === "RegularExpression") {
checkRegex(node, nodeValue, token.range[0]);
checkRegex(
node,
pattern,
rawPattern,
rawPatternStartRange,
flags
);
}
}
/**
* Check if node is a string
* @param {ASTNode} node node to evaluate
* @returns {boolean} True if its a string
* @private
*/
function isString(node) {
return node && node.type === "Literal" && typeof node.value === "string";
}
/**
* Validate strings passed to the RegExp constructor
* @param {ASTNode} node node to validate
@ -100,9 +149,22 @@ module.exports = {
const scope = context.getScope();
const regExpVar = astUtils.getVariableByName(scope, "RegExp");
const shadowed = regExpVar && regExpVar.defs.length > 0;
const patternNode = node.arguments[0];
const flagsNode = node.arguments[1];
if (node.callee.type === "Identifier" && node.callee.name === "RegExp" && isString(node.arguments[0]) && !shadowed) {
checkRegex(node, node.arguments[0].value, node.arguments[0].range[0] + 1);
if (node.callee.type === "Identifier" && node.callee.name === "RegExp" && isString(patternNode) && !shadowed) {
const pattern = patternNode.value;
const rawPattern = patternNode.raw.slice(1, -1);
const rawPatternStartRange = patternNode.range[0] + 1;
const flags = isString(flagsNode) ? flagsNode.value : "";
checkRegex(
node,
pattern,
rawPattern,
rawPatternStartRange,
flags
);
}
}
@ -111,6 +173,5 @@ module.exports = {
CallExpression: checkFunction,
NewExpression: checkFunction
};
}
};

View File

@ -37,7 +37,8 @@ module.exports = {
init = node.init && node.init.name,
scope = context.getScope(),
undefinedVar = astUtils.getVariableByName(scope, "undefined"),
shadowed = undefinedVar && undefinedVar.defs.length > 0;
shadowed = undefinedVar && undefinedVar.defs.length > 0,
lastToken = sourceCode.getLastToken(node);
if (init === "undefined" && node.parent.kind !== "const" && !shadowed) {
context.report({
@ -54,6 +55,11 @@ module.exports = {
// Don't fix destructuring assignment to `undefined`.
return null;
}
if (sourceCode.commentsExistBetween(node.id, lastToken)) {
return null;
}
return fixer.removeRange([node.id.range[1], node.range[1]]);
}
});

View File

@ -36,7 +36,8 @@ module.exports = {
},
create(context) {
const options = context.options[0] || {},
const sourceCode = context.getSourceCode(),
options = context.options[0] || {},
ignoreDestructuring = options.ignoreDestructuring === true,
ignoreImport = options.ignoreImport === true,
ignoreExport = options.ignoreExport === true;
@ -64,10 +65,18 @@ module.exports = {
type
},
fix(fixer) {
if (sourceCode.commentsExistBetween(initial, result)) {
return null;
}
const replacementText = result.type === "AssignmentPattern"
? sourceCode.getText(result)
: name;
return fixer.replaceTextRange([
initial.range[0],
result.range[1]
], name);
], replacementText);
}
});
}
@ -82,26 +91,29 @@ module.exports = {
return;
}
const properties = node.properties;
for (const property of node.properties) {
for (let i = 0; i < properties.length; i++) {
if (properties[i].shorthand) {
/*
* TODO: Remove after babel-eslint removes ExperimentalRestProperty
* https://github.com/eslint/eslint/issues/12335
*/
if (property.type === "ExperimentalRestProperty") {
continue;
}
/**
* If an ObjectPattern property is computed, we have no idea
* if a rename is useless or not. If an ObjectPattern property
* lacks a key, it is likely an ExperimentalRestProperty and
* so there is no "renaming" occurring here.
* Properties using shorthand syntax and rest elements can not be renamed.
* If the property is computed, we have no idea if a rename is useless or not.
*/
if (properties[i].computed || !properties[i].key) {
if (property.shorthand || property.type === "RestElement" || property.computed) {
continue;
}
if (properties[i].key.type === "Identifier" && properties[i].key.name === properties[i].value.name ||
properties[i].key.type === "Literal" && properties[i].key.value === properties[i].value.name) {
reportError(properties[i], properties[i].key, properties[i].value, "Destructuring assignment");
const key = (property.key.type === "Identifier" && property.key.name) || (property.key.type === "Literal" && property.key.value);
const renamedKey = property.value.type === "AssignmentPattern" ? property.value.left.name : property.value.name;
if (key === renamedKey) {
reportError(property, property.key, property.value, "Destructuring assignment");
}
}
}

View File

@ -82,6 +82,7 @@ module.exports = {
create(context) {
const segmentInfoMap = new WeakMap();
const usedUnreachableSegments = new WeakSet();
const sourceCode = context.getSourceCode();
let scopeInfo = null;
/**
@ -216,7 +217,7 @@ module.exports = {
loc: node.loc,
message: "Unnecessary return statement.",
fix(fixer) {
if (isRemovable(node)) {
if (isRemovable(node) && !sourceCode.getCommentsInside(node).length) {
/*
* Extend the replacement range to include the
@ -224,7 +225,7 @@ module.exports = {
* no-else-return.
* https://github.com/eslint/eslint/issues/8026
*/
return new FixTracker(fixer, context.getSourceCode())
return new FixTracker(fixer, sourceCode)
.retainEnclosingFunction(node)
.remove(node);
}

View File

@ -50,16 +50,16 @@ module.exports = {
/**
* Function to check regular expression.
*
* @param {string} regex The regular expression to be check.
* @param {string} pattern The regular expression pattern to be check.
* @param {ASTNode} node AST node which contains regular expression.
* @param {boolean} uFlag Flag indicates whether unicode mode is enabled or not.
* @returns {void}
*/
function checkRegex(regex, node, uFlag) {
function checkRegex(pattern, node, uFlag) {
let ast;
try {
ast = parser.parsePattern(regex, 0, regex.length, uFlag);
ast = parser.parsePattern(pattern, 0, pattern.length, uFlag);
} catch (_) {
// ignore regex syntax errors
@ -69,21 +69,9 @@ module.exports = {
regexpp.visitRegExpAST(ast, {
onCapturingGroupEnter(group) {
if (!group.name) {
const locNode = node.type === "Literal" ? node : node.arguments[0];
context.report({
node,
messageId: "required",
loc: {
start: {
line: locNode.loc.start.line,
column: locNode.loc.start.column + group.start + 1
},
end: {
line: locNode.loc.start.line,
column: locNode.loc.start.column + group.end + 1
}
},
data: {
group: group.raw
}

View File

@ -96,6 +96,10 @@ module.exports = {
fix(fixer) {
const newPrefix = prefixMap[node.arguments[1].value];
if (sourceCode.getCommentsInside(node).length) {
return null;
}
if (+(newPrefix + node.arguments[0].value) !== parseInt(node.arguments[0].value, node.arguments[1].value)) {
/*

View File

@ -5,6 +5,19 @@
"use strict";
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
/**
* Determines if the given node is a NaN `Identifier` node.
* @param {ASTNode|null} node The node to check.
* @returns {boolean} `true` if the node is 'NaN' identifier.
*/
function isNaNIdentifier(node) {
return Boolean(node) && node.type === "Identifier" && node.name === "NaN";
}
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
@ -20,21 +33,69 @@ module.exports = {
url: "https://eslint.org/docs/rules/use-isnan"
},
schema: [],
schema: [
{
type: "object",
properties: {
enforceForSwitchCase: {
type: "boolean",
default: false
}
},
additionalProperties: false
}
],
messages: {
useIsNaN: "Use the isNaN function to compare with NaN."
comparisonWithNaN: "Use the isNaN function to compare with NaN.",
switchNaN: "'switch(NaN)' can never match a case clause. Use Number.isNaN instead of the switch.",
caseNaN: "'case NaN' can never match. Use Number.isNaN before the switch."
}
},
create(context) {
return {
BinaryExpression(node) {
if (/^(?:[<>]|[!=]=)=?$/u.test(node.operator) && (node.left.name === "NaN" || node.right.name === "NaN")) {
context.report({ node, messageId: "useIsNaN" });
const enforceForSwitchCase = context.options[0] && context.options[0].enforceForSwitchCase;
/**
* Checks the given `BinaryExpression` node.
* @param {ASTNode} node The node to check.
* @returns {void}
*/
function checkBinaryExpression(node) {
if (
/^(?:[<>]|[!=]=)=?$/u.test(node.operator) &&
(isNaNIdentifier(node.left) || isNaNIdentifier(node.right))
) {
context.report({ node, messageId: "comparisonWithNaN" });
}
}
/**
* Checks the discriminant and all case clauses of the given `SwitchStatement` node.
* @param {ASTNode} node The node to check.
* @returns {void}
*/
function checkSwitchStatement(node) {
if (isNaNIdentifier(node.discriminant)) {
context.report({ node, messageId: "switchNaN" });
}
for (const switchCase of node.cases) {
if (isNaNIdentifier(switchCase.test)) {
context.report({ node: switchCase, messageId: "caseNaN" });
}
}
}
const listeners = {
BinaryExpression: checkBinaryExpression
};
if (enforceForSwitchCase) {
listeners.SwitchStatement = checkSwitchStatement;
}
return listeners;
}
};

View File

@ -12,6 +12,7 @@ module.exports = {
/**
* Cover for console.log
* @param {...any} args The elements to log.
* @returns {void}
*/
info(...args) {
@ -20,6 +21,7 @@ module.exports = {
/**
* Cover for console.error
* @param {...any} args The elements to log.
* @returns {void}
*/
error(...args) {

163
tools/node_modules/eslint/lib/shared/runtime-info.js generated vendored Normal file
View File

@ -0,0 +1,163 @@
/**
* @fileoverview Utility to get information about the execution environment.
* @author Kai Cataldo
*/
"use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const path = require("path");
const spawn = require("cross-spawn");
const { isEmpty } = require("lodash");
const log = require("../shared/logging");
const packageJson = require("../../package.json");
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
/**
* Generates and returns execution environment information.
* @returns {string} A string that contains execution environment information.
*/
function environment() {
const cache = new Map();
/**
* Checks if a path is a child of a directory.
* @param {string} parentPath - The parent path to check.
* @param {string} childPath - The path to check.
* @returns {boolean} Whether or not the given path is a child of a directory.
*/
function isChildOfDirectory(parentPath, childPath) {
return !path.relative(parentPath, childPath).startsWith("..");
}
/**
* Synchronously executes a shell command and formats the result.
* @param {string} cmd - The command to execute.
* @param {Array} args - The arguments to be executed with the command.
* @returns {string} The version returned by the command.
*/
function execCommand(cmd, args) {
const key = [cmd, ...args].join(" ");
if (cache.has(key)) {
return cache.get(key);
}
const process = spawn.sync(cmd, args, { encoding: "utf8" });
if (process.error) {
throw process.error;
}
const result = process.stdout.trim();
cache.set(key, result);
return result;
}
/**
* Normalizes a version number.
* @param {string} versionStr - The string to normalize.
* @returns {string} The normalized version number.
*/
function normalizeVersionStr(versionStr) {
return versionStr.startsWith("v") ? versionStr : `v${versionStr}`;
}
/**
* Gets bin version.
* @param {string} bin - The bin to check.
* @returns {string} The normalized version returned by the command.
*/
function getBinVersion(bin) {
const binArgs = ["--version"];
try {
return normalizeVersionStr(execCommand(bin, binArgs));
} catch (e) {
log.error(`Error finding ${bin} version running the command \`${bin} ${binArgs.join(" ")}\``);
throw e;
}
}
/**
* Gets installed npm package version.
* @param {string} pkg - The package to check.
* @param {boolean} global - Whether to check globally or not.
* @returns {string} The normalized version returned by the command.
*/
function getNpmPackageVersion(pkg, { global = false } = {}) {
const npmBinArgs = ["bin", "-g"];
const npmLsArgs = ["ls", "--depth=0", "--json", "eslint"];
if (global) {
npmLsArgs.push("-g");
}
try {
const parsedStdout = JSON.parse(execCommand("npm", npmLsArgs));
/*
* Checking globally returns an empty JSON object, while local checks
* include the name and version of the local project.
*/
if (isEmpty(parsedStdout) || !(parsedStdout.dependencies && parsedStdout.dependencies.eslint)) {
return "Not found";
}
const [, processBinPath] = process.argv;
let npmBinPath;
try {
npmBinPath = execCommand("npm", npmBinArgs);
} catch (e) {
log.error(`Error finding npm binary path when running command \`npm ${npmBinArgs.join(" ")}\``);
throw e;
}
const isGlobal = isChildOfDirectory(npmBinPath, processBinPath);
let pkgVersion = parsedStdout.dependencies.eslint.version;
if ((global && isGlobal) || (!global && !isGlobal)) {
pkgVersion += " (Currently used)";
}
return normalizeVersionStr(pkgVersion);
} catch (e) {
log.error(`Error finding ${pkg} version running the command \`npm ${npmLsArgs.join(" ")}\``);
throw e;
}
}
return [
"Environment Info:",
"",
`Node version: ${getBinVersion("node")}`,
`npm version: ${getBinVersion("npm")}`,
`Local ESLint version: ${getNpmPackageVersion("eslint", { global: false })}`,
`Global ESLint version: ${getNpmPackageVersion("eslint", { global: true })}`
].join("\n");
}
/**
* Returns version of currently executing ESLint.
* @returns {string} The version from the currently executing ESLint's package.json.
*/
function version() {
return `v${packageJson.version}`;
}
//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------
module.exports = {
environment,
version
};

View File

@ -93,7 +93,6 @@ class SourceCode extends TokenStore {
* @param {ScopeManager|null} textOrConfig.scopeManager - The scope of this source code.
* @param {Object|null} textOrConfig.visitorKeys - The visitor keys to traverse AST.
* @param {ASTNode} [astIfNoConfig] - The Program node of the AST representing the code. This AST should be created from the text that BOM was stripped.
* @constructor
*/
constructor(textOrConfig, astIfNoConfig) {
let text, ast, parserServices, scopeManager, visitorKeys;
@ -206,9 +205,9 @@ class SourceCode extends TokenStore {
/**
* Gets the source code for the given node.
* @param {ASTNode=} node The AST node to get the text for.
* @param {int=} beforeCount The number of characters before the node to retrieve.
* @param {int=} afterCount The number of characters after the node to retrieve.
* @param {ASTNode} [node] The AST node to get the text for.
* @param {int} [beforeCount] The number of characters before the node to retrieve.
* @param {int} [afterCount] The number of characters after the node to retrieve.
* @returns {string} The text representing the AST node.
* @public
*/

View File

@ -52,9 +52,10 @@ Options can be provided by passing a second argument, which should be
an object containing any of these fields:
- **ecmaVersion**: Indicates the ECMAScript version to parse. Must be
either 3, 5, 6 (2015), 7 (2016), 8 (2017), 9 (2018) or 10 (2019, partial
support). This influences support for strict mode, the set of
reserved words, and support for new syntax features. Default is 10.
either 3, 5, 6 (2015), 7 (2016), 8 (2017), 9 (2018), 10 (2019) or 11
(2020, partial support). This influences support for strict mode,
the set of reserved words, and support for new syntax features.
Default is 10.
**NOTE**: Only 'stage 4' (finalized) ECMAScript features are being
implemented by Acorn. Other proposed new features can be implemented

View File

@ -2173,7 +2173,7 @@
// super [ Expression ]
// super . IdentifierName
// SuperCall:
// super Arguments
// super ( Arguments )
if (this.type !== types.dot && this.type !== types.bracketL && this.type !== types.parenL)
{ this.unexpected(); }
return this.finishNode(node, "Super")
@ -2468,7 +2468,7 @@
while (!this.eat(types.braceR)) {
if (!first) {
this.expect(types.comma);
if (this.afterTrailingComma(types.braceR)) { break }
if (this.options.ecmaVersion >= 5 && this.afterTrailingComma(types.braceR)) { break }
} else { first = false; }
var prop = this.parseProperty(isPattern, refDestructuringErrors);
@ -4923,7 +4923,29 @@
// Acorn is a tiny, fast JavaScript parser written in JavaScript.
var version = "7.0.0";
var version = "7.1.0";
Parser.acorn = {
Parser: Parser,
version: version,
defaultOptions: defaultOptions,
Position: Position,
SourceLocation: SourceLocation,
getLineInfo: getLineInfo,
Node: Node,
TokenType: TokenType,
tokTypes: types,
keywordTypes: keywords$1,
TokContext: TokContext,
tokContexts: types$1,
isIdentifierChar: isIdentifierChar,
isIdentifierStart: isIdentifierStart,
Token: Token,
isNewLine: isNewLine,
lineBreak: lineBreak,
lineBreakG: lineBreakG,
nonASCIIwhitespace: nonASCIIwhitespace
};
// The main exported interface (under `self.acorn` when in the
// browser) is a `parse` function that takes a code string and

View File

@ -2167,7 +2167,7 @@ pp$3.parseExprAtom = function(refDestructuringErrors) {
// super [ Expression ]
// super . IdentifierName
// SuperCall:
// super Arguments
// super ( Arguments )
if (this.type !== types.dot && this.type !== types.bracketL && this.type !== types.parenL)
{ this.unexpected(); }
return this.finishNode(node, "Super")
@ -2462,7 +2462,7 @@ pp$3.parseObj = function(isPattern, refDestructuringErrors) {
while (!this.eat(types.braceR)) {
if (!first) {
this.expect(types.comma);
if (this.afterTrailingComma(types.braceR)) { break }
if (this.options.ecmaVersion >= 5 && this.afterTrailingComma(types.braceR)) { break }
} else { first = false; }
var prop = this.parseProperty(isPattern, refDestructuringErrors);
@ -4917,7 +4917,29 @@ pp$9.readWord = function() {
// Acorn is a tiny, fast JavaScript parser written in JavaScript.
var version = "7.0.0";
var version = "7.1.0";
Parser.acorn = {
Parser: Parser,
version: version,
defaultOptions: defaultOptions,
Position: Position,
SourceLocation: SourceLocation,
getLineInfo: getLineInfo,
Node: Node,
TokenType: TokenType,
tokTypes: types,
keywordTypes: keywords$1,
TokContext: TokContext,
tokContexts: types$1,
isIdentifierChar: isIdentifierChar,
isIdentifierStart: isIdentifierStart,
Token: Token,
isNewLine: isNewLine,
lineBreak: lineBreak,
lineBreakG: lineBreakG,
nonASCIIwhitespace: nonASCIIwhitespace
};
// The main exported interface (under `self.acorn` when in the
// browser) is a `parse` function that takes a code string and

View File

@ -39,5 +39,5 @@
"scripts": {
"prepare": "cd ..; npm run build:main && npm run build:bin"
},
"version": "7.0.0"
"version": "7.1.0"
}

View File

@ -36,10 +36,19 @@ globParent('path/foo'); // 'path' (see issue #3 for details)
## API
### `globParent(maybeGlobString)`
### `globParent(maybeGlobString, [options])`
Takes a string and returns the part of the path before the glob begins. Be aware of Escaping rules and Limitations below.
#### options
```js
{
// Disables the automatic conversion of slashes for Windows
flipBackslashes: true
}
```
## Escaping
The following characters have special significance in glob patterns and must be escaped if you want them to be treated as regular path characters:

View File

@ -10,9 +10,16 @@ var enclosure = /[\{\[].*[\/]*.*[\}\]]$/;
var globby = /(^|[^\\])([\{\[]|\([^\)]+$)/;
var escaped = /\\([\*\?\|\[\]\(\)\{\}])/g;
module.exports = function globParent(str) {
/**
* @param {string} str
* @param {Object} opts
* @param {boolean} [opts.flipBackslashes=true]
*/
module.exports = function globParent(str, opts) {
var options = Object.assign({ flipBackslashes: true }, opts);
// flip windows path separators
if (isWin32 && str.indexOf(slash) < 0) {
if (options.flipBackslashes && isWin32 && str.indexOf(slash) < 0) {
str = str.replace(backslash, slash);
}

View File

@ -63,5 +63,5 @@
"pretest": "npm run lint",
"test": "nyc mocha --async-only"
},
"version": "5.0.0"
"version": "5.1.0"
}

View File

@ -17,7 +17,7 @@
"cross-spawn": "^6.0.5",
"debug": "^4.0.1",
"doctrine": "^3.0.0",
"eslint-plugin-markdown": "1.0.0",
"eslint-plugin-markdown": "^1.0.0",
"eslint-scope": "^5.0.0",
"eslint-utils": "^1.4.2",
"eslint-visitor-keys": "^1.1.0",
@ -67,6 +67,7 @@
"eslint-config-eslint": "file:packages/eslint-config-eslint",
"eslint-plugin-eslint-plugin": "^2.0.1",
"eslint-plugin-internal-rules": "file:tools/internal-rules",
"eslint-plugin-jsdoc": "^15.9.5",
"eslint-plugin-node": "^9.0.0",
"eslint-release": "^1.2.0",
"eslump": "^2.0.0",
@ -137,6 +138,7 @@
},
"scripts": {
"docs": "node Makefile.js docs",
"fix": "node Makefile.js lint -- fix",
"fuzz": "node Makefile.js fuzz",
"generate-alpharelease": "node Makefile.js generatePrerelease -- alpha",
"generate-betarelease": "node Makefile.js generatePrerelease -- beta",
@ -149,5 +151,5 @@
"test": "node Makefile.js test",
"webpack": "node Makefile.js webpack"
},
"version": "6.4.0"
"version": "6.5.1"
}