Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

eslint

Package Overview
Dependencies
Maintainers
4
Versions
371
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 8.40.0 to 8.45.0

7

conf/globals.js

@@ -131,3 +131,7 @@ /**

const es2024 = {
...es2023
};
//-----------------------------------------------------------------------------

@@ -149,3 +153,4 @@ // Exports

es2022,
es2023
es2023,
es2024
};

49

lib/cli-engine/cli-engine.js

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

function calculateStatsPerFile(messages) {
return messages.reduce((stat, message) => {
const stat = {
errorCount: 0,
fatalErrorCount: 0,
warningCount: 0,
fixableErrorCount: 0,
fixableWarningCount: 0
};
for (let i = 0; i < messages.length; i++) {
const message = messages[i];
if (message.fatal || message.severity === 2) {

@@ -177,10 +187,4 @@ stat.errorCount++;

}
return stat;
}, {
errorCount: 0,
fatalErrorCount: 0,
warningCount: 0,
fixableErrorCount: 0,
fixableWarningCount: 0
});
}
return stat;
}

@@ -195,3 +199,13 @@

function calculateStatsPerRun(results) {
return results.reduce((stat, result) => {
const stat = {
errorCount: 0,
fatalErrorCount: 0,
warningCount: 0,
fixableErrorCount: 0,
fixableWarningCount: 0
};
for (let i = 0; i < results.length; i++) {
const result = results[i];
stat.errorCount += result.errorCount;

@@ -202,10 +216,5 @@ stat.fatalErrorCount += result.fatalErrorCount;

stat.fixableWarningCount += result.fixableWarningCount;
return stat;
}, {
errorCount: 0,
fatalErrorCount: 0,
warningCount: 0,
fixableErrorCount: 0,
fixableWarningCount: 0
});
}
return stat;
}

@@ -315,5 +324,7 @@

{
ruleId: null,
fatal: false,
severity: 1,
message
message,
nodeType: null
}

@@ -320,0 +331,0 @@ ],

@@ -22,3 +22,3 @@ /**

{ ESLint } = require("./eslint"),
{ FlatESLint } = require("./eslint/flat-eslint"),
{ FlatESLint, shouldUseFlatConfig } = require("./eslint/flat-eslint"),
createCLIOptions = require("./options"),

@@ -28,3 +28,2 @@ log = require("./shared/logging"),

const { Legacy: { naming } } = require("@eslint/eslintrc");
const { findFlatConfigFile } = require("./eslint/flat-eslint");
const { ModuleImporter } = require("@humanwhocodes/module-importer");

@@ -280,27 +279,2 @@

/**
* Returns whether flat config should be used.
* @param {boolean} [allowFlatConfig] Whether or not to allow flat config.
* @returns {Promise<boolean>} Where flat config should be used.
*/
async function shouldUseFlatConfig(allowFlatConfig) {
if (!allowFlatConfig) {
return false;
}
switch (process.env.ESLINT_USE_FLAT_CONFIG) {
case "true":
return true;
case "false":
return false;
default:
/*
* If neither explicitly enabled nor disabled, then use the presence
* of a flat config file to determine enablement.
*/
return !!(await findFlatConfigFile(process.cwd()));
}
}
//------------------------------------------------------------------------------

@@ -335,3 +309,3 @@ // Public Interface

const usingFlatConfig = await shouldUseFlatConfig(allowFlatConfig);
const usingFlatConfig = allowFlatConfig && await shouldUseFlatConfig();

@@ -338,0 +312,0 @@ debug("Using flat config?", usingFlatConfig);

@@ -51,3 +51,3 @@ /**

ignores: [
"**/node_modules/*",
"**/node_modules/",
".git/"

@@ -54,0 +54,0 @@ ]

@@ -594,10 +594,6 @@ /**

let message;
const isHidden = filePath.split(path.sep)
.find(segment => /^\./u.test(segment));
const isInNodeModules = baseDir && path.relative(baseDir, filePath).startsWith("node_modules");
const isInNodeModules = baseDir && path.dirname(path.relative(baseDir, filePath)).split(path.sep).includes("node_modules");
if (isHidden) {
message = "File ignored by default. Use a negated ignore pattern (like \"--ignore-pattern '!<relative/path/to/filename>'\") to override.";
} else if (isInNodeModules) {
message = "File ignored by default. Use \"--ignore-pattern '!node_modules/*'\" to override.";
if (isInNodeModules) {
message = "File ignored by default because it is located under the node_modules directory. Use ignore pattern \"!**/node_modules/\" to override.";
} else {

@@ -611,5 +607,7 @@ message = "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to override.";

{
ruleId: null,
fatal: false,
severity: 1,
message
message,
nodeType: null
}

@@ -763,2 +761,5 @@ ],

}
if (!isArrayOfNonEmptyString(ignorePatterns) && ignorePatterns !== null) {
errors.push("'ignorePatterns' must be an array of non-empty strings or null.");
}
if (typeof overrideConfig !== "object") {

@@ -800,3 +801,3 @@ errors.push("'overrideConfig' must be an object or null.");

overrideConfig,
cwd,
cwd: path.normalize(cwd),
errorOnUnmatchedPattern,

@@ -803,0 +804,0 @@ fix,

@@ -292,3 +292,3 @@ /**

configFile: overrideConfigFile,
cwd,
cwd: path.normalize(cwd),
errorOnUnmatchedPattern,

@@ -295,0 +295,0 @@ extensions,

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

/** @typedef {import("../shared/types").LintMessage} LintMessage */
/** @typedef {import("../shared/types").LintResult} LintResult */
/** @typedef {import("../shared/types").ParserOptions} ParserOptions */

@@ -80,3 +81,3 @@ /** @typedef {import("../shared/types").Plugin} Plugin */

* @property {boolean} [ignore] False disables all ignore patterns except for the default ones.
* @property {string[]} [ignorePatterns] Ignore file patterns to use in addition to config ignores.
* @property {string[]} [ignorePatterns] Ignore file patterns to use in addition to config ignores. These patterns are relative to `cwd`.
* @property {ConfigData} [overrideConfig] Override config object, overrides all configs used with this instance

@@ -107,3 +108,13 @@ * @property {boolean|string} [overrideConfigFile] Searches for default config file when falsy;

function calculateStatsPerFile(messages) {
return messages.reduce((stat, message) => {
const stat = {
errorCount: 0,
fatalErrorCount: 0,
warningCount: 0,
fixableErrorCount: 0,
fixableWarningCount: 0
};
for (let i = 0; i < messages.length; i++) {
const message = messages[i];
if (message.fatal || message.severity === 2) {

@@ -123,36 +134,7 @@ stat.errorCount++;

}
return stat;
}, {
errorCount: 0,
fatalErrorCount: 0,
warningCount: 0,
fixableErrorCount: 0,
fixableWarningCount: 0
});
}
return stat;
}
/**
* It will calculate the error and warning count for collection of results from all files
* @param {LintResult[]} results Collection of messages from all the files
* @returns {Object} Contains the stats
* @private
*/
function calculateStatsPerRun(results) {
return results.reduce((stat, result) => {
stat.errorCount += result.errorCount;
stat.fatalErrorCount += result.fatalErrorCount;
stat.warningCount += result.warningCount;
stat.fixableErrorCount += result.fixableErrorCount;
stat.fixableWarningCount += result.fixableWarningCount;
return stat;
}, {
errorCount: 0,
fatalErrorCount: 0,
warningCount: 0,
fixableErrorCount: 0,
fixableWarningCount: 0
});
}
/**
* Create rulesMeta object.

@@ -268,3 +250,3 @@ * @param {Map<string,Rule>} rules a map of rules from which to generate the object.

* @param {string} cwd The current working directory to search from.
* @returns {Promise<string|null>} The filename if found or `null` if not.
* @returns {Promise<string|undefined>} The filename if found or `undefined` if not.
*/

@@ -334,3 +316,3 @@ function findFlatConfigFile(cwd) {

* @param {import("./eslint").ESLintOptions} options The ESLint instance options.
* @returns {{configFilePath:string,basePath:string,error:boolean}} Location information for
* @returns {{configFilePath:string|undefined,basePath:string,error:Error|null}} Location information for
* the config file.

@@ -343,3 +325,3 @@ */

let basePath = cwd;
let error = false;
let error = null;

@@ -356,3 +338,3 @@ if (typeof configFile === "string") {

} else {
error = true;
error = new Error("Could not find config file.");
}

@@ -396,3 +378,3 @@

if (error) {
throw new Error("Could not find config file.");
throw error;
}

@@ -416,35 +398,29 @@

let allIgnorePatterns = [];
// append command line ignore patterns
if (ignorePatterns && ignorePatterns.length > 0) {
// append command line ignore patterns
if (ignorePatterns) {
if (typeof ignorePatterns === "string") {
allIgnorePatterns.push(ignorePatterns);
let relativeIgnorePatterns;
/*
* If the config file basePath is different than the cwd, then
* the ignore patterns won't work correctly. Here, we adjust the
* ignore pattern to include the correct relative path. Patterns
* passed as `ignorePatterns` are relative to the cwd, whereas
* the config file basePath can be an ancestor of the cwd.
*/
if (basePath === cwd) {
relativeIgnorePatterns = ignorePatterns;
} else {
allIgnorePatterns.push(...ignorePatterns);
}
}
/*
* If the config file basePath is different than the cwd, then
* the ignore patterns won't work correctly. Here, we adjust the
* ignore pattern to include the correct relative path. Patterns
* loaded from ignore files are always relative to the cwd, whereas
* the config file basePath can be an ancestor of the cwd.
*/
if (basePath !== cwd && allIgnorePatterns.length) {
const relativeIgnorePath = path.relative(basePath, cwd);
const relativeIgnorePath = path.relative(basePath, cwd);
relativeIgnorePatterns = ignorePatterns.map(pattern => {
const negated = pattern.startsWith("!");
const basePattern = negated ? pattern.slice(1) : pattern;
allIgnorePatterns = allIgnorePatterns.map(pattern => {
const negated = pattern.startsWith("!");
const basePattern = negated ? pattern.slice(1) : pattern;
return (negated ? "!" : "") +
return (negated ? "!" : "") +
path.posix.join(relativeIgnorePath, basePattern);
});
}
});
}
if (allIgnorePatterns.length) {
/*

@@ -455,3 +431,3 @@ * Ignore patterns are added to the end of the config array

configs.push({
ignores: allIgnorePatterns
ignores: relativeIgnorePatterns
});

@@ -570,39 +546,2 @@ }

/**
* Collect used deprecated rules.
* @param {Array<FlatConfig>} configs The configs to evaluate.
* @returns {IterableIterator<DeprecatedRuleInfo>} Used deprecated rules.
*/
function *iterateRuleDeprecationWarnings(configs) {
const processedRuleIds = new Set();
for (const config of configs) {
for (const [ruleId, ruleConfig] of Object.entries(config.rules)) {
// Skip if it was processed.
if (processedRuleIds.has(ruleId)) {
continue;
}
processedRuleIds.add(ruleId);
// Skip if it's not used.
if (!getRuleSeverity(ruleConfig)) {
continue;
}
const rule = getRuleFromConfig(ruleId, config);
// Skip if it's not deprecated.
if (!(rule && rule.meta && rule.meta.deprecated)) {
continue;
}
// This rule was used and deprecated.
yield {
ruleId,
replacedBy: rule.meta.replacedBy || []
};
}
}
}
/**
* Creates an error to be thrown when an array of results passed to `getRulesMetaForResults` was not created by the current engine.

@@ -652,3 +591,2 @@ * @returns {TypeError} An error object.

defaultConfigs,
defaultIgnores: () => false,
configs: null

@@ -832,3 +770,2 @@ });

const startTime = Date.now();
const usedConfigs = [];
const fixTypesSet = fixTypes ? new Set(fixTypes) : null;

@@ -891,11 +828,2 @@

/*
* Store used configs for:
* - this method uses to collect used deprecated rules.
* - `--fix-type` option uses to get the loaded rule's meta data.
*/
if (!usedConfigs.includes(config)) {
usedConfigs.push(config);
}
// Skip if there is cached result.

@@ -969,18 +897,6 @@ if (lintResultCache) {

let usedDeprecatedRules;
const finalResults = results.filter(result => !!result);
return processLintReport(this, {
results: finalResults,
...calculateStatsPerRun(finalResults),
// Initialize it lazily because CLI and `ESLint` API don't use it.
get usedDeprecatedRules() {
if (!usedDeprecatedRules) {
usedDeprecatedRules = Array.from(
iterateRuleDeprecationWarnings(usedConfigs)
);
}
return usedDeprecatedRules;
}
results: finalResults
});

@@ -1047,3 +963,2 @@ }

const resolvedFilename = path.resolve(cwd, filePath || "__placeholder__.js");
let config;

@@ -1057,5 +972,2 @@ // Clear the last used config arrays.

// TODO: Needed?
config = configs.getConfig(resolvedFilename);
// Do lint.

@@ -1075,17 +987,5 @@ results.push(verifyText({

debug(`Linting complete in: ${Date.now() - startTime}ms`);
let usedDeprecatedRules;
return processLintReport(this, {
results,
...calculateStatsPerRun(results),
// Initialize it lazily because CLI and `ESLint` API don't use it.
get usedDeprecatedRules() {
if (!usedDeprecatedRules) {
usedDeprecatedRules = Array.from(
iterateRuleDeprecationWarnings(config)
);
}
return usedDeprecatedRules;
}
results
});

@@ -1234,2 +1134,22 @@

/**
* Returns whether flat config should be used.
* @returns {Promise<boolean>} Whether flat config should be used.
*/
async function shouldUseFlatConfig() {
switch (process.env.ESLINT_USE_FLAT_CONFIG) {
case "true":
return true;
case "false":
return false;
default:
/*
* If neither explicitly enabled nor disabled, then use the presence
* of a flat config file to determine enablement.
*/
return !!(await findFlatConfigFile(process.cwd()));
}
}
//------------------------------------------------------------------------------

@@ -1241,3 +1161,3 @@ // Public Interface

FlatESLint,
findFlatConfigFile
shouldUseFlatConfig
};

@@ -8,2 +8,12 @@ /**

//------------------------------------------------------------------------------
// Typedefs
//------------------------------------------------------------------------------
/** @typedef {import("../shared/types").LintMessage} LintMessage */
//------------------------------------------------------------------------------
// Module Definition
//------------------------------------------------------------------------------
const escapeRegExp = require("escape-string-regexp");

@@ -200,3 +210,3 @@

* (this function always reports unused disable directives).
* @returns {{problems: Problem[], unusedDisableDirectives: Problem[]}} An object with a list
* @returns {{problems: LintMessage[], unusedDisableDirectives: LintMessage[]}} An object with a list
* of problems (including suppressed ones) and unused eslint-disable directives

@@ -203,0 +213,0 @@ */

@@ -112,3 +112,3 @@ /**

if (codePath.thrownSegments.length > 0) {
text += "thrown[label=\"✘\",shape=circle,width=0.3,height=0.3,fixedsize];\n";
text += "thrown[label=\"✘\",shape=circle,width=0.3,height=0.3,fixedsize=true];\n";
}

@@ -115,0 +115,0 @@

@@ -23,2 +23,8 @@ /**

//------------------------------------------------------------------------------
// Typedefs
//------------------------------------------------------------------------------
/** @typedef {import("../shared/types").LintMessage} LintMessage */
//------------------------------------------------------------------------------
// Public Interface

@@ -65,3 +71,3 @@ //------------------------------------------------------------------------------

* @param {Object} location Start line and column of comments for potential error message.
* @returns {({success: true, config: Object}|{success: false, error: Problem})} Result map object
* @returns {({success: true, config: Object}|{success: false, error: LintMessage})} Result map object
*/

@@ -114,3 +120,4 @@ parseJsonConfig(string, location) {

line: location.start.line,
column: location.start.column + 1
column: location.start.column + 1,
nodeType: null
}

@@ -117,0 +124,0 @@ };

@@ -20,2 +20,4 @@ /**

/** @typedef {import("../shared/types").LintMessage} LintMessage */
/**

@@ -33,19 +35,2 @@ * An error message description

/**
* Information about the report
* @typedef {Object} ReportInfo
* @property {string} ruleId The rule ID
* @property {(0|1|2)} severity Severity of the error
* @property {(string|undefined)} message The message
* @property {(string|undefined)} [messageId] The message ID
* @property {number} line The line number
* @property {number} column The column number
* @property {(number|undefined)} [endLine] The ending line number
* @property {(number|undefined)} [endColumn] The ending column number
* @property {(string|null)} nodeType Type of node
* @property {string} source Source text
* @property {({text: string, range: (number[]|null)}|null)} [fix] The fix object
* @property {Array<{text: string, range: (number[]|null)}|null>} [suggestions] Suggestion info
*/
//------------------------------------------------------------------------------

@@ -121,2 +106,18 @@ // Module Definition

/**
* Clones the given fix object.
* @param {Fix|null} fix The fix to clone.
* @returns {Fix|null} Deep cloned fix object or `null` if `null` or `undefined` was passed in.
*/
function cloneFix(fix) {
if (!fix) {
return null;
}
return {
range: [fix.range[0], fix.range[1]],
text: fix.text
};
}
/**
* Check that a fix has a valid range.

@@ -158,3 +159,3 @@ * @param {Fix|null} fix The fix to validate.

if (fixes.length === 1) {
return fixes[0];
return cloneFix(fixes[0]);
}

@@ -205,3 +206,3 @@

assertValidFix(fix);
return fix;
return cloneFix(fix);
}

@@ -247,3 +248,3 @@

* @param {Array<{text: string, range: (number[]|null)}>} options.suggestions The array of suggestions objects
* @returns {function(...args): ReportInfo} Function that returns information about the report
* @returns {LintMessage} Information about the report
*/

@@ -323,3 +324,3 @@ function createProblem(options) {

* @param {SourceCode} sourceCode The `SourceCode` instance for the text being linted
* @returns {function(...args): ReportInfo} Function that returns information about the report
* @returns {function(...args): LintMessage} Function that returns information about the report
*/

@@ -326,0 +327,0 @@

@@ -36,3 +36,3 @@ /**

/* eslint-disable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */
/**

@@ -76,3 +76,2 @@ * A test case that is expected to pass lint.

*/
/* eslint-enable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */

@@ -79,0 +78,0 @@ //------------------------------------------------------------------------------

@@ -66,3 +66,3 @@ /**

/* eslint-disable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */
/**

@@ -112,3 +112,2 @@ * A test case that is expected to pass lint.

*/
/* eslint-enable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */

@@ -115,0 +114,0 @@ //------------------------------------------------------------------------------

@@ -227,49 +227,41 @@ /**

/**
* Creates a new `AccessorData` object for the given getter or setter node.
* @param {ASTNode} node A getter or setter node.
* @returns {AccessorData} New `AccessorData` object that contains the given node.
* Checks accessor pairs in the given list of nodes.
* @param {ASTNode[]} nodes The list to check.
* @returns {void}
* @private
*/
function createAccessorData(node) {
const name = astUtils.getStaticPropertyName(node);
const key = (name !== null) ? name : sourceCode.getTokens(node.key);
function checkList(nodes) {
const accessors = [];
let found = false;
return {
key,
getters: node.kind === "get" ? [node] : [],
setters: node.kind === "set" ? [node] : []
};
}
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
/**
* Merges the given `AccessorData` object into the given accessors list.
* @param {AccessorData[]} accessors The list to merge into.
* @param {AccessorData} accessorData The object to merge.
* @returns {AccessorData[]} The same instance with the merged object.
* @private
*/
function mergeAccessorData(accessors, accessorData) {
const equalKeyElement = accessors.find(a => areEqualKeys(a.key, accessorData.key));
if (isAccessorKind(node)) {
if (equalKeyElement) {
equalKeyElement.getters.push(...accessorData.getters);
equalKeyElement.setters.push(...accessorData.setters);
} else {
accessors.push(accessorData);
}
// Creates a new `AccessorData` object for the given getter or setter node.
const name = astUtils.getStaticPropertyName(node);
const key = (name !== null) ? name : sourceCode.getTokens(node.key);
return accessors;
}
// Merges the given `AccessorData` object into the given accessors list.
for (let j = 0; j < accessors.length; j++) {
const accessor = accessors[j];
/**
* Checks accessor pairs in the given list of nodes.
* @param {ASTNode[]} nodes The list to check.
* @returns {void}
* @private
*/
function checkList(nodes) {
const accessors = nodes
.filter(isAccessorKind)
.map(createAccessorData)
.reduce(mergeAccessorData, []);
if (areEqualKeys(accessor.key, key)) {
accessor.getters.push(...node.kind === "get" ? [node] : []);
accessor.setters.push(...node.kind === "set" ? [node] : []);
found = true;
break;
}
}
if (!found) {
accessors.push({
key,
getters: node.kind === "get" ? [node] : [],
setters: node.kind === "set" ? [node] : []
});
}
found = false;
}
}

@@ -276,0 +268,0 @@ for (const { getters, setters } of accessors) {

@@ -243,7 +243,11 @@ /**

const linebreaksCount = node.elements.map((element, i) => {
let linebreaksCount = 0;
for (let i = 0; i < node.elements.length; i++) {
const element = node.elements[i];
const previousElement = elements[i - 1];
if (i === 0 || element === null || previousElement === null) {
return false;
continue;
}

@@ -255,4 +259,6 @@

return !astUtils.isTokenOnSameLine(lastTokenOfPreviousElement, firstTokenOfCurrentElement);
}).filter(isBreak => isBreak === true).length;
if (!astUtils.isTokenOnSameLine(lastTokenOfPreviousElement, firstTokenOfCurrentElement)) {
linebreaksCount++;
}
}

@@ -259,0 +265,0 @@ const needsLinebreaks = (

@@ -25,3 +25,3 @@ /**

messages: {
outOfScope: "'{{name}}' used outside of binding context."
outOfScope: "'{{name}}' declared on line {{definitionLine}} column {{definitionColumn}} is used outside of binding context."
}

@@ -54,8 +54,18 @@ },

* @param {eslint-scope.Reference} reference A reference to report.
* @param {eslint-scope.Definition} definition A definition for which to report reference.
* @returns {void}
*/
function report(reference) {
function report(reference, definition) {
const identifier = reference.identifier;
const definitionPosition = definition.name.loc.start;
context.report({ node: identifier, messageId: "outOfScope", data: { name: identifier.name } });
context.report({
node: identifier,
messageId: "outOfScope",
data: {
name: identifier.name,
definitionLine: definitionPosition.line,
definitionColumn: definitionPosition.column + 1
}
});
}

@@ -97,3 +107,3 @@

.filter(isOutsideOfScope)
.forEach(report);
.forEach(ref => report(ref, variables[i].defs.find(def => def.parent === node)));
}

@@ -100,0 +110,0 @@ }

@@ -136,4 +136,3 @@ /**

node.computed &&
node.property.type === "TemplateLiteral" &&
node.property.expressions.length === 0
astUtils.isStaticTemplateLiteral(node.property)
) {

@@ -140,0 +139,0 @@ checkComputedProperty(node, node.property.quasis[0].value.cooked);

@@ -141,39 +141,2 @@ /**

/**
* Creates a new `AccessorData` object for the given getter or setter node.
* @param {ASTNode} node A getter or setter node.
* @returns {AccessorData} New `AccessorData` object that contains the given node.
* @private
*/
function createAccessorData(node) {
const name = astUtils.getStaticPropertyName(node);
const key = (name !== null) ? name : sourceCode.getTokens(node.key);
return {
key,
getters: node.kind === "get" ? [node] : [],
setters: node.kind === "set" ? [node] : []
};
}
/**
* Merges the given `AccessorData` object into the given accessors list.
* @param {AccessorData[]} accessors The list to merge into.
* @param {AccessorData} accessorData The object to merge.
* @returns {AccessorData[]} The same instance with the merged object.
* @private
*/
function mergeAccessorData(accessors, accessorData) {
const equalKeyElement = accessors.find(a => areEqualKeys(a.key, accessorData.key));
if (equalKeyElement) {
equalKeyElement.getters.push(...accessorData.getters);
equalKeyElement.setters.push(...accessorData.setters);
} else {
accessors.push(accessorData);
}
return accessors;
}
/**
* Checks accessor pairs in the given list of nodes.

@@ -186,8 +149,36 @@ * @param {ASTNode[]} nodes The list to check.

function checkList(nodes, shouldCheck) {
const accessors = nodes
.filter(shouldCheck)
.filter(isAccessorKind)
.map(createAccessorData)
.reduce(mergeAccessorData, []);
const accessors = [];
let found = false;
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (shouldCheck(node) && isAccessorKind(node)) {
// Creates a new `AccessorData` object for the given getter or setter node.
const name = astUtils.getStaticPropertyName(node);
const key = (name !== null) ? name : sourceCode.getTokens(node.key);
// Merges the given `AccessorData` object into the given accessors list.
for (let j = 0; j < accessors.length; j++) {
const accessor = accessors[j];
if (areEqualKeys(accessor.key, key)) {
accessor.getters.push(...node.kind === "get" ? [node] : []);
accessor.setters.push(...node.kind === "set" ? [node] : []);
found = true;
break;
}
}
if (!found) {
accessors.push({
key,
getters: node.kind === "get" ? [node] : [],
setters: node.kind === "set" ? [node] : []
});
}
found = false;
}
}
for (const { getters, setters } of accessors) {

@@ -194,0 +185,0 @@

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

fixable: "code",
// eslint-disable-next-line eslint-plugin/require-meta-has-suggestions -- Does not detect conditional suggestions
hasSuggestions: true,

@@ -375,4 +374,7 @@ messages: {

const requiresOuterParenthesis = logical.parent.type !== "ExpressionStatement" &&
(astUtils.getPrecedence({ type: "AssignmentExpression" }) < astUtils.getPrecedence(logical.parent));
const parentPrecedence = astUtils.getPrecedence(logical.parent);
const requiresOuterParenthesis = logical.parent.type !== "ExpressionStatement" && (
parentPrecedence === -1 ||
astUtils.getPrecedence({ type: "AssignmentExpression" }) < parentPrecedence
);

@@ -379,0 +381,0 @@ if (!astUtils.isParenthesised(sourceCode, logical) && requiresOuterParenthesis) {

@@ -255,15 +255,19 @@ /**

/**
* A reducer to group an AST node by line number, both start and end.
* @param {Object} acc the accumulator
* @param {ASTNode} node the AST node in question
* @returns {Object} the modified accumulator
* @private
*
* reduce an array of AST nodes by line number, both start and end.
* @param {ASTNode[]} arr array of AST nodes
* @returns {Object} accululated AST nodes
*/
function groupByLineNumber(acc, node) {
for (let i = node.loc.start.line; i <= node.loc.end.line; ++i) {
ensureArrayAndPush(acc, i, node);
function groupArrayByLineNumber(arr) {
const obj = {};
for (let i = 0; i < arr.length; i++) {
const node = arr[i];
for (let j = node.loc.start.line; j <= node.loc.end.line; ++j) {
ensureArrayAndPush(obj, j, node);
}
}
return acc;
return obj;
}

@@ -316,9 +320,9 @@

const strings = getAllStrings();
const stringsByLine = strings.reduce(groupByLineNumber, {});
const stringsByLine = groupArrayByLineNumber(strings);
const templateLiterals = getAllTemplateLiterals();
const templateLiteralsByLine = templateLiterals.reduce(groupByLineNumber, {});
const templateLiteralsByLine = groupArrayByLineNumber(templateLiterals);
const regExpLiterals = getAllRegExpLiterals();
const regExpLiteralsByLine = regExpLiterals.reduce(groupByLineNumber, {});
const regExpLiteralsByLine = groupArrayByLineNumber(regExpLiterals);

@@ -325,0 +329,0 @@ lines.forEach((line, i) => {

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

messageId: "unexpected",
data: { identifier: node.name },
fix(fixer) {

@@ -235,3 +234,2 @@ const linesBetween = sourceCode.getText().slice(lastToken.range[1], nextToken.range[0]).split(astUtils.LINEBREAK_MATCHER);

messageId: "expected",
data: { identifier: node.name },
fix(fixer) {

@@ -238,0 +236,0 @@ if ((noNextLineToken ? getLastCommentLineOfBlock(nextLineNum) : lastToken.loc.end.line) === nextToken.loc.start.line) {

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

conditionalAssign: { type: "boolean" },
ternaryOperandBinaryExpressions: { type: "boolean" },
nestedBinaryExpressions: { type: "boolean" },

@@ -80,2 +81,3 @@ returnAssign: { type: "boolean" },

const EXCEPT_COND_ASSIGN = ALL_NODES && context.options[1] && context.options[1].conditionalAssign === false;
const EXCEPT_COND_TERNARY = ALL_NODES && context.options[1] && context.options[1].ternaryOperandBinaryExpressions === false;
const NESTED_BINARY = ALL_NODES && context.options[1] && context.options[1].nestedBinaryExpressions === false;

@@ -392,2 +394,26 @@ const EXCEPT_RETURN_ASSIGN = ALL_NODES && context.options[1] && context.options[1].returnAssign === false;

/**
* Checks if a node is fixable.
* A node is fixable if removing a single pair of surrounding parentheses does not turn it
* into a directive after fixing other nodes.
* Almost all nodes are fixable, except if all of the following conditions are met:
* The node is a string Literal
* It has a single pair of parentheses
* It is the only child of an ExpressionStatement
* @param {ASTNode} node The node to evaluate.
* @returns {boolean} Whether or not the node is fixable.
* @private
*/
function isFixable(node) {
// if it's not a string literal it can be autofixed
if (node.type !== "Literal" || typeof node.value !== "string") {
return true;
}
if (isParenthesisedTwice(node)) {
return true;
}
return !astUtils.isTopLevelExpressionStatement(node.parent);
}
/**
* Report the node

@@ -435,10 +461,12 @@ * @param {ASTNode} node node to evaluate

messageId: "unexpected",
fix(fixer) {
const parenthesizedSource = sourceCode.text.slice(leftParenToken.range[1], rightParenToken.range[0]);
fix: isFixable(node)
? fixer => {
const parenthesizedSource = sourceCode.text.slice(leftParenToken.range[1], rightParenToken.range[0]);
return fixer.replaceTextRange([
leftParenToken.range[0],
rightParenToken.range[1]
], (requiresLeadingSpace(node) ? " " : "") + parenthesizedSource + (requiresTrailingSpace(node) ? " " : ""));
}
return fixer.replaceTextRange([
leftParenToken.range[0],
rightParenToken.range[1]
], (requiresLeadingSpace(node) ? " " : "") + parenthesizedSource + (requiresTrailingSpace(node) ? " " : ""));
}
: null
});

@@ -867,3 +895,7 @@ }

}
const availableTypes = new Set(["BinaryExpression", "LogicalExpression"]);
if (
!(EXCEPT_COND_TERNARY && availableTypes.has(node.test.type)) &&
!isCondAssignException(node) &&

@@ -875,7 +907,11 @@ hasExcessParensWithPrecedence(node.test, precedence({ type: "LogicalExpression", operator: "||" }))

if (hasExcessParensWithPrecedence(node.consequent, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
if (
!(EXCEPT_COND_TERNARY && availableTypes.has(node.consequent.type)) &&
hasExcessParensWithPrecedence(node.consequent, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
report(node.consequent);
}
if (hasExcessParensWithPrecedence(node.alternate, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
if (
!(EXCEPT_COND_TERNARY && availableTypes.has(node.alternate.type)) &&
hasExcessParensWithPrecedence(node.alternate, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
report(node.alternate);

@@ -882,0 +918,0 @@ }

@@ -42,2 +42,19 @@ /**

/**
* Checks if a node or token is fixable.
* A node is fixable if it can be removed without turning a subsequent statement into a directive after fixing other nodes.
* @param {Token} nodeOrToken The node or token to check.
* @returns {boolean} Whether or not the node is fixable.
*/
function isFixable(nodeOrToken) {
const nextToken = sourceCode.getTokenAfter(nodeOrToken);
if (!nextToken || nextToken.type !== "String") {
return true;
}
const stringNode = sourceCode.getNodeByRangeIndex(nextToken.range[0]);
return !astUtils.isTopLevelExpressionStatement(stringNode.parent);
}
/**
* Reports an unnecessary semicolon error.

@@ -51,13 +68,14 @@ * @param {Node|Token} nodeOrToken A node or a token to be reported.

messageId: "unexpected",
fix(fixer) {
fix: isFixable(nodeOrToken)
? fixer =>
/*
* Expand the replacement range to include the surrounding
* tokens to avoid conflicting with semi.
* https://github.com/eslint/eslint/issues/7928
*/
return new FixTracker(fixer, context.sourceCode)
.retainSurroundingTokens(nodeOrToken)
.remove(nodeOrToken);
}
/*
* Expand the replacement range to include the surrounding
* tokens to avoid conflicting with semi.
* https://github.com/eslint/eslint/issues/7928
*/
new FixTracker(fixer, context.sourceCode)
.retainSurroundingTokens(nodeOrToken)
.remove(nodeOrToken)
: null
});

@@ -64,0 +82,0 @@ }

@@ -58,2 +58,6 @@ /**

default: false
},
skipJSXText: {
type: "boolean",
default: false
}

@@ -81,2 +85,3 @@ },

const skipTemplates = !!options.skipTemplates;
const skipJSXText = !!options.skipJSXText;

@@ -105,3 +110,3 @@ const sourceCode = context.sourceCode;

/**
* Checks identifier or literal nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
* Checks literal nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
* @param {ASTNode} node to check for matching errors.

@@ -111,3 +116,3 @@ * @returns {void}

*/
function removeInvalidNodeErrorsInIdentifierOrLiteral(node) {
function removeInvalidNodeErrorsInLiteral(node) {
const shouldCheckStrings = skipStrings && (typeof node.value === "string");

@@ -152,2 +157,14 @@ const shouldCheckRegExps = skipRegExps && Boolean(node.regex);

/**
* Checks JSX nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
* @param {ASTNode} node to check for matching errors.
* @returns {void}
* @private
*/
function removeInvalidNodeErrorsInJSXText(node) {
if (ALL_IRREGULARS.test(node.raw)) {
removeWhitespaceError(node);
}
}
/**
* Checks the program source for irregular whitespace

@@ -245,5 +262,5 @@ * @param {ASTNode} node The program node

nodes.Identifier = removeInvalidNodeErrorsInIdentifierOrLiteral;
nodes.Literal = removeInvalidNodeErrorsInIdentifierOrLiteral;
nodes.Literal = removeInvalidNodeErrorsInLiteral;
nodes.TemplateElement = skipTemplates ? removeInvalidNodeErrorsInTemplateLiteral : noop;
nodes.JSXText = skipJSXText ? removeInvalidNodeErrorsInJSXText : noop;
nodes["Program:exit"] = function() {

@@ -250,0 +267,0 @@ if (skipComments) {

@@ -86,3 +86,3 @@ /**

function addDecimalPointToNumber(stringNumber) {
return `${stringNumber.slice(0, 1)}.${stringNumber.slice(1)}`;
return `${stringNumber[0]}.${stringNumber.slice(1)}`;
}

@@ -96,3 +96,8 @@

function removeLeadingZeros(numberAsString) {
return numberAsString.replace(/^0*/u, "");
for (let i = 0; i < numberAsString.length; i++) {
if (numberAsString[i] !== "0") {
return numberAsString.slice(i);
}
}
return numberAsString;
}

@@ -106,3 +111,8 @@

function removeTrailingZeros(numberAsString) {
return numberAsString.replace(/0*$/u, "");
for (let i = numberAsString.length - 1; i >= 0; i--) {
if (numberAsString[i] !== "0") {
return numberAsString.slice(0, i + 1);
}
}
return numberAsString;
}

@@ -134,3 +144,3 @@

if (trimmedFloat.startsWith(".")) {
const decimalDigits = trimmedFloat.split(".").pop();
const decimalDigits = trimmedFloat.slice(1);
const significantDigits = removeLeadingZeros(decimalDigits);

@@ -151,3 +161,2 @@

/**

@@ -168,3 +177,2 @@ * Converts a base ten number to proper scientific notation

return `${normalizedCoefficient}e${magnitude}`;
}

@@ -171,0 +179,0 @@

@@ -9,2 +9,8 @@ /**

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const astUtils = require("./utils/ast-utils");
//------------------------------------------------------------------------------
// Rule Definition

@@ -121,11 +127,2 @@ //------------------------------------------------------------------------------

/**
* Function to check if a node is a static string template literal.
* @param {ASTNode} node The node to check.
* @returns {boolean} If the node is a string template literal.
*/
function isStaticTemplateLiteral(node) {
return node && node.type === "TemplateLiteral" && node.expressions.length === 0;
}
/**
* Function to check if a node is a require call.

@@ -149,3 +146,3 @@ * @param {ASTNode} node The node to check.

if (isStaticTemplateLiteral(node)) {
if (astUtils.isStaticTemplateLiteral(node)) {
return node.quasis[0].value.cooked.trim();

@@ -152,0 +149,0 @@ }

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

const astUtils = require("./utils/ast-utils");
//------------------------------------------------------------------------------

@@ -116,4 +118,2 @@ // Rule Definition

function isDirective(node) {
const parent = node.parent,
grandparent = parent.parent;

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

*/
return (parent.type === "Program" || parent.type === "BlockStatement" &&
(/Function/u.test(grandparent.type))) &&
directives(parent).includes(node);
return astUtils.isTopLevelExpressionStatement(node) && directives(node.parent).includes(node);
}

@@ -131,0 +129,0 @@

@@ -9,2 +9,8 @@ /**

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const astUtils = require("./utils/ast-utils");
//------------------------------------------------------------------------------
// Rule Definition

@@ -51,2 +57,41 @@ //------------------------------------------------------------------------------

/**
* Checks if a `LabeledStatement` node is fixable.
* For a node to be fixable, there must be no comments between the label and the body.
* Furthermore, is must be possible to remove the label without turning the body statement into a
* directive after other fixes are applied.
* @param {ASTNode} node The node to evaluate.
* @returns {boolean} Whether or not the node is fixable.
*/
function isFixable(node) {
/*
* Only perform a fix if there are no comments between the label and the body. This will be the case
* when there is exactly one token/comment (the ":") between the label and the body.
*/
if (sourceCode.getTokenAfter(node.label, { includeComments: true }) !==
sourceCode.getTokenBefore(node.body, { includeComments: true })) {
return false;
}
// Looking for the node's deepest ancestor which is not a `LabeledStatement`.
let ancestor = node.parent;
while (ancestor.type === "LabeledStatement") {
ancestor = ancestor.parent;
}
if (ancestor.type === "Program" ||
(ancestor.type === "BlockStatement" && astUtils.isFunction(ancestor.parent))) {
const { body } = node;
if (body.type === "ExpressionStatement" &&
((body.expression.type === "Literal" && typeof body.expression.value === "string") ||
astUtils.isStaticTemplateLiteral(body.expression))) {
return false; // potential directive
}
}
return true;
}
/**
* Removes the top of the stack.

@@ -63,15 +108,3 @@ * At the same time, this reports the label if it's never used.

data: node.label,
fix(fixer) {
/*
* Only perform a fix if there are no comments between the label and the body. This will be the case
* when there is exactly one token/comment (the ":") between the label and the body.
*/
if (sourceCode.getTokenAfter(node.label, { includeComments: true }) ===
sourceCode.getTokenBefore(node.body, { includeComments: true })) {
return fixer.removeRange([node.range[0], node.body.range[0]]);
}
return null;
}
fix: isFixable(node) ? fixer => fixer.removeRange([node.range[0], node.body.range[0]]) : null
});

@@ -78,0 +111,0 @@ }

@@ -469,3 +469,4 @@ /**

parent.left === id &&
isUnusedExpression(parent)
isUnusedExpression(parent) &&
!astUtils.isLogicalAssignmentOperator(parent.operator)
) ||

@@ -472,0 +473,0 @@ (

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

removeEscape: "Remove the `\\`. This maintains the current functionality.",
removeEscapeDoNotKeepSemantics: "Remove the `\\` if it was inserted by mistake.",
escapeBackslash: "Replace the `\\` with `\\\\` to include the actual backslash character."

@@ -129,3 +130,6 @@ },

{
messageId: "removeEscape",
// Removing unnecessary `\` characters in a directive is not guaranteed to maintain functionality.
messageId: astUtils.isDirective(node.parent)
? "removeEscapeDoNotKeepSemantics" : "removeEscape",
fix(fixer) {

@@ -132,0 +136,0 @@ return fixer.removeRange(range);

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

const segmentInfoMap = new WeakMap();
const usedUnreachableSegments = new WeakSet();
const sourceCode = context.sourceCode;

@@ -156,5 +155,6 @@ let scopeInfo = null;

* @param {CodePathSegment} segment The segment to get return statements.
* @param {Set<CodePathSegment>} usedUnreachableSegments A set of segments that have already been traversed in this call.
* @returns {void}
*/
function markReturnStatementsOnSegmentAsUsed(segment) {
function markReturnStatementsOnSegmentAsUsed(segment, usedUnreachableSegments) {
if (!segment.reachable) {

@@ -165,3 +165,3 @@ usedUnreachableSegments.add(segment);

.filter(prevSegment => !usedUnreachableSegments.has(prevSegment))
.forEach(markReturnStatementsOnSegmentAsUsed);
.forEach(prevSegment => markReturnStatementsOnSegmentAsUsed(prevSegment, usedUnreachableSegments));
return;

@@ -172,6 +172,25 @@ }

for (const node of info.uselessReturns) {
info.uselessReturns = info.uselessReturns.filter(node => {
if (scopeInfo.traversedTryBlockStatements && scopeInfo.traversedTryBlockStatements.length > 0) {
const returnInitialRange = node.range[0];
const returnFinalRange = node.range[1];
const areBlocksInRange = scopeInfo.traversedTryBlockStatements.some(tryBlockStatement => {
const blockInitialRange = tryBlockStatement.range[0];
const blockFinalRange = tryBlockStatement.range[1];
return (
returnInitialRange >= blockInitialRange &&
returnFinalRange <= blockFinalRange
);
});
if (areBlocksInRange) {
return true;
}
}
remove(scopeInfo.uselessReturns, node);
}
info.uselessReturns = [];
return false;
});
}

@@ -195,3 +214,3 @@

.currentSegments
.forEach(markReturnStatementsOnSegmentAsUsed);
.forEach(segment => markReturnStatementsOnSegmentAsUsed(segment, new Set()));
}

@@ -210,2 +229,3 @@

uselessReturns: [],
traversedTryBlockStatements: [],
codePath

@@ -284,2 +304,10 @@ };

"TryStatement > BlockStatement.block:exit"(node) {
scopeInfo.traversedTryBlockStatements.push(node);
},
"TryStatement:exit"() {
scopeInfo.traversedTryBlockStatements.pop();
},
/*

@@ -286,0 +314,0 @@ * Registers for all statement nodes except FunctionDeclaration, BlockStatement, BreakStatement.

@@ -134,45 +134,2 @@ /**

/**
* 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.

@@ -370,8 +327,6 @@ *

directive: {
test: isDirectivePrologue
test: astUtils.isDirective
},
expression: {
test: (node, sourceCode) =>
node.type === "ExpressionStatement" &&
!isDirectivePrologue(node, sourceCode)
test: node => node.type === "ExpressionStatement" && !astUtils.isDirective(node)
},

@@ -387,6 +342,6 @@ iife: {

"multiline-expression": {
test: (node, sourceCode) =>
test: node =>
node.loc.start.line !== node.loc.end.line &&
node.type === "ExpressionStatement" &&
!isDirectivePrologue(node, sourceCode)
!astUtils.isDirective(node)
},

@@ -393,0 +348,0 @@

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

const parentPrecedence = astUtils.getPrecedence(parent);
const needsParens = (

@@ -63,3 +64,3 @@ parent.type === "ClassDeclaration" ||

parent.type.endsWith("Expression") &&
astUtils.getPrecedence(parent) >= PRECEDENCE_OF_EXPONENTIATION_EXPR &&
(parentPrecedence === -1 || parentPrecedence >= PRECEDENCE_OF_EXPONENTIATION_EXPR) &&
!(parent.type === "BinaryExpression" && parent.operator === "**" && parent.right === node) &&

@@ -66,0 +67,0 @@ !((parent.type === "CallExpression" || parent.type === "NewExpression") && parent.arguments.includes(node)) &&

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

/**
* Determines whether the given node is a template literal without expressions.
* @param {ASTNode} node Node to check.
* @returns {boolean} True if the node is a template literal without expressions.
*/
function isStaticTemplateLiteral(node) {
return node.type === "TemplateLiteral" && node.expressions.length === 0;
}
const validPrecedingTokens = new Set([

@@ -182,3 +173,3 @@ "(",

isGlobalReference(astUtils.skipChainExpression(node.tag).object) &&
isStaticTemplateLiteral(node.quasi);
astUtils.isStaticTemplateLiteral(node.quasi);
}

@@ -196,3 +187,3 @@

if (isStaticTemplateLiteral(node)) {
if (astUtils.isStaticTemplateLiteral(node)) {
return node.quasis[0].value.cooked;

@@ -215,3 +206,3 @@ }

return isStringLiteral(node) ||
isStaticTemplateLiteral(node) ||
astUtils.isStaticTemplateLiteral(node) ||
isStringRawTaggedStaticTemplateLiteral(node);

@@ -218,0 +209,0 @@ }

@@ -160,3 +160,4 @@ /**

* Checks whether or not a given node is a directive.
* The directive is a `ExpressionStatement` which has only a string literal.
* The directive is a `ExpressionStatement` which has only a string literal not surrounded by
* parentheses.
* @param {ASTNode} node A node to check.

@@ -170,3 +171,4 @@ * @returns {boolean} Whether or not the node is a directive.

node.expression.type === "Literal" &&
typeof node.expression.value === "string"
typeof node.expression.value === "string" &&
!astUtils.isParenthesised(sourceCode, node.expression)
);

@@ -176,14 +178,13 @@ }

/**
* Checks whether or not a given node is a part of directive prologues.
* See also: http://www.ecma-international.org/ecma-262/6.0/#sec-directive-prologues-and-the-use-strict-directive
* Checks whether a specified node is either part of, or immediately follows a (possibly empty) directive prologue.
* @see {@link http://www.ecma-international.org/ecma-262/6.0/#sec-directive-prologues-and-the-use-strict-directive}
* @param {ASTNode} node A node to check.
* @returns {boolean} Whether or not the node is a part of directive prologues.
* @returns {boolean} Whether a specified node is either part of, or immediately follows a (possibly empty) directive prologue.
* @private
*/
function isPartOfDirectivePrologue(node) {
const block = node.parent.parent;
if (block.type !== "Program" && (block.type !== "BlockStatement" || !astUtils.isFunction(block.parent))) {
function isExpressionInOrJustAfterDirectivePrologue(node) {
if (!astUtils.isTopLevelExpressionStatement(node.parent)) {
return false;
}
const block = node.parent.parent;

@@ -218,3 +219,3 @@ // Check the node is at a prologue.

case "ExpressionStatement":
return isPartOfDirectivePrologue(node);
return !astUtils.isParenthesised(sourceCode, node) && isExpressionInOrJustAfterDirectivePrologue(node);

@@ -335,8 +336,7 @@ // LiteralPropertyName.

fix(fixer) {
if (isPartOfDirectivePrologue(node)) {
if (astUtils.isTopLevelExpressionStatement(node.parent) && !astUtils.isParenthesised(sourceCode, node)) {
/*
* TemplateLiterals in a directive prologue aren't actually directives, but if they're
* in the directive prologue, then fixing them might turn them into directives and change
* the behavior of the code.
* TemplateLiterals aren't actually directives, but fixing them might turn
* them into directives and change the behavior of the code.
*/

@@ -343,0 +343,0 @@ return null;

@@ -8,2 +8,8 @@ /**

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const astUtils = require("./utils/ast-utils");
//------------------------------------------------------------------------------
// Rule Definition

@@ -92,3 +98,3 @@ //------------------------------------------------------------------------------

if (sibling.type === "Literal" || sibling.type === "TemplateLiteral" && !sibling.expressions.length) {
if (sibling.type === "Literal" || astUtils.isStaticTemplateLiteral(sibling)) {
const value = sibling.type === "Literal" ? sibling.value : sibling.quasis[0].value.cooked;

@@ -95,0 +101,0 @@

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

/**
* Determines whether a node is a Template Literal which can be determined statically.
* @param {ASTNode} node Node to test
* @returns {boolean} True if the node is a Template Literal without expression.
*/
function isStaticTemplateLiteral(node) {
return node.type === "TemplateLiteral" && node.expressions.length === 0;
}
/**
* Determines whether a non-Literal node should be treated as a single Literal node.

@@ -77,3 +68,3 @@ * @param {ASTNode} node Node to test

function looksLikeLiteral(node) {
return isNegativeNumericLiteral(node) || isStaticTemplateLiteral(node);
return isNegativeNumericLiteral(node) || astUtils.isStaticTemplateLiteral(node);
}

@@ -105,3 +96,3 @@

if (isStaticTemplateLiteral(node)) {
if (astUtils.isStaticTemplateLiteral(node)) {
return {

@@ -108,0 +99,0 @@ type: "Literal",

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

const GraphemeSplitter = require("grapheme-splitter");
const Graphemer = require("graphemer").default;

@@ -22,3 +22,3 @@ //------------------------------------------------------------------------------

/** @type {GraphemeSplitter | undefined} */
/** @type {Graphemer | undefined} */
let splitter;

@@ -53,3 +53,3 @@

if (!splitter) {
splitter = new GraphemeSplitter();
splitter = new Graphemer();
}

@@ -56,0 +56,0 @@

@@ -24,3 +24,3 @@ /**

* @property {EcmaFeatures} [ecmaFeatures] The optional features.
* @property {3|5|6|7|8|9|10|11|12|13|14|2015|2016|2017|2018|2019|2020|2021|2022|2023} [ecmaVersion] The ECMAScript version (or revision number).
* @property {3|5|6|7|8|9|10|11|12|13|14|15|2015|2016|2017|2018|2019|2020|2021|2022|2023|2024} [ecmaVersion] The ECMAScript version (or revision number).
* @property {"script"|"module"} [sourceType] The source code type.

@@ -100,6 +100,8 @@ * @property {boolean} [allowReserved] Allowing the use of reserved words as identifiers in ES3.

* @property {number} [endLine] The 1-based line number of the end location.
* @property {boolean} fatal If `true` then this is a fatal error.
* @property {boolean} [fatal] If `true` then this is a fatal error.
* @property {{range:[number,number], text:string}} [fix] Information for autofix.
* @property {number|undefined} line The 1-based line number.
* @property {string} message The error message.
* @property {string} [messageId] The ID of the message in the rule's meta.
* @property {(string|null)} nodeType Type of node
* @property {string|null} ruleId The ID of the rule which makes this message.

@@ -115,6 +117,8 @@ * @property {0|1|2} severity The severity of this message.

* @property {number} [endLine] The 1-based line number of the end location.
* @property {boolean} fatal If `true` then this is a fatal error.
* @property {boolean} [fatal] If `true` then this is a fatal error.
* @property {{range:[number,number], text:string}} [fix] Information for autofix.
* @property {number|undefined} line The 1-based line number.
* @property {string} message The error message.
* @property {string} [messageId] The ID of the message in the rule's meta.
* @property {(string|null)} nodeType Type of node
* @property {string|null} ruleId The ID of the rule which makes this message.

@@ -121,0 +125,0 @@ * @property {0|1|2} severity The severity of this message.

@@ -15,4 +15,5 @@ /**

const { FileEnumerator } = require("./cli-engine/file-enumerator");
const { FlatESLint } = require("./eslint/flat-eslint");
const { FlatESLint, shouldUseFlatConfig } = require("./eslint/flat-eslint");
const FlatRuleTester = require("./rule-tester/flat-rule-tester");
const { ESLint } = require("./eslint/eslint");

@@ -26,4 +27,6 @@ //-----------------------------------------------------------------------------

FlatESLint,
shouldUseFlatConfig,
FlatRuleTester,
FileEnumerator
FileEnumerator,
LegacyESLint: ESLint
};
{
"name": "eslint",
"version": "8.40.0",
"version": "8.45.0",
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",

@@ -65,5 +65,5 @@ "description": "An AST-based pattern checker for JavaScript.",

"@eslint-community/regexpp": "^4.4.0",
"@eslint/eslintrc": "^2.0.3",
"@eslint/js": "8.40.0",
"@humanwhocodes/config-array": "^0.11.8",
"@eslint/eslintrc": "^2.1.0",
"@eslint/js": "8.44.0",
"@humanwhocodes/config-array": "^0.11.10",
"@humanwhocodes/module-importer": "^1.0.1",

@@ -79,3 +79,3 @@ "@nodelib/fs.walk": "^1.2.8",

"eslint-visitor-keys": "^3.4.1",
"espree": "^9.5.2",
"espree": "^9.6.0",
"esquery": "^1.4.2",

@@ -88,9 +88,7 @@ "esutils": "^2.0.2",

"globals": "^13.19.0",
"grapheme-splitter": "^1.0.4",
"graphemer": "^1.4.0",
"ignore": "^5.2.0",
"import-fresh": "^3.0.0",
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
"is-path-inside": "^3.0.3",
"js-sdsl": "^4.1.4",
"js-yaml": "^4.1.0",

@@ -102,5 +100,4 @@ "json-stable-stringify-without-jsonify": "^1.0.1",

"natural-compare": "^1.4.0",
"optionator": "^0.9.1",
"optionator": "^0.9.3",
"strip-ansi": "^6.0.1",
"strip-json-comments": "^3.1.0",
"text-table": "^0.2.0"

@@ -121,6 +118,6 @@ },

"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-eslint-plugin": "^4.4.0",
"eslint-plugin-eslint-plugin": "^5.1.0",
"eslint-plugin-internal-rules": "file:tools/internal-rules",
"eslint-plugin-jsdoc": "^38.1.6",
"eslint-plugin-n": "^15.2.4",
"eslint-plugin-jsdoc": "^46.2.5",
"eslint-plugin-n": "^16.0.0",
"eslint-plugin-unicorn": "^42.0.0",

@@ -162,6 +159,5 @@ "eslint-release": "^3.2.0",

"regenerator-runtime": "^0.13.2",
"semver": "^7.3.5",
"semver": "^7.5.3",
"shelljs": "^0.8.2",
"sinon": "^11.0.0",
"temp": "^0.9.0",
"webpack": "^5.23.0",

@@ -168,0 +164,0 @@ "webpack-cli": "^4.5.0",

@@ -120,3 +120,3 @@ [![npm version](https://img.shields.io/npm/v/eslint.svg)](https://www.npmjs.com/package/eslint)

ESLint has full support for ECMAScript 3, 5 (default), 2015, 2016, 2017, 2018, 2019, 2020, 2021 and 2022. You can set your desired ECMAScript syntax (and other settings, like global variables or your target environments) through [configuration](https://eslint.org/docs/latest/use/configure).
ESLint has full support for ECMAScript 3, 5 (default), 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, and 2023. You can set your desired ECMAScript syntax (and other settings, like global variables or your target environments) through [configuration](https://eslint.org/docs/latest/use/configure).

@@ -288,3 +288,3 @@ ### What about experimental features?

<p><a href="https://sentry.io"><img src="https://avatars.githubusercontent.com/u/1396951?v=4" alt="Sentry" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="64"></a></p><h3>Bronze Sponsors</h3>
<p><a href="https://paydaysay.com/"><img src="https://images.opencollective.com/payday-say-organization/9cd2467/logo.png" alt="PayDay Say" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8: free icons, photos, illustrations, and music" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://transloadit.com/"><img src="https://avatars.githubusercontent.com/u/125754?v=4" alt="Transloadit" height="32"></a> <a href="https://www.ignitionapp.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Ignition" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a> <a href="https://quickbookstoolhub.com"><img src="https://avatars.githubusercontent.com/u/95090305?u=e5bc398ef775c9ed19f955c675cdc1fb6abf01df&v=4" alt="QuickBooks Tool hub" height="32"></a></p>
<p><a href="https://iboysoft.com/"><img src="https://images.opencollective.com/iboysoft-software/7f9d60e/avatar.png" alt="iBoysoft" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8: free icons, photos, illustrations, and music" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://transloadit.com/"><img src="https://avatars.githubusercontent.com/u/125754?v=4" alt="Transloadit" height="32"></a> <a href="https://www.ignitionapp.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Ignition" height="32"></a> <a href="https://opensource.mercedes-benz.com/"><img src="https://avatars.githubusercontent.com/u/34240465?v=4" alt="Mercedes-Benz Group" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a> <a href="https://quickbookstoolhub.com"><img src="https://avatars.githubusercontent.com/u/95090305?u=e5bc398ef775c9ed19f955c675cdc1fb6abf01df&v=4" alt="QuickBooks Tool hub" height="32"></a></p>
<!--sponsorsend-->

@@ -291,0 +291,0 @@

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

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