Socket
Socket
Sign inDemoInstall

eslint

Package Overview
Dependencies
Maintainers
4
Versions
367
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 7.20.0 to 8.50.0

conf/globals.js

32

bin/eslint.js

@@ -8,9 +8,6 @@ #!/usr/bin/env node

/* eslint no-console:off */
/* eslint no-console:off -- CLI */
"use strict";
// to use V8's code cache to speed up instantiation time
require("v8-compile-cache");
// must do this initialization *before* other requires in order to work

@@ -70,9 +67,6 @@ if (process.argv.includes("--debug")) {

// Lazy loading because those are used only if error happened.
const fs = require("fs");
const path = require("path");
// Lazy loading because this is used only if an error happened.
const util = require("util");
const lodash = require("lodash");
// Foolproof -- thirdparty module might throw non-object.
// Foolproof -- third-party module might throw non-object.
if (typeof error !== "object" || error === null) {

@@ -85,11 +79,4 @@ return String(error);

try {
const templateFilePath = path.resolve(
__dirname,
`../messages/${error.messageTemplate}.txt`
);
const template = require(`../messages/${error.messageTemplate}.js`);
// Use sync API because Node.js should exit at this tick.
const templateText = fs.readFileSync(templateFilePath, "utf-8");
const template = lodash.template(templateText);
return template(error.messageData || {});

@@ -140,3 +127,9 @@ } catch {

if (process.argv.includes("--init")) {
await require("../lib/init/config-initializer").initializeConfig();
// `eslint --init` has been moved to `@eslint/create-config`
console.warn("You can also run this command directly using 'npm init @eslint/config'.");
const spawn = require("cross-spawn");
spawn.sync("npm", ["init", "@eslint/config"], { encoding: "utf8", stdio: "inherit" });
return;

@@ -148,4 +141,5 @@ }

process.argv,
process.argv.includes("--stdin") ? await readStdin() : null
process.argv.includes("--stdin") ? await readStdin() : null,
true
);
}()).catch(onFatalError);

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

cacheFile: ".eslintcache",
cacheStrategy: "metadata",
fix: false,

@@ -29,0 +30,0 @@ allowInlineConfig: true,

@@ -8,3 +8,6 @@ /**

const { CLIEngine } = require("./cli-engine");
//-----------------------------------------------------------------------------
// Requirements
//-----------------------------------------------------------------------------
const { ESLint } = require("./eslint");

@@ -15,5 +18,8 @@ const { Linter } = require("./linter");

//-----------------------------------------------------------------------------
// Exports
//-----------------------------------------------------------------------------
module.exports = {
Linter,
CLIEngine,
ESLint,

@@ -23,15 +29,1 @@ RuleTester,

};
// DOTO: remove deprecated API.
let deprecatedLinterInstance = null;
Object.defineProperty(module.exports, "linter", {
enumerable: false,
get() {
if (!deprecatedLinterInstance) {
deprecatedLinterInstance = new Linter();
}
return deprecatedLinterInstance;
}
});

@@ -30,12 +30,7 @@ /**

IgnorePattern,
getUsedExtractedConfigs
getUsedExtractedConfigs,
ModuleResolver
}
} = require("@eslint/eslintrc");
/*
* For some reason, ModuleResolver must be included via filepath instead of by
* API exports in order to work properly. That's why this is separated out onto
* its own require() statement.
*/
const ModuleResolver = require("@eslint/eslintrc/lib/shared/relative-module-resolver");
const { FileEnumerator } = require("./file-enumerator");

@@ -50,3 +45,3 @@

const debug = require("debug")("eslint:cli-engine");
const validFixTypes = new Set(["problem", "suggestion", "layout"]);
const validFixTypes = new Set(["directive", "problem", "suggestion", "layout"]);

@@ -61,2 +56,3 @@ //------------------------------------------------------------------------------

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

@@ -66,4 +62,5 @@ /** @typedef {import("../shared/types").Plugin} Plugin */

/** @typedef {import("../shared/types").Rule} Rule */
/** @typedef {ReturnType<CascadingConfigArrayFactory["getConfigArrayForFile"]>} ConfigArray */
/** @typedef {ReturnType<ConfigArray["extractConfig"]>} ExtractedConfig */
/** @typedef {import("../shared/types").FormatterFunction} FormatterFunction */
/** @typedef {ReturnType<CascadingConfigArrayFactory.getConfigArrayForFile>} ConfigArray */
/** @typedef {ReturnType<ConfigArray.extractConfig>} ExtractedConfig */

@@ -103,3 +100,5 @@ /**

* @property {LintMessage[]} messages All of the messages for the result.
* @property {SuppressedLintMessage[]} suppressedMessages All of the suppressed messages for the result.
* @property {number} errorCount Number of errors for the result.
* @property {number} fatalErrorCount Number of fatal errors for the result.
* @property {number} warningCount Number of warnings for the result.

@@ -117,2 +116,3 @@ * @property {number} fixableErrorCount Number of fixable errors for the result.

* @property {number} errorCount Number of errors for the result.
* @property {number} fatalErrorCount Number of fatal errors for the result.
* @property {number} warningCount Number of warnings for the result.

@@ -167,5 +167,18 @@ * @property {number} fixableErrorCount Number of fixable errors for the result.

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) {
stat.errorCount++;
if (message.fatal) {
stat.fatalErrorCount++;
}
if (message.fix) {

@@ -180,9 +193,4 @@ stat.fixableErrorCount++;

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

@@ -197,14 +205,21 @@

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;
stat.fatalErrorCount += result.fatalErrorCount;
stat.warningCount += result.warningCount;
stat.fixableErrorCount += result.fixableErrorCount;
stat.fixableWarningCount += result.fixableWarningCount;
return stat;
}, {
errorCount: 0,
warningCount: 0,
fixableErrorCount: 0,
fixableWarningCount: 0
});
}
return stat;
}

@@ -272,2 +287,3 @@

messages,
suppressedMessages: linter.getSuppressedMessages(),
...calculateStatsPerFile(messages)

@@ -292,3 +308,3 @@ };

* @param {string} filePath File path of checked code
* @param {string} baseDir Absolute path of base directory
* @param {string} baseDir Absolute path of base directory
* @returns {LintResult} Result with single warning

@@ -315,8 +331,12 @@ * @private

{
ruleId: null,
fatal: false,
severity: 1,
message
message,
nodeType: null
}
],
suppressedMessages: [],
errorCount: 0,
fatalErrorCount: 0,
warningCount: 1,

@@ -346,2 +366,19 @@ fixableErrorCount: 0,

/**
* Checks whether a message's rule type should be fixed.
* @param {LintMessage} message The message to check.
* @param {ConfigArray[]} lastConfigArrays The list of config arrays that the last `executeOnFiles` or `executeOnText` used.
* @param {string[]} fixTypes An array of fix types to check.
* @returns {boolean} Whether the message should be fixed.
*/
function shouldMessageBeFixed(message, lastConfigArrays, fixTypes) {
if (!message.ruleId) {
return fixTypes.has("directive");
}
const rule = message.ruleId && getRule(message.ruleId, lastConfigArrays);
return Boolean(rule && rule.meta && fixTypes.has(rule.meta.type));
}
/**
* Collect used deprecated rules.

@@ -356,5 +393,3 @@ * @param {ConfigArray[]} usedConfigArrays The config arrays which were used.

/** @type {ExtractedConfig[]} */
const configs = [].concat(
...usedConfigArrays.map(getUsedExtractedConfigs)
);
const configs = usedConfigArrays.flatMap(getUsedExtractedConfigs);

@@ -407,3 +442,3 @@ // Traverse rule configs.

*
* if cacheFile points to a file or looks like a file then in will just use that file
* if cacheFile points to a file or looks like a file then it will just use that file
* @param {string} cacheFile The name of file to be used to store the cache

@@ -480,2 +515,3 @@ * @param {string} cwd Current working directory

* @param {string} displayName The property name which is used in error message.
* @throws {Error} Requires array.
* @returns {Record<string,boolean>} The boolean map.

@@ -544,2 +580,3 @@ */

* @param {string} resolvedPath A path from the CWD
* @throws {Error} As thrown by `fs.statSync` or `fs.isDirectory`.
* @returns {boolean} `true` if a directory exists

@@ -562,2 +599,5 @@ */

/**
* Core CLI.
*/
class CLIEngine {

@@ -568,4 +608,6 @@

* @param {CLIEngineOptions} providedOptions The options for this instance.
* @param {Object} [additionalData] Additional settings that are not CLIEngineOptions.
* @param {Record<string,Plugin>|null} [additionalData.preloadedPlugins] Preloaded plugins.
*/
constructor(providedOptions) {
constructor(providedOptions, { preloadedPlugins } = {}) {
const options = Object.assign(

@@ -583,2 +625,9 @@ Object.create(null),

const additionalPluginPool = new Map();
if (preloadedPlugins) {
for (const [id, plugin] of Object.entries(preloadedPlugins)) {
additionalPluginPool.set(id, plugin);
}
}
const cacheFilePath = getCacheFile(

@@ -600,4 +649,4 @@ options.cacheLocation || options.cacheFile,

loadRules,
eslintRecommendedPath: path.resolve(__dirname, "../../conf/eslint-recommended.js"),
eslintAllPath: path.resolve(__dirname, "../../conf/eslint-all.js")
getEslintRecommendedConfig: () => require("@eslint/js").configs.recommended,
getEslintAllConfig: () => require("@eslint/js").configs.all
});

@@ -613,3 +662,3 @@ const fileEnumerator = new FileEnumerator({

const lintResultCache =
options.cache ? new LintResultCache(cacheFilePath) : null;
options.cache ? new LintResultCache(cacheFilePath, options.cacheStrategy) : null;
const linter = new Linter({ cwd: options.cwd });

@@ -647,8 +696,3 @@

options.fix = message => {
const rule = message.ruleId && getRule(message.ruleId, lastConfigArrays);
const matches = rule && rule.meta && fixTypes.has(rule.meta.type);
return matches && originalFix(message);
};
options.fix = message => shouldMessageBeFixed(message, lastConfigArrays, fixTypes) && originalFix(message);
}

@@ -679,2 +723,3 @@ }

const filteredMessages = result.messages.filter(isErrorMessage);
const filteredSuppressedMessages = result.suppressedMessages.filter(isErrorMessage);

@@ -685,2 +730,3 @@ if (filteredMessages.length > 0) {

messages: filteredMessages,
suppressedMessages: filteredSuppressedMessages,
errorCount: filteredMessages.length,

@@ -708,23 +754,3 @@ warningCount: 0,

/**
* Add a plugin by passing its configuration
* @param {string} name Name of the plugin.
* @param {Plugin} pluginObject Plugin configuration object.
* @returns {void}
*/
addPlugin(name, pluginObject) {
const {
additionalPluginPool,
configArrayFactory,
lastConfigArrays
} = internalSlotsMap.get(this);
additionalPluginPool.set(name, pluginObject);
configArrayFactory.clearCache();
lastConfigArrays.length = 1;
lastConfigArrays[0] = configArrayFactory.getConfigArrayForFile();
}
/**
* Resolves the patterns passed into executeOnFiles() into glob-based patterns

@@ -758,2 +784,3 @@ * for easier handling.

* @param {string[]} patterns An array of file and directory names.
* @throws {Error} As may be thrown by `fs.unlinkSync`.
* @returns {LintReport} The results for all files that were linted.

@@ -965,2 +992,3 @@ */

* @param {string} filePath The path of the file to retrieve a config object for.
* @throws {Error} If filepath a directory path.
* @returns {ConfigData} A configuration object for the file.

@@ -1014,3 +1042,4 @@ */

* custom formatter.
* @returns {(Function|null)} The formatter function or null if the `format` is not a string.
* @throws {any} As may be thrown by requiring of formatter
* @returns {(FormatterFunction|null)} The formatter function or null if the `format` is not a string.
*/

@@ -1035,3 +1064,3 @@ getFormatter(format) {

// if there's a slash, then it's a file (TODO: this check seems dubious for scoped npm packages)
if (!namespace && normalizedFormatName.indexOf("/") > -1) {
if (!namespace && normalizedFormatName.includes("/")) {
formatterPath = path.resolve(cwd, normalizedFormatName);

@@ -1051,3 +1080,7 @@ } else {

} catch (ex) {
ex.message = `There was a problem loading formatter: ${formatterPath}\nError: ${ex.message}`;
if (format === "table" || format === "codeframe") {
ex.message = `The ${format} formatter is no longer part of core ESLint. Install it manually with \`npm install -D eslint-formatter-${format}\``;
} else {
ex.message = `There was a problem loading formatter: ${formatterPath}\nError: ${ex.message}`;
}
throw ex;

@@ -1054,0 +1087,0 @@ }

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

const isGlob = require("is-glob");
const { escapeRegExp } = require("lodash");
const escapeRegExp = require("escape-string-regexp");
const { Minimatch } = require("minimatch");

@@ -64,3 +64,3 @@

// For VSCode intellisense
/** @typedef {ReturnType<CascadingConfigArrayFactory["getConfigArrayForFile"]>} ConfigArray */
/** @typedef {ReturnType<CascadingConfigArrayFactory.getConfigArrayForFile>} ConfigArray */

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

* @param {string} filePath The path to target file.
* @throws {Error} As may be thrown by `fs.statSync`.
* @returns {fs.Stats|null} The stats.

@@ -127,3 +128,4 @@ * @private

} catch (error) {
/* istanbul ignore next */
/* c8 ignore next */
if (error.code !== "ENOENT") {

@@ -139,2 +141,3 @@ throw error;

* @param {string} directoryPath The path to target directory.
* @throws {Error} As may be thrown by `fs.readdirSync`.
* @returns {import("fs").Dirent[]} The filenames.

@@ -147,3 +150,4 @@ * @private

} catch (error) {
/* istanbul ignore next */
/* c8 ignore next */
if (error.code !== "ENOENT") {

@@ -182,3 +186,2 @@ throw error;

// eslint-disable-next-line jsdoc/require-description
/**

@@ -200,3 +203,2 @@ * @param {string} pattern The glob pattern which was not found.

// eslint-disable-next-line jsdoc/require-description
/**

@@ -226,4 +228,4 @@ * @param {string} pattern The glob pattern which was not found.

cwd,
eslintRecommendedPath: path.resolve(__dirname, "../../conf/eslint-recommended.js"),
eslintAllPath: path.resolve(__dirname, "../../conf/eslint-all.js")
getEslintRecommendedConfig: () => require("@eslint/js").configs.recommended,
getEslintAllConfig: () => require("@eslint/js").configs.all
}),

@@ -282,2 +284,3 @@ extensions = null,

* @param {string|string[]} patternOrPatterns The glob patterns to iterate files.
* @throws {NoFilesFoundError|AllFilesIgnoredError} On an unmatched pattern.
* @returns {IterableIterator<FileAndConfig>} The found files.

@@ -359,3 +362,3 @@ */

if (globInputPaths && isGlobPattern(pattern)) {
return this._iterateFilesWithGlob(absolutePath, isDot);
return this._iterateFilesWithGlob(pattern, isDot);
}

@@ -409,4 +412,6 @@

const directoryPath = path.resolve(getGlobParent(pattern));
const globPart = pattern.slice(directoryPath.length + 1);
const { cwd } = internalSlotsMap.get(this);
const directoryPath = path.resolve(cwd, getGlobParent(pattern));
const absolutePath = path.resolve(cwd, pattern);
const globPart = absolutePath.slice(directoryPath.length + 1);

@@ -418,3 +423,3 @@ /*

const recursive = /\*\*|\/|\\/u.test(globPart);
const selector = new Minimatch(pattern, minimatchOpts);
const selector = new Minimatch(absolutePath, minimatchOpts);

@@ -449,5 +454,10 @@ debug(`recursive? ${recursive}`);

const filePath = path.join(directoryPath, entry.name);
const fileInfo = entry.isSymbolicLink() ? statSafeSync(filePath) : entry;
if (!fileInfo) {
continue;
}
// Check if the file is matched.
if (entry.isFile()) {
if (fileInfo.isFile()) {
if (!config) {

@@ -488,3 +498,3 @@ config = configArrayFactory.getConfigArrayForFile(

// Dive into the sub directory.
} else if (options.recursive && entry.isDirectory()) {
} else if (options.recursive && fileInfo.isDirectory()) {
if (!config) {

@@ -491,0 +501,0 @@ config = configArrayFactory.getConfigArrayForFile(

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

const lodash = require("lodash");
const fs = require("fs");
const path = require("path");
//------------------------------------------------------------------------------

@@ -16,7 +12,174 @@ // Helpers

const pageTemplate = lodash.template(fs.readFileSync(path.join(__dirname, "html-template-page.html"), "utf-8"));
const messageTemplate = lodash.template(fs.readFileSync(path.join(__dirname, "html-template-message.html"), "utf-8"));
const resultTemplate = lodash.template(fs.readFileSync(path.join(__dirname, "html-template-result.html"), "utf-8"));
const encodeHTML = (function() {
const encodeHTMLRules = {
"&": "&#38;",
"<": "&#60;",
">": "&#62;",
'"': "&#34;",
"'": "&#39;"
};
const matchHTML = /[&<>"']/ug;
return function(code) {
return code
? code.toString().replace(matchHTML, m => encodeHTMLRules[m] || m)
: "";
};
}());
/**
* Get the final HTML document.
* @param {Object} it data for the document.
* @returns {string} HTML document.
*/
function pageTemplate(it) {
const { reportColor, reportSummary, date, results } = it;
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ESLint Report</title>
<link rel="icon" type="image/png" sizes="any" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAHaAAAB2gGFomX7AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAABD1JREFUWMPFl11sk2UUx3/nbYtjxS1MF7MLMTECMgSTtSSyrQkLhAj7UBPnDSEGoxegGzMwojhXVpmTAA5iYpSoMQa8GBhFOrMFk03buei6yRAlcmOM0SEmU9d90b19jxcM1o5+sGnsc/e+z/l6ztf/HFFVMnns6QieeOCHBePGsHM+wrOtvLG2C4WRVDSSygNV7sCjlspxwDnPB44aols/DXk+mbMBmx/6OseITF1CuOtfevkPh2Uu+/jbdX8lujSScRlT5r7/QDlAfsRmfzmpnkQ/H3H13gf6bBrBn1uqK8WylgEnU8eZmk1repbfchJG1TyKyIKEwuBHFd3lD3naY3O1siiwXsVoBV2VgM1ht/QQUJk2ByqKghsQziYQ8ifKgexIXmuyzC4r67Y7R+xPAfuB/Nn3Cpva+0s7khpQVtZtd4bt51BWxtBYAiciprG7c7D4SixzU9PYalDL6110Ifb/w8W9eY7JqFeFHbO8fPGyLHwwFHJNJTSgwtVTB9oaw9BlQ+tO93vOxypoaQnfEYlI43SeCHDC4TDq9+51/h5fxr33q0ZfV9g04wat9Q943rjJgCp3952W2i8Bi6eDvdsfKj0cK/DYMRyXL4/sUJUmIHd2zYMezsvLaamp4WpcWN3BXSiHpuMwbGbZlnZ8tXY4rgosy+G7oRwQ0cAsd28YGgqfU5UjCZQDLALxDg+Hv/P5Rqvj4hwrS8izXzWb4spwc1GgENFnkpWRzxeuB+ssUHgLdb9UVdt8vpGdKQpze7n7y1U3DBChNRUuqOo9c+0+qpKKxyZqtAIYla7gY4JszAAQri93BSsMRZoyBcUC+w3Q3AyOA4sNhAOZ0q7Iq0b2vUNvK5zPgP+/H8+Zetdoa6uOikhdGurxebwvJY8Iz3V1rTMNAH+opEuQj5KTT/qA1yC+wyUjBm12OidaUtCcPNNX2h0Hx2JG69VulANZAJZJwfU7rzd/FHixuXniTdM0m4GtSQT7bTartqEh9yfImUEzkwKZmTwmo5a5JwkYBfcDL01/RkR5y8iWhtPBknB8ZxwtU9UjwOrrKCeizzc25nTGg1F/turEHoU9wMLpDvWKf8DTmNCAKnd/tqUTF4ElMXJ+A5rWDJS+41WsGWzALhJ+ErBWrLj9g+pqojHxlXJX8HGUg0BsR/x1yhxf3jm4cSzpQFLp6tmi6PEE7g1ZhtZ91ufpSZUAFa6gC+UoQslNaSmypT1U8mHKiUgEKS8KfgF4EpYunFI16tsHin+OG0LcgQK7yj7g6cSzpva2D3hKVNG0Y3mVO1BkqfSlmJrHBQ4uvM12gJHc6ETW8HZVfMRmXvyxxNC1Z/o839zyXlDuCr4nsC11J+MXueaVJWn6yPv+/pJtc9oLTNN4AeTvNGByd3rlhE2x9s5pLwDoHCy+grDzWmOZ95lUtLYj5Bma126Y8eX0/zj/ADxGyViSg4BXAAAAAElFTkSuQmCC">
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PScwIDAgMjk0LjgyNSAyNTguOTgyJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnPg0KPHBhdGggZmlsbD0nIzgwODBGMicgZD0nTTk3LjAyMSw5OS4wMTZsNDguNDMyLTI3Ljk2MmMxLjIxMi0wLjcsMi43MDYtMC43LDMuOTE4LDBsNDguNDMzLDI3Ljk2MiBjMS4yMTEsMC43LDEuOTU5LDEuOTkzLDEuOTU5LDMuMzkzdjU1LjkyNGMwLDEuMzk5LTAuNzQ4LDIuNjkzLTEuOTU5LDMuMzk0bC00OC40MzMsMjcuOTYyYy0xLjIxMiwwLjctMi43MDYsMC43LTMuOTE4LDAgbC00OC40MzItMjcuOTYyYy0xLjIxMi0wLjctMS45NTktMS45OTQtMS45NTktMy4zOTR2LTU1LjkyNEM5NS4wNjMsMTAxLjAwOSw5NS44MSw5OS43MTYsOTcuMDIxLDk5LjAxNicvPg0KPHBhdGggZmlsbD0nIzRCMzJDMycgZD0nTTI3My4zMzYsMTI0LjQ4OEwyMTUuNDY5LDIzLjgxNmMtMi4xMDItMy42NC01Ljk4NS02LjMyNS0xMC4xODgtNi4zMjVIODkuNTQ1IGMtNC4yMDQsMC04LjA4OCwyLjY4NS0xMC4xOSw2LjMyNWwtNTcuODY3LDEwMC40NWMtMi4xMDIsMy42NDEtMi4xMDIsOC4yMzYsMCwxMS44NzdsNTcuODY3LDk5Ljg0NyBjMi4xMDIsMy42NCw1Ljk4Niw1LjUwMSwxMC4xOSw1LjUwMWgxMTUuNzM1YzQuMjAzLDAsOC4wODctMS44MDUsMTAuMTg4LTUuNDQ2bDU3Ljg2Ny0xMDAuMDEgQzI3NS40MzksMTMyLjM5NiwyNzUuNDM5LDEyOC4xMjgsMjczLjMzNiwxMjQuNDg4IE0yMjUuNDE5LDE3Mi44OThjMCwxLjQ4LTAuODkxLDIuODQ5LTIuMTc0LDMuNTlsLTczLjcxLDQyLjUyNyBjLTEuMjgyLDAuNzQtMi44ODgsMC43NC00LjE3LDBsLTczLjc2Ny00Mi41MjdjLTEuMjgyLTAuNzQxLTIuMTc5LTIuMTA5LTIuMTc5LTMuNTlWODcuODQzYzAtMS40ODEsMC44ODQtMi44NDksMi4xNjctMy41OSBsNzMuNzA3LTQyLjUyN2MxLjI4Mi0wLjc0MSwyLjg4Ni0wLjc0MSw0LjE2OCwwbDczLjc3Miw0Mi41MjdjMS4yODMsMC43NDEsMi4xODYsMi4xMDksMi4xODYsMy41OVYxNzIuODk4eicvPg0KPC9zdmc+">
<style>
body {
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
font-size: 16px;
font-weight: normal;
margin: 0;
padding: 0;
color: #333;
}
#overview {
padding: 20px 30px;
}
td,
th {
padding: 5px 10px;
}
h1 {
margin: 0;
}
table {
margin: 30px;
width: calc(100% - 60px);
max-width: 1000px;
border-radius: 5px;
border: 1px solid #ddd;
border-spacing: 0;
}
th {
font-weight: 400;
font-size: medium;
text-align: left;
cursor: pointer;
}
td.clr-1,
td.clr-2,
th span {
font-weight: 700;
}
th span {
float: right;
margin-left: 20px;
}
th span::after {
content: "";
clear: both;
display: block;
}
tr:last-child td {
border-bottom: none;
}
tr td:first-child,
tr td:last-child {
color: #9da0a4;
}
#overview.bg-0,
tr.bg-0 th {
color: #468847;
background: #dff0d8;
border-bottom: 1px solid #d6e9c6;
}
#overview.bg-1,
tr.bg-1 th {
color: #f0ad4e;
background: #fcf8e3;
border-bottom: 1px solid #fbeed5;
}
#overview.bg-2,
tr.bg-2 th {
color: #b94a48;
background: #f2dede;
border-bottom: 1px solid #eed3d7;
}
td {
border-bottom: 1px solid #ddd;
}
td.clr-1 {
color: #f0ad4e;
}
td.clr-2 {
color: #b94a48;
}
td a {
color: #3a33d1;
text-decoration: none;
}
td a:hover {
color: #272296;
text-decoration: underline;
}
</style>
</head>
<body>
<div id="overview" class="bg-${reportColor}">
<h1>ESLint Report</h1>
<div>
<span>${reportSummary}</span> - Generated on ${date}
</div>
</div>
<table>
<tbody>
${results}
</tbody>
</table>
<script type="text/javascript">
var groups = document.querySelectorAll("tr[data-group]");
for (i = 0; i < groups.length; i++) {
groups[i].addEventListener("click", function() {
var inGroup = document.getElementsByClassName(this.getAttribute("data-group"));
this.innerHTML = (this.innerHTML.indexOf("+") > -1) ? this.innerHTML.replace("+", "-") : this.innerHTML.replace("-", "+");
for (var j = 0; j < inGroup.length; j++) {
inGroup[j].style.display = (inGroup[j].style.display !== "none") ? "none" : "table-row";
}
});
}
</script>
</body>
</html>
`.trimStart();
}
/**
* Given a word and a count, append an s if count is not one.

@@ -64,2 +227,31 @@ * @param {string} word A word in its singular form.

/**
* Get HTML (table row) describing a single message.
* @param {Object} it data for the message.
* @returns {string} HTML (table row) describing the message.
*/
function messageTemplate(it) {
const {
parentIndex,
lineNumber,
columnNumber,
severityNumber,
severityName,
message,
ruleUrl,
ruleId
} = it;
return `
<tr style="display: none;" class="f-${parentIndex}">
<td>${lineNumber}:${columnNumber}</td>
<td class="clr-${severityNumber}">${severityName}</td>
<td>${encodeHTML(message)}</td>
<td>
<a href="${ruleUrl ? ruleUrl : ""}" target="_blank" rel="noopener noreferrer">${ruleId ? ruleId : ""}</a>
</td>
</tr>
`.trimStart();
}
/**
* Get HTML (table rows) describing the messages.

@@ -78,3 +270,3 @@ * @param {Array} messages Messages.

*/
return lodash.map(messages, message => {
return messages.map(message => {
const lineNumber = message.line || 0;

@@ -87,3 +279,5 @@ const columnNumber = message.column || 0;

ruleUrl = lodash.get(meta, "docs.url", null);
if (meta && meta.docs && meta.docs.url) {
ruleUrl = meta.docs.url;
}
}

@@ -104,4 +298,22 @@

// eslint-disable-next-line jsdoc/require-description
/**
* Get HTML (table row) describing the result for a single file.
* @param {Object} it data for the file.
* @returns {string} HTML (table row) describing the result for the file.
*/
function resultTemplate(it) {
const { color, index, filePath, summary } = it;
return `
<tr class="bg-${color}" data-group="f-${index}">
<th colspan="4">
[+] ${encodeHTML(filePath)}
<span>${encodeHTML(summary)}</span>
</th>
</tr>
`.trimStart();
}
/**
* Render the results.
* @param {Array} results Test results.

@@ -112,3 +324,3 @@ * @param {Object} rulesMeta Dictionary containing metadata for each rule executed by the analysis.

function renderResults(results, rulesMeta) {
return lodash.map(results, (result, index) => resultTemplate({
return results.map((result, index) => resultTemplate({
index,

@@ -118,3 +330,2 @@ color: renderColor(result.errorCount, result.warningCount),

summary: renderSummary(result.errorCount, result.warningCount)
}) + renderMessages(result.messages, index, rulesMeta)).join("\n");

@@ -121,0 +332,0 @@ }

@@ -34,3 +34,3 @@ /**

output += prefix + yaml.safeDump(diagnostic).split("\n").join(`\n${prefix}`);
output += prefix + yaml.dump(diagnostic).split("\n").join(`\n${prefix}`);
output += "...\n";

@@ -37,0 +37,0 @@ return output;

@@ -24,4 +24,4 @@ /**

* hash the given string
* @param {string} str the string to hash
* @returns {string} the hash
* @param {string} str the string to hash
* @returns {string} the hash
*/

@@ -28,0 +28,0 @@ function hash(str) {

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

const debug = require("debug")("eslint:lint-result-cache");
//-----------------------------------------------------------------------------

@@ -26,3 +28,19 @@ // Helpers

const validCacheStrategies = ["metadata", "content"];
const invalidCacheStrategyErrorMessage = `Cache strategy must be one of: ${validCacheStrategies
.map(strategy => `"${strategy}"`)
.join(", ")}`;
/**
* Tests whether a provided cacheStrategy is valid
* @param {string} cacheStrategy The cache strategy to use
* @returns {boolean} true if `cacheStrategy` is one of `validCacheStrategies`; false otherwise
*/
function isValidCacheStrategy(cacheStrategy) {
return (
validCacheStrategies.includes(cacheStrategy)
);
}
/**
* Calculates the hash of the config

@@ -54,8 +72,26 @@ * @param {ConfigArray} config The config.

* @param {string} cacheFileLocation The cache file location.
* configuration lookup by file path).
* @param {"metadata" | "content"} cacheStrategy The cache strategy to use.
*/
constructor(cacheFileLocation) {
constructor(cacheFileLocation, cacheStrategy) {
assert(cacheFileLocation, "Cache file location is required");
assert(cacheStrategy, "Cache strategy is required");
assert(
isValidCacheStrategy(cacheStrategy),
invalidCacheStrategyErrorMessage
);
this.fileEntryCache = fileEntryCache.create(cacheFileLocation);
debug(`Caching results to ${cacheFileLocation}`);
const useChecksum = cacheStrategy === "content";
debug(
`Using "${cacheStrategy}" strategy to detect changes`
);
this.fileEntryCache = fileEntryCache.create(
cacheFileLocation,
void 0,
useChecksum
);
this.cacheFileLocation = cacheFileLocation;
}

@@ -82,13 +118,24 @@

*/
const fileDescriptor = this.fileEntryCache.getFileDescriptor(filePath);
const hashOfConfig = hashOfConfigFor(config);
const changed = fileDescriptor.changed || fileDescriptor.meta.hashOfConfig !== hashOfConfig;
const changed =
fileDescriptor.changed ||
fileDescriptor.meta.hashOfConfig !== hashOfConfig;
if (fileDescriptor.notFound || changed) {
if (fileDescriptor.notFound) {
debug(`File not found on the file system: ${filePath}`);
return null;
}
if (changed) {
debug(`Cache entry not found or no longer valid: ${filePath}`);
return null;
}
// If source is present but null, need to reread the file from the filesystem.
if (fileDescriptor.meta.results && fileDescriptor.meta.results.source === null) {
if (
fileDescriptor.meta.results &&
fileDescriptor.meta.results.source === null
) {
debug(`Rereading cached result source from filesystem: ${filePath}`);
fileDescriptor.meta.results.source = fs.readFileSync(filePath, "utf-8");

@@ -119,2 +166,3 @@ }

if (fileDescriptor && !fileDescriptor.notFound) {
debug(`Updating cached result: ${filePath}`);

@@ -143,2 +191,3 @@ // Serialize the result, except that we want to remove the file source if present.

reconcile() {
debug(`Persisting cached results: ${this.cacheFileLocation}`);
this.fileEntryCache.reconcile();

@@ -145,0 +194,0 @@ }

@@ -18,3 +18,3 @@ /**

module.exports = function(s) {
return (`${s}`).replace(/[<>&"'\x00-\x1F\x7F\u0080-\uFFFF]/gu, c => { // eslint-disable-line no-control-regex
return (`${s}`).replace(/[<>&"'\x00-\x1F\x7F\u0080-\uFFFF]/gu, c => { // eslint-disable-line no-control-regex -- Converting controls to entities
switch (c) {

@@ -21,0 +21,0 @@ case "<":

@@ -9,3 +9,3 @@ /**

/*
* The CLI object should *not* call process.exit() directly. It should only return
* NOTE: The CLI object should *not* call process.exit() directly. It should only return
* exit codes. This allows other programs to use the CLI object and still control

@@ -23,5 +23,8 @@ * when the program exits.

{ ESLint } = require("./eslint"),
CLIOptions = require("./options"),
{ FlatESLint, shouldUseFlatConfig } = require("./eslint/flat-eslint"),
createCLIOptions = require("./options"),
log = require("./shared/logging"),
RuntimeInfo = require("./shared/runtime-info");
const { Legacy: { naming } } = require("@eslint/eslintrc");
const { ModuleImporter } = require("@humanwhocodes/module-importer");

@@ -37,2 +40,4 @@ const debug = require("debug")("eslint:cli");

/** @typedef {import("./eslint/eslint").LintResult} LintResult */
/** @typedef {import("./options").ParsedCLIOptions} ParsedCLIOptions */
/** @typedef {import("./shared/types").ResultsMeta} ResultsMeta */

@@ -59,12 +64,16 @@ //------------------------------------------------------------------------------

/**
* Translates the CLI options into the options expected by the CLIEngine.
* @param {Object} cliOptions The CLI options to translate.
* @returns {ESLintOptions} The options object for the CLIEngine.
* Translates the CLI options into the options expected by the ESLint constructor.
* @param {ParsedCLIOptions} cliOptions The CLI options to translate.
* @param {"flat"|"eslintrc"} [configType="eslintrc"] The format of the
* config to generate.
* @returns {Promise<ESLintOptions>} The options object for the ESLint constructor.
* @private
*/
function translateOptions({
async function translateOptions({
cache,
cacheFile,
cacheLocation,
cacheStrategy,
config,
configLookup,
env,

@@ -90,14 +99,56 @@ errorOnUnmatchedPattern,

rulesdir
}) {
return {
allowInlineConfig: inlineConfig,
cache,
cacheLocation: cacheLocation || cacheFile,
errorOnUnmatchedPattern,
extensions: ext,
fix: (fix || fixDryRun) && (quiet ? quietFixPredicate : true),
fixTypes: fixType,
ignore,
ignorePath,
overrideConfig: {
}, configType) {
let overrideConfig, overrideConfigFile;
const importer = new ModuleImporter();
if (configType === "flat") {
overrideConfigFile = (typeof config === "string") ? config : !configLookup;
if (overrideConfigFile === false) {
overrideConfigFile = void 0;
}
let globals = {};
if (global) {
globals = global.reduce((obj, name) => {
if (name.endsWith(":true")) {
obj[name.slice(0, -5)] = "writable";
} else {
obj[name] = "readonly";
}
return obj;
}, globals);
}
overrideConfig = [{
languageOptions: {
globals,
parserOptions: parserOptions || {}
},
rules: rule ? rule : {}
}];
if (parser) {
overrideConfig[0].languageOptions.parser = await importer.import(parser);
}
if (plugin) {
const plugins = {};
for (const pluginName of plugin) {
const shortName = naming.getShorthandName(pluginName, "eslint-plugin");
const longName = naming.normalizePackageName(pluginName, "eslint-plugin");
plugins[shortName] = await importer.import(longName);
}
overrideConfig[0].plugins = plugins;
}
} else {
overrideConfigFile = config;
overrideConfig = {
env: env && env.reduce((obj, name) => {

@@ -120,9 +171,30 @@ obj[name] = true;

rules: rule
},
overrideConfigFile: config,
reportUnusedDisableDirectives: reportUnusedDisableDirectives ? "error" : void 0,
resolvePluginsRelativeTo,
rulePaths: rulesdir,
useEslintrc: eslintrc
};
}
const options = {
allowInlineConfig: inlineConfig,
cache,
cacheLocation: cacheLocation || cacheFile,
cacheStrategy,
errorOnUnmatchedPattern,
fix: (fix || fixDryRun) && (quiet ? quietFixPredicate : true),
fixTypes: fixType,
ignore,
overrideConfig,
overrideConfigFile,
reportUnusedDisableDirectives: reportUnusedDisableDirectives ? "error" : void 0
};
if (configType === "flat") {
options.ignorePatterns = ignorePattern;
} else {
options.resolvePluginsRelativeTo = resolvePluginsRelativeTo;
options.rulePaths = rulesdir;
options.useEslintrc = eslintrc;
options.extensions = ext;
options.ignorePath = ignorePath;
}
return options;
}

@@ -133,6 +205,7 @@

* @param {LintResult[]} results The lint results.
* @returns {{errorCount:number;warningCount:number}} The number of error messages.
* @returns {{errorCount:number;fatalErrorCount:number,warningCount:number}} The number of error messages.
*/
function countErrors(results) {
let errorCount = 0;
let fatalErrorCount = 0;
let warningCount = 0;

@@ -142,6 +215,7 @@

errorCount += result.errorCount;
fatalErrorCount += result.fatalErrorCount;
warningCount += result.warningCount;
}
return { errorCount, warningCount };
return { errorCount, fatalErrorCount, warningCount };
}

@@ -171,6 +245,7 @@

* @param {string} outputFile The path for the output file.
* @param {ResultsMeta} resultsMeta Warning count and max threshold.
* @returns {Promise<boolean>} True if the printing succeeds, false if not.
* @private
*/
async function printResults(engine, results, format, outputFile) {
async function printResults(engine, results, format, outputFile, resultsMeta) {
let formatter;

@@ -185,3 +260,3 @@

const output = formatter.format(results);
const output = await formatter.format(results, resultsMeta);

@@ -226,8 +301,24 @@ if (output) {

* @param {string} [text] The text to lint (used for TTY).
* @param {boolean} [allowFlatConfig] Whether or not to allow flat config.
* @returns {Promise<number>} The exit code for the operation.
*/
async execute(args, text) {
async execute(args, text, allowFlatConfig) {
if (Array.isArray(args)) {
debug("CLI args: %o", args.slice(2));
}
/*
* Before doing anything, we need to see if we are using a
* flat config file. If so, then we need to change the way command
* line args are parsed. This is temporary, and when we fully
* switch to flat config we can remove this logic.
*/
const usingFlatConfig = allowFlatConfig && await shouldUseFlatConfig();
debug("Using flat config?", usingFlatConfig);
const CLIOptions = createCLIOptions(usingFlatConfig);
/** @type {ParsedCLIOptions} */
let options;

@@ -238,2 +329,3 @@

} catch (error) {
debug("Error parsing CLI options:", error.message);
log.error(error.message);

@@ -259,2 +351,3 @@ return 2;

} catch (err) {
debug("Error retrieving environment info");
log.error(err.message);

@@ -275,3 +368,5 @@ return 2;

const engine = new ESLint(translateOptions(options));
const engine = usingFlatConfig
? new FlatESLint(await translateOptions(options, "flat"))
: new ESLint(await translateOptions(options));
const fileConfig =

@@ -299,3 +394,5 @@ await engine.calculateConfigForFile(options.printConfig);

const engine = new ESLint(translateOptions(options));
const ActiveESLint = usingFlatConfig ? FlatESLint : ESLint;
const engine = new ActiveESLint(await translateOptions(options, usingFlatConfig ? "flat" : "eslintrc"));
let results;

@@ -314,16 +411,30 @@

debug("Fix mode enabled - applying fixes");
await ESLint.outputFixes(results);
await ActiveESLint.outputFixes(results);
}
let resultsToPrint = results;
if (options.quiet) {
debug("Quiet mode enabled - filtering out warnings");
results = ESLint.getErrorResults(results);
resultsToPrint = ActiveESLint.getErrorResults(resultsToPrint);
}
if (await printResults(engine, results, options.format, options.outputFile)) {
const { errorCount, warningCount } = countErrors(results);
const tooManyWarnings =
options.maxWarnings >= 0 && warningCount > options.maxWarnings;
const resultCounts = countErrors(results);
const tooManyWarnings = options.maxWarnings >= 0 && resultCounts.warningCount > options.maxWarnings;
const resultsMeta = tooManyWarnings
? {
maxWarningsExceeded: {
maxWarnings: options.maxWarnings,
foundWarnings: resultCounts.warningCount
}
}
: {};
if (!errorCount && tooManyWarnings) {
if (await printResults(engine, resultsToPrint, options.format, options.outputFile, resultsMeta)) {
// Errors and warnings from the original unfiltered results should determine the exit code
const shouldExitForFatalErrors =
options.exitOnFatalError && resultCounts.fatalErrorCount > 0;
if (!resultCounts.errorCount && tooManyWarnings) {
log.error(

@@ -335,3 +446,7 @@ "ESLint found too many warnings (maximum: %s).",

return (errorCount || tooManyWarnings) ? 1 : 0;
if (shouldExitForFatalErrors) {
return 2;
}
return (resultCounts.errorCount || tooManyWarnings) ? 1 : 0;
}

@@ -338,0 +453,0 @@

@@ -35,7 +35,15 @@ /**

/** @typedef {import("../shared/types").LintMessage} LintMessage */
/** @typedef {import("../shared/types").SuppressedLintMessage} SuppressedLintMessage */
/** @typedef {import("../shared/types").Plugin} Plugin */
/** @typedef {import("../shared/types").Rule} Rule */
/** @typedef {import("./load-formatter").Formatter} Formatter */
/** @typedef {import("../shared/types").LintResult} LintResult */
/** @typedef {import("../shared/types").ResultsMeta} ResultsMeta */
/**
* The main formatter object.
* @typedef LoadedFormatter
* @property {(results: LintResult[], resultsMeta: ResultsMeta) => string | Promise<string>} format format function.
*/
/**
* The options with which to configure the ESLint instance.

@@ -47,2 +55,3 @@ * @typedef {Object} ESLintOptions

* @property {string} [cacheLocation] The cache file to use instead of .eslintcache.
* @property {"metadata" | "content"} [cacheStrategy] The strategy used to detect changed files.
* @property {string} [cwd] The value to use for the current working directory.

@@ -58,3 +67,3 @@ * @property {boolean} [errorOnUnmatchedPattern] If `false` then `ESLint#lintFiles()` doesn't throw even if no target files found. Defaults to `true`.

* @property {string} [overrideConfigFile] The configuration file to use.
* @property {Record<string,Plugin>} [plugins] An array of plugin implementations.
* @property {Record<string,Plugin>|null} [plugins] Preloaded plugins. This is a map-like object, keys are plugin IDs and each value is implementation.
* @property {"error" | "warn" | "off"} [reportUnusedDisableDirectives] the severity to report unused eslint-disable directives.

@@ -74,16 +83,2 @@ * @property {string} [resolvePluginsRelativeTo] The folder where plugins should be resolved from, defaulting to the CWD.

/**
* A linting result.
* @typedef {Object} LintResult
* @property {string} filePath The path to the file that was linted.
* @property {LintMessage[]} messages All of the messages for the result.
* @property {number} errorCount Number of errors for the result.
* @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 {DeprecatedRuleInfo[]} usedDeprecatedRules The list of used deprecated rules.
*/
/**
* Private members for the `ESLint` instance.

@@ -117,5 +112,5 @@ * @typedef {Object} ESLintPrivateMembers

/**
* Check if a given value is an array of non-empty stringss or not.
* Check if a given value is an array of non-empty strings or not.
* @param {any} x The value to check.
* @returns {boolean} `true` if `x` is an array of non-empty stringss.
* @returns {boolean} `true` if `x` is an array of non-empty strings.
*/

@@ -132,3 +127,3 @@ function isArrayOfNonEmptyString(x) {

function isFixType(x) {
return x === "problem" || x === "suggestion" || x === "layout";
return x === "directive" || x === "problem" || x === "suggestion" || x === "layout";
}

@@ -159,2 +154,3 @@

* @param {ESLintOptions} options The options to process.
* @throws {ESLintInvalidOptionsError} If of any of a variety of type errors.
* @returns {ESLintOptions} The normalized options.

@@ -167,2 +163,3 @@ */

cacheLocation = ".eslintcache",
cacheStrategy = "metadata",
cwd = process.cwd(),

@@ -227,2 +224,8 @@ errorOnUnmatchedPattern = true,

}
if (
cacheStrategy !== "metadata" &&
cacheStrategy !== "content"
) {
errors.push("'cacheStrategy' must be any of \"metadata\", \"content\".");
}
if (!isNonEmptyString(cwd) || !path.isAbsolute(cwd)) {

@@ -241,3 +244,3 @@ errors.push("'cwd' must be an absolute path.");

if (fixTypes !== null && !isFixTypeArray(fixTypes)) {
errors.push("'fixTypes' must be an array of any of \"problem\", \"suggestion\", and \"layout\".");
errors.push("'fixTypes' must be an array of any of \"directive\", \"problem\", \"suggestion\", and \"layout\".");
}

@@ -297,4 +300,5 @@ if (typeof globInputPaths !== "boolean") {

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

@@ -426,2 +430,5 @@ extensions,

/**
* Main API.
*/
class ESLint {

@@ -435,5 +442,4 @@

const processedOptions = processOptions(options);
const cliEngine = new CLIEngine(processedOptions);
const cliEngine = new CLIEngine(processedOptions, { preloadedPlugins: options.plugins });
const {
additionalPluginPool,
configArrayFactory,

@@ -445,14 +451,2 @@ lastConfigArrays

/*
* Address `plugins` to add plugin implementations.
* Operate the `additionalPluginPool` internal slot directly to avoid
* using `addPlugin(id, plugin)` method that resets cache everytime.
*/
if (options.plugins) {
for (const [id, plugin] of Object.entries(options.plugins)) {
additionalPluginPool.set(id, plugin);
updated = true;
}
}
/*
* Address `overrideConfig` to set override config.

@@ -523,2 +517,38 @@ * Operate the `configArrayFactory` internal slot directly because this

/**
* Returns meta objects for each rule represented in the lint results.
* @param {LintResult[]} results The results to fetch rules meta for.
* @returns {Object} A mapping of ruleIds to rule meta objects.
*/
getRulesMetaForResults(results) {
const resultRuleIds = new Set();
// first gather all ruleIds from all results
for (const result of results) {
for (const { ruleId } of result.messages) {
resultRuleIds.add(ruleId);
}
for (const { ruleId } of result.suppressedMessages) {
resultRuleIds.add(ruleId);
}
}
// create a map of all rules in the results
const { cliEngine } = privateMembersMap.get(this);
const rules = cliEngine.getRules();
const resultRules = new Map();
for (const [ruleId, rule] of rules) {
if (resultRuleIds.has(ruleId)) {
resultRules.set(ruleId, rule);
}
}
return createRulesMeta(resultRules);
}
/**
* Executes the current configuration on an array of file and directory names.

@@ -561,5 +591,8 @@ * @param {string[]} patterns An array of file and directory names.

for (const key of Object.keys(unknownOptions)) {
throw new Error(`'options' must not include the unknown option '${key}'`);
const unknownOptionKeys = Object.keys(unknownOptions);
if (unknownOptionKeys.length > 0) {
throw new Error(`'options' must not include the unknown option(s): ${unknownOptionKeys.join(", ")}`);
}
if (filePath !== void 0 && !isNonEmptyString(filePath)) {

@@ -586,3 +619,3 @@ throw new Error("'options.filePath' must be a non-empty string or undefined");

* - A builtin formatter name ... Load the builtin formatter.
* - A thirdparty formatter name:
* - A third-party formatter name:
* - `foo` → `eslint-formatter-foo`

@@ -592,3 +625,3 @@ * - `@foo` → `@foo/eslint-formatter`

* - A file path ... Load the file.
* @returns {Promise<Formatter>} A promise resolving to the formatter object.
* @returns {Promise<LoadedFormatter>} A promise resolving to the formatter object.
* This promise will be rejected if the given formatter was not found or not

@@ -602,3 +635,3 @@ * a function.

const { cliEngine } = privateMembersMap.get(this);
const { cliEngine, options } = privateMembersMap.get(this);
const formatter = cliEngine.getFormatter(name);

@@ -614,6 +647,7 @@

* The main formatter method.
* @param {LintResults[]} results The lint results to format.
* @returns {string} The formatted lint results.
* @param {LintResult[]} results The lint results to format.
* @param {ResultsMeta} resultsMeta Warning count and max threshold.
* @returns {string | Promise<string>} The formatted lint results.
*/
format(results) {
format(results, resultsMeta) {
let rulesMeta = null;

@@ -624,2 +658,6 @@

return formatter(results, {
...resultsMeta,
get cwd() {
return options.cwd;
},
get rulesMeta() {

@@ -626,0 +664,0 @@ if (!rulesMeta) {

"use strict";
const { ESLint } = require("./eslint");
const { FlatESLint } = require("./flat-eslint");
module.exports = {
ESLint
ESLint,
FlatESLint
};

@@ -8,4 +8,14 @@ /**

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

@@ -23,2 +33,173 @@ * Compares the locations of two objects in a source file

/**
* Groups a set of directives into sub-arrays by their parent comment.
* @param {Directive[]} directives Unused directives to be removed.
* @returns {Directive[][]} Directives grouped by their parent comment.
*/
function groupByParentComment(directives) {
const groups = new Map();
for (const directive of directives) {
const { unprocessedDirective: { parentComment } } = directive;
if (groups.has(parentComment)) {
groups.get(parentComment).push(directive);
} else {
groups.set(parentComment, [directive]);
}
}
return [...groups.values()];
}
/**
* Creates removal details for a set of directives within the same comment.
* @param {Directive[]} directives Unused directives to be removed.
* @param {Token} commentToken The backing Comment token.
* @returns {{ description, fix, unprocessedDirective }[]} Details for later creation of output Problems.
*/
function createIndividualDirectivesRemoval(directives, commentToken) {
/*
* `commentToken.value` starts right after `//` or `/*`.
* All calculated offsets will be relative to this index.
*/
const commentValueStart = commentToken.range[0] + "//".length;
// Find where the list of rules starts. `\S+` matches with the directive name (e.g. `eslint-disable-line`)
const listStartOffset = /^\s*\S+\s+/u.exec(commentToken.value)[0].length;
/*
* Get the list text without any surrounding whitespace. In order to preserve the original
* formatting, we don't want to change that whitespace.
*
* // eslint-disable-line rule-one , rule-two , rule-three -- comment
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
*/
const listText = commentToken.value
.slice(listStartOffset) // remove directive name and all whitespace before the list
.split(/\s-{2,}\s/u)[0] // remove `-- comment`, if it exists
.trimEnd(); // remove all whitespace after the list
/*
* We can assume that `listText` contains multiple elements.
* Otherwise, this function wouldn't be called - if there is
* only one rule in the list, then the whole comment must be removed.
*/
return directives.map(directive => {
const { ruleId } = directive;
const regex = new RegExp(String.raw`(?:^|\s*,\s*)${escapeRegExp(ruleId)}(?:\s*,\s*|$)`, "u");
const match = regex.exec(listText);
const matchedText = match[0];
const matchStartOffset = listStartOffset + match.index;
const matchEndOffset = matchStartOffset + matchedText.length;
const firstIndexOfComma = matchedText.indexOf(",");
const lastIndexOfComma = matchedText.lastIndexOf(",");
let removalStartOffset, removalEndOffset;
if (firstIndexOfComma !== lastIndexOfComma) {
/*
* Since there are two commas, this must one of the elements in the middle of the list.
* Matched range starts where the previous rule name ends, and ends where the next rule name starts.
*
* // eslint-disable-line rule-one , rule-two , rule-three -- comment
* ^^^^^^^^^^^^^^
*
* We want to remove only the content between the two commas, and also one of the commas.
*
* // eslint-disable-line rule-one , rule-two , rule-three -- comment
* ^^^^^^^^^^^
*/
removalStartOffset = matchStartOffset + firstIndexOfComma;
removalEndOffset = matchStartOffset + lastIndexOfComma;
} else {
/*
* This is either the first element or the last element.
*
* If this is the first element, matched range starts where the first rule name starts
* and ends where the second rule name starts. This is exactly the range we want
* to remove so that the second rule name will start where the first one was starting
* and thus preserve the original formatting.
*
* // eslint-disable-line rule-one , rule-two , rule-three -- comment
* ^^^^^^^^^^^
*
* Similarly, if this is the last element, we've already matched the range we want to
* remove. The previous rule name will end where the last one was ending, relative
* to the content on the right side.
*
* // eslint-disable-line rule-one , rule-two , rule-three -- comment
* ^^^^^^^^^^^^^
*/
removalStartOffset = matchStartOffset;
removalEndOffset = matchEndOffset;
}
return {
description: `'${ruleId}'`,
fix: {
range: [
commentValueStart + removalStartOffset,
commentValueStart + removalEndOffset
],
text: ""
},
unprocessedDirective: directive.unprocessedDirective
};
});
}
/**
* Creates a description of deleting an entire unused disable comment.
* @param {Directive[]} directives Unused directives to be removed.
* @param {Token} commentToken The backing Comment token.
* @returns {{ description, fix, unprocessedDirective }} Details for later creation of an output Problem.
*/
function createCommentRemoval(directives, commentToken) {
const { range } = commentToken;
const ruleIds = directives.filter(directive => directive.ruleId).map(directive => `'${directive.ruleId}'`);
return {
description: ruleIds.length <= 2
? ruleIds.join(" or ")
: `${ruleIds.slice(0, ruleIds.length - 1).join(", ")}, or ${ruleIds[ruleIds.length - 1]}`,
fix: {
range,
text: " "
},
unprocessedDirective: directives[0].unprocessedDirective
};
}
/**
* Parses details from directives to create output Problems.
* @param {Directive[]} allDirectives Unused directives to be removed.
* @returns {{ description, fix, unprocessedDirective }[]} Details for later creation of output Problems.
*/
function processUnusedDisableDirectives(allDirectives) {
const directiveGroups = groupByParentComment(allDirectives);
return directiveGroups.flatMap(
directives => {
const { parentComment } = directives[0].unprocessedDirective;
const remainingRuleIds = new Set(parentComment.ruleIds);
for (const directive of directives) {
remainingRuleIds.delete(directive.ruleId);
}
return remainingRuleIds.size
? createIndividualDirectivesRemoval(directives, parentComment.commentToken)
: [createCommentRemoval(directives, parentComment.commentToken)];
}
);
}
/**
* This is the same as the exported function, except that it

@@ -30,16 +211,13 @@ * doesn't handle disable-line and disable-next-line directives, and it always reports unused

* (this function always reports unused disable directives).
* @returns {{problems: Problem[], unusedDisableDirectives: Problem[]}} An object with a list
* of filtered problems and unused eslint-disable directives
* @returns {{problems: LintMessage[], unusedDisableDirectives: LintMessage[]}} An object with a list
* of problems (including suppressed ones) and unused eslint-disable directives
*/
function applyDirectives(options) {
const problems = [];
let nextDirectiveIndex = 0;
let currentGlobalDisableDirective = null;
const disabledRuleMap = new Map();
// enabledRules is only used when there is a current global disable directive.
const enabledRules = new Set();
const usedDisableDirectives = new Set();
for (const problem of options.problems) {
let disableDirectivesForProblem = [];
let nextDirectiveIndex = 0;
while (

@@ -51,54 +229,56 @@ nextDirectiveIndex < options.directives.length &&

switch (directive.type) {
case "disable":
if (directive.ruleId === null) {
currentGlobalDisableDirective = directive;
disabledRuleMap.clear();
enabledRules.clear();
} else if (currentGlobalDisableDirective) {
enabledRules.delete(directive.ruleId);
disabledRuleMap.set(directive.ruleId, directive);
} else {
disabledRuleMap.set(directive.ruleId, directive);
}
break;
if (directive.ruleId === null || directive.ruleId === problem.ruleId) {
switch (directive.type) {
case "disable":
disableDirectivesForProblem.push(directive);
break;
case "enable":
if (directive.ruleId === null) {
currentGlobalDisableDirective = null;
disabledRuleMap.clear();
} else if (currentGlobalDisableDirective) {
enabledRules.add(directive.ruleId);
disabledRuleMap.delete(directive.ruleId);
} else {
disabledRuleMap.delete(directive.ruleId);
}
break;
case "enable":
disableDirectivesForProblem = [];
break;
// no default
// no default
}
}
}
if (disabledRuleMap.has(problem.ruleId)) {
usedDisableDirectives.add(disabledRuleMap.get(problem.ruleId));
} else if (currentGlobalDisableDirective && !enabledRules.has(problem.ruleId)) {
usedDisableDirectives.add(currentGlobalDisableDirective);
} else {
problems.push(problem);
if (disableDirectivesForProblem.length > 0) {
const suppressions = disableDirectivesForProblem.map(directive => ({
kind: "directive",
justification: directive.unprocessedDirective.justification
}));
if (problem.suppressions) {
problem.suppressions = problem.suppressions.concat(suppressions);
} else {
problem.suppressions = suppressions;
usedDisableDirectives.add(disableDirectivesForProblem[disableDirectivesForProblem.length - 1]);
}
}
problems.push(problem);
}
const unusedDisableDirectives = options.directives
.filter(directive => directive.type === "disable" && !usedDisableDirectives.has(directive))
.map(directive => ({
ruleId: null,
message: directive.ruleId
? `Unused eslint-disable directive (no problems were reported from '${directive.ruleId}').`
: "Unused eslint-disable directive (no problems were reported).",
line: directive.unprocessedDirective.line,
column: directive.unprocessedDirective.column,
severity: options.reportUnusedDisableDirectives === "warn" ? 1 : 2,
nodeType: null
}));
const unusedDisableDirectivesToReport = options.directives
.filter(directive => directive.type === "disable" && !usedDisableDirectives.has(directive));
const processed = processUnusedDisableDirectives(unusedDisableDirectivesToReport);
const unusedDisableDirectives = processed
.map(({ description, fix, unprocessedDirective }) => {
const { parentComment, type, line, column } = unprocessedDirective;
return {
ruleId: null,
message: description
? `Unused eslint-disable directive (no problems were reported from ${description}).`
: "Unused eslint-disable directive (no problems were reported).",
line: type === "disable-next-line" ? parentComment.commentToken.loc.start.line : line,
column: type === "disable-next-line" ? parentComment.commentToken.loc.start.column + 1 : column,
severity: options.reportUnusedDisableDirectives === "warn" ? 1 : 2,
nodeType: null,
...options.disableFixes ? {} : { fix }
};
});
return { problems, unusedDisableDirectives };

@@ -109,3 +289,3 @@ }

* Given a list of directive comments (i.e. metadata about eslint-disable and eslint-enable comments) and a list
* of reported problems, determines which problems should be reported.
* of reported problems, adds the suppression information to the problems.
* @param {Object} options Information about directives and problems

@@ -116,3 +296,4 @@ * @param {{

* line: number,
* column: number
* column: number,
* justification: string
* }} options.directives Directive comments found in the file, with one-based columns.

@@ -124,6 +305,7 @@ * Two directive comments can only have the same location if they also have the same type (e.g. a single eslint-disable

* @param {"off" | "warn" | "error"} options.reportUnusedDisableDirectives If `"warn"` or `"error"`, adds additional problems for unused directives
* @returns {{ruleId: (string|null), line: number, column: number}[]}
* A list of reported problems that were not disabled by the directive comments.
* @param {boolean} options.disableFixes If true, it doesn't make `fix` properties.
* @returns {{ruleId: (string|null), line: number, column: number, suppressions?: {kind: string, justification: string}}[]}
* An object with a list of reported problems, the suppressed of which contain the suppression information.
*/
module.exports = ({ directives, problems, reportUnusedDisableDirectives = "off" }) => {
module.exports = ({ directives, disableFixes, problems, reportUnusedDisableDirectives = "off" }) => {
const blockDirectives = directives

@@ -134,3 +316,3 @@ .filter(directive => directive.type === "disable" || directive.type === "enable")

const lineDirectives = lodash.flatMap(directives, directive => {
const lineDirectives = directives.flatMap(directive => {
switch (directive.type) {

@@ -161,2 +343,3 @@ case "disable":

directives: blockDirectives,
disableFixes,
reportUnusedDisableDirectives

@@ -167,2 +350,3 @@ });

directives: lineDirectives,
disableFixes,
reportUnusedDisableDirectives

@@ -169,0 +353,0 @@ });

@@ -33,2 +33,14 @@ /**

/**
* Checks if a given node appears as the value of a PropertyDefinition node.
* @param {ASTNode} node THe node to check.
* @returns {boolean} `true` if the node is a PropertyDefinition value,
* false if not.
*/
function isPropertyDefinitionValue(node) {
const parent = node.parent;
return parent && parent.type === "PropertyDefinition" && parent.value === node;
}
/**
* Checks whether the given logical operator is taken into account for the code

@@ -142,2 +154,3 @@ * path analysis.

case "Property":
case "PropertyDefinition":
case "MethodDefinition":

@@ -184,11 +197,14 @@ return (

if (currentSegment !== headSegment && currentSegment) {
debug.dump(`onCodePathSegmentEnd ${currentSegment.id}`);
if (currentSegment.reachable) {
analyzer.emitter.emit(
"onCodePathSegmentEnd",
currentSegment,
node
);
}
const eventName = currentSegment.reachable
? "onCodePathSegmentEnd"
: "onUnreachableCodePathSegmentEnd";
debug.dump(`${eventName} ${currentSegment.id}`);
analyzer.emitter.emit(
eventName,
currentSegment,
node
);
}

@@ -206,12 +222,15 @@ }

if (currentSegment !== headSegment && headSegment) {
debug.dump(`onCodePathSegmentStart ${headSegment.id}`);
const eventName = headSegment.reachable
? "onCodePathSegmentStart"
: "onUnreachableCodePathSegmentStart";
debug.dump(`${eventName} ${headSegment.id}`);
CodePathSegment.markUsed(headSegment);
if (headSegment.reachable) {
analyzer.emitter.emit(
"onCodePathSegmentStart",
headSegment,
node
);
}
analyzer.emitter.emit(
eventName,
headSegment,
node
);
}

@@ -235,11 +254,13 @@ }

const currentSegment = currentSegments[i];
const eventName = currentSegment.reachable
? "onCodePathSegmentEnd"
: "onUnreachableCodePathSegmentEnd";
debug.dump(`onCodePathSegmentEnd ${currentSegment.id}`);
if (currentSegment.reachable) {
analyzer.emitter.emit(
"onCodePathSegmentEnd",
currentSegment,
node
);
}
debug.dump(`${eventName} ${currentSegment.id}`);
analyzer.emitter.emit(
eventName,
currentSegment,
node
);
}

@@ -396,25 +417,64 @@

/**
* Creates a new code path and trigger the onCodePathStart event
* based on the currently selected node.
* @param {string} origin The reason the code path was started.
* @returns {void}
*/
function startCodePath(origin) {
if (codePath) {
// Emits onCodePathSegmentStart events if updated.
forwardCurrentToHead(analyzer, node);
debug.dumpState(node, state, false);
}
// Create the code path of this scope.
codePath = analyzer.codePath = new CodePath({
id: analyzer.idGenerator.next(),
origin,
upper: codePath,
onLooped: analyzer.onLooped
});
state = CodePath.getState(codePath);
// Emits onCodePathStart events.
debug.dump(`onCodePathStart ${codePath.id}`);
analyzer.emitter.emit("onCodePathStart", codePath, node);
}
/*
* Special case: The right side of class field initializer is considered
* to be its own function, so we need to start a new code path in this
* case.
*/
if (isPropertyDefinitionValue(node)) {
startCodePath("class-field-initializer");
/*
* Intentional fall through because `node` needs to also be
* processed by the code below. For example, if we have:
*
* class Foo {
* a = () => {}
* }
*
* In this case, we also need start a second code path.
*/
}
switch (node.type) {
case "Program":
startCodePath("program");
break;
case "FunctionDeclaration":
case "FunctionExpression":
case "ArrowFunctionExpression":
if (codePath) {
startCodePath("function");
break;
// Emits onCodePathSegmentStart events if updated.
forwardCurrentToHead(analyzer, node);
debug.dumpState(node, state, false);
}
// Create the code path of this scope.
codePath = analyzer.codePath = new CodePath(
analyzer.idGenerator.next(),
codePath,
analyzer.onLooped
);
state = CodePath.getState(codePath);
// Emits onCodePathStart events.
debug.dump(`onCodePathStart ${codePath.id}`);
analyzer.emitter.emit("onCodePathStart", codePath, node);
case "StaticBlock":
startCodePath("class-static-block");
break;

@@ -512,2 +572,3 @@

function processCodePathToExit(analyzer, node) {
const codePath = analyzer.codePath;

@@ -637,2 +698,28 @@ const state = CodePath.getState(codePath);

function postprocess(analyzer, node) {
/**
* Ends the code path for the current node.
* @returns {void}
*/
function endCodePath() {
let codePath = analyzer.codePath;
// Mark the current path as the final node.
CodePath.getState(codePath).makeFinal();
// Emits onCodePathSegmentEnd event of the current segments.
leaveFromCurrentSegment(analyzer, node);
// Emits onCodePathEnd event of this code path.
debug.dump(`onCodePathEnd ${codePath.id}`);
analyzer.emitter.emit("onCodePathEnd", codePath, node);
debug.dumpDot(codePath);
codePath = analyzer.codePath = analyzer.codePath.upper;
if (codePath) {
debug.dumpState(node, CodePath.getState(codePath), true);
}
}
switch (node.type) {

@@ -642,20 +729,5 @@ case "Program":

case "FunctionExpression":
case "ArrowFunctionExpression": {
let codePath = analyzer.codePath;
// Mark the current path as the final node.
CodePath.getState(codePath).makeFinal();
// Emits onCodePathSegmentEnd event of the current segments.
leaveFromCurrentSegment(analyzer, node);
// Emits onCodePathEnd event of this code path.
debug.dump(`onCodePathEnd ${codePath.id}`);
analyzer.emitter.emit("onCodePathEnd", codePath, node);
debug.dumpDot(codePath);
codePath = analyzer.codePath = analyzer.codePath.upper;
if (codePath) {
debug.dumpState(node, CodePath.getState(codePath), true);
}
case "ArrowFunctionExpression":
case "StaticBlock": {
endCodePath();
break;

@@ -674,2 +746,23 @@ }

}
/*
* Special case: The right side of class field initializer is considered
* to be its own function, so we need to end a code path in this
* case.
*
* We need to check after the other checks in order to close the
* code paths in the correct order for code like this:
*
*
* class Foo {
* a = () => {}
* }
*
* In this case, The ArrowFunctionExpression code path is closed first
* and then we need to close the code path for the PropertyDefinition
* value.
*/
if (isPropertyDefinitionValue(node)) {
endCodePath();
}
}

@@ -687,3 +780,2 @@

// eslint-disable-next-line jsdoc/require-description
/**

@@ -690,0 +782,0 @@ * @param {EventGenerator} eventGenerator An event generator to wrap.

/**
* @fileoverview A class of the code path segment.
* @fileoverview The CodePathSegment class.
* @author Toru Nagashima

@@ -33,7 +33,18 @@ */

* A code path segment.
*
* Each segment is arranged in a series of linked lists (implemented by arrays)
* that keep track of the previous and next segments in a code path. In this way,
* you can navigate between all segments in any code path so long as you have a
* reference to any segment in that code path.
*
* When first created, the segment is in a detached state, meaning that it knows the
* segments that came before it but those segments don't know that this new segment
* follows it. Only when `CodePathSegment#markUsed()` is called on a segment does it
* officially become part of the code path by updating the previous segments to know
* that this new segment follows.
*/
class CodePathSegment {
// eslint-disable-next-line jsdoc/require-description
/**
* Creates a new instance.
* @param {string} id An identifier.

@@ -54,3 +65,3 @@ * @param {CodePathSegment[]} allPrevSegments An array of the previous segments.

/**
* An array of the next segments.
* An array of the next reachable segments.
* @type {CodePathSegment[]}

@@ -61,3 +72,3 @@ */

/**
* An array of the previous segments.
* An array of the previous reachable segments.
* @type {CodePathSegment[]}

@@ -68,4 +79,3 @@ */

/**
* An array of the next segments.
* This array includes unreachable segments.
* An array of all next segments including reachable and unreachable.
* @type {CodePathSegment[]}

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

/**
* An array of the previous segments.
* This array includes unreachable segments.
* An array of all previous segments including reachable and unreachable.
* @type {CodePathSegment[]}

@@ -92,3 +101,7 @@ */

value: {
// determines if the segment has been attached to the code path
used: false,
// array of previous segments coming from the end of a loop
loopedPrevSegments: []

@@ -98,6 +111,6 @@ }

/* istanbul ignore if */
/* c8 ignore start */
if (debug.enabled) {
this.internal.nodes = [];
}
}/* c8 ignore stop */
}

@@ -111,3 +124,3 @@

isLoopedPrevSegment(segment) {
return this.internal.loopedPrevSegments.indexOf(segment) !== -1;
return this.internal.loopedPrevSegments.includes(segment);
}

@@ -125,5 +138,6 @@

/**
* Creates a segment that follows given segments.
* Creates a new segment and appends it after the given segments.
* @param {string} id An identifier.
* @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
* @param {CodePathSegment[]} allPrevSegments An array of the previous segments
* to append to.
* @returns {CodePathSegment} The created segment.

@@ -140,3 +154,3 @@ */

/**
* Creates an unreachable segment that follows given segments.
* Creates an unreachable segment and appends it after the given segments.
* @param {string} id An identifier.

@@ -151,3 +165,3 @@ * @param {CodePathSegment[]} allPrevSegments An array of the previous segments.

* In `if (a) return a; foo();` case, the unreachable segment preceded by
* the return statement is not used but must not be remove.
* the return statement is not used but must not be removed.
*/

@@ -172,3 +186,3 @@ CodePathSegment.markUsed(segment);

/**
* Makes a given segment being used.
* Marks a given segment as used.
*

@@ -188,2 +202,9 @@ * And this function registers the segment into the previous segments as a next.

if (segment.reachable) {
/*
* If the segment is reachable, then it's officially part of the
* code path. This loops through all previous segments to update
* their list of next segments. Because the segment is reachable,
* it's added to both `nextSegments` and `allNextSegments`.
*/
for (i = 0; i < segment.allPrevSegments.length; ++i) {

@@ -196,2 +217,9 @@ const prevSegment = segment.allPrevSegments[i];

} else {
/*
* If the segment is not reachable, then it's not officially part of the
* code path. This loops through all previous segments to update
* their list of next segments. Because the segment is not reachable,
* it's added only to `allNextSegments`.
*/
for (i = 0; i < segment.allPrevSegments.length; ++i) {

@@ -214,9 +242,10 @@ segment.allPrevSegments[i].allNextSegments.push(segment);

/**
* Replaces unused segments with the previous segments of each unused segment.
* @param {CodePathSegment[]} segments An array of segments to replace.
* @returns {CodePathSegment[]} The replaced array.
* Creates a new array based on an array of segments. If any segment in the
* array is unused, then it is replaced by all of its previous segments.
* All used segments are returned as-is without replacement.
* @param {CodePathSegment[]} segments The array of segments to flatten.
* @returns {CodePathSegment[]} The flattened array.
*/
static flattenUnusedSegments(segments) {
const done = Object.create(null);
const retv = [];
const done = new Set();

@@ -227,3 +256,3 @@ for (let i = 0; i < segments.length; ++i) {

// Ignores duplicated.
if (done[segment.id]) {
if (done.has(segment)) {
continue;

@@ -237,14 +266,12 @@ }

if (!done[prevSegment.id]) {
done[prevSegment.id] = true;
retv.push(prevSegment);
if (!done.has(prevSegment)) {
done.add(prevSegment);
}
}
} else {
done[segment.id] = true;
retv.push(segment);
done.add(segment);
}
}
return retv;
return [...done];
}

@@ -251,0 +278,0 @@ }

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

dest.push(segment);
if (others.indexOf(segment) === -1) {
if (!others.includes(segment)) {
all.push(segment);

@@ -63,3 +63,3 @@ }

/* istanbul ignore next: foolproof (syntax error) */
/* c8 ignore next */
return null;

@@ -84,3 +84,3 @@ }

/* istanbul ignore next: foolproof (syntax error) */
/* c8 ignore next */
return null;

@@ -225,3 +225,2 @@ }

// eslint-disable-next-line jsdoc/require-description
/**

@@ -367,2 +366,3 @@ * @param {IdGenerator} idGenerator An id generator to generate id for code

* Pops the last choice context and finalizes it.
* @throws {Error} (Unreachable.)
* @returns {ChoiceContext} The popped context.

@@ -441,3 +441,3 @@ */

/* istanbul ignore next */
/* c8 ignore next */
default:

@@ -459,2 +459,3 @@ throw new Error("unreachable");

* expression.
* @throws {Error} (Unreachable.)
* @returns {void}

@@ -975,2 +976,3 @@ */

* @param {string|null} label A label of the node which was triggered.
* @throws {Error} (Unreachable - unknown type.)
* @returns {void}

@@ -1039,3 +1041,3 @@ */

/* istanbul ignore next */
/* c8 ignore next */
default:

@@ -1048,2 +1050,3 @@ throw new Error(`unknown type: "${type}"`);

* Pops the last context of a loop statement and finalizes it.
* @throws {Error} (Unreachable - unknown type.)
* @returns {void}

@@ -1105,3 +1108,3 @@ */

/* istanbul ignore next */
/* c8 ignore next */
default:

@@ -1403,3 +1406,3 @@ throw new Error("unreachable");

/* istanbul ignore else: foolproof (syntax error) */
if (context) {

@@ -1409,2 +1412,3 @@ context.brokenForkContext.add(forkContext.head);

/* c8 ignore next */
forkContext.replaceHead(forkContext.makeUnreachable(-1, -1));

@@ -1430,3 +1434,2 @@ }

/* istanbul ignore else: foolproof (syntax error) */
if (context) {

@@ -1433,0 +1436,0 @@ if (context.continueDestSegments) {

@@ -24,9 +24,11 @@ /**

// eslint-disable-next-line jsdoc/require-description
/**
* @param {string} id An identifier.
* @param {CodePath|null} upper The code path of the upper function scope.
* @param {Function} onLooped A callback function to notify looping.
* Creates a new instance.
* @param {Object} options Options for the function (see below).
* @param {string} options.id An identifier.
* @param {string} options.origin The type of code path origin.
* @param {CodePath|null} options.upper The code path of the upper function scope.
* @param {Function} options.onLooped A callback function to notify looping.
*/
constructor(id, upper, onLooped) {
constructor({ id, origin, upper, onLooped }) {

@@ -41,2 +43,9 @@ /**

/**
* The reason that this code path was started. May be "program",
* "function", "class-field-initializer", or "class-static-block".
* @type {string}
*/
this.origin = origin;
/**
* The code path of the upper function scope.

@@ -113,2 +122,3 @@ * @type {CodePath|null}

* @type {CodePathSegment[]}
* @deprecated
*/

@@ -209,3 +219,3 @@ get currentSegments() {

// Reset the flag of skipping if all branches have been skipped.
if (skippedSegment && segment.prevSegments.indexOf(skippedSegment) !== -1) {
if (skippedSegment && segment.prevSegments.includes(skippedSegment)) {
skippedSegment = null;

@@ -212,0 +222,0 @@ }

@@ -23,4 +23,4 @@ /**

*/
/* istanbul ignore next */
function getId(segment) { // eslint-disable-line jsdoc/require-jsdoc
/* c8 ignore next */
function getId(segment) { // eslint-disable-line jsdoc/require-jsdoc -- Ignoring
return segment.id + (segment.reachable ? "" : "!");

@@ -71,3 +71,3 @@ }

*/
dumpState: !debug.enabled ? debug : /* istanbul ignore next */ function(node, state, leaving) {
dumpState: !debug.enabled ? debug : /* c8 ignore next */ function(node, state, leaving) {
for (let i = 0; i < state.currentSegments.length; ++i) {

@@ -103,3 +103,3 @@ const segInternal = state.currentSegments[i].internal;

*/
dumpDot: !debug.enabled ? debug : /* istanbul ignore next */ function(codePath) {
dumpDot: !debug.enabled ? debug : /* c8 ignore next */ function(codePath) {
let text =

@@ -115,3 +115,3 @@ "\n" +

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";
}

@@ -122,3 +122,3 @@

for (const id in traceMap) { // eslint-disable-line guard-for-in
for (const id in traceMap) { // eslint-disable-line guard-for-in -- Want ability to traverse prototype
const segment = traceMap[id];

@@ -125,0 +125,0 @@

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

// eslint-disable-next-line jsdoc/require-description
/**

@@ -103,0 +102,0 @@ * @param {IdGenerator} idGenerator An identifier generator for segments.

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

// eslint-disable-next-line jsdoc/require-description
/**

@@ -38,6 +37,6 @@ * @param {string} prefix Optional. A prefix of generated ids.

/* istanbul ignore if */
/* c8 ignore start */
if (this.n < 0) {
this.n = 1;
}
}/* c8 ignore stop */

@@ -44,0 +43,0 @@ return this.prefix + this.n;

@@ -6,3 +6,3 @@ /**

/* eslint-disable class-methods-use-this*/
/* eslint class-methods-use-this: off -- Methods desired on instance */
"use strict";

@@ -15,3 +15,7 @@

const levn = require("levn"),
ConfigOps = require("@eslint/eslintrc/lib/shared/config-ops");
{
Legacy: {
ConfigOps
}
} = require("@eslint/eslintrc/universal");

@@ -21,2 +25,8 @@ const debug = require("debug")("eslint:config-comment-parser");

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

@@ -63,3 +73,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
*/

@@ -112,3 +122,4 @@ parseJsonConfig(string, location) {

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

@@ -135,4 +146,3 @@ };

// Collapse whitespace around commas
string.replace(/\s*,\s*/gu, ",").split(/,+/u).forEach(name => {
string.split(",").forEach(name => {
const trimmedName = name.trim();

@@ -139,0 +149,0 @@

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

const esquery = require("esquery");
const lodash = require("lodash");

@@ -37,2 +36,29 @@ //------------------------------------------------------------------------------

/**
* Computes the union of one or more arrays
* @param {...any[]} arrays One or more arrays to union
* @returns {any[]} The union of the input arrays
*/
function union(...arrays) {
return [...new Set(arrays.flat())];
}
/**
* Computes the intersection of one or more arrays
* @param {...any[]} arrays One or more arrays to intersect
* @returns {any[]} The intersection of the input arrays
*/
function intersection(...arrays) {
if (arrays.length === 0) {
return [];
}
let result = [...new Set(arrays[0])];
for (const array of arrays.slice(1)) {
result = result.filter(x => array.includes(x));
}
return result;
}
/**
* Gets the possible types of a selector

@@ -51,3 +77,3 @@ * @param {Object} parsedSelector An object (from esquery) describing the matching behavior of the selector

if (typesForComponents.every(Boolean)) {
return lodash.union(...typesForComponents);
return union(...typesForComponents);
}

@@ -69,3 +95,3 @@ return null;

*/
return lodash.intersection(...typesForComponents);
return intersection(...typesForComponents);
}

@@ -79,2 +105,9 @@

case "class":
if (parsedSelector.name === "function") {
return ["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"];
}
return null;
default:

@@ -174,2 +207,4 @@ return null;

const selectorCache = new Map();
/**

@@ -180,6 +215,10 @@ * Parses a raw selector string, and returns the parsed selector along with specificity and type information.

*/
const parseSelector = lodash.memoize(rawSelector => {
function parseSelector(rawSelector) {
if (selectorCache.has(rawSelector)) {
return selectorCache.get(rawSelector);
}
const parsedSelector = tryParseSelector(rawSelector);
return {
const result = {
rawSelector,

@@ -192,4 +231,7 @@ isExit: rawSelector.endsWith(":exit"),

};
});
selectorCache.set(rawSelector, result);
return result;
}
//------------------------------------------------------------------------------

@@ -213,3 +255,2 @@ // Public Interface

// eslint-disable-next-line jsdoc/require-description
/**

@@ -216,0 +257,0 @@ * @param {SafeEmitter} emitter

@@ -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
* @property {(0|1|2)} severity
* @property {(string|undefined)} message
* @property {(string|undefined)} [messageId]
* @property {number} line
* @property {number} column
* @property {(number|undefined)} [endLine]
* @property {(number|undefined)} [endColumn]
* @property {(string|null)} nodeType
* @property {string} source
* @property {({text: string, range: (number[]|null)}|null)} [fix]
* @property {Array<{text: string, range: (number[]|null)}|null>} [suggestions]
*/
//------------------------------------------------------------------------------

@@ -121,2 +106,29 @@ // 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.
* @param {Fix|null} fix The fix to validate.
* @returns {void}
*/
function assertValidFix(fix) {
if (fix) {
assert(fix.range && typeof fix.range[0] === "number" && typeof fix.range[1] === "number", `Fix has invalid range: ${JSON.stringify(fix, null, 2)}`);
}
}
/**
* Compares items in a fixes array by range.

@@ -139,2 +151,6 @@ * @param {Fix} a The first message.

function mergeFixes(fixes, sourceCode) {
for (const fix of fixes) {
assertValidFix(fix);
}
if (fixes.length === 0) {

@@ -144,3 +160,3 @@ return null;

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

@@ -189,3 +205,5 @@

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

@@ -231,3 +249,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
*/

@@ -307,3 +325,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
*/

@@ -310,0 +328,0 @@

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

/**
* A storage for rules.
*/
class Rules {

@@ -35,0 +38,0 @@ constructor() {

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

* @typedef {Object} SafeEmitter
* @property {function(eventName: string, listenerFunc: Function): void} on Adds a listener for a given event name
* @property {function(eventName: string, arg1?: any, arg2?: any, arg3?: any)} emit Emits an event with a given name.
* @property {(eventName: string, listenerFunc: Function) => void} on Adds a listener for a given event name
* @property {(eventName: string, arg1?: any, arg2?: any, arg3?: any) => void} emit Emits an event with a given name.
* This calls all the listeners that were listening for that name, with `arg1`, `arg2`, and `arg3` as arguments.

@@ -19,0 +19,0 @@ * @property {function(): string[]} eventNames Gets the list of event names that have registered listeners.

@@ -83,4 +83,4 @@ /**

* Try to use the 'fix' from a problem.
* @param {Message} problem The message object to apply fixes from
* @returns {boolean} Whether fix was successfully applied
* @param {Message} problem The message object to apply fixes from
* @returns {boolean} Whether fix was successfully applied
*/

@@ -87,0 +87,0 @@ function attemptFix(problem) {

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

/* istanbul ignore next */
/* c8 ignore next */
/**

@@ -26,3 +26,3 @@ * Align the string to left

/* istanbul ignore next */
/* c8 ignore next */
/**

@@ -69,3 +69,3 @@ * Align the string to right

/* istanbul ignore next */
/* c8 ignore next */
/**

@@ -122,6 +122,6 @@ * display the data

console.log(table.join("\n")); // eslint-disable-line no-console
console.log(table.join("\n")); // eslint-disable-line no-console -- Debugging function
}
/* istanbul ignore next */
/* c8 ignore next */
module.exports = (function() {

@@ -133,3 +133,3 @@

* Time the run
* @param {*} key key from the data object
* @param {any} key key from the data object
* @param {Function} fn function to be called

@@ -146,6 +146,7 @@ * @returns {Function} function to be executed

let t = process.hrtime();
const result = fn(...args);
fn(...args);
t = process.hrtime(t);
data[key] += t[0] * 1e3 + t[1] / 1e6;
return result;
};

@@ -152,0 +153,0 @@ }

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

//------------------------------------------------------------------------------
// Typedefs
//------------------------------------------------------------------------------
/**
* The options object parsed by Optionator.
* @typedef {Object} ParsedCLIOptions
* @property {boolean} cache Only check changed files
* @property {string} cacheFile Path to the cache file. Deprecated: use --cache-location
* @property {string} [cacheLocation] Path to the cache file or directory
* @property {"metadata" | "content"} cacheStrategy Strategy to use for detecting changed files in the cache
* @property {boolean} [color] Force enabling/disabling of color
* @property {string} [config] Use this configuration, overriding .eslintrc.* config options if present
* @property {boolean} debug Output debugging information
* @property {string[]} [env] Specify environments
* @property {boolean} envInfo Output execution environment information
* @property {boolean} errorOnUnmatchedPattern Prevent errors when pattern is unmatched
* @property {boolean} eslintrc Disable use of configuration from .eslintrc.*
* @property {string[]} [ext] Specify JavaScript file extensions
* @property {boolean} fix Automatically fix problems
* @property {boolean} fixDryRun Automatically fix problems without saving the changes to the file system
* @property {("directive" | "problem" | "suggestion" | "layout")[]} [fixType] Specify the types of fixes to apply (directive, problem, suggestion, layout)
* @property {string} format Use a specific output format
* @property {string[]} [global] Define global variables
* @property {boolean} [help] Show help
* @property {boolean} ignore Disable use of ignore files and patterns
* @property {string} [ignorePath] Specify path of ignore file
* @property {string[]} [ignorePattern] Pattern of files to ignore (in addition to those in .eslintignore)
* @property {boolean} init Run config initialization wizard
* @property {boolean} inlineConfig Prevent comments from changing config or rules
* @property {number} maxWarnings Number of warnings to trigger nonzero exit code
* @property {string} [outputFile] Specify file to write report to
* @property {string} [parser] Specify the parser to be used
* @property {Object} [parserOptions] Specify parser options
* @property {string[]} [plugin] Specify plugins
* @property {string} [printConfig] Print the configuration for the given file
* @property {boolean | undefined} reportUnusedDisableDirectives Adds reported errors for unused eslint-disable directives
* @property {string} [resolvePluginsRelativeTo] A folder where plugins should be resolved from, CWD by default
* @property {Object} [rule] Specify rules
* @property {string[]} [rulesdir] Load additional rules from this directory. Deprecated: Use rules from plugins
* @property {boolean} stdin Lint code provided on <STDIN>
* @property {string} [stdinFilename] Specify filename to process STDIN as
* @property {boolean} quiet Report errors only
* @property {boolean} [version] Output the version number
* @property {string[]} _ Positional filenames or patterns
*/
//------------------------------------------------------------------------------
// Initialization and Public Interface

@@ -20,13 +67,21 @@ //------------------------------------------------------------------------------

// exports "parse(args)", "generateHelp()", and "generateHelpForOption(optionName)"
module.exports = optionator({
prepend: "eslint [options] file.js [file.js] [dir]",
defaults: {
concatRepeatedArrays: true,
mergeRepeatedObjects: true
},
options: [
{
heading: "Basic configuration"
},
{
/**
* Creates the CLI options for ESLint.
* @param {boolean} usingFlatConfig Indicates if flat config is being used.
* @returns {Object} The optionator instance.
*/
module.exports = function(usingFlatConfig) {
let lookupFlag;
if (usingFlatConfig) {
lookupFlag = {
option: "config-lookup",
type: "Boolean",
default: "true",
description: "Disable look up for eslint.config.js"
};
} else {
lookupFlag = {
option: "eslintrc",

@@ -36,230 +91,290 @@ type: "Boolean",

description: "Disable use of configuration from .eslintrc.*"
},
{
option: "config",
alias: "c",
type: "path::String",
description: "Use this configuration, overriding .eslintrc.* config options if present"
},
{
};
}
let envFlag;
if (!usingFlatConfig) {
envFlag = {
option: "env",
type: "[String]",
description: "Specify environments"
},
{
};
}
let extFlag;
if (!usingFlatConfig) {
extFlag = {
option: "ext",
type: "[String]",
description: "Specify JavaScript file extensions"
},
{
option: "global",
type: "[String]",
description: "Define global variables"
},
{
option: "parser",
type: "String",
description: "Specify the parser to be used"
},
{
option: "parser-options",
type: "Object",
description: "Specify parser options"
},
{
};
}
let resolvePluginsFlag;
if (!usingFlatConfig) {
resolvePluginsFlag = {
option: "resolve-plugins-relative-to",
type: "path::String",
description: "A folder where plugins should be resolved from, CWD by default"
},
{
heading: "Specifying rules and plugins"
},
{
};
}
let rulesDirFlag;
if (!usingFlatConfig) {
rulesDirFlag = {
option: "rulesdir",
type: "[path::String]",
description: "Use additional rules from this directory"
},
{
option: "plugin",
type: "[String]",
description: "Specify plugins"
},
{
option: "rule",
type: "Object",
description: "Specify rules"
},
{
heading: "Fixing problems"
},
{
option: "fix",
type: "Boolean",
default: false,
description: "Automatically fix problems"
},
{
option: "fix-dry-run",
type: "Boolean",
default: false,
description: "Automatically fix problems without saving the changes to the file system"
},
{
option: "fix-type",
type: "Array",
description: "Specify the types of fixes to apply (problem, suggestion, layout)"
},
{
heading: "Ignoring files"
},
{
description: "Load additional rules from this directory. Deprecated: Use rules from plugins"
};
}
let ignorePathFlag;
if (!usingFlatConfig) {
ignorePathFlag = {
option: "ignore-path",
type: "path::String",
description: "Specify path of ignore file"
};
}
return optionator({
prepend: "eslint [options] file.js [file.js] [dir]",
defaults: {
concatRepeatedArrays: true,
mergeRepeatedObjects: true
},
{
option: "ignore",
type: "Boolean",
default: "true",
description: "Disable use of ignore files and patterns"
},
{
option: "ignore-pattern",
type: "[String]",
description: "Pattern of files to ignore (in addition to those in .eslintignore)",
concatRepeatedArrays: [true, {
oneValuePerFlag: true
}]
},
{
heading: "Using stdin"
},
{
option: "stdin",
type: "Boolean",
default: "false",
description: "Lint code provided on <STDIN>"
},
{
option: "stdin-filename",
type: "String",
description: "Specify filename to process STDIN as"
},
{
heading: "Handling warnings"
},
{
option: "quiet",
type: "Boolean",
default: "false",
description: "Report errors only"
},
{
option: "max-warnings",
type: "Int",
default: "-1",
description: "Number of warnings to trigger nonzero exit code"
},
{
heading: "Output"
},
{
option: "output-file",
alias: "o",
type: "path::String",
description: "Specify file to write report to"
},
{
option: "format",
alias: "f",
type: "String",
default: "stylish",
description: "Use a specific output format"
},
{
option: "color",
type: "Boolean",
alias: "no-color",
description: "Force enabling/disabling of color"
},
{
heading: "Inline configuration comments"
},
{
option: "inline-config",
type: "Boolean",
default: "true",
description: "Prevent comments from changing config or rules"
},
{
option: "report-unused-disable-directives",
type: "Boolean",
default: void 0,
description: "Adds reported errors for unused eslint-disable directives"
},
{
heading: "Caching"
},
{
option: "cache",
type: "Boolean",
default: "false",
description: "Only check changed files"
},
{
option: "cache-file",
type: "path::String",
default: ".eslintcache",
description: "Path to the cache file. Deprecated: use --cache-location"
},
{
option: "cache-location",
type: "path::String",
description: "Path to the cache file or directory"
},
{
heading: "Miscellaneous"
},
{
option: "init",
type: "Boolean",
default: "false",
description: "Run config initialization wizard"
},
{
option: "env-info",
type: "Boolean",
default: "false",
description: "Output execution environment information"
},
{
option: "error-on-unmatched-pattern",
type: "Boolean",
default: "true",
description: "Prevent errors when pattern is unmatched"
},
{
option: "debug",
type: "Boolean",
default: false,
description: "Output debugging information"
},
{
option: "help",
alias: "h",
type: "Boolean",
description: "Show help"
},
{
option: "version",
alias: "v",
type: "Boolean",
description: "Output the version number"
},
{
option: "print-config",
type: "path::String",
description: "Print the configuration for the given file"
}
]
});
options: [
{
heading: "Basic configuration"
},
lookupFlag,
{
option: "config",
alias: "c",
type: "path::String",
description: usingFlatConfig
? "Use this configuration instead of eslint.config.js"
: "Use this configuration, overriding .eslintrc.* config options if present"
},
envFlag,
extFlag,
{
option: "global",
type: "[String]",
description: "Define global variables"
},
{
option: "parser",
type: "String",
description: "Specify the parser to be used"
},
{
option: "parser-options",
type: "Object",
description: "Specify parser options"
},
resolvePluginsFlag,
{
heading: "Specify Rules and Plugins"
},
{
option: "plugin",
type: "[String]",
description: "Specify plugins"
},
{
option: "rule",
type: "Object",
description: "Specify rules"
},
rulesDirFlag,
{
heading: "Fix Problems"
},
{
option: "fix",
type: "Boolean",
default: false,
description: "Automatically fix problems"
},
{
option: "fix-dry-run",
type: "Boolean",
default: false,
description: "Automatically fix problems without saving the changes to the file system"
},
{
option: "fix-type",
type: "Array",
description: "Specify the types of fixes to apply (directive, problem, suggestion, layout)"
},
{
heading: "Ignore Files"
},
ignorePathFlag,
{
option: "ignore",
type: "Boolean",
default: "true",
description: "Disable use of ignore files and patterns"
},
{
option: "ignore-pattern",
type: "[String]",
description: "Pattern of files to ignore (in addition to those in .eslintignore)",
concatRepeatedArrays: [true, {
oneValuePerFlag: true
}]
},
{
heading: "Use stdin"
},
{
option: "stdin",
type: "Boolean",
default: "false",
description: "Lint code provided on <STDIN>"
},
{
option: "stdin-filename",
type: "String",
description: "Specify filename to process STDIN as"
},
{
heading: "Handle Warnings"
},
{
option: "quiet",
type: "Boolean",
default: "false",
description: "Report errors only"
},
{
option: "max-warnings",
type: "Int",
default: "-1",
description: "Number of warnings to trigger nonzero exit code"
},
{
heading: "Output"
},
{
option: "output-file",
alias: "o",
type: "path::String",
description: "Specify file to write report to"
},
{
option: "format",
alias: "f",
type: "String",
default: "stylish",
description: "Use a specific output format"
},
{
option: "color",
type: "Boolean",
alias: "no-color",
description: "Force enabling/disabling of color"
},
{
heading: "Inline configuration comments"
},
{
option: "inline-config",
type: "Boolean",
default: "true",
description: "Prevent comments from changing config or rules"
},
{
option: "report-unused-disable-directives",
type: "Boolean",
default: void 0,
description: "Adds reported errors for unused eslint-disable directives"
},
{
heading: "Caching"
},
{
option: "cache",
type: "Boolean",
default: "false",
description: "Only check changed files"
},
{
option: "cache-file",
type: "path::String",
default: ".eslintcache",
description: "Path to the cache file. Deprecated: use --cache-location"
},
{
option: "cache-location",
type: "path::String",
description: "Path to the cache file or directory"
},
{
option: "cache-strategy",
dependsOn: ["cache"],
type: "String",
default: "metadata",
enum: ["metadata", "content"],
description: "Strategy to use for detecting changed files in the cache"
},
{
heading: "Miscellaneous"
},
{
option: "init",
type: "Boolean",
default: "false",
description: "Run config initialization wizard"
},
{
option: "env-info",
type: "Boolean",
default: "false",
description: "Output execution environment information"
},
{
option: "error-on-unmatched-pattern",
type: "Boolean",
default: "true",
description: "Prevent errors when pattern is unmatched"
},
{
option: "exit-on-fatal-error",
type: "Boolean",
default: "false",
description: "Exit with exit code 2 in case of fatal error"
},
{
option: "debug",
type: "Boolean",
default: false,
description: "Output debugging information"
},
{
option: "help",
alias: "h",
type: "Boolean",
description: "Show help"
},
{
option: "version",
alias: "v",
type: "Boolean",
description: "Output the version number"
},
{
option: "print-config",
type: "path::String",
description: "Print the configuration for the given file"
}
].filter(value => !!value)
});
};

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

/* global describe, it */
/* globals describe, it -- Mocha globals */

@@ -48,6 +48,8 @@ /*

util = require("util"),
lodash = require("lodash"),
merge = require("lodash.merge"),
equal = require("fast-deep-equal"),
Traverser = require("../../lib/shared/traverser"),
{ getRuleOptionsSchema, validate } = require("../shared/config-validator"),
{ Linter, SourceCodeFixer, interpolate } = require("../linter");
{ Linter, SourceCodeFixer, interpolate } = require("../linter"),
CodePath = require("../linter/code-path-analysis/code-path");

@@ -57,3 +59,6 @@ const ajv = require("../shared/ajv")({ strictDefaults: true });

const espreePath = require.resolve("espree");
const parserSymbol = Symbol.for("eslint.RuleTester.parser");
const { SourceCode } = require("../source-code");
//------------------------------------------------------------------------------

@@ -64,6 +69,9 @@ // Typedefs

/** @typedef {import("../shared/types").Parser} Parser */
/** @typedef {import("../shared/types").Rule} Rule */
/**
* A test case that is expected to pass lint.
* @typedef {Object} ValidTestCase
* @property {string} [name] Name for the test case.
* @property {string} code Code for the test case.

@@ -77,2 +85,3 @@ * @property {any[]} [options] Options for the test case.

* @property {{ [name: string]: boolean }} [env] Environments for the test case.
* @property {boolean} [only] Run only this test case or the subset of test cases with this property.
*/

@@ -83,2 +92,3 @@

* @typedef {Object} InvalidTestCase
* @property {string} [name] Name for the test case.
* @property {string} code Code for the test case.

@@ -94,2 +104,3 @@ * @property {number | Array<TestCaseError | string | RegExp>} errors Expected errors.

* @property {{ [name: string]: boolean }} [env] Environments for the test case.
* @property {boolean} [only] Run only this test case or the subset of test cases with this property.
*/

@@ -126,2 +137,3 @@

const RuleTesterParameters = [
"name",
"code",

@@ -131,3 +143,4 @@ "filename",

"errors",
"output"
"output",
"only"
];

@@ -162,4 +175,39 @@

const forbiddenMethods = [
"applyInlineConfig",
"applyLanguageOptions",
"finalize"
];
const hasOwnProperty = Function.call.bind(Object.hasOwnProperty);
const DEPRECATED_SOURCECODE_PASSTHROUGHS = {
getSource: "getText",
getSourceLines: "getLines",
getAllComments: "getAllComments",
getNodeByRangeIndex: "getNodeByRangeIndex",
// getComments: "getComments", -- already handled by a separate error
getCommentsBefore: "getCommentsBefore",
getCommentsAfter: "getCommentsAfter",
getCommentsInside: "getCommentsInside",
getJSDocComment: "getJSDocComment",
getFirstToken: "getFirstToken",
getFirstTokens: "getFirstTokens",
getLastToken: "getLastToken",
getLastTokens: "getLastTokens",
getTokenAfter: "getTokenAfter",
getTokenBefore: "getTokenBefore",
getTokenByRangeStart: "getTokenByRangeStart",
getTokens: "getTokens",
getTokensAfter: "getTokensAfter",
getTokensBefore: "getTokensBefore",
getTokensBetween: "getTokensBetween",
getScope: "getScope",
getAncestors: "getAncestors",
getDeclaredVariables: "getDeclaredVariables",
markVariableAsUsed: "markVariableAsUsed"
};
/**

@@ -217,4 +265,7 @@ * Clones a given value deeply.

function sanitize(text) {
if (typeof text !== "string") {
return "";
}
return text.replace(
/[\u0000-\u0009\u000b-\u001a]/gu, // eslint-disable-line no-control-regex
/[\u0000-\u0009\u000b-\u001a]/gu, // eslint-disable-line no-control-regex -- Escaping controls
c => `\\u${c.codePointAt(0).toString(16).padStart(4, "0")}`

@@ -249,2 +300,3 @@ );

/**

@@ -269,4 +321,6 @@ * Define `start`/`end` properties of all nodes of the given AST as throwing error.

function wrapParser(parser) {
if (typeof parser.parseForESLint === "function") {
return {
[parserSymbol]: parser,
parseForESLint(...args) {

@@ -280,3 +334,5 @@ const ret = parser.parseForESLint(...args);

}
return {
[parserSymbol]: parser,
parse(...args) {

@@ -291,2 +347,103 @@ const ast = parser.parse(...args);

/**
* Function to replace `SourceCode.prototype.getComments`.
* @returns {void}
* @throws {Error} Deprecation message.
*/
function getCommentsDeprecation() {
throw new Error(
"`SourceCode#getComments()` is deprecated and will be removed in a future major version. Use `getCommentsBefore()`, `getCommentsAfter()`, and `getCommentsInside()` instead."
);
}
/**
* Function to replace forbidden `SourceCode` methods.
* @param {string} methodName The name of the method to forbid.
* @returns {Function} The function that throws the error.
*/
function throwForbiddenMethodError(methodName) {
return () => {
throw new Error(
`\`SourceCode#${methodName}()\` cannot be called inside a rule.`
);
};
}
/**
* Emit a deprecation warning if function-style format is being used.
* @param {string} ruleName Name of the rule.
* @returns {void}
*/
function emitLegacyRuleAPIWarning(ruleName) {
if (!emitLegacyRuleAPIWarning[`warned-${ruleName}`]) {
emitLegacyRuleAPIWarning[`warned-${ruleName}`] = true;
process.emitWarning(
`"${ruleName}" rule is using the deprecated function-style format and will stop working in ESLint v9. Please use object-style format: https://eslint.org/docs/latest/extend/custom-rules`,
"DeprecationWarning"
);
}
}
/**
* Emit a deprecation warning if rule has options but is missing the "meta.schema" property
* @param {string} ruleName Name of the rule.
* @returns {void}
*/
function emitMissingSchemaWarning(ruleName) {
if (!emitMissingSchemaWarning[`warned-${ruleName}`]) {
emitMissingSchemaWarning[`warned-${ruleName}`] = true;
process.emitWarning(
`"${ruleName}" rule has options but is missing the "meta.schema" property and will stop working in ESLint v9. Please add a schema: https://eslint.org/docs/latest/extend/custom-rules#options-schemas`,
"DeprecationWarning"
);
}
}
/**
* Emit a deprecation warning if a rule uses a deprecated `context` method.
* @param {string} ruleName Name of the rule.
* @param {string} methodName The name of the method on `context` that was used.
* @returns {void}
*/
function emitDeprecatedContextMethodWarning(ruleName, methodName) {
if (!emitDeprecatedContextMethodWarning[`warned-${ruleName}-${methodName}`]) {
emitDeprecatedContextMethodWarning[`warned-${ruleName}-${methodName}`] = true;
process.emitWarning(
`"${ruleName}" rule is using \`context.${methodName}()\`, which is deprecated and will be removed in ESLint v9. Please use \`sourceCode.${DEPRECATED_SOURCECODE_PASSTHROUGHS[methodName]}()\` instead.`,
"DeprecationWarning"
);
}
}
/**
* Emit a deprecation warning if rule uses CodePath#currentSegments.
* @param {string} ruleName Name of the rule.
* @returns {void}
*/
function emitCodePathCurrentSegmentsWarning(ruleName) {
if (!emitCodePathCurrentSegmentsWarning[`warned-${ruleName}`]) {
emitCodePathCurrentSegmentsWarning[`warned-${ruleName}`] = true;
process.emitWarning(
`"${ruleName}" rule uses CodePath#currentSegments and will stop working in ESLint v9. Please read the documentation for how to update your code: https://eslint.org/docs/latest/extend/code-path-analysis#usage-examples`,
"DeprecationWarning"
);
}
}
/**
* Emit a deprecation warning if `context.parserServices` is used.
* @param {string} ruleName Name of the rule.
* @returns {void}
*/
function emitParserServicesWarning(ruleName) {
if (!emitParserServicesWarning[`warned-${ruleName}`]) {
emitParserServicesWarning[`warned-${ruleName}`] = true;
process.emitWarning(
`"${ruleName}" rule is using \`context.parserServices\`, which is deprecated and will be removed in ESLint v9. Please use \`sourceCode.parserServices\` instead.`,
"DeprecationWarning"
);
}
}
//------------------------------------------------------------------------------

@@ -299,2 +456,3 @@ // Public Interface

const IT = Symbol("it");
const IT_ONLY = Symbol("itOnly");

@@ -306,2 +464,3 @@ /**

* @param {Function} method The logic of the test case.
* @throws {Error} Any error upon execution of `method`.
* @returns {any} Returned value of `method`.

@@ -331,2 +490,5 @@ */

/**
* Mocha test wrapper.
*/
class RuleTester {

@@ -345,6 +507,5 @@

*/
this.testerConfig = lodash.merge(
// we have to clone because merge uses the first argument for recipient
lodash.cloneDeep(defaultConfig),
this.testerConfig = merge(
{},
defaultConfig,
testerConfig,

@@ -365,6 +526,7 @@ { rules: { "rule-tester/validate-ast": "error" } }

* @param {Object} config the configuration to use.
* @throws {TypeError} If non-object config.
* @returns {void}
*/
static setDefaultConfig(config) {
if (typeof config !== "object") {
if (typeof config !== "object" || config === null) {
throw new TypeError("RuleTester.setDefaultConfig: config must be an object");

@@ -392,3 +554,3 @@ }

static resetDefaultConfig() {
defaultConfig = lodash.cloneDeep(testerDefaultConfig);
defaultConfig = merge({}, testerDefaultConfig);
}

@@ -425,8 +587,51 @@

/**
* Adds the `only` property to a test to run it in isolation.
* @param {string | ValidTestCase | InvalidTestCase} item A single test to run by itself.
* @returns {ValidTestCase | InvalidTestCase} The test with `only` set.
*/
static only(item) {
if (typeof item === "string") {
return { code: item, only: true };
}
return { ...item, only: true };
}
static get itOnly() {
if (typeof this[IT_ONLY] === "function") {
return this[IT_ONLY];
}
if (typeof this[IT] === "function" && typeof this[IT].only === "function") {
return Function.bind.call(this[IT].only, this[IT]);
}
if (typeof it === "function" && typeof it.only === "function") {
return Function.bind.call(it.only, it);
}
if (typeof this[DESCRIBE] === "function" || typeof this[IT] === "function") {
throw new Error(
"Set `RuleTester.itOnly` to use `only` with a custom test framework.\n" +
"See https://eslint.org/docs/latest/integrate/nodejs-api#customizing-ruletester for more."
);
}
if (typeof it === "function") {
throw new Error("The current test framework does not support exclusive tests with `only`.");
}
throw new Error("To use `only`, use RuleTester with a test framework that provides `it.only()` like Mocha.");
}
static set itOnly(value) {
this[IT_ONLY] = value;
}
/**
* Define a rule for one particular run of tests.
* @param {string} name The name of the rule to define.
* @param {Function} rule The rule definition.
* @param {Function | Rule} rule The rule definition.
* @returns {void}
*/
defineRule(name, rule) {
if (typeof rule === "function") {
emitLegacyRuleAPIWarning(name);
}
this.rules[name] = rule;

@@ -438,3 +643,3 @@ }

* @param {string} ruleName The name of the rule to run.
* @param {Function} rule The rule to test.
* @param {Function | Rule} rule The rule to test.
* @param {{

@@ -444,2 +649,4 @@ * valid: (ValidTestCase | string)[],

* }} test The collection of tests to run.
* @throws {TypeError|Error} If non-object `test`, or if a required
* scenario of the given type is missing.
* @returns {void}

@@ -454,3 +661,3 @@ */

if (lodash.isNil(test) || typeof test !== "object") {
if (!test || typeof test !== "object") {
throw new TypeError(`Test Scenarios for rule ${ruleName} : Could not find test scenario object`);

@@ -460,3 +667,3 @@ }

requiredScenarios.forEach(scenarioType => {
if (lodash.isNil(test[scenarioType])) {
if (!test[scenarioType]) {
scenarioErrors.push(`Could not find any ${scenarioType} test scenarios`);

@@ -472,2 +679,5 @@ }

if (typeof rule === "function") {
emitLegacyRuleAPIWarning(ruleName);
}

@@ -482,3 +692,34 @@ linter.defineRule(ruleName, Object.assign({}, rule, {

return (typeof rule === "function" ? rule : rule.create)(context);
// wrap all deprecated methods
const newContext = Object.create(
context,
Object.fromEntries(Object.keys(DEPRECATED_SOURCECODE_PASSTHROUGHS).map(methodName => [
methodName,
{
value(...args) {
// emit deprecation warning
emitDeprecatedContextMethodWarning(ruleName, methodName);
// call the original method
return context[methodName].call(this, ...args);
},
enumerable: true
}
]))
);
// emit warning about context.parserServices
const parserServices = context.parserServices;
Object.defineProperty(newContext, "parserServices", {
get() {
emitParserServicesWarning(ruleName);
return parserServices;
}
});
Object.freeze(newContext);
return (typeof rule === "function" ? rule : rule.create)(newContext);
}

@@ -492,2 +733,3 @@ }));

* @param {string|Object} item Item to run the rule against
* @throws {Error} If an invalid schema.
* @returns {Object} Eslint run result

@@ -497,3 +739,3 @@ * @private

function runRuleForItem(item) {
let config = lodash.cloneDeep(testerConfig),
let config = merge({}, testerConfig),
code, filename, output, beforeAST, afterAST;

@@ -510,4 +752,8 @@

*/
const itemConfig = lodash.omit(item, RuleTesterParameters);
const itemConfig = { ...item };
for (const parameter of RuleTesterParameters) {
delete itemConfig[parameter];
}
/*

@@ -517,3 +763,3 @@ * Create the config object from the tester config and this item

*/
config = lodash.merge(
config = merge(
config,

@@ -530,2 +776,11 @@ itemConfig

assert(Array.isArray(item.options), "options must be an array");
if (
item.options.length > 0 &&
typeof rule === "object" &&
(
!rule.meta || (rule.meta && (typeof rule.meta.schema === "undefined" || rule.meta.schema === null))
)
) {
emitMissingSchemaWarning(ruleName);
}
config.rules[ruleName] = [1].concat(item.options);

@@ -543,10 +798,14 @@ } else {

*/
linter.defineRule("rule-tester/validate-ast", () => ({
Program(node) {
beforeAST = cloneDeeplyExcludesParent(node);
},
"Program:exit"(node) {
afterAST = node;
linter.defineRule("rule-tester/validate-ast", {
create() {
return {
Program(node) {
beforeAST = cloneDeeplyExcludesParent(node);
},
"Program:exit"(node) {
afterAST = node;
}
};
}
}));
});

@@ -590,3 +849,28 @@ if (typeof config.parser === "string") {

// Verify the code.
const messages = linter.verify(code, config, filename);
const { getComments, applyLanguageOptions, applyInlineConfig, finalize } = SourceCode.prototype;
const originalCurrentSegments = Object.getOwnPropertyDescriptor(CodePath.prototype, "currentSegments");
let messages;
try {
SourceCode.prototype.getComments = getCommentsDeprecation;
Object.defineProperty(CodePath.prototype, "currentSegments", {
get() {
emitCodePathCurrentSegmentsWarning(ruleName);
return originalCurrentSegments.get.call(this);
}
});
forbiddenMethods.forEach(methodName => {
SourceCode.prototype[methodName] = throwForbiddenMethodError(methodName);
});
messages = linter.verify(code, config, filename);
} finally {
SourceCode.prototype.getComments = getComments;
Object.defineProperty(CodePath.prototype, "currentSegments", originalCurrentSegments);
SourceCode.prototype.applyInlineConfig = applyInlineConfig;
SourceCode.prototype.applyLanguageOptions = applyLanguageOptions;
SourceCode.prototype.finalize = finalize;
}
const fatalErrorMessage = messages.find(m => m.fatal);

@@ -627,3 +911,3 @@

function assertASTDidntChange(beforeAST, afterAST) {
if (!lodash.isEqual(beforeAST, afterAST)) {
if (!equal(beforeAST, afterAST)) {
assert.fail("Rule should not modify AST.");

@@ -641,2 +925,9 @@ }

function testValidTemplate(item) {
const code = typeof item === "object" ? item.code : item;
assert.ok(typeof code === "string", "Test case must specify a string value for 'code'");
if (item.name) {
assert.ok(typeof item.name === "string", "Optional test case property 'name' must be a string");
}
const result = runRuleForItem(item);

@@ -646,3 +937,4 @@ const messages = result.messages;

assert.strictEqual(messages.length, 0, util.format("Should have no errors but had %d: %s",
messages.length, util.inspect(messages)));
messages.length,
util.inspect(messages)));

@@ -682,2 +974,6 @@ assertASTDidntChange(result.beforeAST, result.afterAST);

function testInvalidTemplate(item) {
assert.ok(typeof item.code === "string", "Test case must specify a string value for 'code'");
if (item.name) {
assert.ok(typeof item.name === "string", "Optional test case property 'name' must be a string");
}
assert.ok(item.errors || item.errors === 0,

@@ -703,9 +999,14 @@ `Did not specify errors for an invalid test of ${ruleName}`);

assert.strictEqual(messages.length, item.errors, util.format("Should have %d error%s but had %d: %s",
item.errors, item.errors === 1 ? "" : "s", messages.length, util.inspect(messages)));
item.errors,
item.errors === 1 ? "" : "s",
messages.length,
util.inspect(messages)));
} else {
assert.strictEqual(
messages.length, item.errors.length,
util.format(
messages.length, item.errors.length, util.format(
"Should have %d error%s but had %d: %s",
item.errors.length, item.errors.length === 1 ? "" : "s", messages.length, util.inspect(messages)
item.errors.length,
item.errors.length === 1 ? "" : "s",
messages.length,
util.inspect(messages)
)

@@ -904,12 +1205,2 @@ );

// Rules that produce fixes must have `meta.fixable` property.
if (result.output !== item.code) {
assert.ok(
hasOwnProperty(rule, "meta"),
"Fixable rules should export a `meta.fixable` property."
);
// Linter throws if a rule that produced a fix has `meta` but doesn't have `meta.fixable`.
}
assertASTDidntChange(result.beforeAST, result.afterAST);

@@ -921,19 +1212,31 @@ }

* one of the templates above.
* The test suites for valid/invalid are created conditionally as
* test runners (eg. vitest) fail for empty test suites.
*/
RuleTester.describe(ruleName, () => {
RuleTester.describe("valid", () => {
test.valid.forEach(valid => {
RuleTester.it(sanitize(typeof valid === "object" ? valid.code : valid), () => {
testValidTemplate(valid);
this.constructor.describe(ruleName, () => {
if (test.valid.length > 0) {
this.constructor.describe("valid", () => {
test.valid.forEach(valid => {
this.constructor[valid.only ? "itOnly" : "it"](
sanitize(typeof valid === "object" ? valid.name || valid.code : valid),
() => {
testValidTemplate(valid);
}
);
});
});
});
}
RuleTester.describe("invalid", () => {
test.invalid.forEach(invalid => {
RuleTester.it(sanitize(invalid.code), () => {
testInvalidTemplate(invalid);
if (test.invalid.length > 0) {
this.constructor.describe("invalid", () => {
test.invalid.forEach(invalid => {
this.constructor[invalid.only ? "itOnly" : "it"](
sanitize(invalid.name || invalid.code),
() => {
testInvalidTemplate(invalid);
}
);
});
});
});
}
});

@@ -943,4 +1246,4 @@ }

RuleTester[DESCRIBE] = RuleTester[IT] = null;
RuleTester[DESCRIBE] = RuleTester[IT] = RuleTester[IT_ONLY] = null;
module.exports = RuleTester;

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -143,6 +144,5 @@ meta: {

docs: {
description: "enforce getter and setter pairs in objects and classes",
category: "Best Practices",
description: "Enforce getter and setter pairs in objects and classes",
recommended: false,
url: "https://eslint.org/docs/rules/accessor-pairs"
url: "https://eslint.org/docs/latest/rules/accessor-pairs"
},

@@ -183,3 +183,3 @@

const enforceForClassMembers = config.enforceForClassMembers !== false;
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -230,49 +230,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;
}
}

@@ -306,8 +298,8 @@ for (const { getters, setters } of accessors) {

function checkPropertyDescriptor(node) {
const namesToCheck = node.properties
const namesToCheck = new Set(node.properties
.filter(p => p.type === "Property" && p.kind === "init" && !p.computed)
.map(({ key }) => key.name);
.map(({ key }) => key.name));
const hasGetter = namesToCheck.includes("get");
const hasSetter = namesToCheck.includes("set");
const hasGetter = namesToCheck.has("get");
const hasSetter = namesToCheck.has("set");

@@ -314,0 +306,0 @@ if (checkSetWithoutGet && hasSetter && !hasGetter) {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -20,6 +21,5 @@ meta: {

docs: {
description: "enforce linebreaks after opening and before closing array brackets",
category: "Stylistic Issues",
description: "Enforce linebreaks after opening and before closing array brackets",
recommended: false,
url: "https://eslint.org/docs/rules/array-bracket-newline"
url: "https://eslint.org/docs/latest/rules/array-bracket-newline"
},

@@ -61,3 +61,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -64,0 +64,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -19,6 +20,5 @@ meta: {

docs: {
description: "enforce consistent spacing inside array brackets",
category: "Stylistic Issues",
description: "Enforce consistent spacing inside array brackets",
recommended: false,
url: "https://eslint.org/docs/rules/array-bracket-spacing"
url: "https://eslint.org/docs/latest/rules/array-bracket-spacing"
},

@@ -58,3 +58,3 @@

const spaced = context.options[0] === "always",
sourceCode = context.getSourceCode();
sourceCode = context.sourceCode;

@@ -61,0 +61,0 @@ /**

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

const TARGET_NODE_TYPE = /^(?:Arrow)?FunctionExpression$/u;
const TARGET_METHODS = /^(?:every|filter|find(?:Index)?|flatMap|forEach|map|reduce(?:Right)?|some|sort)$/u;
const TARGET_METHODS = /^(?:every|filter|find(?:Last)?(?:Index)?|flatMap|forEach|map|reduce(?:Right)?|some|sort|toSorted)$/u;
/**
* Checks a given code path segment is reachable.
* @param {CodePathSegment} segment A segment to check.
* @returns {boolean} `true` if the segment is reachable.
*/
function isReachable(segment) {
return segment.reachable;
}
/**
* Checks a given node is a member access which has the specified name's

@@ -43,2 +34,18 @@ * property.

/**
* Checks all segments in a set and returns true if any are reachable.
* @param {Set<CodePathSegment>} segments The segments to check.
* @returns {boolean} True if any segment is reachable; false otherwise.
*/
function isAnySegmentReachable(segments) {
for (const segment of segments) {
if (segment.reachable) {
return true;
}
}
return false;
}
/**
* Returns a human-legible description of an array method

@@ -130,6 +137,76 @@ * @param {string} arrayMethodName A method name to fully qualify

/* istanbul ignore next: unreachable */
/* c8 ignore next */
return null;
}
/**
* Checks if the given node is a void expression.
* @param {ASTNode} node The node to check.
* @returns {boolean} - `true` if the node is a void expression
*/
function isExpressionVoid(node) {
return node.type === "UnaryExpression" && node.operator === "void";
}
/**
* Fixes the linting error by prepending "void " to the given node
* @param {Object} sourceCode context given by context.sourceCode
* @param {ASTNode} node The node to fix.
* @param {Object} fixer The fixer object provided by ESLint.
* @returns {Array<Object>} - An array of fix objects to apply to the node.
*/
function voidPrependFixer(sourceCode, node, fixer) {
const requiresParens =
// prepending `void ` will fail if the node has a lower precedence than void
astUtils.getPrecedence(node) < astUtils.getPrecedence({ type: "UnaryExpression", operator: "void" }) &&
// check if there are parentheses around the node to avoid redundant parentheses
!astUtils.isParenthesised(sourceCode, node);
// avoid parentheses issues
const returnOrArrowToken = sourceCode.getTokenBefore(
node,
node.parent.type === "ArrowFunctionExpression"
? astUtils.isArrowToken
// isReturnToken
: token => token.type === "Keyword" && token.value === "return"
);
const firstToken = sourceCode.getTokenAfter(returnOrArrowToken);
const prependSpace =
// is return token, as => allows void to be adjacent
returnOrArrowToken.value === "return" &&
// If two tokens (return and "(") are adjacent
returnOrArrowToken.range[1] === firstToken.range[0];
return [
fixer.insertTextBefore(firstToken, `${prependSpace ? " " : ""}void ${requiresParens ? "(" : ""}`),
fixer.insertTextAfter(node, requiresParens ? ")" : "")
];
}
/**
* Fixes the linting error by `wrapping {}` around the given node's body.
* @param {Object} sourceCode context given by context.sourceCode
* @param {ASTNode} node The node to fix.
* @param {Object} fixer The fixer object provided by ESLint.
* @returns {Array<Object>} - An array of fix objects to apply to the node.
*/
function curlyWrapFixer(sourceCode, node, fixer) {
const arrowToken = sourceCode.getTokenBefore(node.body, astUtils.isArrowToken);
const firstToken = sourceCode.getTokenAfter(arrowToken);
const lastToken = sourceCode.getLastToken(node);
return [
fixer.insertTextBefore(firstToken, "{"),
fixer.insertTextAfter(lastToken, "}")
];
}
//------------------------------------------------------------------------------

@@ -139,2 +216,3 @@ // Rule Definition

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -145,8 +223,10 @@ meta: {

docs: {
description: "enforce `return` statements in callbacks of array methods",
category: "Best Practices",
description: "Enforce `return` statements in callbacks of array methods",
recommended: false,
url: "https://eslint.org/docs/rules/array-callback-return"
url: "https://eslint.org/docs/latest/rules/array-callback-return"
},
// eslint-disable-next-line eslint-plugin/require-meta-has-suggestions -- false positive
hasSuggestions: true,
schema: [

@@ -163,2 +243,6 @@ {

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

@@ -174,3 +258,5 @@ },

expectedReturnValue: "{{arrayMethodName}}() expects a return value from {{name}}.",
expectedNoReturnValue: "{{arrayMethodName}}() expects no useless return value from {{name}}."
expectedNoReturnValue: "{{arrayMethodName}}() expects no useless return value from {{name}}.",
wrapBraces: "Wrap the expression in `{}`.",
prependVoid: "Prepend `void` to the expression."
}

@@ -181,4 +267,4 @@ },

const options = context.options[0] || { allowImplicit: false, checkForEach: false };
const sourceCode = context.getSourceCode();
const options = context.options[0] || { allowImplicit: false, checkForEach: false, allowVoid: false };
const sourceCode = context.sourceCode;

@@ -209,15 +295,44 @@ let funcInfo = {

let messageId = null;
const messageAndSuggestions = { messageId: "", suggest: [] };
if (funcInfo.arrayMethodName === "forEach") {
if (options.checkForEach && node.type === "ArrowFunctionExpression" && node.expression) {
messageId = "expectedNoReturnValue";
if (options.allowVoid) {
if (isExpressionVoid(node.body)) {
return;
}
messageAndSuggestions.messageId = "expectedNoReturnValue";
messageAndSuggestions.suggest = [
{
messageId: "wrapBraces",
fix(fixer) {
return curlyWrapFixer(sourceCode, node, fixer);
}
},
{
messageId: "prependVoid",
fix(fixer) {
return voidPrependFixer(sourceCode, node.body, fixer);
}
}
];
} else {
messageAndSuggestions.messageId = "expectedNoReturnValue";
messageAndSuggestions.suggest = [{
messageId: "wrapBraces",
fix(fixer) {
return curlyWrapFixer(sourceCode, node, fixer);
}
}];
}
}
} else {
if (node.body.type === "BlockStatement" && funcInfo.codePath.currentSegments.some(isReachable)) {
messageId = funcInfo.hasReturn ? "expectedAtEnd" : "expectedInside";
if (node.body.type === "BlockStatement" && isAnySegmentReachable(funcInfo.currentSegments)) {
messageAndSuggestions.messageId = funcInfo.hasReturn ? "expectedAtEnd" : "expectedInside";
}
}
if (messageId) {
if (messageAndSuggestions.messageId) {
const name = astUtils.getFunctionNameWithKind(node);

@@ -228,4 +343,5 @@

loc: astUtils.getFunctionHeadLoc(node, sourceCode),
messageId,
data: { name, arrayMethodName: fullMethodName(funcInfo.arrayMethodName) }
messageId: messageAndSuggestions.messageId,
data: { name, arrayMethodName: fullMethodName(funcInfo.arrayMethodName) },
suggest: messageAndSuggestions.suggest.length !== 0 ? messageAndSuggestions.suggest : null
});

@@ -255,3 +371,4 @@ }

!node.generator,
node
node,
currentSegments: new Set()
};

@@ -265,2 +382,19 @@ },

onUnreachableCodePathSegmentStart(segment) {
funcInfo.currentSegments.add(segment);
},
onUnreachableCodePathSegmentEnd(segment) {
funcInfo.currentSegments.delete(segment);
},
onCodePathSegmentStart(segment) {
funcInfo.currentSegments.add(segment);
},
onCodePathSegmentEnd(segment) {
funcInfo.currentSegments.delete(segment);
},
// Checks the return statement is valid.

@@ -275,3 +409,3 @@ ReturnStatement(node) {

let messageId = null;
const messageAndSuggestions = { messageId: "", suggest: [] };

@@ -282,3 +416,18 @@ if (funcInfo.arrayMethodName === "forEach") {

if (options.checkForEach && node.argument) {
messageId = "expectedNoReturnValue";
if (options.allowVoid) {
if (isExpressionVoid(node.argument)) {
return;
}
messageAndSuggestions.messageId = "expectedNoReturnValue";
messageAndSuggestions.suggest = [{
messageId: "prependVoid",
fix(fixer) {
return voidPrependFixer(sourceCode, node.argument, fixer);
}
}];
} else {
messageAndSuggestions.messageId = "expectedNoReturnValue";
}
}

@@ -289,14 +438,15 @@ } else {

if (!options.allowImplicit && !node.argument) {
messageId = "expectedReturnValue";
messageAndSuggestions.messageId = "expectedReturnValue";
}
}
if (messageId) {
if (messageAndSuggestions.messageId) {
context.report({
node,
messageId,
messageId: messageAndSuggestions.messageId,
data: {
name: astUtils.getFunctionNameWithKind(funcInfo.node),
arrayMethodName: fullMethodName(funcInfo.arrayMethodName)
}
},
suggest: messageAndSuggestions.suggest.length !== 0 ? messageAndSuggestions.suggest : null
});

@@ -303,0 +453,0 @@ }

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -20,6 +21,5 @@ meta: {

docs: {
description: "enforce line breaks after each array element",
category: "Stylistic Issues",
description: "Enforce line breaks after each array element",
recommended: false,
url: "https://eslint.org/docs/rules/array-element-newline"
url: "https://eslint.org/docs/latest/rules/array-element-newline"
},

@@ -52,2 +52,3 @@

},
type: "array",
items: [

@@ -84,3 +85,3 @@ {

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -246,7 +247,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;
}

@@ -258,4 +263,6 @@

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

@@ -262,0 +269,0 @@ const needsLinebreaks = (

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -23,6 +24,5 @@ meta: {

docs: {
description: "require braces around arrow function bodies",
category: "ECMAScript 6",
description: "Require braces around arrow function bodies",
recommended: false,
url: "https://eslint.org/docs/rules/arrow-body-style"
url: "https://eslint.org/docs/latest/rules/arrow-body-style"
},

@@ -79,3 +79,3 @@

const requireReturnForObjectLiteral = options[1] && options[1].requireReturnForObjectLiteral;
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
let funcInfo = null;

@@ -93,13 +93,13 @@

/**
* Gets the closing parenthesis which is the pair of the given opening parenthesis.
* @param {Token} token The opening parenthesis token to get.
* Gets the closing parenthesis by the given node.
* @param {ASTNode} node first node after an opening parenthesis.
* @returns {Token} The found closing parenthesis token.
*/
function findClosingParen(token) {
let node = sourceCode.getNodeByRangeIndex(token.range[0]);
function findClosingParen(node) {
let nodeToCheck = node;
while (!astUtils.isParenthesised(sourceCode, node)) {
node = node.parent;
while (!astUtils.isParenthesised(sourceCode, nodeToCheck)) {
nodeToCheck = nodeToCheck.parent;
}
return sourceCode.getTokenAfter(node);
return sourceCode.getTokenAfter(nodeToCheck);
}

@@ -233,8 +233,18 @@

const lastToken = sourceCode.getLastToken(node);
const isParenthesisedObjectLiteral =
let parenthesisedObjectLiteral = null;
if (
astUtils.isOpeningParenToken(firstTokenAfterArrow) &&
astUtils.isOpeningBraceToken(secondTokenAfterArrow);
astUtils.isOpeningBraceToken(secondTokenAfterArrow)
) {
const braceNode = sourceCode.getNodeByRangeIndex(secondTokenAfterArrow.range[0]);
if (braceNode.type === "ObjectExpression") {
parenthesisedObjectLiteral = braceNode;
}
}
// If the value is object literal, remove parentheses which were forced by syntax.
if (isParenthesisedObjectLiteral) {
if (parenthesisedObjectLiteral) {
const openingParenToken = firstTokenAfterArrow;

@@ -255,3 +265,3 @@ const openingBraceToken = secondTokenAfterArrow;

// Closing paren for the object doesn't have to be lastToken, e.g.: () => ({}).foo()
fixes.push(fixer.remove(findClosingParen(openingBraceToken)));
fixes.push(fixer.remove(findClosingParen(parenthesisedObjectLiteral)));
fixes.push(fixer.insertTextAfter(lastToken, "}"));

@@ -258,0 +268,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -36,6 +37,5 @@ meta: {

docs: {
description: "require parentheses around arrow function arguments",
category: "ECMAScript 6",
description: "Require parentheses around arrow function arguments",
recommended: false,
url: "https://eslint.org/docs/rules/arrow-parens"
url: "https://eslint.org/docs/latest/rules/arrow-parens"
},

@@ -74,3 +74,3 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -77,0 +77,0 @@ /**

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -23,6 +24,5 @@ meta: {

docs: {
description: "enforce consistent spacing before and after the arrow in arrow functions",
category: "ECMAScript 6",
description: "Enforce consistent spacing before and after the arrow in arrow functions",
recommended: false,
url: "https://eslint.org/docs/rules/arrow-spacing"
url: "https://eslint.org/docs/latest/rules/arrow-spacing"
},

@@ -66,3 +66,3 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -69,0 +69,0 @@ /**

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -17,6 +18,5 @@ meta: {

docs: {
description: "enforce the use of variables within the scope they are defined",
category: "Best Practices",
description: "Enforce the use of variables within the scope they are defined",
recommended: false,
url: "https://eslint.org/docs/rules/block-scoped-var"
url: "https://eslint.org/docs/latest/rules/block-scoped-var"
},

@@ -27,3 +27,3 @@

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

@@ -34,2 +34,3 @@ },

let stack = [];
const sourceCode = context.sourceCode;

@@ -56,8 +57,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
}
});
}

@@ -91,3 +102,3 @@

// Gets declared variables, and checks its references.
const variables = context.getDeclaredVariables(node);
const variables = sourceCode.getDeclaredVariables(node);

@@ -100,3 +111,3 @@ for (let i = 0; i < variables.length; ++i) {

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

@@ -123,2 +134,4 @@ }

"CatchClause:exit": exitScope,
StaticBlock: enterScope,
"StaticBlock:exit": exitScope,

@@ -125,0 +138,0 @@ // Finds and reports references which are outside of valid scope.

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -20,6 +21,5 @@ meta: {

docs: {
description: "disallow or enforce spaces inside of blocks after opening block and before closing block",
category: "Stylistic Issues",
description: "Disallow or enforce spaces inside of blocks after opening block and before closing block",
recommended: false,
url: "https://eslint.org/docs/rules/block-spacing"
url: "https://eslint.org/docs/latest/rules/block-spacing"
},

@@ -42,7 +42,7 @@

messageId = always ? "missing" : "extra",
sourceCode = context.getSourceCode();
sourceCode = context.sourceCode;
/**
* Gets the open brace token from a given node.
* @param {ASTNode} node A BlockStatement/SwitchStatement node to get.
* @param {ASTNode} node A BlockStatement/StaticBlock/SwitchStatement node to get.
* @returns {Token} The token of the open brace.

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

}
if (node.type === "StaticBlock") {
return sourceCode.getFirstToken(node, { skip: 1 }); // skip the `static` token
}
// "BlockStatement"
return sourceCode.getFirstToken(node);

@@ -80,4 +86,4 @@ }

/**
* Reports invalid spacing style inside braces.
* @param {ASTNode} node A BlockStatement/SwitchStatement node to get.
* Checks and reports invalid spacing style inside braces.
* @param {ASTNode} node A BlockStatement/StaticBlock/SwitchStatement node to check.
* @returns {void}

@@ -166,2 +172,3 @@ */

BlockStatement: checkSpacingInsideBraces,
StaticBlock: checkSpacingInsideBraces,
SwitchStatement: checkSpacingInsideBraces

@@ -168,0 +175,0 @@ };

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -20,6 +21,5 @@ meta: {

docs: {
description: "enforce consistent brace style for blocks",
category: "Stylistic Issues",
description: "Enforce consistent brace style for blocks",
recommended: false,
url: "https://eslint.org/docs/rules/brace-style"
url: "https://eslint.org/docs/latest/rules/brace-style"
},

@@ -58,3 +58,3 @@

params = context.options[1] || {},
sourceCode = context.getSourceCode();
sourceCode = context.sourceCode;

@@ -162,2 +162,8 @@ //--------------------------------------------------------------------------

},
StaticBlock(node) {
validateCurlyPair(
sourceCode.getFirstToken(node, { skip: 1 }), // skip the `static` token
sourceCode.getLastToken(node)
);
},
ClassBody(node) {

@@ -164,0 +170,0 @@ validateCurlyPair(sourceCode.getFirstToken(node), sourceCode.getLastToken(node));

/**
* @fileoverview Enforce return after a callback.
* @author Jamund Ferguson
* @deprecated in ESLint v7.0.0
*/

@@ -11,2 +12,3 @@ "use strict";

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -21,6 +23,5 @@ meta: {

docs: {
description: "require `return` statements after callbacks",
category: "Node.js and CommonJS",
description: "Require `return` statements after callbacks",
recommended: false,
url: "https://eslint.org/docs/rules/callback-return"
url: "https://eslint.org/docs/latest/rules/callback-return"
},

@@ -41,3 +42,3 @@

const callbacks = context.options[0] || ["callback", "cb", "next"],
sourceCode = context.getSourceCode();
sourceCode = context.sourceCode;

@@ -58,3 +59,3 @@ //--------------------------------------------------------------------------

}
if (types.indexOf(node.parent.type) === -1) {
if (!types.includes(node.parent.type)) {
return findClosestParentOfType(node.parent, types);

@@ -93,3 +94,3 @@ }

function isCallback(node) {
return containsOnlyIdentifiers(node.callee) && callbacks.indexOf(sourceCode.getText(node.callee)) > -1;
return containsOnlyIdentifiers(node.callee) && callbacks.includes(sourceCode.getText(node.callee));
}

@@ -96,0 +97,0 @@

@@ -9,5 +9,12 @@ /**

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const astUtils = require("./utils/ast-utils");
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +25,5 @@ meta: {

docs: {
description: "enforce camelcase naming convention",
category: "Stylistic Issues",
description: "Enforce camelcase naming convention",
recommended: false,
url: "https://eslint.org/docs/rules/camelcase"
url: "https://eslint.org/docs/latest/rules/camelcase"
},

@@ -60,3 +66,4 @@

messages: {
notCamelCase: "Identifier '{{name}}' is not in camel case."
notCamelCase: "Identifier '{{name}}' is not in camel case.",
notCamelCasePrivate: "#{{name}} is not in camel case."
}

@@ -66,5 +73,4 @@ },

create(context) {
const options = context.options[0] || {};
let properties = options.properties || "";
const properties = options.properties === "never" ? "never" : "always";
const ignoreDestructuring = options.ignoreDestructuring;

@@ -74,9 +80,4 @@ const ignoreImports = options.ignoreImports;

const allow = options.allow || [];
const sourceCode = context.sourceCode;
let globalScope;
if (properties !== "always" && properties !== "never") {
properties = "always";
}
//--------------------------------------------------------------------------

@@ -87,4 +88,3 @@ // Helpers

// contains reported nodes to avoid reporting twice on destructuring with shorthand notation
const reported = [];
const ALLOWED_PARENT_TYPES = new Set(["CallExpression", "NewExpression"]);
const reported = new Set();

@@ -98,5 +98,6 @@ /**

function isUnderscored(name) {
const nameBody = name.replace(/^_+|_+$/gu, "");
// if there's an underscore, it might be A_CONSTANT, which is okay
return name.includes("_") && name !== name.toUpperCase();
return nameBody.includes("_") && nameBody !== nameBody.toUpperCase();
}

@@ -117,93 +118,75 @@

/**
* Checks if a parent of a node is an ObjectPattern.
* @param {ASTNode} node The node to check.
* @returns {boolean} if the node is inside an ObjectPattern
* Checks if a given name is good or not.
* @param {string} name The name to check.
* @returns {boolean} `true` if the name is good.
* @private
*/
function isInsideObjectPattern(node) {
let current = node;
function isGoodName(name) {
return !isUnderscored(name) || isAllowed(name);
}
while (current) {
const parent = current.parent;
/**
* Checks if a given identifier reference or member expression is an assignment
* target.
* @param {ASTNode} node The node to check.
* @returns {boolean} `true` if the node is an assignment target.
*/
function isAssignmentTarget(node) {
const parent = node.parent;
if (parent && parent.type === "Property" && parent.computed && parent.key === current) {
return false;
}
switch (parent.type) {
case "AssignmentExpression":
case "AssignmentPattern":
return parent.left === node;
if (current.type === "ObjectPattern") {
case "Property":
return (
parent.parent.type === "ObjectPattern" &&
parent.value === node
);
case "ArrayPattern":
case "RestElement":
return true;
}
current = parent;
default:
return false;
}
return false;
}
/**
* Checks whether the given node represents assignment target property in destructuring.
*
* For examples:
* ({a: b.foo} = c); // => true for `foo`
* ([a.foo] = b); // => true for `foo`
* ([a.foo = 1] = b); // => true for `foo`
* ({...a.foo} = b); // => true for `foo`
* @param {ASTNode} node An Identifier node to check
* @returns {boolean} True if the node is an assignment target property in destructuring.
* Checks if a given binding identifier uses the original name as-is.
* - If it's in object destructuring or object expression, the original name is its property name.
* - If it's in import declaration, the original name is its exported name.
* @param {ASTNode} node The `Identifier` node to check.
* @returns {boolean} `true` if the identifier uses the original name as-is.
*/
function isAssignmentTargetPropertyInDestructuring(node) {
if (
node.parent.type === "MemberExpression" &&
node.parent.property === node &&
!node.parent.computed
) {
const effectiveParent = node.parent.parent;
function equalsToOriginalName(node) {
const localName = node.name;
const valueNode = node.parent.type === "AssignmentPattern"
? node.parent
: node;
const parent = valueNode.parent;
return (
effectiveParent.type === "Property" &&
effectiveParent.value === node.parent &&
effectiveParent.parent.type === "ObjectPattern" ||
effectiveParent.type === "ArrayPattern" ||
effectiveParent.type === "RestElement" ||
(
effectiveParent.type === "AssignmentPattern" &&
effectiveParent.left === node.parent
)
);
}
return false;
}
switch (parent.type) {
case "Property":
return (
(parent.parent.type === "ObjectPattern" || parent.parent.type === "ObjectExpression") &&
parent.value === valueNode &&
!parent.computed &&
parent.key.type === "Identifier" &&
parent.key.name === localName
);
/**
* Checks whether the given node represents a reference to a global variable that is not declared in the source code.
* These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables.
* @param {ASTNode} node `Identifier` node to check.
* @returns {boolean} `true` if the node is a reference to a global variable.
*/
function isReferenceToGlobalVariable(node) {
const variable = globalScope.set.get(node.name);
case "ImportSpecifier":
return (
parent.local === node &&
astUtils.getModuleExportName(parent.imported) === localName
);
return variable && variable.defs.length === 0 &&
variable.references.some(ref => ref.identifier === node);
default:
return false;
}
}
/**
* Checks whether the given node represents a reference to a property of an object in an object literal expression.
* This allows to differentiate between a global variable that is allowed to be used as a reference, and the key
* of the expressed object (which shouldn't be allowed).
* @param {ASTNode} node `Identifier` node to check.
* @returns {boolean} `true` if the node is a property name of an object literal expression
*/
function isPropertyNameInObjectLiteral(node) {
const parent = node.parent;
return (
parent.type === "Property" &&
parent.parent.type === "ObjectExpression" &&
!parent.computed &&
parent.key === node
);
}
/**
* Reports an AST node as a rule violation.

@@ -215,120 +198,211 @@ * @param {ASTNode} node The node to report.

function report(node) {
if (!reported.includes(node)) {
reported.push(node);
context.report({ node, messageId: "notCamelCase", data: { name: node.name } });
if (reported.has(node.range[0])) {
return;
}
reported.add(node.range[0]);
// Report it.
context.report({
node,
messageId: node.type === "PrivateIdentifier"
? "notCamelCasePrivate"
: "notCamelCase",
data: { name: node.name }
});
}
return {
/**
* Reports an identifier reference or a binding identifier.
* @param {ASTNode} node The `Identifier` node to report.
* @returns {void}
*/
function reportReferenceId(node) {
Program() {
globalScope = context.getScope();
},
/*
* For backward compatibility, if it's in callings then ignore it.
* Not sure why it is.
*/
if (
node.parent.type === "CallExpression" ||
node.parent.type === "NewExpression"
) {
return;
}
Identifier(node) {
/*
* For backward compatibility, if it's a default value of
* destructuring/parameters then ignore it.
* Not sure why it is.
*/
if (
node.parent.type === "AssignmentPattern" &&
node.parent.right === node
) {
return;
}
/*
* Leading and trailing underscores are commonly used to flag
* private/protected identifiers, strip them before checking if underscored
*/
const name = node.name,
nameIsUnderscored = isUnderscored(name.replace(/^_+|_+$/gu, "")),
effectiveParent = (node.parent.type === "MemberExpression") ? node.parent.parent : node.parent;
/*
* The `ignoreDestructuring` flag skips the identifiers that uses
* the property name as-is.
*/
if (ignoreDestructuring && equalsToOriginalName(node)) {
return;
}
// First, we ignore the node if it match the ignore list
if (isAllowed(name)) {
return;
}
report(node);
}
// Check if it's a global variable
if (ignoreGlobals && isReferenceToGlobalVariable(node) && !isPropertyNameInObjectLiteral(node)) {
return;
}
return {
// MemberExpressions get special rules
if (node.parent.type === "MemberExpression") {
// Report camelcase of global variable references ------------------
Program(node) {
const scope = sourceCode.getScope(node);
// "never" check properties
if (properties === "never") {
return;
}
if (!ignoreGlobals) {
// Always report underscored object names
if (node.parent.object.type === "Identifier" && node.parent.object.name === node.name && nameIsUnderscored) {
report(node);
// Defined globals in config files or directive comments.
for (const variable of scope.variables) {
if (
variable.identifiers.length > 0 ||
isGoodName(variable.name)
) {
continue;
}
for (const reference of variable.references) {
// Report AssignmentExpressions only if they are the left side of the assignment
} else if (effectiveParent.type === "AssignmentExpression" && nameIsUnderscored && (effectiveParent.right.type !== "MemberExpression" || effectiveParent.left.type === "MemberExpression" && effectiveParent.left.property.name === node.name)) {
report(node);
} else if (isAssignmentTargetPropertyInDestructuring(node) && nameIsUnderscored) {
report(node);
/*
* For backward compatibility, this rule reports read-only
* references as well.
*/
reportReferenceId(reference.identifier);
}
}
}
/*
* Properties have their own rules, and
* AssignmentPattern nodes can be treated like Properties:
* e.g.: const { no_camelcased = false } = bar;
*/
} else if (node.parent.type === "Property" || node.parent.type === "AssignmentPattern") {
// Undefined globals.
for (const reference of scope.through) {
const id = reference.identifier;
if (node.parent.parent && node.parent.parent.type === "ObjectPattern") {
if (node.parent.shorthand && node.parent.value.left && nameIsUnderscored) {
report(node);
}
if (isGoodName(id.name)) {
continue;
}
const assignmentKeyEqualsValue = node.parent.key.name === node.parent.value.name;
/*
* For backward compatibility, this rule reports read-only
* references as well.
*/
reportReferenceId(id);
}
},
if (nameIsUnderscored && node.parent.computed) {
report(node);
}
// Report camelcase of declared variables --------------------------
[[
"VariableDeclaration",
"FunctionDeclaration",
"FunctionExpression",
"ArrowFunctionExpression",
"ClassDeclaration",
"ClassExpression",
"CatchClause"
]](node) {
for (const variable of sourceCode.getDeclaredVariables(node)) {
if (isGoodName(variable.name)) {
continue;
}
const id = variable.identifiers[0];
// prevent checking righthand side of destructured object
if (node.parent.key === node && node.parent.value !== node) {
return;
}
// Report declaration.
if (!(ignoreDestructuring && equalsToOriginalName(id))) {
report(id);
}
const valueIsUnderscored = node.parent.value.name && nameIsUnderscored;
// ignore destructuring if the option is set, unless a new identifier is created
if (valueIsUnderscored && !(assignmentKeyEqualsValue && ignoreDestructuring)) {
report(node);
/*
* For backward compatibility, report references as well.
* It looks unnecessary because declarations are reported.
*/
for (const reference of variable.references) {
if (reference.init) {
continue; // Skip the write references of initializers.
}
reportReferenceId(reference.identifier);
}
}
},
// "never" check properties or always ignore destructuring
if (properties === "never" || (ignoreDestructuring && isInsideObjectPattern(node))) {
return;
}
// Report camelcase in properties ----------------------------------
[[
"ObjectExpression > Property[computed!=true] > Identifier.key",
"MethodDefinition[computed!=true] > Identifier.key",
"PropertyDefinition[computed!=true] > Identifier.key",
"MethodDefinition > PrivateIdentifier.key",
"PropertyDefinition > PrivateIdentifier.key"
]](node) {
if (properties === "never" || isGoodName(node.name)) {
return;
}
report(node);
},
"MemberExpression[computed!=true] > Identifier.property"(node) {
if (
properties === "never" ||
!isAssignmentTarget(node.parent) || // ← ignore read-only references.
isGoodName(node.name)
) {
return;
}
report(node);
},
// don't check right hand side of AssignmentExpression to prevent duplicate warnings
if (nameIsUnderscored && !ALLOWED_PARENT_TYPES.has(effectiveParent.type) && !(node.parent.right === node)) {
report(node);
// Report camelcase in import --------------------------------------
ImportDeclaration(node) {
for (const variable of sourceCode.getDeclaredVariables(node)) {
if (isGoodName(variable.name)) {
continue;
}
const id = variable.identifiers[0];
// Check if it's an import specifier
} else if (["ImportSpecifier", "ImportNamespaceSpecifier", "ImportDefaultSpecifier"].includes(node.parent.type)) {
if (node.parent.type === "ImportSpecifier" && ignoreImports) {
return;
// Report declaration.
if (!(ignoreImports && equalsToOriginalName(id))) {
report(id);
}
// Report only if the local imported identifier is underscored
if (
node.parent.local &&
node.parent.local.name === node.name &&
nameIsUnderscored
) {
report(node);
/*
* For backward compatibility, report references as well.
* It looks unnecessary because declarations are reported.
*/
for (const reference of variable.references) {
reportReferenceId(reference.identifier);
}
}
},
// Report anything that is underscored that isn't a CallExpression
} else if (nameIsUnderscored && !ALLOWED_PARENT_TYPES.has(effectiveParent.type)) {
report(node);
// Report camelcase in re-export -----------------------------------
[[
"ExportAllDeclaration > Identifier.exported",
"ExportSpecifier > Identifier.exported"
]](node) {
if (isGoodName(node.name)) {
return;
}
report(node);
},
// Report camelcase in labels --------------------------------------
[[
"LabeledStatement > Identifier.label",
/*
* For backward compatibility, report references as well.
* It looks unnecessary because declarations are reported.
*/
"BreakStatement > Identifier.label",
"ContinueStatement > Identifier.label"
]](node) {
if (isGoodName(node.name)) {
return;
}
report(node);
}
};
}
};

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -108,6 +109,5 @@ meta: {

docs: {
description: "enforce or disallow capitalization of the first letter of a comment",
category: "Stylistic Issues",
description: "Enforce or disallow capitalization of the first letter of a comment",
recommended: false,
url: "https://eslint.org/docs/rules/capitalized-comments"
url: "https://eslint.org/docs/latest/rules/capitalized-comments"
},

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

normalizedOptions = getAllNormalizedOptions(context.options[1]),
sourceCode = context.getSourceCode();
sourceCode = context.sourceCode;

@@ -191,3 +191,3 @@ createRegExpForIgnorePatterns(normalizedOptions);

previousTokenOrComment &&
["Block", "Line"].indexOf(previousTokenOrComment.type) !== -1
["Block", "Line"].includes(previousTokenOrComment.type)
);

@@ -194,0 +194,0 @@ }

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,6 +25,5 @@ meta: {

docs: {
description: "enforce that class methods utilize `this`",
category: "Best Practices",
description: "Enforce that class methods utilize `this`",
recommended: false,
url: "https://eslint.org/docs/rules/class-methods-use-this"
url: "https://eslint.org/docs/latest/rules/class-methods-use-this"
},

@@ -39,2 +39,6 @@

}
},
enforceForClassFields: {
type: "boolean",
default: true
}

@@ -51,2 +55,3 @@ },

const config = Object.assign({}, context.options[0]);
const enforceForClassFields = config.enforceForClassFields !== false;
const exceptMethods = new Set(config.exceptMethods || []);

@@ -57,2 +62,18 @@

/**
* Push `this` used flag initialized with `false` onto the stack.
* @returns {void}
*/
function pushContext() {
stack.push(false);
}
/**
* Pop `this` used flag from the stack.
* @returns {boolean | undefined} `this` used flag
*/
function popContext() {
return stack.pop();
}
/**
* Initializes the current context to false and pushes it onto the stack.

@@ -64,3 +85,3 @@ * These booleans represent whether 'this' has been used in the context.

function enterFunction() {
stack.push(false);
pushContext();
}

@@ -75,3 +96,10 @@

function isInstanceMethod(node) {
return !node.static && node.kind !== "constructor" && node.type === "MethodDefinition";
switch (node.type) {
case "MethodDefinition":
return !node.static && node.kind !== "constructor";
case "PropertyDefinition":
return !node.static && enforceForClassFields;
default:
return false;
}
}

@@ -86,4 +114,15 @@

function isIncludedInstanceMethod(node) {
return isInstanceMethod(node) &&
(node.computed || !exceptMethods.has(node.key.name));
if (isInstanceMethod(node)) {
if (node.computed) {
return true;
}
const hashIfNeeded = node.key.type === "PrivateIdentifier" ? "#" : "";
const name = node.key.type === "Literal"
? astUtils.getStaticStringValue(node.key)
: (node.key.name || "");
return !exceptMethods.has(hashIfNeeded + name);
}
return false;
}

@@ -100,3 +139,3 @@

function exitFunction(node) {
const methodUsesThis = stack.pop();
const methodUsesThis = popContext();

@@ -106,2 +145,3 @@ if (isIncludedInstanceMethod(node.parent) && !methodUsesThis) {

node,
loc: astUtils.getFunctionHeadLoc(node, context.sourceCode),
messageId: "missingThis",

@@ -131,6 +171,28 @@ data: {

"FunctionExpression:exit": exitFunction,
/*
* Class field value are implicit functions.
*/
"PropertyDefinition > *.key:exit": pushContext,
"PropertyDefinition:exit": popContext,
/*
* Class static blocks are implicit functions. They aren't required to use `this`,
* but we have to push context so that it captures any use of `this` in the static block
* separately from enclosing contexts, because static blocks have their own `this` and it
* shouldn't count as used `this` in enclosing contexts.
*/
StaticBlock: pushContext,
"StaticBlock:exit": popContext,
ThisExpression: markThisUsed,
Super: markThisUsed
Super: markThisUsed,
...(
enforceForClassFields && {
"PropertyDefinition > ArrowFunctionExpression.value": enterFunction,
"PropertyDefinition > ArrowFunctionExpression.value:exit": exitFunction
}
)
};
}
};

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

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

@@ -55,3 +54,3 @@

exports: optionValue,
functions: (!ecmaVersion || ecmaVersion < 8) ? "ignore" : optionValue
functions: ecmaVersion < 2017 ? "ignore" : optionValue
};

@@ -76,2 +75,3 @@ }

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -82,6 +82,5 @@ meta: {

docs: {
description: "require or disallow trailing commas",
category: "Stylistic Issues",
description: "Require or disallow trailing commas",
recommended: false,
url: "https://eslint.org/docs/rules/comma-dangle"
url: "https://eslint.org/docs/latest/rules/comma-dangle"
},

@@ -131,3 +130,4 @@

}
]
],
additionalItems: false
},

@@ -142,5 +142,5 @@

create(context) {
const options = normalizeOptions(context.options[0], context.parserOptions.ecmaVersion);
const options = normalizeOptions(context.options[0], context.languageOptions.ecmaVersion);
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -153,19 +153,29 @@ /**

function getLastItem(node) {
/**
* Returns the last element of an array
* @param {any[]} array The input array
* @returns {any} The last element
*/
function last(array) {
return array[array.length - 1];
}
switch (node.type) {
case "ObjectExpression":
case "ObjectPattern":
return lodash.last(node.properties);
return last(node.properties);
case "ArrayExpression":
case "ArrayPattern":
return lodash.last(node.elements);
return last(node.elements);
case "ImportDeclaration":
case "ExportNamedDeclaration":
return lodash.last(node.specifiers);
return last(node.specifiers);
case "FunctionDeclaration":
case "FunctionExpression":
case "ArrowFunctionExpression":
return lodash.last(node.params);
return last(node.params);
case "CallExpression":
case "NewExpression":
return lodash.last(node.arguments);
return last(node.arguments);
default:

@@ -243,4 +253,14 @@ return null;

messageId: "unexpected",
fix(fixer) {
return fixer.remove(trailingToken);
*fix(fixer) {
yield fixer.remove(trailingToken);
/*
* Extend the range of the fix to include surrounding tokens to ensure
* that the element after which the comma is removed stays _last_.
* This intentionally makes conflicts in fix ranges with rules that may be
* adding or removing elements in the same autofix pass.
* https://github.com/eslint/eslint/issues/15660
*/
yield fixer.insertTextBefore(sourceCode.getTokenBefore(trailingToken), "");
yield fixer.insertTextAfter(sourceCode.getTokenAfter(trailingToken), "");
}

@@ -283,4 +303,14 @@ });

messageId: "missing",
fix(fixer) {
return fixer.insertTextAfter(trailingToken, ",");
*fix(fixer) {
yield fixer.insertTextAfter(trailingToken, ",");
/*
* Extend the range of the fix to include surrounding tokens to ensure
* that the element after which the comma is inserted stays _last_.
* This intentionally makes conflicts in fix ranges with rules that may be
* adding or removing elements in the same autofix pass.
* https://github.com/eslint/eslint/issues/15660
*/
yield fixer.insertTextBefore(trailingToken, "");
yield fixer.insertTextAfter(sourceCode.getTokenAfter(trailingToken), "");
}

@@ -328,3 +358,3 @@ });

never: forbidTrailingComma,
ignore: lodash.noop
ignore() {}
};

@@ -331,0 +361,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -19,6 +20,5 @@ meta: {

docs: {
description: "enforce consistent spacing before and after commas",
category: "Stylistic Issues",
description: "Enforce consistent spacing before and after commas",
recommended: false,
url: "https://eslint.org/docs/rules/comma-spacing"
url: "https://eslint.org/docs/latest/rules/comma-spacing"
},

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

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const tokensAndComments = sourceCode.tokensAndComments;

@@ -110,34 +110,2 @@

/**
* Validates the spacing around a comma token.
* @param {Object} tokens The tokens to be validated.
* @param {Token} tokens.comma The token representing the comma.
* @param {Token} [tokens.left] The last token before the comma.
* @param {Token} [tokens.right] The first token after the comma.
* @param {Token|ASTNode} reportItem The item to use when reporting an error.
* @returns {void}
* @private
*/
function validateCommaItemSpacing(tokens, reportItem) {
if (tokens.left && astUtils.isTokenOnSameLine(tokens.left, tokens.comma) &&
(options.before !== sourceCode.isSpaceBetweenTokens(tokens.left, tokens.comma))
) {
report(reportItem, "before", tokens.left);
}
if (tokens.right && astUtils.isClosingParenToken(tokens.right)) {
return;
}
if (tokens.right && !options.after && tokens.right.type === "Line") {
return;
}
if (tokens.right && astUtils.isTokenOnSameLine(tokens.comma, tokens.right) &&
(options.after !== sourceCode.isSpaceBetweenTokens(tokens.comma, tokens.right))
) {
report(reportItem, "after", tokens.right);
}
}
/**
* Adds null elements of the given ArrayExpression or ArrayPattern node to the ignore list.

@@ -179,14 +147,40 @@ * @param {ASTNode} node An ArrayExpression or ArrayPattern node.

if (token && token.type === "JSXText") {
return;
}
const previousToken = tokensAndComments[i - 1];
const nextToken = tokensAndComments[i + 1];
validateCommaItemSpacing({
comma: token,
left: astUtils.isCommaToken(previousToken) || commaTokensToIgnore.indexOf(token) > -1 ? null : previousToken,
right: astUtils.isCommaToken(nextToken) ? null : nextToken
}, token);
if (
previousToken &&
!astUtils.isCommaToken(previousToken) && // ignore spacing between two commas
/*
* `commaTokensToIgnore` are ending commas of `null` elements (array holes/elisions).
* In addition to spacing between two commas, this can also ignore:
*
* - Spacing after `[` (controlled by array-bracket-spacing)
* Example: [ , ]
* ^
* - Spacing after a comment (for backwards compatibility, this was possibly unintentional)
* Example: [a, /* * / ,]
* ^
*/
!commaTokensToIgnore.includes(token) &&
astUtils.isTokenOnSameLine(previousToken, token) &&
options.before !== sourceCode.isSpaceBetweenTokens(previousToken, token)
) {
report(token, "before", previousToken);
}
if (
nextToken &&
!astUtils.isCommaToken(nextToken) && // ignore spacing between two commas
!astUtils.isClosingParenToken(nextToken) && // controlled by space-in-parens
!astUtils.isClosingBracketToken(nextToken) && // controlled by array-bracket-spacing
!astUtils.isClosingBraceToken(nextToken) && // controlled by object-curly-spacing
!(!options.after && nextToken.type === "Line") && // special case, allow space before line comment
astUtils.isTokenOnSameLine(token, nextToken) &&
options.after !== sourceCode.isSpaceBetweenTokens(token, nextToken)
) {
report(token, "after", nextToken);
}
});

@@ -193,0 +187,0 @@ },

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -20,6 +21,5 @@ meta: {

docs: {
description: "enforce consistent comma style",
category: "Stylistic Issues",
description: "Enforce consistent comma style",
recommended: false,
url: "https://eslint.org/docs/rules/comma-style"
url: "https://eslint.org/docs/latest/rules/comma-style"
},

@@ -56,3 +56,3 @@

const style = context.options[0] || "last",
sourceCode = context.getSourceCode();
sourceCode = context.sourceCode;
const exceptions = {

@@ -213,4 +213,3 @@ ArrayPattern: true,

if (astUtils.isCommaToken(commaToken)) {
validateCommaItemSpacing(previousItemToken, commaToken,
currentItemToken, reportItem);
validateCommaItemSpacing(previousItemToken, commaToken, currentItemToken, reportItem);
}

@@ -224,2 +223,4 @@

: sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1];
} else {
previousItemToken = currentItemToken;
}

@@ -226,0 +227,0 @@ });

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

const lodash = require("lodash");
const astUtils = require("./utils/ast-utils");
const { upperCaseFirst } = require("../shared/string-utils");

@@ -22,2 +21,3 @@ //------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -28,6 +28,5 @@ meta: {

docs: {
description: "enforce a maximum cyclomatic complexity allowed in a program",
category: "Best Practices",
description: "Enforce a maximum cyclomatic complexity allowed in a program",
recommended: false,
url: "https://eslint.org/docs/rules/complexity"
url: "https://eslint.org/docs/latest/rules/complexity"
},

@@ -82,58 +81,14 @@

// Using a stack to store complexity (handling nested functions)
const fns = [];
// Using a stack to store complexity per code path
const complexities = [];
/**
* When parsing a new function, store it in our function stack
* Increase the complexity of the code path in context
* @returns {void}
* @private
*/
function startFunction() {
fns.push(1);
}
/**
* Evaluate the node at the end of function
* @param {ASTNode} node node to evaluate
* @returns {void}
* @private
*/
function endFunction(node) {
const name = lodash.upperFirst(astUtils.getFunctionNameWithKind(node));
const complexity = fns.pop();
if (complexity > THRESHOLD) {
context.report({
node,
messageId: "complex",
data: { name, complexity, max: THRESHOLD }
});
}
}
/**
* Increase the complexity of the function in context
* @returns {void}
* @private
*/
function increaseComplexity() {
if (fns.length) {
fns[fns.length - 1]++;
}
complexities[complexities.length - 1]++;
}
/**
* Increase the switch complexity in context
* @param {ASTNode} node node to evaluate
* @returns {void}
* @private
*/
function increaseSwitchComplexity(node) {
// Avoiding `default`
if (node.test) {
increaseComplexity();
}
}
//--------------------------------------------------------------------------

@@ -144,9 +99,10 @@ // Public API

return {
FunctionDeclaration: startFunction,
FunctionExpression: startFunction,
ArrowFunctionExpression: startFunction,
"FunctionDeclaration:exit": endFunction,
"FunctionExpression:exit": endFunction,
"ArrowFunctionExpression:exit": endFunction,
onCodePathStart() {
// The initial complexity is 1, representing one execution path in the CodePath
complexities.push(1);
},
// Each branching in the code adds 1 to the complexity
CatchClause: increaseComplexity,

@@ -159,6 +115,9 @@ ConditionalExpression: increaseComplexity,

IfStatement: increaseComplexity,
SwitchCase: increaseSwitchComplexity,
WhileStatement: increaseComplexity,
DoWhileStatement: increaseComplexity,
// Avoid `default`
"SwitchCase[test]": increaseComplexity,
// Logical assignment operators have short-circuiting behavior
AssignmentExpression(node) {

@@ -168,2 +127,42 @@ if (astUtils.isLogicalAssignmentOperator(node.operator)) {

}
},
onCodePathEnd(codePath, node) {
const complexity = complexities.pop();
/*
* This rule only evaluates complexity of functions, so "program" is excluded.
* Class field initializers and class static blocks are implicit functions. Therefore,
* they shouldn't contribute to the enclosing function's complexity, but their
* own complexity should be evaluated.
*/
if (
codePath.origin !== "function" &&
codePath.origin !== "class-field-initializer" &&
codePath.origin !== "class-static-block"
) {
return;
}
if (complexity > THRESHOLD) {
let name;
if (codePath.origin === "class-field-initializer") {
name = "class field initializer";
} else if (codePath.origin === "class-static-block") {
name = "class static block";
} else {
name = astUtils.getFunctionNameWithKind(node);
}
context.report({
node,
messageId: "complex",
data: {
name: upperCaseFirst(name),
complexity,
max: THRESHOLD
}
});
}
}

@@ -170,0 +169,0 @@ };

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -19,6 +20,5 @@ meta: {

docs: {
description: "enforce consistent spacing inside computed property brackets",
category: "Stylistic Issues",
description: "Enforce consistent spacing inside computed property brackets",
recommended: false,
url: "https://eslint.org/docs/rules/computed-property-spacing"
url: "https://eslint.org/docs/latest/rules/computed-property-spacing"
},

@@ -54,3 +54,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const propertyNameMustBeSpaced = context.options[0] === "always"; // default is "never"

@@ -201,3 +201,4 @@ const enforceForClassMembers = !context.options[1] || context.options[1].enforceForClassMembers;

if (enforceForClassMembers) {
listeners.MethodDefinition = checkSpacing("key");
listeners.MethodDefinition =
listeners.PropertyDefinition = listeners.Property;
}

@@ -204,0 +205,0 @@

@@ -11,4 +11,4 @@ /**

const lodash = require("lodash");
const astUtils = require("./utils/ast-utils");
const { upperCaseFirst } = require("../shared/string-utils");

@@ -20,8 +20,15 @@ //------------------------------------------------------------------------------

/**
* Checks whether or not a given code path segment is unreachable.
* @param {CodePathSegment} segment A CodePathSegment to check.
* @returns {boolean} `true` if the segment is unreachable.
* Checks all segments in a set and returns true if all are unreachable.
* @param {Set<CodePathSegment>} segments The segments to check.
* @returns {boolean} True if all segments are unreachable; false otherwise.
*/
function isUnreachable(segment) {
return !segment.reachable;
function areAllSegmentsUnreachable(segments) {
for (const segment of segments) {
if (segment.reachable) {
return false;
}
}
return true;
}

@@ -45,2 +52,3 @@

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -51,6 +59,5 @@ meta: {

docs: {
description: "require `return` statements to either always or never specify values",
category: "Best Practices",
description: "Require `return` statements to either always or never specify values",
recommended: false,
url: "https://eslint.org/docs/rules/consistent-return"
url: "https://eslint.org/docs/latest/rules/consistent-return"
},

@@ -95,3 +102,3 @@

if (!funcInfo.hasReturnValue ||
funcInfo.codePath.currentSegments.every(isUnreachable) ||
areAllSegmentsUnreachable(funcInfo.currentSegments) ||
astUtils.isES5Constructor(node) ||

@@ -112,3 +119,3 @@ isClassConstructor(node)

// `=>` token
loc = context.getSourceCode().getTokenBefore(node.body, astUtils.isArrowToken).loc.start;
loc = context.sourceCode.getTokenBefore(node.body, astUtils.isArrowToken).loc;
} else if (

@@ -120,7 +127,7 @@ node.parent.type === "MethodDefinition" ||

// Method name.
loc = node.parent.key.loc.start;
loc = node.parent.key.loc;
} else {
// Function name or `function` keyword.
loc = (node.id || node).loc.start;
loc = (node.id || context.sourceCode.getFirstToken(node)).loc;
}

@@ -151,3 +158,4 @@

messageId: "",
node
node,
currentSegments: new Set()
};

@@ -159,2 +167,19 @@ },

onUnreachableCodePathSegmentStart(segment) {
funcInfo.currentSegments.add(segment);
},
onUnreachableCodePathSegmentEnd(segment) {
funcInfo.currentSegments.delete(segment);
},
onCodePathSegmentStart(segment) {
funcInfo.currentSegments.add(segment);
},
onCodePathSegmentEnd(segment) {
funcInfo.currentSegments.delete(segment);
},
// Reports a given return statement if it's inconsistent.

@@ -176,3 +201,3 @@ ReturnStatement(node) {

? "Program"
: lodash.upperFirst(astUtils.getFunctionNameWithKind(funcInfo.node))
: upperCaseFirst(astUtils.getFunctionNameWithKind(funcInfo.node))
};

@@ -179,0 +204,0 @@ } else if (funcInfo.hasReturnValue !== hasReturnValue) {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -17,6 +18,5 @@ meta: {

docs: {
description: "enforce consistent naming when capturing the current execution context",
category: "Stylistic Issues",
description: "Enforce consistent naming when capturing the current execution context",
recommended: false,
url: "https://eslint.org/docs/rules/consistent-this"
url: "https://eslint.org/docs/latest/rules/consistent-this"
},

@@ -41,2 +41,3 @@

let aliases = [];
const sourceCode = context.sourceCode;

@@ -53,3 +54,3 @@ if (context.options.length === 0) {

* @param {ASTNode} node The assigning node.
* @param {string} name the name of the alias that was incorrectly used.
* @param {string} name the name of the alias that was incorrectly used.
* @returns {void}

@@ -72,3 +73,3 @@ */

if (aliases.indexOf(name) !== -1) {
if (aliases.includes(name)) {
if (!isThis || node.operator && node.operator !== "=") {

@@ -123,6 +124,7 @@ reportBadAssignment(node, name);

* Check each alias to ensure that is was assigned to the correct value.
* @param {ASTNode} node The node that represents the scope to check.
* @returns {void}
*/
function ensureWasAssigned() {
const scope = context.getScope();
function ensureWasAssigned(node) {
const scope = sourceCode.getScope(node);

@@ -129,0 +131,0 @@ aliases.forEach(alias => {

@@ -13,8 +13,15 @@ /**

/**
* Checks whether a given code path segment is reachable or not.
* @param {CodePathSegment} segment A code path segment to check.
* @returns {boolean} `true` if the segment is reachable.
* Checks all segments in a set and returns true if any are reachable.
* @param {Set<CodePathSegment>} segments The segments to check.
* @returns {boolean} True if any segment is reachable; false otherwise.
*/
function isReachable(segment) {
return segment.reachable;
function isAnySegmentReachable(segments) {
for (const segment of segments) {
if (segment.reachable) {
return true;
}
}
return false;
}

@@ -120,2 +127,3 @@

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -126,6 +134,5 @@ meta: {

docs: {
description: "require `super()` calls in constructors",
category: "ECMAScript 6",
description: "Require `super()` calls in constructors",
recommended: true,
url: "https://eslint.org/docs/rules/constructor-super"
url: "https://eslint.org/docs/latest/rules/constructor-super"
},

@@ -216,3 +223,4 @@

superIsConstructor: isPossibleConstructor(superClass),
codePath
codePath,
currentSegments: new Set()
};

@@ -225,3 +233,4 @@ } else {

superIsConstructor: false,
codePath
codePath,
currentSegments: new Set()
};

@@ -269,2 +278,5 @@ }

onCodePathSegmentStart(segment) {
funcInfo.currentSegments.add(segment);
if (!(funcInfo && funcInfo.isConstructor && funcInfo.hasExtends)) {

@@ -290,2 +302,15 @@ return;

onUnreachableCodePathSegmentStart(segment) {
funcInfo.currentSegments.add(segment);
},
onUnreachableCodePathSegmentEnd(segment) {
funcInfo.currentSegments.delete(segment);
},
onCodePathSegmentEnd(segment) {
funcInfo.currentSegments.delete(segment);
},
/**

@@ -354,8 +379,7 @@ * Update information of the code path segment when a code path was

if (funcInfo.hasExtends) {
const segments = funcInfo.codePath.currentSegments;
const segments = funcInfo.currentSegments;
let duplicate = false;
let info = null;
for (let i = 0; i < segments.length; ++i) {
const segment = segments[i];
for (const segment of segments) {

@@ -385,3 +409,3 @@ if (segment.reachable) {

}
} else if (funcInfo.codePath.currentSegments.some(isReachable)) {
} else if (isAnySegmentReachable(funcInfo.currentSegments)) {
context.report({

@@ -410,6 +434,5 @@ messageId: "unexpected",

// Returning argument is a substitute of 'super()'.
const segments = funcInfo.codePath.currentSegments;
const segments = funcInfo.currentSegments;
for (let i = 0; i < segments.length; ++i) {
const segment = segments[i];
for (const segment of segments) {

@@ -416,0 +439,0 @@ if (segment.reachable) {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -23,6 +24,5 @@ meta: {

docs: {
description: "enforce consistent brace style for all control statements",
category: "Best Practices",
description: "Enforce consistent brace style for all control statements",
recommended: false,
url: "https://eslint.org/docs/rules/curly"
url: "https://eslint.org/docs/latest/rules/curly"
},

@@ -75,3 +75,3 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -138,11 +138,2 @@ //--------------------------------------------------------------------------

/**
* Gets the `else` keyword token of a given `IfStatement` node.
* @param {ASTNode} node A `IfStatement` node to get.
* @returns {Token} The `else` keyword token.
*/
function getElseKeyword(node) {
return node.alternate && sourceCode.getFirstTokenBetween(node.consequent, node.alternate, isElseKeywordToken);
}
/**
* Determines whether the given node has an `else` keyword token as the first token after.

@@ -368,3 +359,3 @@ * @param {ASTNode} node The node to check.

node,
loc: (name !== "else" ? node : getElseKeyword(node)).loc.start,
loc: body.loc,
messageId: opts && opts.condition ? "missingCurlyAfterCondition" : "missingCurlyAfter",

@@ -379,3 +370,3 @@ data: {

node,
loc: (name !== "else" ? node : getElseKeyword(node)).loc.start,
loc: body.loc,
messageId: opts && opts.condition ? "unexpectedCurlyAfterCondition" : "unexpectedCurlyAfter",

@@ -382,0 +373,0 @@ data: {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "enforce default clauses in switch statements to be last",
category: "Best Practices",
description: "Enforce default clauses in switch statements to be last",
recommended: false,
url: "https://eslint.org/docs/rules/default-case-last"
url: "https://eslint.org/docs/latest/rules/default-case-last"
},

@@ -24,0 +24,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -19,6 +20,5 @@ meta: {

docs: {
description: "require `default` cases in `switch` statements",
category: "Best Practices",
description: "Require `default` cases in `switch` statements",
recommended: false,
url: "https://eslint.org/docs/rules/default-case"
url: "https://eslint.org/docs/latest/rules/default-case"
},

@@ -47,3 +47,3 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -56,4 +56,4 @@ //--------------------------------------------------------------------------

* Shortcut to get last element of array
* @param {*[]} collection Array
* @returns {*} Last element
* @param {*[]} collection Array
* @returns {any} Last element
*/

@@ -60,0 +60,0 @@ function last(collection) {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -14,6 +15,5 @@ meta: {

docs: {
description: "enforce default parameters to be last",
category: "Best Practices",
description: "Enforce default parameters to be last",
recommended: false,
url: "https://eslint.org/docs/rules/default-param-last"
url: "https://eslint.org/docs/latest/rules/default-param-last"
},

@@ -30,4 +30,4 @@

// eslint-disable-next-line jsdoc/require-description
/**
* Handler for function contexts.
* @param {ASTNode} node function node

@@ -34,0 +34,0 @@ * @returns {void}

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -20,6 +21,5 @@ meta: {

docs: {
description: "enforce consistent newlines before and after dots",
category: "Best Practices",
description: "Enforce consistent newlines before and after dots",
recommended: false,
url: "https://eslint.org/docs/rules/dot-location"
url: "https://eslint.org/docs/latest/rules/dot-location"
},

@@ -48,3 +48,3 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -51,0 +51,0 @@ /**

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -29,6 +30,5 @@ meta: {

docs: {
description: "enforce dot notation whenever possible",
category: "Best Practices",
description: "Enforce dot notation whenever possible",
recommended: false,
url: "https://eslint.org/docs/rules/dot-notation"
url: "https://eslint.org/docs/latest/rules/dot-notation"
},

@@ -64,3 +64,3 @@

const allowKeywords = options.allowKeywords === void 0 || options.allowKeywords;
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -82,3 +82,3 @@ let allowPattern;

validIdentifier.test(value) &&
(allowKeywords || keywords.indexOf(String(value)) === -1) &&
(allowKeywords || !keywords.includes(String(value))) &&
!(allowPattern && allowPattern.test(value))

@@ -101,3 +101,3 @@ ) {

if (sourceCode.commentsExistBetween(leftBracket, rightBracket)) {
return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
return;
}

@@ -141,4 +141,3 @@

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

@@ -150,3 +149,4 @@ checkComputedProperty(node, node.property.quasis[0].value.cooked);

!node.computed &&
keywords.indexOf(String(node.property.name)) !== -1
node.property.type === "Identifier" &&
keywords.includes(String(node.property.name))
) {

@@ -164,3 +164,3 @@ context.report({

if (node.object.type === "Identifier" && node.object.name === "let" && !node.optional) {
return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
return;
}

@@ -170,3 +170,3 @@

if (sourceCode.commentsExistBetween(dotToken, node.property)) {
return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
return;
}

@@ -173,0 +173,0 @@

@@ -8,11 +8,6 @@ /**

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const lodash = require("lodash");
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -23,6 +18,5 @@ meta: {

docs: {
description: "require or disallow newline at the end of files",
category: "Stylistic Issues",
description: "Require or disallow newline at the end of files",
recommended: false,
url: "https://eslint.org/docs/rules/eol-last"
url: "https://eslint.org/docs/latest/rules/eol-last"
},

@@ -51,6 +45,7 @@

Program: function checkBadEOF(node) {
const sourceCode = context.getSourceCode(),
const sourceCode = context.sourceCode,
src = sourceCode.getText(),
lastLine = sourceCode.lines[sourceCode.lines.length - 1],
location = {
column: lodash.last(sourceCode.lines).length,
column: lastLine.length,
line: sourceCode.lines.length

@@ -60,3 +55,3 @@ },

CRLF = `\r${LF}`,
endsWithNewline = lodash.endsWith(src, LF);
endsWithNewline = src.endsWith(LF);

@@ -98,6 +93,11 @@ /*

const secondLastLine = sourceCode.lines[sourceCode.lines.length - 2];
// File is newline-terminated, but shouldn't be
context.report({
node,
loc: location,
loc: {
start: { line: sourceCode.lines.length - 1, column: secondLastLine.length },
end: { line: sourceCode.lines.length, column: 0 }
},
messageId: "unexpected",

@@ -104,0 +104,0 @@ fix(fixer) {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,6 +25,5 @@ meta: {

docs: {
description: "require the use of `===` and `!==`",
category: "Best Practices",
description: "Require the use of `===` and `!==`",
recommended: false,
url: "https://eslint.org/docs/rules/eqeqeq"
url: "https://eslint.org/docs/latest/rules/eqeqeq"
},

@@ -73,3 +73,3 @@

const options = context.options[1] || {};
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -84,3 +84,3 @@ const nullOption = (config === "always")

* Checks if an expression is a typeof expression
* @param {ASTNode} node The node to check
* @param {ASTNode} node The node to check
* @returns {boolean} if the node is a typeof expression

@@ -87,0 +87,0 @@ */

@@ -9,5 +9,12 @@ /**

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const { getStaticValue } = require("@eslint-community/eslint-utils");
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +25,5 @@ meta: {

docs: {
description: "enforce \"for\" loop update clause moving the counter in the right direction.",
category: "Possible Errors",
description: "Enforce \"for\" loop update clause moving the counter in the right direction",
recommended: true,
url: "https://eslint.org/docs/rules/for-direction"
url: "https://eslint.org/docs/latest/rules/for-direction"
},

@@ -34,2 +40,3 @@

create(context) {
const { sourceCode } = context;

@@ -52,13 +59,13 @@ /**

* @param {int} dir expected direction that could either be turned around or invalidated
* @returns {int} return dir, the negated dir or zero if it's not clear for identifiers
* @returns {int} return dir, the negated dir, or zero if the counter does not change or the direction is not clear
*/
function getRightDirection(update, dir) {
if (update.right.type === "UnaryExpression") {
if (update.right.operator === "-") {
return -dir;
}
} else if (update.right.type === "Identifier") {
return 0;
const staticValue = getStaticValue(update.right, sourceCode.getScope(update));
if (staticValue && ["bigint", "boolean", "number"].includes(typeof staticValue.value)) {
const sign = Math.sign(Number(staticValue.value)) || 0; // convert NaN to 0
return dir * sign;
}
return dir;
return 0;
}

@@ -65,0 +72,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,6 +25,5 @@ meta: {

docs: {
description: "require or disallow spacing between function identifiers and their invocations",
category: "Stylistic Issues",
description: "Require or disallow spacing between function identifiers and their invocations",
recommended: false,
url: "https://eslint.org/docs/rules/func-call-spacing"
url: "https://eslint.org/docs/latest/rules/func-call-spacing"
},

@@ -78,3 +78,3 @@

const allowNewlines = !never && context.options[1] && context.options[1].allowNewlines;
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const text = sourceCode.getText();

@@ -81,0 +81,0 @@

@@ -47,3 +47,3 @@ /**

function isIdentifier(name, ecmaVersion) {
if (ecmaVersion >= 6) {
if (ecmaVersion >= 2015) {
return esutils.keyword.isIdentifierES6(name);

@@ -72,2 +72,3 @@ }

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -78,6 +79,5 @@ meta: {

docs: {
description: "require function names to match the name of the variable or property to which they are assigned",
category: "Stylistic Issues",
description: "Require function names to match the name of the variable or property to which they are assigned",
recommended: false,
url: "https://eslint.org/docs/rules/func-name-matching"
url: "https://eslint.org/docs/latest/rules/func-name-matching"
},

@@ -110,3 +110,3 @@

const includeModuleExports = options.includeCommonJSModuleExports;
const ecmaVersion = context.parserOptions && context.parserOptions.ecmaVersion ? context.parserOptions.ecmaVersion : 5;
const ecmaVersion = context.languageOptions.ecmaVersion;

@@ -203,3 +203,3 @@ /**

if (node.right.id && isIdentifier(name) && shouldWarn(name, node.right.id.name)) {
if (node.right.id && name && isIdentifier(name) && shouldWarn(name, node.right.id.name)) {
report(node, name, node.right.id.name, isProp);

@@ -209,12 +209,16 @@ }

Property(node) {
if (node.value.type !== "FunctionExpression" || !node.value.id || node.computed && !isStringLiteral(node.key)) {
"Property, PropertyDefinition[value]"(node) {
if (!(node.value.type === "FunctionExpression" && node.value.id)) {
return;
}
if (node.key.type === "Identifier") {
if (node.key.type === "Identifier" && !node.computed) {
const functionName = node.value.id.name;
let propertyName = node.key.name;
if (considerPropertyDescriptor && propertyName === "value") {
if (
considerPropertyDescriptor &&
propertyName === "value" &&
node.parent.type === "ObjectExpression"
) {
if (isPropertyCall("Object", "defineProperty", node.parent.parent) || isPropertyCall("Reflect", "defineProperty", node.parent.parent)) {

@@ -221,0 +225,0 @@ const property = node.parent.parent.arguments[1];

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -33,6 +34,5 @@ meta: {

docs: {
description: "require or disallow named `function` expressions",
category: "Stylistic Issues",
description: "Require or disallow named `function` expressions",
recommended: false,
url: "https://eslint.org/docs/rules/func-names"
url: "https://eslint.org/docs/latest/rules/func-names"
},

@@ -74,3 +74,3 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

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

(parent.type === "Property" && parent.value === node) ||
(parent.type === "PropertyDefinition" && parent.value === node) ||
(parent.type === "AssignmentExpression" && parent.left.type === "Identifier" && parent.right === node) ||

@@ -165,3 +166,3 @@ (parent.type === "AssignmentPattern" && parent.left.type === "Identifier" && parent.right === node);

// Skip recursive functions.
const nameVar = context.getDeclaredVariables(node)[0];
const nameVar = sourceCode.getDeclaredVariables(node)[0];

@@ -168,0 +169,0 @@ if (isFunctionName(nameVar) && nameVar.references.length > 0) {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -17,6 +18,5 @@ meta: {

docs: {
description: "enforce the consistent use of either `function` declarations or expressions",
category: "Stylistic Issues",
description: "Enforce the consistent use of either `function` declarations or expressions",
recommended: false,
url: "https://eslint.org/docs/rules/func-style"
url: "https://eslint.org/docs/latest/rules/func-style"
},

@@ -23,0 +23,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "enforce line breaks between arguments of a function call",
category: "Stylistic Issues",
description: "Enforce line breaks between arguments of a function call",
recommended: false,
url: "https://eslint.org/docs/rules/function-call-argument-newline"
url: "https://eslint.org/docs/latest/rules/function-call-argument-newline"
},

@@ -40,3 +40,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -43,0 +43,0 @@ const checkers = {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -23,6 +24,5 @@ meta: {

docs: {
description: "enforce consistent line breaks inside function parentheses",
category: "Stylistic Issues",
description: "Enforce consistent line breaks inside function parentheses",
recommended: false,
url: "https://eslint.org/docs/rules/function-paren-newline"
url: "https://eslint.org/docs/latest/rules/function-paren-newline"
},

@@ -62,3 +62,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const rawOption = context.options[0] || "multiline";

@@ -189,2 +189,3 @@ const multilineOption = rawOption === "multiline";

* @param {ASTNode} node The node with parens
* @throws {TypeError} Unexpected node type.
* @returns {Object} An object with keys `leftParen` for the left paren token, and `rightParen` for the right paren token.

@@ -197,6 +198,9 @@ * Can also return `null` if an expression has no parens (e.g. a NewExpression with no arguments, or an ArrowFunctionExpression

case "NewExpression":
if (!node.arguments.length && !(
astUtils.isOpeningParenToken(sourceCode.getLastToken(node, { skip: 1 })) &&
astUtils.isClosingParenToken(sourceCode.getLastToken(node))
)) {
if (!node.arguments.length &&
!(
astUtils.isOpeningParenToken(sourceCode.getLastToken(node, { skip: 1 })) &&
astUtils.isClosingParenToken(sourceCode.getLastToken(node)) &&
node.callee.range[1] < node.range[1]
)
) {

@@ -234,5 +238,9 @@ // If the NewExpression does not have parens (e.g. `new Foo`), return null.

const rightParen = node.params.length
? sourceCode.getTokenAfter(node.params[node.params.length - 1], astUtils.isClosingParenToken)
: sourceCode.getTokenAfter(firstToken);
return {
leftParen: firstToken,
rightParen: sourceCode.getTokenBefore(node.body, astUtils.isClosingParenToken)
rightParen
};

@@ -239,0 +247,0 @@ }

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -34,6 +35,5 @@ meta: {

docs: {
description: "enforce consistent spacing around `*` operators in generator functions",
category: "ECMAScript 6",
description: "Enforce consistent spacing around `*` operators in generator functions",
recommended: false,
url: "https://eslint.org/docs/rules/generator-star-spacing"
url: "https://eslint.org/docs/latest/rules/generator-star-spacing"
},

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

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -110,0 +110,0 @@ /**

@@ -17,11 +17,19 @@ /**

//------------------------------------------------------------------------------
const TARGET_NODE_TYPE = /^(?:Arrow)?FunctionExpression$/u;
/**
* Checks a given code path segment is reachable.
* @param {CodePathSegment} segment A segment to check.
* @returns {boolean} `true` if the segment is reachable.
* Checks all segments in a set and returns true if any are reachable.
* @param {Set<CodePathSegment>} segments The segments to check.
* @returns {boolean} True if any segment is reachable; false otherwise.
*/
function isReachable(segment) {
return segment.reachable;
function isAnySegmentReachable(segments) {
for (const segment of segments) {
if (segment.reachable) {
return true;
}
}
return false;
}

@@ -33,2 +41,3 @@

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -39,6 +48,5 @@ meta: {

docs: {
description: "enforce `return` statements in getters",
category: "Possible Errors",
description: "Enforce `return` statements in getters",
recommended: true,
url: "https://eslint.org/docs/rules/getter-return"
url: "https://eslint.org/docs/latest/rules/getter-return"
},

@@ -70,3 +78,3 @@

const options = context.options[0] || { allowImplicit: false };
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -78,3 +86,4 @@ let funcInfo = {

shouldCheck: false,
node: null
node: null,
currentSegments: []
};

@@ -93,3 +102,3 @@

if (funcInfo.shouldCheck &&
funcInfo.codePath.currentSegments.some(isReachable)
isAnySegmentReachable(funcInfo.currentSegments)
) {

@@ -121,14 +130,20 @@ context.report({

// Object.defineProperty()
if (parent.parent.parent.type === "CallExpression" &&
astUtils.getStaticPropertyName(parent.parent.parent.callee) === "defineProperty") {
return true;
// Object.defineProperty() or Reflect.defineProperty()
if (parent.parent.parent.type === "CallExpression") {
const callNode = parent.parent.parent.callee;
if (astUtils.isSpecificMemberAccess(callNode, "Object", "defineProperty") ||
astUtils.isSpecificMemberAccess(callNode, "Reflect", "defineProperty")) {
return true;
}
}
// Object.defineProperties()
// Object.defineProperties() or Object.create()
if (parent.parent.parent.type === "Property" &&
parent.parent.parent.parent.type === "ObjectExpression" &&
parent.parent.parent.parent.parent.type === "CallExpression" &&
astUtils.getStaticPropertyName(parent.parent.parent.parent.parent.callee) === "defineProperties") {
return true;
parent.parent.parent.parent.parent.type === "CallExpression") {
const callNode = parent.parent.parent.parent.parent.callee;
return astUtils.isSpecificMemberAccess(callNode, "Object", "defineProperties") ||
astUtils.isSpecificMemberAccess(callNode, "Object", "create");
}

@@ -148,3 +163,4 @@ }

shouldCheck: isGetter(node),
node
node,
currentSegments: new Set()
};

@@ -157,3 +173,18 @@ },

},
onUnreachableCodePathSegmentStart(segment) {
funcInfo.currentSegments.add(segment);
},
onUnreachableCodePathSegmentEnd(segment) {
funcInfo.currentSegments.delete(segment);
},
onCodePathSegmentStart(segment) {
funcInfo.currentSegments.add(segment);
},
onCodePathSegmentEnd(segment) {
funcInfo.currentSegments.delete(segment);
},
// Checks the return statement is valid.

@@ -160,0 +191,0 @@ ReturnStatement(node) {

/**
* @fileoverview Rule for disallowing require() outside of the top-level module context
* @author Jamund Ferguson
* @deprecated in ESLint v7.0.0
*/

@@ -8,3 +9,3 @@

const ACCEPTABLE_PARENTS = [
const ACCEPTABLE_PARENTS = new Set([
"AssignmentExpression",

@@ -19,3 +20,3 @@ "VariableDeclarator",

"ChainExpression"
];
]);

@@ -32,6 +33,7 @@ /**

/* istanbul ignore else: correctly returns null */
if (references.length === 1) {
return references[0];
}
/* c8 ignore next */
return null;

@@ -53,2 +55,3 @@

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -63,6 +66,5 @@ meta: {

docs: {
description: "require `require()` calls to be placed at top-level module scope",
category: "Node.js and CommonJS",
description: "Require `require()` calls to be placed at top-level module scope",
recommended: false,
url: "https://eslint.org/docs/rules/global-require"
url: "https://eslint.org/docs/latest/rules/global-require"
},

@@ -77,8 +79,10 @@

create(context) {
const sourceCode = context.sourceCode;
return {
CallExpression(node) {
const currentScope = context.getScope();
const currentScope = sourceCode.getScope(node);
if (node.callee.name === "require" && !isShadowed(currentScope, node.callee)) {
const isGoodRequire = context.getAncestors().every(parent => ACCEPTABLE_PARENTS.indexOf(parent.type) > -1);
const isGoodRequire = sourceCode.getAncestors(node).every(parent => ACCEPTABLE_PARENTS.has(parent.type));

@@ -85,0 +89,0 @@ if (!isGoodRequire) {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -99,6 +100,5 @@ meta: {

docs: {
description: "require grouped accessor pairs in object literals and classes",
category: "Best Practices",
description: "Require grouped accessor pairs in object literals and classes",
recommended: false,
url: "https://eslint.org/docs/rules/grouped-accessor-pairs"
url: "https://eslint.org/docs/latest/rules/grouped-accessor-pairs"
},

@@ -120,3 +120,3 @@

const order = context.options[0] || "anyOrder";
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -144,39 +144,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.

@@ -189,8 +152,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) {

@@ -197,0 +188,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "require `for-in` loops to include an `if` statement",
category: "Best Practices",
description: "Require `for-in` loops to include an `if` statement",
recommended: false,
url: "https://eslint.org/docs/rules/guard-for-in"
url: "https://eslint.org/docs/latest/rules/guard-for-in"
},

@@ -24,0 +24,0 @@

/**
* @fileoverview Ensure handling of errors when we know they exist.
* @author Jamund Ferguson
* @deprecated in ESLint v7.0.0
*/

@@ -12,2 +13,3 @@

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -22,6 +24,5 @@ meta: {

docs: {
description: "require error handling in callbacks",
category: "Node.js and CommonJS",
description: "Require error handling in callbacks",
recommended: false,
url: "https://eslint.org/docs/rules/handle-callback-err"
url: "https://eslint.org/docs/latest/rules/handle-callback-err"
},

@@ -42,2 +43,3 @@

const errorArgument = context.options[0] || "err";
const sourceCode = context.sourceCode;

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

function checkForError(node) {
const scope = context.getScope(),
const scope = sourceCode.getScope(node),
parameters = getParameters(scope),

@@ -87,0 +89,0 @@ firstParameter = parameters[0];

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

* @author Keith Cirkel (http://keithcirkel.co.uk)
* @deprecated in ESLint v7.5.0
*/

@@ -113,2 +114,3 @@

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -122,6 +124,5 @@ meta: {

docs: {
description: "disallow specified identifiers",
category: "Stylistic Issues",
description: "Disallow specified identifiers",
recommended: false,
url: "https://eslint.org/docs/rules/id-blacklist"
url: "https://eslint.org/docs/latest/rules/id-blacklist"
},

@@ -145,2 +146,3 @@

const reportedNodes = new Set();
const sourceCode = context.sourceCode;

@@ -212,3 +214,13 @@ let globalScope;

function report(node) {
if (!reportedNodes.has(node)) {
/*
* We used the range instead of the node because it's possible
* for the same identifier to be represented by two different
* nodes, with the most clear example being shorthand properties:
* { foo }
* In this case, "foo" is represented by one node for the name
* and one for the value. The only way to know they are the same
* is to look at the range.
*/
if (!reportedNodes.has(node.range.toString())) {
context.report({

@@ -221,4 +233,5 @@ node,

});
reportedNodes.add(node);
reportedNodes.add(node.range.toString());
}
}

@@ -228,4 +241,4 @@

Program() {
globalScope = context.getScope();
Program(node) {
globalScope = sourceCode.getScope(node);
},

@@ -232,0 +245,0 @@

@@ -72,10 +72,10 @@ /**

/**
* Checks whether the given node is a renamed identifier node in an ObjectPattern destructuring.
* Checks whether the given node is an ObjectPattern destructuring.
*
* Examples:
* const { a : b } = foo; // node `a` is renamed node.
* const { a : b } = foo;
* @param {ASTNode} node `Identifier` node to check.
* @returns {boolean} `true` if the node is a renamed node in an ObjectPattern destructuring.
* @returns {boolean} `true` if the node is in an ObjectPattern destructuring.
*/
function isRenamedInDestructuring(node) {
function isPropertyNameInDestructuring(node) {
const parent = node.parent;

@@ -88,3 +88,2 @@

parent.parent.type === "ObjectPattern" &&
parent.value !== node &&
parent.key === node

@@ -95,17 +94,2 @@ )

/**
* Checks whether the given node represents shorthand definition of a property in an object literal.
* @param {ASTNode} node `Identifier` node to check.
* @returns {boolean} `true` if the node is a shorthand property definition.
*/
function isShorthandPropertyDefinition(node) {
const parent = node.parent;
return (
parent.type === "Property" &&
parent.parent.type === "ObjectExpression" &&
parent.shorthand
);
}
//------------------------------------------------------------------------------

@@ -115,2 +99,3 @@ // Rule Definition

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -121,6 +106,5 @@ meta: {

docs: {
description: "disallow specified identifiers",
category: "Stylistic Issues",
description: "Disallow specified identifiers",
recommended: false,
url: "https://eslint.org/docs/rules/id-denylist"
url: "https://eslint.org/docs/latest/rules/id-denylist"
},

@@ -136,3 +120,4 @@

messages: {
restricted: "Identifier '{{name}}' is restricted."
restricted: "Identifier '{{name}}' is restricted.",
restrictedPrivate: "Identifier '#{{name}}' is restricted."
}

@@ -145,2 +130,3 @@ },

const reportedNodes = new Set();
const sourceCode = context.sourceCode;

@@ -197,7 +183,4 @@ let globalScope;

!isRenamedImport(node) &&
!isRenamedInDestructuring(node) &&
!(
isReferenceToGlobalVariable(node) &&
!isShorthandPropertyDefinition(node)
)
!isPropertyNameInDestructuring(node) &&
!isReferenceToGlobalVariable(node)
);

@@ -213,6 +196,18 @@ }

function report(node) {
if (!reportedNodes.has(node)) {
/*
* We used the range instead of the node because it's possible
* for the same identifier to be represented by two different
* nodes, with the most clear example being shorthand properties:
* { foo }
* In this case, "foo" is represented by one node for the name
* and one for the value. The only way to know they are the same
* is to look at the range.
*/
if (!reportedNodes.has(node.range.toString())) {
const isPrivate = node.type === "PrivateIdentifier";
context.report({
node,
messageId: "restricted",
messageId: isPrivate ? "restrictedPrivate" : "restricted",
data: {

@@ -222,3 +217,3 @@ name: node.name

});
reportedNodes.add(node);
reportedNodes.add(node.range.toString());
}

@@ -229,7 +224,10 @@ }

Program() {
globalScope = context.getScope();
Program(node) {
globalScope = sourceCode.getScope(node);
},
Identifier(node) {
[[
"Identifier",
"PrivateIdentifier"
]](node) {
if (isRestricted(node.name) && shouldCheck(node)) {

@@ -236,0 +234,0 @@ report(node);

@@ -10,5 +10,12 @@ /**

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const { getGraphemeCount } = require("../shared/string-utils");
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -19,6 +26,5 @@ meta: {

docs: {
description: "enforce minimum and maximum identifier lengths",
category: "Stylistic Issues",
description: "Enforce minimum and maximum identifier lengths",
recommended: false,
url: "https://eslint.org/docs/rules/id-length"
url: "https://eslint.org/docs/latest/rules/id-length"
},

@@ -60,3 +66,5 @@

tooShort: "Identifier name '{{name}}' is too short (< {{min}}).",
tooLong: "Identifier name '{{name}}' is too long (> {{max}})."
tooShortPrivate: "Identifier name '#{{name}}' is too short (< {{min}}).",
tooLong: "Identifier name '{{name}}' is too long (> {{max}}).",
tooLongPrivate: "Identifier name #'{{name}}' is too long (> {{max}})."
}

@@ -72,3 +80,3 @@ },

const exceptionPatterns = (options.exceptionPatterns || []).map(pattern => new RegExp(pattern, "u"));
const reportedNode = new Set();
const reportedNodes = new Set();

@@ -106,8 +114,10 @@ /**

if (parent.parent.type === "ObjectPattern") {
const isKeyAndValueSame = parent.value.name === parent.key.name;
return (
parent.value !== parent.key && parent.value === node ||
parent.value === parent.key && parent.key === node && properties
!isKeyAndValueSame && parent.value === node ||
isKeyAndValueSame && parent.key === node && properties
);
}
return properties && !parent.computed && parent.key === node;
return properties && !parent.computed && parent.key.name === node.name;
},

@@ -121,2 +131,3 @@ ImportDefaultSpecifier: true,

MethodDefinition: true,
PropertyDefinition: true,
CatchClause: true,

@@ -127,9 +138,14 @@ ArrayPattern: true

return {
Identifier(node) {
[[
"Identifier",
"PrivateIdentifier"
]](node) {
const name = node.name;
const parent = node.parent;
const isShort = name.length < minLength;
const isLong = name.length > maxLength;
const nameLength = getGraphemeCount(name);
const isShort = nameLength < minLength;
const isLong = nameLength > maxLength;
if (!(isShort || isLong) || exceptions.has(name) || matchesExceptionPattern(name)) {

@@ -141,7 +157,23 @@ return; // Nothing to report

if (isValidExpression && !reportedNode.has(node) && (isValidExpression === true || isValidExpression(parent, node))) {
reportedNode.add(node);
/*
* We used the range instead of the node because it's possible
* for the same identifier to be represented by two different
* nodes, with the most clear example being shorthand properties:
* { foo }
* In this case, "foo" is represented by one node for the name
* and one for the value. The only way to know they are the same
* is to look at the range.
*/
if (isValidExpression && !reportedNodes.has(node.range.toString()) && (isValidExpression === true || isValidExpression(parent, node))) {
reportedNodes.add(node.range.toString());
let messageId = isShort ? "tooShort" : "tooLong";
if (node.type === "PrivateIdentifier") {
messageId += "Private";
}
context.report({
node,
messageId: isShort ? "tooShort" : "tooLong",
messageId,
data: { name, min: minLength, max: maxLength }

@@ -148,0 +180,0 @@ });

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "require identifiers to match a specified regular expression",
category: "Stylistic Issues",
description: "Require identifiers to match a specified regular expression",
recommended: false,
url: "https://eslint.org/docs/rules/id-match"
url: "https://eslint.org/docs/latest/rules/id-match"
},

@@ -36,2 +36,6 @@

},
classFields: {
type: "boolean",
default: false
},
onlyDeclarations: {

@@ -50,3 +54,4 @@ type: "boolean",

messages: {
notMatch: "Identifier '{{name}}' does not match the pattern '{{pattern}}'."
notMatch: "Identifier '{{name}}' does not match the pattern '{{pattern}}'.",
notMatchPrivate: "Identifier '#{{name}}' does not match the pattern '{{pattern}}'."
}

@@ -64,6 +69,10 @@ },

const options = context.options[1] || {},
properties = !!options.properties,
checkProperties = !!options.properties,
checkClassFields = !!options.classFields,
onlyDeclarations = !!options.onlyDeclarations,
ignoreDestructuring = !!options.ignoreDestructuring;
const sourceCode = context.sourceCode;
let globalScope;
//--------------------------------------------------------------------------

@@ -74,3 +83,3 @@ // Helpers

// contains reported nodes to avoid reporting twice on destructuring with shorthand notation
const reported = new Map();
const reportedNodes = new Set();
const ALLOWED_PARENT_TYPES = new Set(["CallExpression", "NewExpression"]);

@@ -81,2 +90,15 @@ const DECLARATION_TYPES = new Set(["FunctionDeclaration", "VariableDeclarator"]);

/**
* Checks whether the given node represents a reference to a global variable that is not declared in the source code.
* These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables.
* @param {ASTNode} node `Identifier` node to check.
* @returns {boolean} `true` if the node is a reference to a global variable.
*/
function isReferenceToGlobalVariable(node) {
const variable = globalScope.set.get(node.name);
return variable && variable.defs.length === 0 &&
variable.references.some(ref => ref.identifier === node);
}
/**
* Checks if a string matches the provided pattern

@@ -130,6 +152,20 @@ * @param {string} name The string to check.

function report(node) {
if (!reported.has(node)) {
/*
* We used the range instead of the node because it's possible
* for the same identifier to be represented by two different
* nodes, with the most clear example being shorthand properties:
* { foo }
* In this case, "foo" is represented by one node for the name
* and one for the value. The only way to know they are the same
* is to look at the range.
*/
if (!reportedNodes.has(node.range.toString())) {
const messageId = (node.type === "PrivateIdentifier")
? "notMatchPrivate" : "notMatch";
context.report({
node,
messageId: "notMatch",
messageId,
data: {

@@ -140,3 +176,3 @@ name: node.name,

});
reported.set(node, true);
reportedNodes.add(node.range.toString());
}

@@ -147,2 +183,6 @@ }

Program(node) {
globalScope = sourceCode.getScope(node);
},
Identifier(node) {

@@ -153,5 +193,9 @@ const name = node.name,

if (isReferenceToGlobalVariable(node)) {
return;
}
if (parent.type === "MemberExpression") {
if (!properties) {
if (!checkProperties) {
return;

@@ -182,2 +226,13 @@ }

// For https://github.com/eslint/eslint/issues/15123
} else if (
parent.type === "Property" &&
parent.parent.type === "ObjectExpression" &&
parent.key === node &&
!parent.computed
) {
if (checkProperties && isInvalid(name)) {
report(node);
}
/*

@@ -191,4 +246,3 @@ * Properties have their own rules, and

if (parent.parent && parent.parent.type === "ObjectPattern") {
if (parent.shorthand && parent.value.left && isInvalid(name)) {
if (!ignoreDestructuring && parent.shorthand && parent.value.left && isInvalid(name)) {
report(node);

@@ -213,3 +267,3 @@ }

// never check properties or always ignore destructuring
if (!properties || (ignoreDestructuring && isInsideObjectPattern(node))) {
if ((!checkProperties && !parent.computed) || (ignoreDestructuring && isInsideObjectPattern(node))) {
return;

@@ -231,2 +285,8 @@ }

} else if (parent.type === "PropertyDefinition") {
if (checkClassFields && isInvalid(name)) {
report(node);
}
// Report anything that is invalid that isn't a CallExpression

@@ -236,2 +296,15 @@ } else if (shouldReport(effectiveParent, name)) {

}
},
"PrivateIdentifier"(node) {
const isClassField = node.parent.type === "PropertyDefinition";
if (isClassField && !checkClassFields) {
return;
}
if (isInvalid(node.name)) {
report(node);
}
}

@@ -238,0 +311,0 @@

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

//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "enforce the location of arrow function bodies",
category: "Stylistic Issues",
description: "Enforce the location of arrow function bodies",
recommended: false,
url: "https://eslint.org/docs/rules/implicit-arrow-linebreak"
url: "https://eslint.org/docs/latest/rules/implicit-arrow-linebreak"
},

@@ -39,3 +39,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const option = context.options[0] || "beside";

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

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

* @author Gyandeep Singh
* @deprecated in ESLint v4.0.0
*/

@@ -21,4 +22,5 @@

//------------------------------------------------------------------------------
/* istanbul ignore next: this rule has known coverage issues, but it's deprecated and shouldn't be updated in the future anyway. */
// this rule has known coverage issues, but it's deprecated and shouldn't be updated in the future anyway.
/* c8 ignore next */
/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -29,6 +31,5 @@ meta: {

docs: {
description: "enforce consistent indentation",
category: "Stylistic Issues",
description: "Enforce consistent indentation",
recommended: false,
url: "https://eslint.org/docs/rules/indent-legacy"
url: "https://eslint.org/docs/latest/rules/indent-legacy"
},

@@ -211,3 +212,3 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -218,6 +219,6 @@ if (context.options.length) {

indentType = "tab";
} else /* istanbul ignore else : this will be caught by options validation */ if (typeof context.options[0] === "number") {
} else /* c8 ignore start */ if (typeof context.options[0] === "number") {
indentSize = context.options[0];
indentType = "space";
}
}/* c8 ignore stop */

@@ -760,3 +761,3 @@ if (context.options[1]) {

} else if (options.CallExpression.arguments === "first") {
if (parent.arguments.indexOf(node) !== -1) {
if (parent.arguments.includes(node)) {
nodeIndent = parent.arguments[0].loc.start.column;

@@ -848,3 +849,3 @@ }

if (node.parent && statementsWithProperties.indexOf(node.parent.type) !== -1 && isNodeBodyBlock(node)) {
if (node.parent && statementsWithProperties.includes(node.parent.type) && isNodeBodyBlock(node)) {
indent = getNodeIndent(node.parent).goodChar;

@@ -964,3 +965,3 @@ } else if (node.parent && node.parent.type === "CatchClause") {

/**
* Checks wether a return statement is wrapped in ()
* Checks whether a return statement is wrapped in ()
* @param {ASTNode} node node to examine

@@ -967,0 +968,0 @@ * @returns {boolean} the result

@@ -9,3 +9,3 @@ /**

/* eslint sort-keys: ["error", "asc"] */
/* eslint sort-keys: ["error", "asc"] -- More readable for long list */

@@ -76,2 +76,3 @@ const { LazyLoadingRuleMap } = require("./utils/lazy-loading-rule-map");

"lines-between-class-members": () => require("./lines-between-class-members"),
"logical-assignment-operators": () => require("./logical-assignment-operators"),
"max-classes-per-file": () => require("./max-classes-per-file"),

@@ -108,2 +109,3 @@ "max-depth": () => require("./max-depth"),

"no-const-assign": () => require("./no-const-assign"),
"no-constant-binary-expression": () => require("./no-constant-binary-expression"),
"no-constant-condition": () => require("./no-constant-condition"),

@@ -127,2 +129,3 @@ "no-constructor-return": () => require("./no-constructor-return"),

"no-empty-pattern": () => require("./no-empty-pattern"),
"no-empty-static-block": () => require("./no-empty-static-block"),
"no-eq-null": () => require("./no-eq-null"),

@@ -172,2 +175,3 @@ "no-eval": () => require("./no-eval"),

"no-new-func": () => require("./no-new-func"),
"no-new-native-nonconstructor": () => require("./no-new-native-nonconstructor"),
"no-new-object": () => require("./no-new-object"),

@@ -179,2 +183,3 @@ "no-new-require": () => require("./no-new-require"),

"no-obj-calls": () => require("./no-obj-calls"),
"no-object-constructor": () => require("./no-object-constructor"),
"no-octal": () => require("./no-octal"),

@@ -230,2 +235,3 @@ "no-octal-escape": () => require("./no-octal-escape"),

"no-unused-labels": () => require("./no-unused-labels"),
"no-unused-private-class-members": () => require("./no-unused-private-class-members"),
"no-unused-vars": () => require("./no-unused-vars"),

@@ -264,2 +270,3 @@ "no-use-before-define": () => require("./no-use-before-define"),

"prefer-numeric-literals": () => require("./prefer-numeric-literals"),
"prefer-object-has-own": () => require("./prefer-object-has-own"),
"prefer-object-spread": () => require("./prefer-object-spread"),

@@ -266,0 +273,0 @@ "prefer-promise-reject-errors": () => require("./prefer-promise-reject-errors"),

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -51,6 +52,5 @@ meta: {

docs: {
description: "require or disallow initialization in variable declarations",
category: "Variables",
description: "Require or disallow initialization in variable declarations",
recommended: false,
url: "https://eslint.org/docs/rules/init-declarations"
url: "https://eslint.org/docs/latest/rules/init-declarations"
},

@@ -57,0 +57,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -45,6 +46,5 @@ meta: {

docs: {
description: "enforce the consistent use of either double or single quotes in JSX attributes",
category: "Stylistic Issues",
description: "Enforce the consistent use of either double or single quotes in JSX attributes",
recommended: false,
url: "https://eslint.org/docs/rules/jsx-quotes"
url: "https://eslint.org/docs/latest/rules/jsx-quotes"
},

@@ -75,3 +75,3 @@

function usesExpectedQuotes(node) {
return node.value.indexOf(setting.quote) !== -1 || astUtils.isSurroundedBy(node.raw, setting.quote);
return node.value.includes(setting.quote) || astUtils.isSurroundedBy(node.raw, setting.quote);
}

@@ -78,0 +78,0 @@

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

const astUtils = require("./utils/ast-utils");
const { getGraphemeCount } = require("../shared/string-utils");
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
/**

@@ -137,2 +134,3 @@ * Checks whether a string contains a line terminator as defined in

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -143,6 +141,5 @@ meta: {

docs: {
description: "enforce consistent spacing between keys and values in object literal properties",
category: "Stylistic Issues",
description: "Enforce consistent spacing between keys and values in object literal properties",
recommended: false,
url: "https://eslint.org/docs/rules/key-spacing"
url: "https://eslint.org/docs/latest/rules/key-spacing"
},

@@ -335,5 +332,52 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
/**
* Determines if the given property is key-value property.
* @param {ASTNode} property Property node to check.
* @returns {boolean} Whether the property is a key-value property.
*/
function isKeyValueProperty(property) {
return !(
(property.method ||
property.shorthand ||
property.kind !== "init" || property.type !== "Property") // Could be "ExperimentalSpreadProperty" or "SpreadElement"
);
}
/**
* Starting from the given node (a property.key node here) looks forward
* until it finds the colon punctuator and returns it.
* @param {ASTNode} node The node to start looking from.
* @returns {ASTNode} The colon punctuator.
*/
function getNextColon(node) {
return sourceCode.getTokenAfter(node, astUtils.isColonToken);
}
/**
* Starting from the given node (a property.key node here) looks forward
* until it finds the last token before a colon punctuator and returns it.
* @param {ASTNode} node The node to start looking from.
* @returns {ASTNode} The last token before a colon punctuator.
*/
function getLastTokenBeforeColon(node) {
const colonToken = getNextColon(node);
return sourceCode.getTokenBefore(colonToken);
}
/**
* Starting from the given node (a property.key node here) looks forward
* until it finds the first token after a colon punctuator and returns it.
* @param {ASTNode} node The node to start looking from.
* @returns {ASTNode} The first token after a colon punctuator.
*/
function getFirstTokenAfterColon(node) {
const colonToken = getNextColon(node);
return sourceCode.getTokenAfter(colonToken);
}
/**
* Checks whether a property is a member of the property group it follows.

@@ -346,5 +390,5 @@ * @param {ASTNode} lastMember The last Property known to be in the group.

const groupEndLine = lastMember.loc.start.line,
candidateStartLine = candidate.loc.start.line;
candidateValueStartLine = (isKeyValueProperty(candidate) ? getFirstTokenAfterColon(candidate.key) : candidate).loc.start.line;
if (candidateStartLine - groupEndLine <= 1) {
if (candidateValueStartLine - groupEndLine <= 1) {
return true;

@@ -363,3 +407,3 @@ }

leadingComments[0].loc.start.line - groupEndLine <= 1 &&
candidateStartLine - last(leadingComments).loc.end.line <= 1
candidateValueStartLine - last(leadingComments).loc.end.line <= 1
) {

@@ -378,37 +422,2 @@ for (let i = 1; i < leadingComments.length; i++) {

/**
* Determines if the given property is key-value property.
* @param {ASTNode} property Property node to check.
* @returns {boolean} Whether the property is a key-value property.
*/
function isKeyValueProperty(property) {
return !(
(property.method ||
property.shorthand ||
property.kind !== "init" || property.type !== "Property") // Could be "ExperimentalSpreadProperty" or "SpreadElement"
);
}
/**
* Starting from the given a node (a property.key node here) looks forward
* until it finds the last token before a colon punctuator and returns it.
* @param {ASTNode} node The node to start looking from.
* @returns {ASTNode} The last token before a colon punctuator.
*/
function getLastTokenBeforeColon(node) {
const colonToken = sourceCode.getTokenAfter(node, astUtils.isColonToken);
return sourceCode.getTokenBefore(colonToken);
}
/**
* Starting from the given a node (a property.key node here) looks forward
* until it finds the colon punctuator and returns it.
* @param {ASTNode} node The node to start looking from.
* @returns {ASTNode} The colon punctuator.
*/
function getNextColon(node) {
return sourceCode.getTokenAfter(node, astUtils.isColonToken);
}
/**
* Gets an object literal property's key as the identifier name or string value.

@@ -438,16 +447,4 @@ * @param {ASTNode} property Property node whose key to retrieve.

function report(property, side, whitespace, expected, mode) {
const diff = whitespace.length - expected,
nextColon = getNextColon(property.key),
tokenBeforeColon = sourceCode.getTokenBefore(nextColon, { includeComments: true }),
tokenAfterColon = sourceCode.getTokenAfter(nextColon, { includeComments: true }),
isKeySide = side === "key",
isExtra = diff > 0,
diffAbs = Math.abs(diff),
spaces = Array(diffAbs + 1).join(" ");
const diff = whitespace.length - expected;
const locStart = isKeySide ? tokenBeforeColon.loc.end : nextColon.loc.start;
const locEnd = isKeySide ? nextColon.loc.start : tokenAfterColon.loc.start;
const missingLoc = isKeySide ? tokenBeforeColon.loc : tokenAfterColon.loc;
const loc = isExtra ? { start: locStart, end: locEnd } : missingLoc;
if ((

@@ -459,2 +456,15 @@ diff && mode === "strict" ||

) {
const nextColon = getNextColon(property.key),
tokenBeforeColon = sourceCode.getTokenBefore(nextColon, { includeComments: true }),
tokenAfterColon = sourceCode.getTokenAfter(nextColon, { includeComments: true }),
isKeySide = side === "key",
isExtra = diff > 0,
diffAbs = Math.abs(diff),
spaces = Array(diffAbs + 1).join(" ");
const locStart = isKeySide ? tokenBeforeColon.loc.end : nextColon.loc.start;
const locEnd = isKeySide ? nextColon.loc.start : tokenAfterColon.loc.start;
const missingLoc = isKeySide ? tokenBeforeColon.loc : tokenAfterColon.loc;
const loc = isExtra ? { start: locStart, end: locEnd } : missingLoc;
let fix;

@@ -519,3 +529,3 @@

return endToken.range[1] - startToken.range[0];
return getGraphemeCount(sourceCode.getText().slice(startToken.range[0], endToken.range[1]));
}

@@ -544,4 +554,4 @@

* Creates groups of properties.
* @param {ASTNode} node ObjectExpression node being evaluated.
* @returns {Array.<ASTNode[]>} Groups of property AST node lists.
* @param {ASTNode} node ObjectExpression node being evaluated.
* @returns {Array<ASTNode[]>} Groups of property AST node lists.
*/

@@ -614,3 +624,3 @@ function createGroups(node) {

* Verifies spacing of property conforms to specified options.
* @param {ASTNode} node Property node being evaluated.
* @param {ASTNode} node Property node being evaluated.
* @param {Object} lineOptions Configured singleLine or multiLine options

@@ -644,3 +654,3 @@ * @returns {void}

* Verifies vertical alignment, taking into account groups of properties.
* @param {ASTNode} node ObjectExpression node being evaluated.
* @param {ASTNode} node ObjectExpression node being evaluated.
* @returns {void}

@@ -647,0 +657,0 @@ */

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

const TEMPLATE_CLOSE_PAREN = /^\}/u;
const CHECK_TYPE = /^(?:JSXElement|RegularExpression|String|Template)$/u;
const CHECK_TYPE = /^(?:JSXElement|RegularExpression|String|Template|PrivateIdentifier)$/u;
const KEYS = keywords.concat(["as", "async", "await", "from", "get", "let", "of", "set", "yield"]);

@@ -65,2 +65,3 @@

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -71,6 +72,5 @@ meta: {

docs: {
description: "enforce consistent spacing before and after keywords",
category: "Stylistic Issues",
description: "Enforce consistent spacing before and after keywords",
recommended: false,
url: "https://eslint.org/docs/rules/keyword-spacing"
url: "https://eslint.org/docs/latest/rules/keyword-spacing"
},

@@ -114,4 +114,6 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const tokensToIgnore = new WeakSet();
/**

@@ -129,2 +131,3 @@ * Reports a given token if there are not space(s) before the token.

!isOpenParenOfTemplate(prevToken) &&
!tokensToIgnore.has(prevToken) &&
astUtils.isTokenOnSameLine(prevToken, token) &&

@@ -156,2 +159,3 @@ !sourceCode.isSpaceBetweenTokens(prevToken, token)

!isOpenParenOfTemplate(prevToken) &&
!tokensToIgnore.has(prevToken) &&
astUtils.isTokenOnSameLine(prevToken, token) &&

@@ -183,2 +187,3 @@ sourceCode.isSpaceBetweenTokens(prevToken, token)

!isCloseParenOfTemplate(nextToken) &&
!tokensToIgnore.has(nextToken) &&
astUtils.isTokenOnSameLine(token, nextToken) &&

@@ -210,2 +215,3 @@ !sourceCode.isSpaceBetweenTokens(token, nextToken)

!isCloseParenOfTemplate(nextToken) &&
!tokensToIgnore.has(nextToken) &&
astUtils.isTokenOnSameLine(token, nextToken) &&

@@ -414,3 +420,11 @@ sourceCode.isSpaceBetweenTokens(token, nextToken)

checkSpacingAroundFirstToken(node);
checkSpacingAroundTokenBefore(node.right);
const inToken = sourceCode.getTokenBefore(node.right, astUtils.isNotOpeningParenToken);
const previousToken = sourceCode.getTokenBefore(inToken);
if (previousToken.type !== "PrivateIdentifier") {
checkSpacingBefore(inToken);
}
checkSpacingAfter(inToken);
}

@@ -431,3 +445,11 @@

}
checkSpacingAround(sourceCode.getTokenBefore(node.right, astUtils.isNotOpeningParenToken));
const ofToken = sourceCode.getTokenBefore(node.right, astUtils.isNotOpeningParenToken);
const previousToken = sourceCode.getTokenBefore(ofToken);
if (previousToken.type !== "PrivateIdentifier") {
checkSpacingBefore(ofToken);
}
checkSpacingAfter(ofToken);
}

@@ -460,2 +482,3 @@

checkSpacingBefore(asToken, PREV_TOKEN_M);
checkSpacingAfter(asToken, NEXT_TOKEN_M);
}

@@ -474,2 +497,31 @@

* keyword is invalid.
* @param {ASTNode} node An `ImportSpecifier` node to check.
* @returns {void}
*/
function checkSpacingForImportSpecifier(node) {
if (node.imported.range[0] !== node.local.range[0]) {
const asToken = sourceCode.getTokenBefore(node.local);
checkSpacingBefore(asToken, PREV_TOKEN_M);
}
}
/**
* Reports `as` keyword of a given node if usage of spacing around this
* keyword is invalid.
* @param {ASTNode} node An `ExportSpecifier` node to check.
* @returns {void}
*/
function checkSpacingForExportSpecifier(node) {
if (node.local.range[0] !== node.exported.range[0]) {
const asToken = sourceCode.getTokenBefore(node.exported);
checkSpacingBefore(asToken, PREV_TOKEN_M);
checkSpacingAfter(asToken, NEXT_TOKEN_M);
}
}
/**
* Reports `as` keyword of a given node if usage of spacing around this
* keyword is invalid.
* @param {ASTNode} node A node to report.

@@ -488,2 +540,3 @@ * @returns {void}

* @param {ASTNode} node A node to report.
* @throws {Error} If unable to find token get, set, or async beside method name.
* @returns {void}

@@ -581,7 +634,18 @@ */

// Others
ImportSpecifier: checkSpacingForImportSpecifier,
ExportSpecifier: checkSpacingForExportSpecifier,
ImportNamespaceSpecifier: checkSpacingForImportNamespaceSpecifier,
MethodDefinition: checkSpacingForProperty,
Property: checkSpacingForProperty
PropertyDefinition: checkSpacingForProperty,
StaticBlock: checkSpacingAroundFirstToken,
Property: checkSpacingForProperty,
// To avoid conflicts with `space-infix-ops`, e.g. `a > this.b`
"BinaryExpression[operator='>']"(node) {
const operatorToken = sourceCode.getTokenBefore(node.right, astUtils.isNotOpeningParenToken);
tokensToIgnore.add(operatorToken);
}
};
}
};

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -19,6 +20,5 @@ meta: {

docs: {
description: "enforce position of line comments",
category: "Stylistic Issues",
description: "Enforce position of line comments",
recommended: false,
url: "https://eslint.org/docs/rules/line-comment-position"
url: "https://eslint.org/docs/latest/rules/line-comment-position"
},

@@ -83,3 +83,3 @@

const customIgnoreRegExp = new RegExp(ignorePattern, "u");
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -86,0 +86,0 @@ //--------------------------------------------------------------------------

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,6 +25,5 @@ meta: {

docs: {
description: "enforce consistent linebreak style",
category: "Stylistic Issues",
description: "Enforce consistent linebreak style",
recommended: false,
url: "https://eslint.org/docs/rules/linebreak-style"
url: "https://eslint.org/docs/latest/rules/linebreak-style"
},

@@ -45,3 +45,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -48,0 +48,0 @@ //--------------------------------------------------------------------------

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

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

@@ -20,3 +19,3 @@ //------------------------------------------------------------------------------

/**
* Return an array with with any line numbers that are empty.
* Return an array with any line numbers that are empty.
* @param {Array} lines An array of each line of the file.

@@ -35,3 +34,3 @@ * @returns {Array} An array of line numbers.

/**
* Return an array with with any line numbers that contain comments.
* Return an array with any line numbers that contain comments.
* @param {Array} comments An array of comment tokens.

@@ -56,2 +55,3 @@ * @returns {Array} An array of line numbers.

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -62,6 +62,5 @@ meta: {

docs: {
description: "require empty lines around comments",
category: "Stylistic Issues",
description: "Require empty lines around comments",
recommended: false,
url: "https://eslint.org/docs/rules/lines-around-comment"
url: "https://eslint.org/docs/latest/rules/lines-around-comment"
},

@@ -122,2 +121,6 @@

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

@@ -144,3 +147,3 @@ },

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -152,3 +155,3 @@ const lines = sourceCode.lines,

emptyLines = getEmptyLineNums(lines),
commentAndEmptyLines = commentLines.concat(emptyLines);
commentAndEmptyLines = new Set(commentLines.concat(emptyLines));

@@ -198,6 +201,35 @@ /**

* @param {token} token The token to check.
* @returns {ASTNode} The parent node that contains the given token.
* @returns {ASTNode|null} The parent node that contains the given token.
*/
function getParentNodeOfToken(token) {
return sourceCode.getNodeByRangeIndex(token.range[0]);
const node = sourceCode.getNodeByRangeIndex(token.range[0]);
/*
* For the purpose of this rule, the comment token is in a `StaticBlock` node only
* if it's inside the braces of that `StaticBlock` node.
*
* Example where this function returns `null`:
*
* static
* // comment
* {
* }
*
* Example where this function returns `StaticBlock` node:
*
* static
* {
* // comment
* }
*
*/
if (node && node.type === "StaticBlock") {
const openingBrace = sourceCode.getFirstToken(node, { skip: 1 }); // skip the `static` token
return token.range[0] >= openingBrace.range[0]
? node
: null;
}
return node;
}

@@ -214,4 +246,17 @@

return parent && isParentNodeType(parent, nodeType) &&
token.loc.start.line - parent.loc.start.line === 1;
if (parent && isParentNodeType(parent, nodeType)) {
let parentStartNodeOrToken = parent;
if (parent.type === "StaticBlock") {
parentStartNodeOrToken = sourceCode.getFirstToken(parent, { skip: 1 }); // opening brace of the static block
} else if (parent.type === "SwitchStatement") {
parentStartNodeOrToken = sourceCode.getTokenAfter(parent.discriminant, {
filter: astUtils.isOpeningBraceToken
}); // opening brace of the switch statement
}
return token.loc.start.line - parentStartNodeOrToken.loc.start.line === 1;
}
return false;
}

@@ -228,3 +273,3 @@

return parent && isParentNodeType(parent, nodeType) &&
return !!parent && isParentNodeType(parent, nodeType) &&
parent.loc.end.line - token.loc.end.line === 1;

@@ -239,3 +284,9 @@ }

function isCommentAtBlockStart(token) {
return isCommentAtParentStart(token, "ClassBody") || isCommentAtParentStart(token, "BlockStatement") || isCommentAtParentStart(token, "SwitchCase");
return (
isCommentAtParentStart(token, "ClassBody") ||
isCommentAtParentStart(token, "BlockStatement") ||
isCommentAtParentStart(token, "StaticBlock") ||
isCommentAtParentStart(token, "SwitchCase") ||
isCommentAtParentStart(token, "SwitchStatement")
);
}

@@ -249,3 +300,9 @@

function isCommentAtBlockEnd(token) {
return isCommentAtParentEnd(token, "ClassBody") || isCommentAtParentEnd(token, "BlockStatement") || isCommentAtParentEnd(token, "SwitchCase") || isCommentAtParentEnd(token, "SwitchStatement");
return (
isCommentAtParentEnd(token, "ClassBody") ||
isCommentAtParentEnd(token, "BlockStatement") ||
isCommentAtParentEnd(token, "StaticBlock") ||
isCommentAtParentEnd(token, "SwitchCase") ||
isCommentAtParentEnd(token, "SwitchStatement")
);
}

@@ -363,3 +420,3 @@

// check for newline before
if (!exceptionStartAllowed && before && !lodash.includes(commentAndEmptyLines, prevLineNum) &&
if (!exceptionStartAllowed && before && !commentAndEmptyLines.has(prevLineNum) &&
!(astUtils.isCommentToken(previousTokenOrComment) && astUtils.isTokenOnSameLine(previousTokenOrComment, token))) {

@@ -379,3 +436,3 @@ const lineStart = token.range[0] - token.loc.start.column;

// check for newline after
if (!exceptionEndAllowed && after && !lodash.includes(commentAndEmptyLines, nextLineNum) &&
if (!exceptionEndAllowed && after && !commentAndEmptyLines.has(nextLineNum) &&
!(astUtils.isCommentToken(nextTokenOrComment) && astUtils.isTokenOnSameLine(token, nextTokenOrComment))) {

@@ -414,2 +471,9 @@ context.report({

}
} else if (token.type === "Shebang") {
if (options.afterHashbangComment) {
checkForEmptyLine(token, {
after: options.afterHashbangComment,
before: false
});
}
}

@@ -416,0 +480,0 @@ });

/**
* @fileoverview Require or disallow newlines around directives.
* @author Kai Cataldo
* @deprecated
* @deprecated in ESLint v4.0.0
*/

@@ -15,2 +15,3 @@

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -21,6 +22,5 @@ meta: {

docs: {
description: "require or disallow newlines around directives",
category: "Stylistic Issues",
description: "Require or disallow newlines around directives",
recommended: false,
url: "https://eslint.org/docs/rules/lines-around-directive"
url: "https://eslint.org/docs/latest/rules/lines-around-directive"
},

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

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const config = context.options[0] || "always";

@@ -62,0 +62,0 @@ const expectLineBefore = typeof config === "string" ? config : config.before;

@@ -7,8 +7,28 @@ /**

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const astUtils = require("./utils/ast-utils");
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
/**
* Types of class members.
* Those have `test` method to check it matches to the given class member.
* @private
*/
const ClassMemberTypes = {
"*": { test: () => true },
field: { test: node => node.type === "PropertyDefinition" },
method: { test: node => node.type === "MethodDefinition" }
};
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -19,6 +39,5 @@ meta: {

docs: {
description: "require or disallow an empty line between class members",
category: "Stylistic Issues",
description: "Require or disallow an empty line between class members",
recommended: false,
url: "https://eslint.org/docs/rules/lines-between-class-members"
url: "https://eslint.org/docs/latest/rules/lines-between-class-members"
},

@@ -30,3 +49,28 @@

{
enum: ["always", "never"]
anyOf: [
{
type: "object",
properties: {
enforce: {
type: "array",
items: {
type: "object",
properties: {
blankLine: { enum: ["always", "never"] },
prev: { enum: ["method", "field", "*"] },
next: { enum: ["method", "field", "*"] }
},
additionalProperties: false,
required: ["blankLine", "prev", "next"]
},
minItems: 1
}
},
additionalProperties: false,
required: ["enforce"]
},
{
enum: ["always", "never"]
}
]
},

@@ -57,5 +101,51 @@ {

const sourceCode = context.getSourceCode();
const configureList = typeof options[0] === "object" ? options[0].enforce : [{ blankLine: options[0], prev: "*", next: "*" }];
const sourceCode = context.sourceCode;
/**
* Gets a pair of tokens that should be used to check lines between two class member nodes.
*
* In most cases, this returns the very last token of the current node and
* the very first token of the next node.
* For example:
*
* class C {
* x = 1; // curLast: `;` nextFirst: `in`
* in = 2
* }
*
* There is only one exception. If the given node ends with a semicolon, and it looks like
* a semicolon-less style's semicolon - one that is not on the same line as the preceding
* token, but is on the line where the next class member starts - this returns the preceding
* token and the semicolon as boundary tokens.
* For example:
*
* class C {
* x = 1 // curLast: `1` nextFirst: `;`
* ;in = 2
* }
* When determining the desired layout of the code, we should treat this semicolon as
* a part of the next class member node instead of the one it technically belongs to.
* @param {ASTNode} curNode Current class member node.
* @param {ASTNode} nextNode Next class member node.
* @returns {Token} The actual last token of `node`.
* @private
*/
function getBoundaryTokens(curNode, nextNode) {
const lastToken = sourceCode.getLastToken(curNode);
const prevToken = sourceCode.getTokenBefore(lastToken);
const nextToken = sourceCode.getFirstToken(nextNode); // skip possible lone `;` between nodes
const isSemicolonLessStyle = (
astUtils.isSemicolonToken(lastToken) &&
!astUtils.isTokenOnSameLine(prevToken, lastToken) &&
astUtils.isTokenOnSameLine(lastToken, nextToken)
);
return isSemicolonLessStyle
? { curLast: prevToken, nextFirst: lastToken }
: { curLast: lastToken, nextFirst: nextToken };
}
/**
* Return the last token among the consecutive tokens that have no exceed max line difference in between, before the first token in the next member.

@@ -102,2 +192,34 @@ * @param {Token} prevLastToken The last token in the previous member node.

/**
* Checks whether the given node matches the given type.
* @param {ASTNode} node The class member node to check.
* @param {string} type The class member type to check.
* @returns {boolean} `true` if the class member node matched the type.
* @private
*/
function match(node, type) {
return ClassMemberTypes[type].test(node);
}
/**
* Finds the last matched configuration from the configureList.
* @param {ASTNode} prevNode The previous node to match.
* @param {ASTNode} nextNode The current node to match.
* @returns {string|null} Padding type or `null` if no matches were found.
* @private
*/
function getPaddingType(prevNode, nextNode) {
for (let i = configureList.length - 1; i >= 0; --i) {
const configure = configureList[i];
const matched =
match(prevNode, configure.prev) &&
match(nextNode, configure.next);
if (matched) {
return configure.blankLine;
}
}
return null;
}
return {

@@ -109,4 +231,3 @@ ClassBody(node) {

const curFirst = sourceCode.getFirstToken(body[i]);
const curLast = sourceCode.getLastToken(body[i]);
const nextFirst = sourceCode.getFirstToken(body[i + 1]);
const { curLast, nextFirst } = getBoundaryTokens(body[i], body[i + 1]);
const isMulti = !astUtils.isTokenOnSameLine(curFirst, curLast);

@@ -119,8 +240,9 @@ const skip = !isMulti && options[1].exceptAfterSingleLine;

const curLineLastToken = findLastConsecutiveTokenAfter(curLast, nextFirst, 0);
const paddingType = getPaddingType(body[i], body[i + 1]);
if ((options[0] === "always" && !skip && !isPadded) ||
(options[0] === "never" && isPadded)) {
if (paddingType === "never" && isPadded) {
context.report({
node: body[i + 1],
messageId: isPadded ? "never" : "always",
messageId: "never",
fix(fixer) {

@@ -130,8 +252,19 @@ if (hasTokenInPadding) {

}
return isPadded
? fixer.replaceTextRange([beforePadding.range[1], afterPadding.range[0]], "\n")
: fixer.insertTextAfter(curLineLastToken, "\n");
return fixer.replaceTextRange([beforePadding.range[1], afterPadding.range[0]], "\n");
}
});
} else if (paddingType === "always" && !skip && !isPadded) {
context.report({
node: body[i + 1],
messageId: "always",
fix(fixer) {
if (hasTokenInPadding) {
return null;
}
return fixer.insertTextAfter(curLineLastToken, "\n");
}
});
}
}

@@ -138,0 +271,0 @@ }

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -22,6 +23,5 @@ meta: {

docs: {
description: "enforce a maximum number of classes per file",
category: "Best Practices",
description: "Enforce a maximum number of classes per file",
recommended: false,
url: "https://eslint.org/docs/rules/max-classes-per-file"
url: "https://eslint.org/docs/latest/rules/max-classes-per-file"
},

@@ -31,4 +31,21 @@

{
type: "integer",
minimum: 1
oneOf: [
{
type: "integer",
minimum: 1
},
{
type: "object",
properties: {
ignoreExpressions: {
type: "boolean"
},
max: {
type: "integer",
minimum: 1
}
},
additionalProperties: false
}
]
}

@@ -42,5 +59,7 @@ ],

create(context) {
const [option = {}] = context.options;
const [ignoreExpressions, max] = typeof option === "number"
? [false, option || 1]
: [option.ignoreExpressions, option.max || 1];
const maxClasses = context.options[0] || 1;
let classCount = 0;

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

"Program:exit"(node) {
if (classCount > maxClasses) {
if (classCount > max) {
context.report({

@@ -60,3 +79,3 @@ node,

classCount,
max: maxClasses
max
}

@@ -66,4 +85,9 @@ });

},
"ClassDeclaration, ClassExpression"() {
"ClassDeclaration"() {
classCount++;
},
"ClassExpression"() {
if (!ignoreExpressions) {
classCount++;
}
}

@@ -70,0 +94,0 @@ };

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "enforce a maximum depth that blocks can be nested",
category: "Stylistic Issues",
description: "Enforce a maximum depth that blocks can be nested",
recommended: false,
url: "https://eslint.org/docs/rules/max-depth"
url: "https://eslint.org/docs/latest/rules/max-depth"
},

@@ -124,2 +124,3 @@

ArrowFunctionExpression: startFunction,
StaticBlock: startFunction,

@@ -153,2 +154,3 @@ IfStatement(node) {

"ArrowFunctionExpression:exit": endFunction,
"StaticBlock:exit": endFunction,
"Program:exit": endFunction

@@ -155,0 +157,0 @@ };

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -72,6 +73,5 @@ meta: {

docs: {
description: "enforce a maximum line length",
category: "Stylistic Issues",
description: "Enforce a maximum line length",
recommended: false,
url: "https://eslint.org/docs/rules/max-len"
url: "https://eslint.org/docs/latest/rules/max-len"
},

@@ -102,3 +102,3 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -221,3 +221,3 @@ /**

* @param {string} key the object's key
* @param {*} value the value to add
* @param {any} value the value to add
* @returns {void}

@@ -259,15 +259,19 @@ * @private

/**
* 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;
}

@@ -320,9 +324,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);

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

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

const astUtils = require("./utils/ast-utils");
const { upperCaseFirst } = require("../shared/string-utils");
const lodash = require("lodash");
//------------------------------------------------------------------------------

@@ -53,3 +52,3 @@ // Constants

* @param {Array} comments An array of comment nodes.
* @returns {Map.<string,Node>} A map with numeric keys (source code line numbers) and comment token values.
* @returns {Map<string, Node>} A map with numeric keys (source code line numbers) and comment token values.
*/

@@ -71,2 +70,3 @@ function getCommentLineNumbers(comments) {

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -77,6 +77,5 @@ meta: {

docs: {
description: "enforce a maximum number of line of code in a function",
category: "Stylistic Issues",
description: "Enforce a maximum number of lines of code in a function",
recommended: false,
url: "https://eslint.org/docs/rules/max-lines-per-function"
url: "https://eslint.org/docs/latest/rules/max-lines-per-function"
},

@@ -93,3 +92,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const lines = sourceCode.lines;

@@ -199,3 +198,3 @@

if (lineCount > maxLines) {
const name = lodash.upperFirst(astUtils.getFunctionNameWithKind(funcNode));
const name = upperCaseFirst(astUtils.getFunctionNameWithKind(funcNode));

@@ -202,0 +201,0 @@ context.report({

@@ -11,9 +11,23 @@ /**

const lodash = require("lodash");
const astUtils = require("./utils/ast-utils");
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
/**
* Creates an array of numbers from `start` up to, but not including, `end`
* @param {number} start The start of the range
* @param {number} end The end of the range
* @returns {number[]} The range of numbers
*/
function range(start, end) {
return [...Array(end - start).keys()].map(x => x + start);
}
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,6 +38,5 @@ meta: {

docs: {
description: "enforce a maximum number of lines per file",
category: "Stylistic Issues",
description: "Enforce a maximum number of lines per file",
recommended: false,
url: "https://eslint.org/docs/rules/max-lines"
url: "https://eslint.org/docs/latest/rules/max-lines"
},

@@ -79,3 +92,3 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -125,3 +138,3 @@ /**

if (start <= end) {
return lodash.range(start, end + 1);
return range(start, end + 1);
}

@@ -142,3 +155,3 @@ return [];

*/
if (lines.length > 1 && lodash.last(lines).text === "") {
if (lines.length > 1 && lines[lines.length - 1].text === "") {
lines.pop();

@@ -154,8 +167,6 @@ }

const commentLines = lodash.flatten(
comments.map(comment => getLinesWithoutCode(comment))
);
const commentLines = new Set(comments.flatMap(getLinesWithoutCode));
lines = lines.filter(
l => !lodash.includes(commentLines, l.lineNumber)
l => !commentLines.has(l.lineNumber)
);

@@ -172,3 +183,3 @@ }

line: sourceCode.lines.length,
column: lodash.last(sourceCode.lines).length
column: sourceCode.lines[sourceCode.lines.length - 1].length
}

@@ -175,0 +186,0 @@ };

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "enforce a maximum depth that callbacks can be nested",
category: "Stylistic Issues",
description: "Enforce a maximum depth that callbacks can be nested",
recommended: false,
url: "https://eslint.org/docs/rules/max-nested-callbacks"
url: "https://eslint.org/docs/latest/rules/max-nested-callbacks"
},

@@ -24,0 +24,0 @@

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

const lodash = require("lodash");
const astUtils = require("./utils/ast-utils");
const { upperCaseFirst } = require("../shared/string-utils");

@@ -21,2 +20,3 @@ //------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -27,6 +27,5 @@ meta: {

docs: {
description: "enforce a maximum number of parameters in function definitions",
category: "Stylistic Issues",
description: "Enforce a maximum number of parameters in function definitions",
recommended: false,
url: "https://eslint.org/docs/rules/max-params"
url: "https://eslint.org/docs/latest/rules/max-params"
},

@@ -64,3 +63,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const option = context.options[0];

@@ -92,3 +91,3 @@ let numParams = 3;

data: {
name: lodash.upperFirst(astUtils.getFunctionNameWithKind(node)),
name: upperCaseFirst(astUtils.getFunctionNameWithKind(node)),
count: node.params.length,

@@ -95,0 +94,0 @@ max: numParams

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -23,6 +24,5 @@ meta: {

docs: {
description: "enforce a maximum number of statements allowed per line",
category: "Stylistic Issues",
description: "Enforce a maximum number of statements allowed per line",
recommended: false,
url: "https://eslint.org/docs/rules/max-statements-per-line"
url: "https://eslint.org/docs/latest/rules/max-statements-per-line"
},

@@ -50,3 +50,3 @@

const sourceCode = context.getSourceCode(),
const sourceCode = context.sourceCode,
options = context.options[0] || {},

@@ -53,0 +53,0 @@ maxStatementsPerLine = typeof options.max !== "undefined" ? options.max : 1;

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

const lodash = require("lodash");
const astUtils = require("./utils/ast-utils");
const { upperCaseFirst } = require("../shared/string-utils");

@@ -21,2 +20,3 @@ //------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -27,6 +27,5 @@ meta: {

docs: {
description: "enforce a maximum number of statements allowed in function blocks",
category: "Stylistic Issues",
description: "Enforce a maximum number of statements allowed in function blocks",
recommended: false,
url: "https://eslint.org/docs/rules/max-statements"
url: "https://eslint.org/docs/latest/rules/max-statements"
},

@@ -103,3 +102,3 @@

if (count > max) {
const name = lodash.upperFirst(astUtils.getFunctionNameWithKind(node));
const name = upperCaseFirst(astUtils.getFunctionNameWithKind(node));

@@ -132,2 +131,10 @@ context.report({

/*
* This rule does not apply to class static blocks, but we have to track them so
* that statements in them do not count as statements in the enclosing function.
*/
if (node.type === "StaticBlock") {
return;
}
if (ignoreTopLevelFunctions && functionStack.length === 0) {

@@ -158,2 +165,3 @@ topLevelFunctions.push({ node, count });

ArrowFunctionExpression: startFunction,
StaticBlock: startFunction,

@@ -165,2 +173,3 @@ BlockStatement: countStatements,

"ArrowFunctionExpression:exit": endFunction,
"StaticBlock:exit": endFunction,

@@ -167,0 +176,0 @@ "Program:exit"() {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -19,10 +20,39 @@ meta: {

docs: {
description: "enforce a particular style for multiline comments",
category: "Stylistic Issues",
description: "Enforce a particular style for multiline comments",
recommended: false,
url: "https://eslint.org/docs/rules/multiline-comment-style"
url: "https://eslint.org/docs/latest/rules/multiline-comment-style"
},
fixable: "whitespace",
schema: [{ enum: ["starred-block", "separate-lines", "bare-block"] }],
schema: {
anyOf: [
{
type: "array",
items: [
{
enum: ["starred-block", "bare-block"]
}
],
additionalItems: false
},
{
type: "array",
items: [
{
enum: ["separate-lines"]
},
{
type: "object",
properties: {
checkJSDoc: {
type: "boolean"
}
},
additionalProperties: false
}
],
additionalItems: false
}
]
},
messages: {

@@ -40,4 +70,6 @@ expectedBlock: "Expected a block comment instead of consecutive line comments.",

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const option = context.options[0] || "starred-block";
const params = context.options[1] || {};
const checkJSDoc = !!params.checkJSDoc;

@@ -339,7 +371,14 @@ //----------------------------------------------------------------------

if (firstComment.type !== "Block" || isJSDocComment(commentGroup)) {
const isJSDoc = isJSDocComment(commentGroup);
if (firstComment.type !== "Block" || (!checkJSDoc && isJSDoc)) {
return;
}
const commentLines = getCommentLines(commentGroup);
let commentLines = getCommentLines(commentGroup);
if (isJSDoc) {
commentLines = commentLines.slice(1, commentLines.length - 1);
}
const tokenAfter = sourceCode.getTokenAfter(firstComment, { includeComments: true });

@@ -346,0 +385,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -20,6 +21,5 @@ meta: {

docs: {
description: "enforce newlines between operands of ternary expressions",
category: "Stylistic Issues",
description: "Enforce newlines between operands of ternary expressions",
recommended: false,
url: "https://eslint.org/docs/rules/multiline-ternary"
url: "https://eslint.org/docs/latest/rules/multiline-ternary"
},

@@ -44,3 +44,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const option = context.options[0];

@@ -79,3 +79,3 @@ const multiline = option !== "never";

messageId: "unexpectedTestCons",
fix: fixer => {
fix(fixer) {
if (hasComments) {

@@ -108,3 +108,3 @@ return null;

messageId: "unexpectedConsAlt",
fix: fixer => {
fix(fixer) {
if (hasComments) {

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

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

* @param {string} key Object key to check on `obj`.
* @param {*} fallback If obj[key] is not present, this will be returned.
* @param {any} fallback If obj[key] is not present, this will be returned.
* @throws {TypeError} If key is not an own array type property of `obj`.
* @returns {string[]} Returns obj[key] if it's an Array, otherwise `fallback`

@@ -42,6 +43,6 @@ */

/* istanbul ignore if */
/* c8 ignore start */
if (Object.prototype.hasOwnProperty.call(obj, key) && !Array.isArray(obj[key])) {
throw new TypeError(`${key}, if provided, must be an Array`);
}
}/* c8 ignore stop */
return obj[key] || fallback;

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -86,6 +88,5 @@ meta: {

docs: {
description: "require constructor names to begin with a capital letter",
category: "Stylistic Issues",
description: "Require constructor names to begin with a capital letter",
recommended: false,
url: "https://eslint.org/docs/rules/new-cap"
url: "https://eslint.org/docs/latest/rules/new-cap"
},

@@ -153,3 +154,3 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -156,0 +157,0 @@ //--------------------------------------------------------------------------

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -28,23 +29,13 @@ meta: {

docs: {
description: "enforce or disallow parentheses when invoking a constructor with no arguments",
category: "Stylistic Issues",
description: "Enforce or disallow parentheses when invoking a constructor with no arguments",
recommended: false,
url: "https://eslint.org/docs/rules/new-parens"
url: "https://eslint.org/docs/latest/rules/new-parens"
},
fixable: "code",
schema: {
anyOf: [
{
type: "array",
items: [
{
enum: ["always", "never"]
}
],
minItems: 0,
maxItems: 1
}
]
},
schema: [
{
enum: ["always", "never"]
}
],
messages: {

@@ -60,3 +51,3 @@ missing: "Missing '()' invoking a constructor.",

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -63,0 +54,0 @@ return {

/**
* @fileoverview Rule to check empty newline after "var" statement
* @author Gopal Venkatesan
* @deprecated
* @deprecated in ESLint v4.0.0
*/

@@ -19,2 +19,3 @@

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -25,6 +26,5 @@ meta: {

docs: {
description: "require or disallow an empty line after variable declarations",
category: "Stylistic Issues",
description: "Require or disallow an empty line after variable declarations",
recommended: false,
url: "https://eslint.org/docs/rules/newline-after-var"
url: "https://eslint.org/docs/latest/rules/newline-after-var"
},

@@ -48,3 +48,3 @@ schema: [

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -151,5 +151,5 @@ // Default `mode` to "always".

* Determine if a token starts more than one line after a comment ends
* @param {token} token The token being checked
* @param {integer} commentStartLine The line number on which the comment starts
* @returns {boolean} True if `token` does not start immediately after a comment
* @param {token} token The token being checked
* @param {integer} commentStartLine The line number on which the comment starts
* @returns {boolean} True if `token` does not start immediately after a comment
*/

@@ -219,3 +219,2 @@ function hasBlankLineAfterComment(token, commentStartLine) {

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

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

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

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

/**
* @fileoverview Rule to require newlines before `return` statement
* @author Kai Cataldo
* @deprecated
* @deprecated in ESLint v4.0.0
*/

@@ -12,2 +12,3 @@ "use strict";

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "require an empty line before `return` statements",
category: "Stylistic Issues",
description: "Require an empty line before `return` statements",
recommended: false,
url: "https://eslint.org/docs/rules/newline-before-return"
url: "https://eslint.org/docs/latest/rules/newline-before-return"
},

@@ -36,3 +36,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

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

return testTokens.some(token => tokenBefore.value === token);
return testTokens.includes(tokenBefore.value);
}

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

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -21,6 +22,5 @@ meta: {

docs: {
description: "require a newline after each call in a method chain",
category: "Stylistic Issues",
description: "Require a newline after each call in a method chain",
recommended: false,
url: "https://eslint.org/docs/rules/newline-per-chained-call"
url: "https://eslint.org/docs/latest/rules/newline-per-chained-call"
},

@@ -52,3 +52,3 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -59,3 +59,3 @@ /**

* left bracket. If not it returns a period.
* @param {ASTNode} node A MemberExpression node to get
* @param {ASTNode} node A MemberExpression node to get
* @returns {string} The prefix of the node.

@@ -62,0 +62,0 @@ */

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -91,6 +92,5 @@ meta: {

docs: {
description: "disallow the use of `alert`, `confirm`, and `prompt`",
category: "Best Practices",
description: "Disallow the use of `alert`, `confirm`, and `prompt`",
recommended: false,
url: "https://eslint.org/docs/rules/no-alert"
url: "https://eslint.org/docs/latest/rules/no-alert"
},

@@ -106,6 +106,8 @@

create(context) {
const sourceCode = context.sourceCode;
return {
CallExpression(node) {
const callee = skipChainExpression(node.callee),
currentScope = context.getScope();
currentScope = sourceCode.getScope(node);

@@ -112,0 +114,0 @@ // without window.

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "disallow `Array` constructors",
category: "Stylistic Issues",
description: "Disallow `Array` constructors",
recommended: false,
url: "https://eslint.org/docs/rules/no-array-constructor"
url: "https://eslint.org/docs/latest/rules/no-array-constructor"
},

@@ -24,0 +24,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -17,6 +18,5 @@ meta: {

docs: {
description: "disallow using an async function as a Promise executor",
category: "Possible Errors",
description: "Disallow using an async function as a Promise executor",
recommended: true,
url: "https://eslint.org/docs/rules/no-async-promise-executor"
url: "https://eslint.org/docs/latest/rules/no-async-promise-executor"
},

@@ -35,3 +35,3 @@

context.report({
node: context.getSourceCode().getFirstToken(node.arguments[0], token => token.value === "async"),
node: context.sourceCode.getFirstToken(node.arguments[0], token => token.value === "async"),
messageId: "async"

@@ -38,0 +38,0 @@ });

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -62,6 +63,5 @@ meta: {

docs: {
description: "disallow `await` inside of loops",
category: "Possible Errors",
description: "Disallow `await` inside of loops",
recommended: false,
url: "https://eslint.org/docs/rules/no-await-in-loop"
url: "https://eslint.org/docs/latest/rules/no-await-in-loop"
},

@@ -68,0 +68,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -29,6 +30,5 @@ meta: {

docs: {
description: "disallow bitwise operators",
category: "Stylistic Issues",
description: "Disallow bitwise operators",
recommended: false,
url: "https://eslint.org/docs/rules/no-bitwise"
url: "https://eslint.org/docs/latest/rules/no-bitwise"
},

@@ -68,3 +68,3 @@

* Reports an unexpected use of a bitwise operator.
* @param {ASTNode} node Node which contains the bitwise operator.
* @param {ASTNode} node Node which contains the bitwise operator.
* @returns {void}

@@ -78,7 +78,7 @@ */

* Checks if the given node has a bitwise operator.
* @param {ASTNode} node The node to check.
* @param {ASTNode} node The node to check.
* @returns {boolean} Whether or not the node has a bitwise operator.
*/
function hasBitwiseOperator(node) {
return BITWISE_OPERATORS.indexOf(node.operator) !== -1;
return BITWISE_OPERATORS.includes(node.operator);
}

@@ -88,7 +88,7 @@

* Checks if exceptions were provided, e.g. `{ allow: ['~', '|'] }`.
* @param {ASTNode} node The node to check.
* @param {ASTNode} node The node to check.
* @returns {boolean} Whether or not the node has a bitwise operator.
*/
function allowedOperator(node) {
return allowed.indexOf(node.operator) !== -1;
return allowed.includes(node.operator);
}

@@ -98,3 +98,3 @@

* Checks if the given bitwise operator is used for integer typecasting, i.e. "|0"
* @param {ASTNode} node The node to check.
* @param {ASTNode} node The node to check.
* @returns {boolean} whether the node is used in integer typecasting.

@@ -109,3 +109,3 @@ */

* Report if the given node contains a bitwise operator.
* @param {ASTNode} node The node to check.
* @param {ASTNode} node The node to check.
* @returns {void}

@@ -112,0 +112,0 @@ */

/**
* @fileoverview disallow use of the Buffer() constructor
* @author Teddy Katz
* @deprecated in ESLint v7.0.0
*/

@@ -11,2 +12,3 @@ "use strict";

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -21,6 +23,5 @@ meta: {

docs: {
description: "disallow use of the `Buffer()` constructor",
category: "Node.js and CommonJS",
description: "Disallow use of the `Buffer()` constructor",
recommended: false,
url: "https://eslint.org/docs/rules/no-buffer-constructor"
url: "https://eslint.org/docs/latest/rules/no-buffer-constructor"
},

@@ -27,0 +28,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "disallow the use of `arguments.caller` or `arguments.callee`",
category: "Best Practices",
description: "Disallow the use of `arguments.caller` or `arguments.callee`",
recommended: false,
url: "https://eslint.org/docs/rules/no-caller"
url: "https://eslint.org/docs/latest/rules/no-caller"
},

@@ -24,0 +24,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -17,6 +18,5 @@ meta: {

docs: {
description: "disallow lexical declarations in case clauses",
category: "Best Practices",
description: "Disallow lexical declarations in case clauses",
recommended: true,
url: "https://eslint.org/docs/rules/no-case-declarations"
url: "https://eslint.org/docs/latest/rules/no-case-declarations"
},

@@ -23,0 +23,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -25,6 +26,5 @@ meta: {

docs: {
description: "disallow `catch` clause parameters from shadowing variables in the outer scope",
category: "Variables",
description: "Disallow `catch` clause parameters from shadowing variables in the outer scope",
recommended: false,
url: "https://eslint.org/docs/rules/no-catch-shadow"
url: "https://eslint.org/docs/latest/rules/no-catch-shadow"
},

@@ -44,2 +44,4 @@

const sourceCode = context.sourceCode;
//--------------------------------------------------------------------------

@@ -66,3 +68,3 @@ // Helpers

"CatchClause[param!=null]"(node) {
let scope = context.getScope();
let scope = sourceCode.getScope(node);

@@ -69,0 +71,0 @@ /*

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -20,6 +21,5 @@ meta: {

docs: {
description: "disallow reassigning class members",
category: "ECMAScript 6",
description: "Disallow reassigning class members",
recommended: true,
url: "https://eslint.org/docs/rules/no-class-assign"
url: "https://eslint.org/docs/latest/rules/no-class-assign"
},

@@ -36,2 +36,4 @@

const sourceCode = context.sourceCode;
/**

@@ -55,3 +57,3 @@ * Finds and reports references that are non initializer and writable.

function checkForClass(node) {
context.getDeclaredVariables(node).forEach(checkVariable);
sourceCode.getDeclaredVariables(node).forEach(checkVariable);
}

@@ -58,0 +60,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -17,6 +18,5 @@ meta: {

docs: {
description: "disallow comparing against -0",
category: "Possible Errors",
description: "Disallow comparing against -0",
recommended: true,
url: "https://eslint.org/docs/rules/no-compare-neg-zero"
url: "https://eslint.org/docs/latest/rules/no-compare-neg-zero"
},

@@ -23,0 +23,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -37,6 +38,5 @@ meta: {

docs: {
description: "disallow assignment operators in conditional expressions",
category: "Possible Errors",
description: "Disallow assignment operators in conditional expressions",
recommended: true,
url: "https://eslint.org/docs/rules/no-cond-assign"
url: "https://eslint.org/docs/latest/rules/no-cond-assign"
},

@@ -62,3 +62,3 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -65,0 +65,0 @@ /**

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -34,6 +35,5 @@ meta: {

docs: {
description: "disallow arrow functions where they could be confused with comparisons",
category: "ECMAScript 6",
description: "Disallow arrow functions where they could be confused with comparisons",
recommended: false,
url: "https://eslint.org/docs/rules/no-confusing-arrow"
url: "https://eslint.org/docs/latest/rules/no-confusing-arrow"
},

@@ -46,3 +46,4 @@

properties: {
allowParens: { type: "boolean", default: true }
allowParens: { type: "boolean", default: true },
onlyOneSimpleParam: { type: "boolean", default: false }
},

@@ -60,3 +61,4 @@ additionalProperties: false

const allowParens = config.allowParens || (config.allowParens === void 0);
const sourceCode = context.getSourceCode();
const onlyOneSimpleParam = config.onlyOneSimpleParam;
const sourceCode = context.sourceCode;

@@ -72,3 +74,5 @@

if (isConditional(body) && !(allowParens && astUtils.isParenthesised(sourceCode, body))) {
if (isConditional(body) &&
!(allowParens && astUtils.isParenthesised(sourceCode, body)) &&
!(onlyOneSimpleParam && !(node.params.length === 1 && node.params[0].type === "Identifier"))) {
context.report({

@@ -75,0 +79,0 @@ node,

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,6 +25,5 @@ meta: {

docs: {
description: "disallow the use of `console`",
category: "Possible Errors",
description: "Disallow the use of `console`",
recommended: false,
url: "https://eslint.org/docs/rules/no-console"
url: "https://eslint.org/docs/latest/rules/no-console"
},

@@ -56,2 +56,3 @@

const allowed = options.allow || [];
const sourceCode = context.sourceCode;

@@ -78,3 +79,3 @@ /**

return propertyName && allowed.indexOf(propertyName) !== -1;
return propertyName && allowed.includes(propertyName);
}

@@ -116,4 +117,4 @@

return {
"Program:exit"() {
const scope = context.getScope();
"Program:exit"(node) {
const scope = sourceCode.getScope(node);
const consoleVar = astUtils.getVariableByName(scope, "console");

@@ -120,0 +121,0 @@ const shadowed = consoleVar && consoleVar.defs.length > 0;

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -20,6 +21,5 @@ meta: {

docs: {
description: "disallow reassigning `const` variables",
category: "ECMAScript 6",
description: "Disallow reassigning `const` variables",
recommended: true,
url: "https://eslint.org/docs/rules/no-const-assign"
url: "https://eslint.org/docs/latest/rules/no-const-assign"
},

@@ -36,2 +36,4 @@

const sourceCode = context.sourceCode;
/**

@@ -51,3 +53,3 @@ * Finds and reports references that are non initializer and writable.

if (node.kind === "const") {
context.getDeclaredVariables(node).forEach(checkVariable);
sourceCode.getDeclaredVariables(node).forEach(checkVariable);
}

@@ -54,0 +56,0 @@ }

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

const { isConstant } = require("./utils/ast-utils");
//------------------------------------------------------------------------------

@@ -17,2 +19,3 @@ // Helpers

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -23,6 +26,5 @@ meta: {

docs: {
description: "disallow constant expressions in conditions",
category: "Possible Errors",
description: "Disallow constant expressions in conditions",
recommended: true,
url: "https://eslint.org/docs/rules/no-constant-condition"
url: "https://eslint.org/docs/latest/rules/no-constant-condition"
},

@@ -52,2 +54,3 @@

loopSetStack = [];
const sourceCode = context.sourceCode;

@@ -61,149 +64,2 @@ let loopsInCurrentScope = new Set();

/**
* Returns literal's value converted to the Boolean type
* @param {ASTNode} node any `Literal` node
* @returns {boolean | null} `true` when node is truthy, `false` when node is falsy,
* `null` when it cannot be determined.
*/
function getBooleanValue(node) {
if (node.value === null) {
/*
* it might be a null literal or bigint/regex literal in unsupported environments .
* https://github.com/estree/estree/blob/14df8a024956ea289bd55b9c2226a1d5b8a473ee/es5.md#regexpliteral
* https://github.com/estree/estree/blob/14df8a024956ea289bd55b9c2226a1d5b8a473ee/es2020.md#bigintliteral
*/
if (node.raw === "null") {
return false;
}
// regex is always truthy
if (typeof node.regex === "object") {
return true;
}
return null;
}
return !!node.value;
}
/**
* Checks if a branch node of LogicalExpression short circuits the whole condition
* @param {ASTNode} node The branch of main condition which needs to be checked
* @param {string} operator The operator of the main LogicalExpression.
* @returns {boolean} true when condition short circuits whole condition
*/
function isLogicalIdentity(node, operator) {
switch (node.type) {
case "Literal":
return (operator === "||" && getBooleanValue(node) === true) ||
(operator === "&&" && getBooleanValue(node) === false);
case "UnaryExpression":
return (operator === "&&" && node.operator === "void");
case "LogicalExpression":
/*
* handles `a && false || b`
* `false` is an identity element of `&&` but not `||`
*/
return operator === node.operator &&
(
isLogicalIdentity(node.left, operator) ||
isLogicalIdentity(node.right, operator)
);
case "AssignmentExpression":
return ["||=", "&&="].includes(node.operator) &&
operator === node.operator.slice(0, -1) &&
isLogicalIdentity(node.right, operator);
// no default
}
return false;
}
/**
* Checks if a node has a constant truthiness value.
* @param {ASTNode} node The AST node to check.
* @param {boolean} inBooleanPosition `false` if checking branch of a condition.
* `true` in all other cases
* @returns {Bool} true when node's truthiness is constant
* @private
*/
function isConstant(node, inBooleanPosition) {
// node.elements can return null values in the case of sparse arrays ex. [,]
if (!node) {
return true;
}
switch (node.type) {
case "Literal":
case "ArrowFunctionExpression":
case "FunctionExpression":
case "ObjectExpression":
return true;
case "TemplateLiteral":
return (inBooleanPosition && node.quasis.some(quasi => quasi.value.cooked.length)) ||
node.expressions.every(exp => isConstant(exp, inBooleanPosition));
case "ArrayExpression": {
if (node.parent.type === "BinaryExpression" && node.parent.operator === "+") {
return node.elements.every(element => isConstant(element, false));
}
return true;
}
case "UnaryExpression":
if (
node.operator === "void" ||
node.operator === "typeof" && inBooleanPosition
) {
return true;
}
if (node.operator === "!") {
return isConstant(node.argument, true);
}
return isConstant(node.argument, false);
case "BinaryExpression":
return isConstant(node.left, false) &&
isConstant(node.right, false) &&
node.operator !== "in";
case "LogicalExpression": {
const isLeftConstant = isConstant(node.left, inBooleanPosition);
const isRightConstant = isConstant(node.right, inBooleanPosition);
const isLeftShortCircuit = (isLeftConstant && isLogicalIdentity(node.left, node.operator));
const isRightShortCircuit = (inBooleanPosition && isRightConstant && isLogicalIdentity(node.right, node.operator));
return (isLeftConstant && isRightConstant) ||
isLeftShortCircuit ||
isRightShortCircuit;
}
case "AssignmentExpression":
if (node.operator === "=") {
return isConstant(node.right, inBooleanPosition);
}
if (["||=", "&&="].includes(node.operator) && inBooleanPosition) {
return isLogicalIdentity(node.right, node.operator.slice(0, -1));
}
return false;
case "SequenceExpression":
return isConstant(node.expressions[node.expressions.length - 1], inBooleanPosition);
// no default
}
return false;
}
/**
* Tracks when the given node contains a constant condition.

@@ -215,3 +71,3 @@ * @param {ASTNode} node The AST node to check.

function trackConstantConditionLoop(node) {
if (node.test && isConstant(node.test, true)) {
if (node.test && isConstant(sourceCode.getScope(node), node.test, true)) {
loopsInCurrentScope.add(node);

@@ -241,3 +97,3 @@ }

function reportIfConstant(node) {
if (node.test && isConstant(node.test, true)) {
if (node.test && isConstant(sourceCode.getScope(node), node.test, true)) {
context.report({ node: node.test, messageId: "unexpected" });

@@ -244,0 +100,0 @@ }

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "disallow returning value from constructor",
category: "Best Practices",
description: "Disallow returning value from constructor",
recommended: false,
url: "https://eslint.org/docs/rules/no-constructor-return"
url: "https://eslint.org/docs/latest/rules/no-constructor-return"
},

@@ -24,0 +24,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "disallow `continue` statements",
category: "Stylistic Issues",
description: "Disallow `continue` statements",
recommended: false,
url: "https://eslint.org/docs/rules/no-continue"
url: "https://eslint.org/docs/latest/rules/no-continue"
},

@@ -24,0 +24,0 @@

@@ -8,3 +8,3 @@ /**

const RegExpValidator = require("regexpp").RegExpValidator;
const RegExpValidator = require("@eslint-community/regexpp").RegExpValidator;
const collector = new (class {

@@ -18,2 +18,12 @@ constructor() {

onPatternEnter() {
/*
* `RegExpValidator` may parse the pattern twice in one `validatePattern`.
* So `this._controlChars` should be cleared here as well.
*
* For example, the `/(?<a>\x1f)/` regex will parse the pattern twice.
* This is based on the content described in Annex B.
* If the regex contains a `GroupName` and the `u` flag is not used, `ParseText` will be called twice.
* See https://tc39.es/ecma262/2023/multipage/additional-ecmascript-features-for-web-browsers.html#sec-parsepattern-annexb
*/
this._controlChars = [];

@@ -35,6 +45,11 @@ }

collectControlChars(regexpStr) {
collectControlChars(regexpStr, flags) {
const uFlag = typeof flags === "string" && flags.includes("u");
const vFlag = typeof flags === "string" && flags.includes("v");
this._controlChars = [];
this._source = regexpStr;
try {
this._source = regexpStr;
this._validator.validatePattern(regexpStr); // Call onCharacter hook
this._validator.validatePattern(regexpStr, void 0, void 0, { unicode: uFlag, unicodeSets: vFlag }); // Call onCharacter hook
} catch {

@@ -52,2 +67,3 @@

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -58,6 +74,5 @@ meta: {

docs: {
description: "disallow control characters in regular expressions",
category: "Possible Errors",
description: "Disallow control characters in regular expressions",
recommended: true,
url: "https://eslint.org/docs/rules/no-control-regex"
url: "https://eslint.org/docs/latest/rules/no-control-regex"
},

@@ -76,9 +91,11 @@

* Get the regex expression
* @param {ASTNode} node node to evaluate
* @returns {RegExp|null} Regex if found else null
* @param {ASTNode} node `Literal` node to evaluate
* @returns {{ pattern: string, flags: string | null } | null} Regex if found (the given node is either a regex literal
* or a string literal that is the pattern argument of a RegExp constructor call). Otherwise `null`. If flags cannot be determined,
* the `flags` property will be `null`.
* @private
*/
function getRegExpPattern(node) {
function getRegExp(node) {
if (node.regex) {
return node.regex.pattern;
return node.regex;
}

@@ -91,3 +108,11 @@ if (typeof node.value === "string" &&

) {
return node.value;
const pattern = node.value;
const flags =
node.parent.arguments.length > 1 &&
node.parent.arguments[1].type === "Literal" &&
typeof node.parent.arguments[1].value === "string"
? node.parent.arguments[1].value
: null;
return { pattern, flags };
}

@@ -100,6 +125,7 @@

Literal(node) {
const pattern = getRegExpPattern(node);
const regExp = getRegExp(node);
if (pattern) {
const controlCharacters = collector.collectControlChars(pattern);
if (regExp) {
const { pattern, flags } = regExp;
const controlCharacters = collector.collectControlChars(pattern, flags);

@@ -106,0 +132,0 @@ if (controlCharacters.length > 0) {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "disallow the use of `debugger`",
category: "Possible Errors",
description: "Disallow the use of `debugger`",
recommended: true,
url: "https://eslint.org/docs/rules/no-debugger"
url: "https://eslint.org/docs/latest/rules/no-debugger"
},

@@ -24,0 +24,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "disallow deleting variables",
category: "Variables",
description: "Disallow deleting variables",
recommended: true,
url: "https://eslint.org/docs/rules/no-delete-var"
url: "https://eslint.org/docs/latest/rules/no-delete-var"
},

@@ -24,0 +24,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "disallow division operators explicitly at the beginning of regular expressions",
category: "Best Practices",
description: "Disallow equal signs explicitly at the beginning of regular expressions",
recommended: false,
url: "https://eslint.org/docs/rules/no-div-regex"
url: "https://eslint.org/docs/latest/rules/no-div-regex"
},

@@ -35,3 +35,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -38,0 +38,0 @@ return {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "disallow duplicate arguments in `function` definitions",
category: "Possible Errors",
description: "Disallow duplicate arguments in `function` definitions",
recommended: true,
url: "https://eslint.org/docs/rules/no-dupe-args"
url: "https://eslint.org/docs/latest/rules/no-dupe-args"
},

@@ -34,2 +34,4 @@

const sourceCode = context.sourceCode;
//--------------------------------------------------------------------------

@@ -55,3 +57,3 @@ // Helpers

function checkParams(node) {
const variables = context.getDeclaredVariables(node);
const variables = sourceCode.getDeclaredVariables(node);

@@ -58,0 +60,0 @@ for (let i = 0; i < variables.length; ++i) {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -20,6 +21,5 @@ meta: {

docs: {
description: "disallow duplicate class members",
category: "ECMAScript 6",
description: "Disallow duplicate class members",
recommended: true,
url: "https://eslint.org/docs/rules/no-dupe-class-members"
url: "https://eslint.org/docs/latest/rules/no-dupe-class-members"
},

@@ -78,6 +78,7 @@

// Reports the node if its name has been declared already.
MethodDefinition(node) {
"MethodDefinition, PropertyDefinition"(node) {
const name = astUtils.getStaticPropertyName(node);
const kind = node.type === "MethodDefinition" ? node.kind : "field";
if (name === null || node.kind === "constructor") {
if (name === null || kind === "constructor") {
return;

@@ -89,6 +90,6 @@ }

if (node.kind === "get") {
if (kind === "get") {
isDuplicate = (state.init || state.get);
state.get = true;
} else if (node.kind === "set") {
} else if (kind === "set") {
isDuplicate = (state.init || state.set);

@@ -95,0 +96,0 @@ state.set = true;

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -55,6 +56,5 @@ meta: {

docs: {
description: "disallow duplicate conditions in if-else-if chains",
category: "Possible Errors",
description: "Disallow duplicate conditions in if-else-if chains",
recommended: true,
url: "https://eslint.org/docs/rules/no-dupe-else-if"
url: "https://eslint.org/docs/latest/rules/no-dupe-else-if"
},

@@ -70,3 +70,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -73,0 +73,0 @@ /**

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

// eslint-disable-next-line jsdoc/require-description
/**

@@ -87,2 +86,3 @@ * @param {ObjectInfo|null} upper The information of the outer object.

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -93,6 +93,5 @@ meta: {

docs: {
description: "disallow duplicate keys in object literals",
category: "Possible Errors",
description: "Disallow duplicate keys in object literals",
recommended: true,
url: "https://eslint.org/docs/rules/no-dupe-keys"
url: "https://eslint.org/docs/latest/rules/no-dupe-keys"
},

@@ -99,0 +98,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -25,6 +26,5 @@ meta: {

docs: {
description: "disallow duplicate case labels",
category: "Possible Errors",
description: "Disallow duplicate case labels",
recommended: true,
url: "https://eslint.org/docs/rules/no-duplicate-case"
url: "https://eslint.org/docs/latest/rules/no-duplicate-case"
},

@@ -40,3 +40,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -43,0 +43,0 @@ /**

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

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
const NAMED_TYPES = ["ImportSpecifier", "ExportSpecifier"];
const NAMESPACE_TYPES = [
"ImportNamespaceSpecifier",
"ExportNamespaceSpecifier"
];
//------------------------------------------------------------------------------
// Rule Definition

@@ -13,83 +23,206 @@ //------------------------------------------------------------------------------

/**
* Returns the name of the module imported or re-exported.
* Check if an import/export type belongs to (ImportSpecifier|ExportSpecifier) or (ImportNamespaceSpecifier|ExportNamespaceSpecifier).
* @param {string} importExportType An import/export type to check.
* @param {string} type Can be "named" or "namespace"
* @returns {boolean} True if import/export type belongs to (ImportSpecifier|ExportSpecifier) or (ImportNamespaceSpecifier|ExportNamespaceSpecifier) and false if it doesn't.
*/
function isImportExportSpecifier(importExportType, type) {
const arrayToCheck = type === "named" ? NAMED_TYPES : NAMESPACE_TYPES;
return arrayToCheck.includes(importExportType);
}
/**
* Return the type of (import|export).
* @param {ASTNode} node A node to get.
* @returns {string} the name of the module, or empty string if no name.
* @returns {string} The type of the (import|export).
*/
function getValue(node) {
if (node && node.source && node.source.value) {
return node.source.value.trim();
function getImportExportType(node) {
if (node.specifiers && node.specifiers.length > 0) {
const nodeSpecifiers = node.specifiers;
const index = nodeSpecifiers.findIndex(
({ type }) =>
isImportExportSpecifier(type, "named") ||
isImportExportSpecifier(type, "namespace")
);
const i = index > -1 ? index : 0;
return nodeSpecifiers[i].type;
}
if (node.type === "ExportAllDeclaration") {
if (node.exported) {
return "ExportNamespaceSpecifier";
}
return "ExportAll";
}
return "SideEffectImport";
}
return "";
/**
* Returns a boolean indicates if two (import|export) can be merged
* @param {ASTNode} node1 A node to check.
* @param {ASTNode} node2 A node to check.
* @returns {boolean} True if two (import|export) can be merged, false if they can't.
*/
function isImportExportCanBeMerged(node1, node2) {
const importExportType1 = getImportExportType(node1);
const importExportType2 = getImportExportType(node2);
if (
(importExportType1 === "ExportAll" &&
importExportType2 !== "ExportAll" &&
importExportType2 !== "SideEffectImport") ||
(importExportType1 !== "ExportAll" &&
importExportType1 !== "SideEffectImport" &&
importExportType2 === "ExportAll")
) {
return false;
}
if (
(isImportExportSpecifier(importExportType1, "namespace") &&
isImportExportSpecifier(importExportType2, "named")) ||
(isImportExportSpecifier(importExportType2, "namespace") &&
isImportExportSpecifier(importExportType1, "named"))
) {
return false;
}
return true;
}
/**
* Checks if the name of the import or export exists in the given array, and reports if so.
* @param {RuleContext} context The ESLint rule context object.
* @param {ASTNode} node A node to get.
* @param {string} value The name of the imported or exported module.
* @param {string[]} array The array containing other imports or exports in the file.
* @param {string} messageId A messageId to be reported after the name of the module
*
* @returns {void} No return value
* Returns a boolean if we should report (import|export).
* @param {ASTNode} node A node to be reported or not.
* @param {[ASTNode]} previousNodes An array contains previous nodes of the module imported or exported.
* @returns {boolean} True if the (import|export) should be reported.
*/
function checkAndReport(context, node, value, array, messageId) {
if (array.indexOf(value) !== -1) {
context.report({
node,
messageId,
data: {
module: value
}
});
function shouldReportImportExport(node, previousNodes) {
let i = 0;
while (i < previousNodes.length) {
if (isImportExportCanBeMerged(node, previousNodes[i])) {
return true;
}
i++;
}
return false;
}
/**
* @callback nodeCallback
* @param {ASTNode} node A node to handle.
* Returns array contains only nodes with declarations types equal to type.
* @param {[{node: ASTNode, declarationType: string}]} nodes An array contains objects, each object contains a node and a declaration type.
* @param {string} type Declaration type.
* @returns {[ASTNode]} An array contains only nodes with declarations types equal to type.
*/
function getNodesByDeclarationType(nodes, type) {
return nodes
.filter(({ declarationType }) => declarationType === type)
.map(({ node }) => node);
}
/**
* Returns a function handling the imports of a given file
* Returns the name of the module imported or re-exported.
* @param {ASTNode} node A node to get.
* @returns {string} The name of the module, or empty string if no name.
*/
function getModule(node) {
if (node && node.source && node.source.value) {
return node.source.value.trim();
}
return "";
}
/**
* Checks if the (import|export) can be merged with at least one import or one export, and reports if so.
* @param {RuleContext} context The ESLint rule context object.
* @param {ASTNode} node A node to get.
* @param {Map} modules A Map object contains as a key a module name and as value an array contains objects, each object contains a node and a declaration type.
* @param {string} declarationType A declaration type can be an import or export.
* @param {boolean} includeExports Whether or not to check for exports in addition to imports.
* @param {string[]} importsInFile The array containing other imports in the file.
* @param {string[]} exportsInFile The array containing other exports in the file.
*
* @returns {nodeCallback} A function passed to ESLint to handle the statement.
* @returns {void} No return value.
*/
function handleImports(context, includeExports, importsInFile, exportsInFile) {
return function(node) {
const value = getValue(node);
function checkAndReport(
context,
node,
modules,
declarationType,
includeExports
) {
const module = getModule(node);
if (value) {
checkAndReport(context, node, value, importsInFile, "import");
if (modules.has(module)) {
const previousNodes = modules.get(module);
const messagesIds = [];
const importNodes = getNodesByDeclarationType(previousNodes, "import");
let exportNodes;
if (includeExports) {
exportNodes = getNodesByDeclarationType(previousNodes, "export");
}
if (declarationType === "import") {
if (shouldReportImportExport(node, importNodes)) {
messagesIds.push("import");
}
if (includeExports) {
checkAndReport(context, node, value, exportsInFile, "importAs");
if (shouldReportImportExport(node, exportNodes)) {
messagesIds.push("importAs");
}
}
importsInFile.push(value);
} else if (declarationType === "export") {
if (shouldReportImportExport(node, exportNodes)) {
messagesIds.push("export");
}
if (shouldReportImportExport(node, importNodes)) {
messagesIds.push("exportAs");
}
}
};
messagesIds.forEach(messageId =>
context.report({
node,
messageId,
data: {
module
}
}));
}
}
/**
* Returns a function handling the exports of a given file
* @callback nodeCallback
* @param {ASTNode} node A node to handle.
*/
/**
* Returns a function handling the (imports|exports) of a given file
* @param {RuleContext} context The ESLint rule context object.
* @param {string[]} importsInFile The array containing other imports in the file.
* @param {string[]} exportsInFile The array containing other exports in the file.
*
* @param {Map} modules A Map object contains as a key a module name and as value an array contains objects, each object contains a node and a declaration type.
* @param {string} declarationType A declaration type can be an import or export.
* @param {boolean} includeExports Whether or not to check for exports in addition to imports.
* @returns {nodeCallback} A function passed to ESLint to handle the statement.
*/
function handleExports(context, importsInFile, exportsInFile) {
function handleImportsExports(
context,
modules,
declarationType,
includeExports
) {
return function(node) {
const value = getValue(node);
const module = getModule(node);
if (value) {
checkAndReport(context, node, value, exportsInFile, "export");
checkAndReport(context, node, value, importsInFile, "exportAs");
if (module) {
checkAndReport(
context,
node,
modules,
declarationType,
includeExports
);
const currentNode = { node, declarationType };
let nodes = [currentNode];
exportsInFile.push(value);
if (modules.has(module)) {
const previousNodes = modules.get(module);
nodes = [...previousNodes, currentNode];
}
modules.set(module, nodes);
}

@@ -99,2 +232,3 @@ };

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -105,18 +239,20 @@ meta: {

docs: {
description: "disallow duplicate module imports",
category: "ECMAScript 6",
description: "Disallow duplicate module imports",
recommended: false,
url: "https://eslint.org/docs/rules/no-duplicate-imports"
url: "https://eslint.org/docs/latest/rules/no-duplicate-imports"
},
schema: [{
type: "object",
properties: {
includeExports: {
type: "boolean",
default: false
}
},
additionalProperties: false
}],
schema: [
{
type: "object",
properties: {
includeExports: {
type: "boolean",
default: false
}
},
additionalProperties: false
}
],
messages: {

@@ -132,16 +268,28 @@ import: "'{{module}}' import is duplicated.",

const includeExports = (context.options[0] || {}).includeExports,
importsInFile = [],
exportsInFile = [];
modules = new Map();
const handlers = {
ImportDeclaration: handleImports(context, includeExports, importsInFile, exportsInFile)
ImportDeclaration: handleImportsExports(
context,
modules,
"import",
includeExports
)
};
if (includeExports) {
handlers.ExportNamedDeclaration = handleExports(context, importsInFile, exportsInFile);
handlers.ExportAllDeclaration = handleExports(context, importsInFile, exportsInFile);
handlers.ExportNamedDeclaration = handleImportsExports(
context,
modules,
"export",
includeExports
);
handlers.ExportAllDeclaration = handleImportsExports(
context,
modules,
"export",
includeExports
);
}
return handlers;
}
};

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -25,6 +26,5 @@ meta: {

docs: {
description: "disallow `else` blocks after `return` statements in `if` statements",
category: "Best Practices",
description: "Disallow `else` blocks after `return` statements in `if` statements",
recommended: false,
url: "https://eslint.org/docs/rules/no-else-return"
url: "https://eslint.org/docs/latest/rules/no-else-return"
},

@@ -52,2 +52,4 @@

const sourceCode = context.sourceCode;
//--------------------------------------------------------------------------

@@ -175,21 +177,20 @@ // Helpers

* Display the context report if rule is violated
* @param {Node} node The 'else' node
* @param {Node} elseNode The 'else' node
* @returns {void}
*/
function displayReport(node) {
const currentScope = context.getScope();
function displayReport(elseNode) {
const currentScope = sourceCode.getScope(elseNode.parent);
context.report({
node,
node: elseNode,
messageId: "unexpected",
fix: fixer => {
fix(fixer) {
if (!isSafeFromNameCollisions(node, currentScope)) {
if (!isSafeFromNameCollisions(elseNode, currentScope)) {
return null;
}
const sourceCode = context.getSourceCode();
const startToken = sourceCode.getFirstToken(node);
const startToken = sourceCode.getFirstToken(elseNode);
const elseToken = sourceCode.getTokenBefore(startToken);
const source = sourceCode.getText(node);
const source = sourceCode.getText(elseNode);
const lastIfToken = sourceCode.getTokenBefore(elseToken);

@@ -210,3 +211,3 @@ let fixedSource, firstTokenOfElseBlock;

*/
const ifBlockMaybeUnsafe = node.parent.consequent.type !== "BlockStatement" && lastIfToken.value !== ";";
const ifBlockMaybeUnsafe = elseNode.parent.consequent.type !== "BlockStatement" && lastIfToken.value !== ";";
const elseBlockUnsafe = /^[([/+`-]/u.test(firstTokenOfElseBlock.value);

@@ -218,3 +219,3 @@

const endToken = sourceCode.getLastToken(node);
const endToken = sourceCode.getLastToken(elseNode);
const lastTokenOfElseBlock = sourceCode.getTokenBefore(endToken);

@@ -253,4 +254,4 @@

return new FixTracker(fixer, sourceCode)
.retainEnclosingFunction(node)
.replaceTextRange([elseToken.range[0], node.range[1]], fixedSource);
.retainEnclosingFunction(elseNode)
.replaceTextRange([elseToken.range[0], elseNode.range[1]], fixedSource);
}

@@ -257,0 +258,0 @@ });

@@ -9,18 +9,13 @@ /**

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const { RegExpParser, visitRegExpAST } = require("@eslint-community/regexpp");
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
/*
* plain-English description of the following regexp:
* 0. `^` fix the match at the beginning of the string
* 1. `\/`: the `/` that begins the regexp
* 2. `([^\\[]|\\.|\[([^\\\]]|\\.)+\])*`: regexp contents; 0 or more of the following
* 2.0. `[^\\[]`: any character that's not a `\` or a `[` (anything but escape sequences and character classes)
* 2.1. `\\.`: an escape sequence
* 2.2. `\[([^\\\]]|\\.)+\]`: a character class that isn't empty
* 3. `\/` the `/` that ends the regexp
* 4. `[gimuy]*`: optional regexp flags
* 5. `$`: fix the match at the end of the string
*/
const regex = /^\/([^\\[]|\\.|\[([^\\\]]|\\.)+\])*\/[gimuys]*$/u;
const parser = new RegExpParser();
const QUICK_TEST_REGEX = /\[\]/u;

@@ -31,2 +26,3 @@ //------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -37,6 +33,5 @@ meta: {

docs: {
description: "disallow empty character classes in regular expressions",
category: "Possible Errors",
description: "Disallow empty character classes in regular expressions",
recommended: true,
url: "https://eslint.org/docs/rules/no-empty-character-class"
url: "https://eslint.org/docs/latest/rules/no-empty-character-class"
},

@@ -52,14 +47,31 @@

create(context) {
const sourceCode = context.getSourceCode();
return {
"Literal[regex]"(node) {
const { pattern, flags } = node.regex;
Literal(node) {
const token = sourceCode.getFirstToken(node);
if (!QUICK_TEST_REGEX.test(pattern)) {
return;
}
if (token.type === "RegularExpression" && !regex.test(token.value)) {
context.report({ node, messageId: "unexpected" });
let regExpAST;
try {
regExpAST = parser.parsePattern(pattern, 0, pattern.length, {
unicode: flags.includes("u"),
unicodeSets: flags.includes("v")
});
} catch {
// Ignore regular expressions that regexpp cannot parse
return;
}
visitRegExpAST(regExpAST, {
onCharacterClassEnter(characterClass) {
if (!characterClass.negate && characterClass.elements.length === 0) {
context.report({ node, messageId: "unexpected" });
}
}
});
}
};

@@ -66,0 +78,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -98,6 +99,5 @@ meta: {

docs: {
description: "disallow empty functions",
category: "Best Practices",
description: "Disallow empty functions",
recommended: false,
url: "https://eslint.org/docs/rules/no-empty-function"
url: "https://eslint.org/docs/latest/rules/no-empty-function"
},

@@ -128,3 +128,3 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -150,3 +150,3 @@ /**

if (allowed.indexOf(kind) === -1 &&
if (!allowed.includes(kind) &&
node.body.type === "BlockStatement" &&

@@ -153,0 +153,0 @@ node.body.body.length === 0 &&

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

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

@@ -12,2 +14,3 @@ // Rule Definition

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,9 +21,19 @@ meta: {

docs: {
description: "disallow empty destructuring patterns",
category: "Best Practices",
description: "Disallow empty destructuring patterns",
recommended: true,
url: "https://eslint.org/docs/rules/no-empty-pattern"
url: "https://eslint.org/docs/latest/rules/no-empty-pattern"
},
schema: [],
schema: [
{
type: "object",
properties: {
allowObjectPatternsAsParameters: {
type: "boolean",
default: false
}
},
additionalProperties: false
}
],

@@ -33,7 +46,29 @@ messages: {

create(context) {
const options = context.options[0] || {},
allowObjectPatternsAsParameters = options.allowObjectPatternsAsParameters || false;
return {
ObjectPattern(node) {
if (node.properties.length === 0) {
context.report({ node, messageId: "unexpected", data: { type: "object" } });
if (node.properties.length > 0) {
return;
}
// Allow {} and {} = {} empty object patterns as parameters when allowObjectPatternsAsParameters is true
if (
allowObjectPatternsAsParameters &&
(
astUtils.isFunction(node.parent) ||
(
node.parent.type === "AssignmentPattern" &&
astUtils.isFunction(node.parent.parent) &&
node.parent.right.type === "ObjectExpression" &&
node.parent.right.properties.length === 0
)
)
) {
return;
}
context.report({ node, messageId: "unexpected", data: { type: "object" } });
},

@@ -40,0 +75,0 @@ ArrayPattern(node) {

@@ -17,11 +17,12 @@ /**

/** @type {import('../shared/types').Rule} */
module.exports = {
meta: {
hasSuggestions: true,
type: "suggestion",
docs: {
description: "disallow empty block statements",
category: "Possible Errors",
description: "Disallow empty block statements",
recommended: true,
url: "https://eslint.org/docs/rules/no-empty"
url: "https://eslint.org/docs/latest/rules/no-empty"
},

@@ -43,3 +44,4 @@

messages: {
unexpected: "Empty {{type}} statement."
unexpected: "Empty {{type}} statement.",
suggestComment: "Add comment inside empty {{type}} statement."
}

@@ -52,3 +54,3 @@ },

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -77,3 +79,18 @@ return {

context.report({ node, messageId: "unexpected", data: { type: "block" } });
context.report({
node,
messageId: "unexpected",
data: { type: "block" },
suggest: [
{
messageId: "suggestComment",
data: { type: "block" },
fix(fixer) {
const range = [node.range[0] + 1, node.range[1] - 1];
return fixer.replaceTextRange(range, " /* empty */ ");
}
}
]
});
},

@@ -80,0 +97,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -19,6 +20,5 @@ meta: {

docs: {
description: "disallow `null` comparisons without type-checking operators",
category: "Best Practices",
description: "Disallow `null` comparisons without type-checking operators",
recommended: false,
url: "https://eslint.org/docs/rules/no-eq-null"
url: "https://eslint.org/docs/latest/rules/no-eq-null"
},

@@ -25,0 +25,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -46,6 +47,5 @@ meta: {

docs: {
description: "disallow the use of `eval()`",
category: "Best Practices",
description: "Disallow the use of `eval()`",
recommended: false,
url: "https://eslint.org/docs/rules/no-eval"
url: "https://eslint.org/docs/latest/rules/no-eval"
},

@@ -73,16 +73,19 @@

);
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
let funcInfo = null;
/**
* Pushs a variable scope (Program or Function) information to the stack.
* Pushes a `this` scope (non-arrow function, class static block, or class field initializer) information to the stack.
* Top-level scopes are handled separately.
*
* This is used in order to check whether or not `this` binding is a
* reference to the global object.
* @param {ASTNode} node A node of the scope. This is one of Program,
* FunctionDeclaration, FunctionExpression, and ArrowFunctionExpression.
* @param {ASTNode} node A node of the scope.
* For functions, this is one of FunctionDeclaration, FunctionExpression.
* For class static blocks, this is StaticBlock.
* For class field initializers, this can be any node that is PropertyDefinition#value.
* @returns {void}
*/
function enterVarScope(node) {
const strict = context.getScope().isStrict;
function enterThisScope(node) {
const strict = sourceCode.getScope(node).isStrict;

@@ -93,2 +96,3 @@ funcInfo = {

strict,
isTopLevelOfScript: false,
defaultThis: false,

@@ -103,3 +107,3 @@ initialized: strict

*/
function exitVarScope() {
function exitThisScope() {
funcInfo = funcInfo.upper;

@@ -225,3 +229,3 @@ }

Program(node) {
const scope = context.getScope(),
const scope = sourceCode.getScope(node),
features = context.parserOptions.ecmaFeatures || {},

@@ -231,3 +235,4 @@ strict =

node.sourceType === "module" ||
(features.globalReturn && scope.childScopes[0].isStrict);
(features.globalReturn && scope.childScopes[0].isStrict),
isTopLevelOfScript = node.sourceType !== "module" && !features.globalReturn;

@@ -238,2 +243,3 @@ funcInfo = {

strict,
isTopLevelOfScript,
defaultThis: true,

@@ -244,6 +250,6 @@ initialized: true

"Program:exit"() {
const globalScope = context.getScope();
"Program:exit"(node) {
const globalScope = sourceCode.getScope(node);
exitVarScope();
exitThisScope();
reportAccessingEval(globalScope);

@@ -253,8 +259,10 @@ reportAccessingEvalViaGlobalObject(globalScope);

FunctionDeclaration: enterVarScope,
"FunctionDeclaration:exit": exitVarScope,
FunctionExpression: enterVarScope,
"FunctionExpression:exit": exitVarScope,
ArrowFunctionExpression: enterVarScope,
"ArrowFunctionExpression:exit": exitVarScope,
FunctionDeclaration: enterThisScope,
"FunctionDeclaration:exit": exitThisScope,
FunctionExpression: enterThisScope,
"FunctionExpression:exit": exitThisScope,
"PropertyDefinition > *.value": enterThisScope,
"PropertyDefinition > *.value:exit": exitThisScope,
StaticBlock: enterThisScope,
"StaticBlock:exit": exitThisScope,

@@ -278,3 +286,4 @@ ThisExpression(node) {

if (!funcInfo.strict && funcInfo.defaultThis) {
// `this` at the top level of scripts always refers to the global object
if (funcInfo.isTopLevelOfScript || (!funcInfo.strict && funcInfo.defaultThis)) {

@@ -281,0 +290,0 @@ // `this.eval` is possible built-in `eval`.

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -20,6 +21,5 @@ meta: {

docs: {
description: "disallow reassigning exceptions in `catch` clauses",
category: "Possible Errors",
description: "Disallow reassigning exceptions in `catch` clauses",
recommended: true,
url: "https://eslint.org/docs/rules/no-ex-assign"
url: "https://eslint.org/docs/latest/rules/no-ex-assign"
},

@@ -36,2 +36,4 @@

const sourceCode = context.sourceCode;
/**

@@ -50,3 +52,3 @@ * Finds and reports references that are non initializer and writable.

CatchClause(node) {
context.getDeclaredVariables(node).forEach(checkVariable);
sourceCode.getDeclaredVariables(node).forEach(checkVariable);
}

@@ -53,0 +55,0 @@ };

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -25,6 +26,5 @@ meta: {

docs: {
description: "disallow extending native types",
category: "Best Practices",
description: "Disallow extending native types",
recommended: false,
url: "https://eslint.org/docs/rules/no-extend-native"
url: "https://eslint.org/docs/latest/rules/no-extend-native"
},

@@ -56,2 +56,3 @@

const config = context.options[0] || {};
const sourceCode = context.sourceCode;
const exceptions = new Set(config.exceptions || []);

@@ -165,4 +166,4 @@ const modifiedBuiltins = new Set(

"Program:exit"() {
const globalScope = context.getScope();
"Program:exit"(node) {
const globalScope = sourceCode.getScope(node);

@@ -169,0 +170,0 @@ modifiedBuiltins.forEach(builtin => {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -29,6 +30,5 @@ meta: {

docs: {
description: "disallow unnecessary calls to `.bind()`",
category: "Best Practices",
description: "Disallow unnecessary calls to `.bind()`",
recommended: false,
url: "https://eslint.org/docs/rules/no-extra-bind"
url: "https://eslint.org/docs/latest/rules/no-extra-bind"
},

@@ -45,3 +45,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
let scopeInfo = null;

@@ -48,0 +48,0 @@

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

const astUtils = require("./utils/ast-utils");
const eslintUtils = require("eslint-utils");
const eslintUtils = require("@eslint-community/eslint-utils");

@@ -22,2 +22,3 @@ const precedence = astUtils.getPrecedence;

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -28,6 +29,5 @@ meta: {

docs: {
description: "disallow unnecessary boolean casts",
category: "Possible Errors",
description: "Disallow unnecessary boolean casts",
recommended: true,
url: "https://eslint.org/docs/rules/no-extra-boolean-cast"
url: "https://eslint.org/docs/latest/rules/no-extra-boolean-cast"
},

@@ -54,6 +54,6 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
// Node types which have a test which will coerce values to booleans.
const BOOLEAN_NODE_TYPES = [
const BOOLEAN_NODE_TYPES = new Set([
"IfStatement",

@@ -64,3 +64,3 @@ "DoWhileStatement",

"ForStatement"
];
]);

@@ -103,3 +103,3 @@ /**

(BOOLEAN_NODE_TYPES.indexOf(node.parent.type) !== -1 &&
(BOOLEAN_NODE_TYPES.has(node.parent.type) &&
node === node.parent.test) ||

@@ -159,2 +159,3 @@

* @param {ASTNode} node The node to check.
* @throws {Error} (Unreachable.)
* @returns {boolean} `true` if the node needs to be parenthesized.

@@ -197,3 +198,3 @@ */

/* istanbul ignore next */
/* c8 ignore next */
default:

@@ -200,0 +201,0 @@ throw new Error(`Unexpected parent type: ${parent.type}`);

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,6 +25,5 @@ meta: {

docs: {
description: "disallow unnecessary labels",
category: "Best Practices",
description: "Disallow unnecessary labels",
recommended: false,
url: "https://eslint.org/docs/rules/no-extra-label"
url: "https://eslint.org/docs/latest/rules/no-extra-label"
},

@@ -40,3 +40,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
let scopeInfo = null;

@@ -43,0 +43,0 @@

@@ -11,5 +11,6 @@ /**

const { isParenthesized: isParenthesizedRaw } = require("eslint-utils");
const { isParenthesized: isParenthesizedRaw } = require("@eslint-community/eslint-utils");
const astUtils = require("./utils/ast-utils.js");
/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -20,6 +21,5 @@ meta: {

docs: {
description: "disallow unnecessary parentheses",
category: "Possible Errors",
description: "Disallow unnecessary parentheses",
recommended: false,
url: "https://eslint.org/docs/rules/no-extra-parens"
url: "https://eslint.org/docs/latest/rules/no-extra-parens"
},

@@ -51,2 +51,3 @@

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

@@ -58,3 +59,4 @@ returnAssign: { type: "boolean" },

enforceForNewInMemberExpressions: { type: "boolean" },
enforceForFunctionPrototypeMethods: { type: "boolean" }
enforceForFunctionPrototypeMethods: { type: "boolean" },
allowParensAfterCommentPattern: { type: "string" }
},

@@ -76,3 +78,3 @@ additionalProperties: false

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -83,2 +85,3 @@ const tokensToIgnore = new WeakSet();

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;

@@ -95,2 +98,3 @@ const EXCEPT_RETURN_ASSIGN = ALL_NODES && context.options[1] && context.options[1].returnAssign === false;

context.options[1].enforceForFunctionPrototypeMethods === false;
const ALLOW_PARENS_AFTER_COMMENT_PATTERN = ALL_NODES && context.options[1] && context.options[1].allowParensAfterCommentPattern;

@@ -395,2 +399,26 @@ const PRECEDENCE_OF_ASSIGNMENT_EXPR = precedence({ type: "AssignmentExpression" });

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

@@ -413,2 +441,15 @@ * @param {ASTNode} node node to evaluate

}
if (ALLOW_PARENS_AFTER_COMMENT_PATTERN) {
const commentsBeforeLeftParenToken = sourceCode.getCommentsBefore(leftParenToken);
const totalCommentsBeforeLeftParenTokenCount = commentsBeforeLeftParenToken.length;
const ignorePattern = new RegExp(ALLOW_PARENS_AFTER_COMMENT_PATTERN, "u");
if (
totalCommentsBeforeLeftParenTokenCount > 0 &&
ignorePattern.test(commentsBeforeLeftParenToken[totalCommentsBeforeLeftParenTokenCount - 1].value)
) {
return;
}
}
}

@@ -426,10 +467,12 @@

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
});

@@ -647,6 +690,6 @@ }

/* istanbul ignore if */
/* c8 ignore start */
if (currentNode === null) {
throw new Error("Nodes are not in the ancestor-descendant relationship.");
}
}/* c8 ignore stop */

@@ -765,2 +808,34 @@ path.push(currentNode);

/**
* Checks if the left-hand side of an assignment is an identifier, the operator is one of
* `=`, `&&=`, `||=` or `??=` and the right-hand side is an anonymous class or function.
*
* As per https://tc39.es/ecma262/#sec-assignment-operators-runtime-semantics-evaluation, an
* assignment involving one of the operators `=`, `&&=`, `||=` or `??=` where the right-hand
* side is an anonymous class or function and the left-hand side is an *unparenthesized*
* identifier has different semantics than other assignments.
* Specifically, when an expression like `foo = function () {}` is evaluated, `foo.name`
* will be set to the string "foo", i.e. the identifier name. The same thing does not happen
* when evaluating `(foo) = function () {}`.
* Since the parenthesizing of the identifier in the left-hand side is significant in this
* special case, the parentheses, if present, should not be flagged as unnecessary.
* @param {ASTNode} node an AssignmentExpression node.
* @returns {boolean} `true` if the left-hand side of the assignment is an identifier, the
* operator is one of `=`, `&&=`, `||=` or `??=` and the right-hand side is an anonymous
* class or function; otherwise, `false`.
*/
function isAnonymousFunctionAssignmentException({ left, operator, right }) {
if (left.type === "Identifier" && ["=", "&&=", "||=", "??="].includes(operator)) {
const rhsType = right.type;
if (rhsType === "ArrowFunctionExpression") {
return true;
}
if ((rhsType === "FunctionExpression" || rhsType === "ClassExpression") && !right.id) {
return true;
}
}
return false;
}
return {

@@ -804,3 +879,4 @@ ArrayExpression(node) {

AssignmentExpression(node) {
if (canBeAssignmentTarget(node.left) && hasExcessParens(node.left)) {
if (canBeAssignmentTarget(node.left) && hasExcessParens(node.left) &&
(!isAnonymousFunctionAssignmentException(node) || isParenthesisedTwice(node.left))) {
report(node.left);

@@ -824,9 +900,2 @@ }

ClassBody(node) {
node.body
.filter(member => member.type === "MethodDefinition" && member.computed && member.key)
.filter(member => hasExcessParensWithPrecedence(member.key, PRECEDENCE_OF_ASSIGNMENT_EXPR))
.forEach(member => report(member.key));
},
ConditionalExpression(node) {

@@ -836,3 +905,7 @@ if (isReturnAssignException(node)) {

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

@@ -844,7 +917,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);

@@ -1082,2 +1159,8 @@ }

"MethodDefinition[computed=true]"(node) {
if (hasExcessParensWithPrecedence(node.key, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
report(node.key);
}
},
NewExpression: checkCallNew,

@@ -1110,2 +1193,12 @@

PropertyDefinition(node) {
if (node.computed && hasExcessParensWithPrecedence(node.key, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
report(node.key);
}
if (node.value && hasExcessParensWithPrecedence(node.value, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
report(node.value);
}
},
RestElement(node) {

@@ -1112,0 +1205,0 @@ const argument = node.argument;

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -25,6 +26,5 @@ meta: {

docs: {
description: "disallow unnecessary semicolons",
category: "Possible Errors",
description: "Disallow unnecessary semicolons",
recommended: true,
url: "https://eslint.org/docs/rules/no-extra-semi"
url: "https://eslint.org/docs/latest/rules/no-extra-semi"
},

@@ -41,5 +41,22 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
/**
* 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.

@@ -53,13 +70,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.getSourceCode())
.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
});

@@ -105,3 +123,3 @@ }

if (allowedParentTypes.indexOf(parent.type) === -1) {
if (!allowedParentTypes.includes(parent.type)) {
report(node);

@@ -125,3 +143,3 @@ }

*/
MethodDefinition(node) {
"MethodDefinition, PropertyDefinition, StaticBlock"(node) {
checkForPartOfClassBody(sourceCode.getTokenAfter(node));

@@ -128,0 +146,0 @@ }

@@ -11,3 +11,3 @@ /**

const lodash = require("lodash");
const { directivesPattern } = require("../shared/directives");

@@ -21,25 +21,53 @@ //------------------------------------------------------------------------------

/**
* Checks whether or not a given node has a fallthrough comment.
* @param {ASTNode} node A SwitchCase node to get comments.
* @param {RuleContext} context A rule context which stores comments.
* @param {RegExp} fallthroughCommentPattern A pattern to match comment to.
* @returns {boolean} `true` if the node has a valid fallthrough comment.
* Checks all segments in a set and returns true if any are reachable.
* @param {Set<CodePathSegment>} segments The segments to check.
* @returns {boolean} True if any segment is reachable; false otherwise.
*/
function hasFallthroughComment(node, context, fallthroughCommentPattern) {
const sourceCode = context.getSourceCode();
const comment = lodash.last(sourceCode.getCommentsBefore(node));
function isAnySegmentReachable(segments) {
return Boolean(comment && fallthroughCommentPattern.test(comment.value));
for (const segment of segments) {
if (segment.reachable) {
return true;
}
}
return false;
}
/**
* Checks whether or not a given code path segment is reachable.
* @param {CodePathSegment} segment A CodePathSegment to check.
* @returns {boolean} `true` if the segment is reachable.
* Checks whether or not a given comment string is really a fallthrough comment and not an ESLint directive.
* @param {string} comment The comment string to check.
* @param {RegExp} fallthroughCommentPattern The regular expression used for checking for fallthrough comments.
* @returns {boolean} `true` if the comment string is truly a fallthrough comment.
*/
function isReachable(segment) {
return segment.reachable;
function isFallThroughComment(comment, fallthroughCommentPattern) {
return fallthroughCommentPattern.test(comment) && !directivesPattern.test(comment.trim());
}
/**
* Checks whether or not a given case has a fallthrough comment.
* @param {ASTNode} caseWhichFallsThrough SwitchCase node which falls through.
* @param {ASTNode} subsequentCase The case after caseWhichFallsThrough.
* @param {RuleContext} context A rule context which stores comments.
* @param {RegExp} fallthroughCommentPattern A pattern to match comment to.
* @returns {boolean} `true` if the case has a valid fallthrough comment.
*/
function hasFallthroughComment(caseWhichFallsThrough, subsequentCase, context, fallthroughCommentPattern) {
const sourceCode = context.sourceCode;
if (caseWhichFallsThrough.consequent.length === 1 && caseWhichFallsThrough.consequent[0].type === "BlockStatement") {
const trailingCloseBrace = sourceCode.getLastToken(caseWhichFallsThrough.consequent[0]);
const commentInBlock = sourceCode.getCommentsBefore(trailingCloseBrace).pop();
if (commentInBlock && isFallThroughComment(commentInBlock.value, fallthroughCommentPattern)) {
return true;
}
}
const comment = sourceCode.getCommentsBefore(subsequentCase).pop();
return Boolean(comment && isFallThroughComment(comment.value, fallthroughCommentPattern));
}
/**
* Checks whether a node and a token are separated by blank lines

@@ -58,2 +86,3 @@ * @param {ASTNode} node The node to check

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -64,6 +93,5 @@ meta: {

docs: {
description: "disallow fallthrough of `case` statements",
category: "Best Practices",
description: "Disallow fallthrough of `case` statements",
recommended: true,
url: "https://eslint.org/docs/rules/no-fallthrough"
url: "https://eslint.org/docs/latest/rules/no-fallthrough"
},

@@ -78,2 +106,6 @@

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

@@ -92,4 +124,6 @@ },

const options = context.options[0] || {};
let currentCodePath = null;
const sourceCode = context.getSourceCode();
const codePathSegments = [];
let currentCodePathSegments = new Set();
const sourceCode = context.sourceCode;
const allowEmptyCase = options.allowEmptyCase || false;

@@ -108,11 +142,30 @@ /*

}
return {
return {
onCodePathStart(codePath) {
currentCodePath = codePath;
onCodePathStart() {
codePathSegments.push(currentCodePathSegments);
currentCodePathSegments = new Set();
},
onCodePathEnd() {
currentCodePath = currentCodePath.upper;
currentCodePathSegments = codePathSegments.pop();
},
onUnreachableCodePathSegmentStart(segment) {
currentCodePathSegments.add(segment);
},
onUnreachableCodePathSegmentEnd(segment) {
currentCodePathSegments.delete(segment);
},
onCodePathSegmentStart(segment) {
currentCodePathSegments.add(segment);
},
onCodePathSegmentEnd(segment) {
currentCodePathSegments.delete(segment);
},
SwitchCase(node) {

@@ -124,3 +177,4 @@

*/
if (fallthroughCase && !hasFallthroughComment(node, context, fallthroughCommentPattern)) {
if (fallthroughCase && (!hasFallthroughComment(fallthroughCase, node, context, fallthroughCommentPattern))) {
context.report({

@@ -142,5 +196,5 @@ messageId: node.test ? "case" : "default",

*/
if (currentCodePath.currentSegments.some(isReachable) &&
(node.consequent.length > 0 || hasBlankLinesBetween(node, nextToken)) &&
lodash.last(node.parent.cases) !== node) {
if (isAnySegmentReachable(currentCodePathSegments) &&
(node.consequent.length > 0 || (!allowEmptyCase && hasBlankLinesBetween(node, nextToken))) &&
node.parent.cases[node.parent.cases.length - 1] !== node) {
fallthroughCase = node;

@@ -147,0 +201,0 @@ }

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,6 +25,5 @@ meta: {

docs: {
description: "disallow leading or trailing decimal points in numeric literals",
category: "Best Practices",
description: "Disallow leading or trailing decimal points in numeric literals",
recommended: false,
url: "https://eslint.org/docs/rules/no-floating-decimal"
url: "https://eslint.org/docs/latest/rules/no-floating-decimal"
},

@@ -40,3 +40,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -43,0 +43,0 @@ return {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -20,6 +21,5 @@ meta: {

docs: {
description: "disallow reassigning `function` declarations",
category: "Possible Errors",
description: "Disallow reassigning `function` declarations",
recommended: true,
url: "https://eslint.org/docs/rules/no-func-assign"
url: "https://eslint.org/docs/latest/rules/no-func-assign"
},

@@ -36,2 +36,4 @@

const sourceCode = context.sourceCode;
/**

@@ -71,3 +73,3 @@ * Reports a reference if is non initializer and writable.

function checkForFunction(node) {
context.getDeclaredVariables(node).forEach(checkVariable);
sourceCode.getDeclaredVariables(node).forEach(checkVariable);
}

@@ -74,0 +76,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "disallow assignments to native objects or read-only global variables",
category: "Best Practices",
description: "Disallow assignments to native objects or read-only global variables",
recommended: true,
url: "https://eslint.org/docs/rules/no-global-assign"
url: "https://eslint.org/docs/latest/rules/no-global-assign"
},

@@ -46,2 +46,3 @@

const config = context.options[0];
const sourceCode = context.sourceCode;
const exceptions = (config && config.exceptions) || [];

@@ -84,3 +85,3 @@

function checkVariable(variable) {
if (variable.writeable === false && exceptions.indexOf(variable.name) === -1) {
if (variable.writeable === false && !exceptions.includes(variable.name)) {
variable.references.forEach(checkReference);

@@ -91,4 +92,4 @@ }

return {
Program() {
const globalScope = context.getScope();
Program(node) {
const globalScope = sourceCode.getScope(node);

@@ -95,0 +96,0 @@ globalScope.variables.forEach(checkVariable);

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

string: "string" in options ? options.string : true,
disallowTemplateShorthand: "disallowTemplateShorthand" in options ? options.disallowTemplateShorthand : false,
allow: options.allow || []

@@ -33,5 +34,5 @@ };

/**
* Checks whether or not a node is a double logical nigating.
* Checks whether or not a node is a double logical negating.
* @param {ASTNode} node An UnaryExpression node to check.
* @returns {boolean} Whether or not the node is a double logical nigating.
* @returns {boolean} Whether or not the node is a double logical negating.
*/

@@ -76,2 +77,20 @@ function isDoubleLogicalNegating(node) {

/**
* Checks whether the given node logically represents multiplication by a fraction of `1`.
* For example, `a * 1` in `a * 1 / b` is technically multiplication by `1`, but the
* whole expression can be logically interpreted as `a * (1 / b)` rather than `(a * 1) / b`.
* @param {BinaryExpression} node A BinaryExpression node to check.
* @param {SourceCode} sourceCode The source code object.
* @returns {boolean} Whether or not the node is a multiplying by a fraction of `1`.
*/
function isMultiplyByFractionOfOne(node, sourceCode) {
return node.type === "BinaryExpression" &&
node.operator === "*" &&
(node.right.type === "Literal" && node.right.value === 1) &&
node.parent.type === "BinaryExpression" &&
node.parent.operator === "/" &&
node.parent.left === node &&
!astUtils.isParenthesised(sourceCode, node);
}
/**
* Checks whether the result of a node is numeric or not

@@ -115,2 +134,16 @@ * @param {ASTNode} node The node to test

/**
* Checks whether an expression evaluates to a string.
* @param {ASTNode} node node that represents the expression to check.
* @returns {boolean} Whether or not the expression evaluates to a string.
*/
function isStringType(node) {
return astUtils.isStringLiteral(node) ||
(
node.type === "CallExpression" &&
node.callee.type === "Identifier" &&
node.callee.name === "String"
);
}
/**
* Checks whether a node is an empty string literal or not.

@@ -132,4 +165,4 @@ * @param {ASTNode} node The node to check.

return node.operator === "+" && (
(isEmptyString(node.left) && !astUtils.isStringLiteral(node.right)) ||
(isEmptyString(node.right) && !astUtils.isStringLiteral(node.left))
(isEmptyString(node.left) && !isStringType(node.right)) ||
(isEmptyString(node.right) && !isStringType(node.left))
);

@@ -160,2 +193,3 @@ }

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -166,6 +200,5 @@ meta: {

docs: {
description: "disallow shorthand type conversions",
category: "Best Practices",
description: "Disallow shorthand type conversions",
recommended: false,
url: "https://eslint.org/docs/rules/no-implicit-coercion"
url: "https://eslint.org/docs/latest/rules/no-implicit-coercion"
},

@@ -190,2 +223,6 @@

},
disallowTemplateShorthand: {
type: "boolean",
default: false
},
allow: {

@@ -209,3 +246,3 @@ type: "array",

const options = parseOptions(context.options[0] || {});
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -250,3 +287,3 @@ /**

// !!foo
operatorAllowed = options.allow.indexOf("!!") >= 0;
operatorAllowed = options.allow.includes("!!");
if (!operatorAllowed && options.boolean && isDoubleLogicalNegating(node)) {

@@ -259,3 +296,3 @@ const recommendation = `Boolean(${sourceCode.getText(node.argument.argument)})`;

// ~foo.indexOf(bar)
operatorAllowed = options.allow.indexOf("~") >= 0;
operatorAllowed = options.allow.includes("~");
if (!operatorAllowed && options.boolean && isBinaryNegatingOfIndexOf(node)) {

@@ -271,3 +308,3 @@

// +foo
operatorAllowed = options.allow.indexOf("+") >= 0;
operatorAllowed = options.allow.includes("+");
if (!operatorAllowed && options.number && node.operator === "+" && !isNumeric(node.argument)) {

@@ -285,4 +322,5 @@ const recommendation = `Number(${sourceCode.getText(node.argument)})`;

// 1 * foo
operatorAllowed = options.allow.indexOf("*") >= 0;
const nonNumericOperand = !operatorAllowed && options.number && isMultiplyByOne(node) && getNonNumericOperand(node);
operatorAllowed = options.allow.includes("*");
const nonNumericOperand = !operatorAllowed && options.number && isMultiplyByOne(node) && !isMultiplyByFractionOfOne(node, sourceCode) &&
getNonNumericOperand(node);

@@ -296,3 +334,3 @@ if (nonNumericOperand) {

// "" + foo
operatorAllowed = options.allow.indexOf("+") >= 0;
operatorAllowed = options.allow.includes("+");
if (!operatorAllowed && options.string && isConcatWithEmptyString(node)) {

@@ -308,3 +346,3 @@ const recommendation = `String(${sourceCode.getText(getNonEmptyOperand(node))})`;

// foo += ""
const operatorAllowed = options.allow.indexOf("+") >= 0;
const operatorAllowed = options.allow.includes("+");

@@ -317,2 +355,39 @@ if (!operatorAllowed && options.string && isAppendEmptyString(node)) {

}
},
TemplateLiteral(node) {
if (!options.disallowTemplateShorthand) {
return;
}
// tag`${foo}`
if (node.parent.type === "TaggedTemplateExpression") {
return;
}
// `` or `${foo}${bar}`
if (node.expressions.length !== 1) {
return;
}
// `prefix${foo}`
if (node.quasis[0].value.cooked !== "") {
return;
}
// `${foo}postfix`
if (node.quasis[1].value.cooked !== "") {
return;
}
// if the expression is already a string, then this isn't a coercion
if (isStringType(node.expressions[0])) {
return;
}
const code = sourceCode.getText(node.expressions[0]);
const recommendation = `String(${code})`;
report(node, recommendation, true);
}

@@ -319,0 +394,0 @@ };

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "disallow declarations in the global scope",
category: "Best Practices",
description: "Disallow declarations in the global scope",
recommended: false,
url: "https://eslint.org/docs/rules/no-implicit-globals"
url: "https://eslint.org/docs/latest/rules/no-implicit-globals"
},

@@ -48,2 +48,3 @@

const checkLexicalBindings = context.options[0] && context.options[0].lexicalBindings === true;
const sourceCode = context.sourceCode;

@@ -68,4 +69,4 @@ /**

return {
Program() {
const scope = context.getScope();
Program(node) {
const scope = sourceCode.getScope(node);

@@ -84,2 +85,7 @@ scope.variables.forEach(variable => {

// Variables exported by "exported" block comments
if (variable.eslintExported) {
return;
}
variable.defs.forEach(def => {

@@ -86,0 +92,0 @@ const defNode = def.node;

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

const astUtils = require("./utils/ast-utils");
const { getStaticValue } = require("eslint-utils");
const { getStaticValue } = require("@eslint-community/eslint-utils");

@@ -20,2 +20,3 @@ //------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -26,6 +27,5 @@ meta: {

docs: {
description: "disallow the use of `eval()`-like methods",
category: "Best Practices",
description: "Disallow the use of `eval()`-like methods",
recommended: false,
url: "https://eslint.org/docs/rules/no-implied-eval"
url: "https://eslint.org/docs/latest/rules/no-implied-eval"
},

@@ -43,2 +43,3 @@

const EVAL_LIKE_FUNC_PATTERN = /^(?:set(?:Interval|Timeout)|execScript)$/u;
const sourceCode = context.sourceCode;

@@ -73,3 +74,3 @@ /**

const staticValue = getStaticValue(firstArgument, context.getScope());
const staticValue = getStaticValue(firstArgument, sourceCode.getScope(node));
const isStaticString = staticValue && typeof staticValue.value === "string";

@@ -125,4 +126,4 @@ const isString = isStaticString || isEvaluatedString(firstArgument);

},
"Program:exit"() {
const globalScope = context.getScope();
"Program:exit"(node) {
const globalScope = sourceCode.getScope(node);

@@ -129,0 +130,0 @@ GLOBAL_CANDIDATES

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

const { findVariable } = require("eslint-utils");
const { findVariable } = require("@eslint-community/eslint-utils");
const astUtils = require("./utils/ast-utils");

@@ -178,2 +178,3 @@

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -184,6 +185,5 @@ meta: {

docs: {
description: "disallow assigning to imported bindings",
category: "Possible Errors",
description: "Disallow assigning to imported bindings",
recommended: true,
url: "https://eslint.org/docs/rules/no-import-assign"
url: "https://eslint.org/docs/latest/rules/no-import-assign"
},

@@ -200,7 +200,9 @@

create(context) {
const sourceCode = context.sourceCode;
return {
ImportDeclaration(node) {
const scope = context.getScope();
const scope = sourceCode.getScope(node);
for (const variable of context.getDeclaredVariables(node)) {
for (const variable of sourceCode.getDeclaredVariables(node)) {
const shouldCheckMembers = variable.defs.some(

@@ -207,0 +209,0 @@ d => d.node.type === "ImportNamespaceSpecifier"

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -19,6 +20,5 @@ meta: {

docs: {
description: "disallow inline comments after code",
category: "Stylistic Issues",
description: "Disallow inline comments after code",
recommended: false,
url: "https://eslint.org/docs/rules/no-inline-comments"
url: "https://eslint.org/docs/latest/rules/no-inline-comments"
},

@@ -44,3 +44,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const options = context.options[0];

@@ -47,0 +47,0 @@ let customIgnoreRegExp;

@@ -18,5 +18,30 @@ /**

const validParent = new Set(["Program", "ExportNamedDeclaration", "ExportDefaultDeclaration"]);
const validParent = new Set(["Program", "StaticBlock", "ExportNamedDeclaration", "ExportDefaultDeclaration"]);
const validBlockStatementParent = new Set(["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"]);
/**
* Finds the nearest enclosing context where this rule allows declarations and returns its description.
* @param {ASTNode} node Node to search from.
* @returns {string} Description. One of "program", "function body", "class static block body".
*/
function getAllowedBodyDescription(node) {
let { parent } = node;
while (parent) {
if (parent.type === "StaticBlock") {
return "class static block body";
}
if (astUtils.isFunction(parent)) {
return "function body";
}
({ parent } = parent);
}
return "program";
}
/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -27,6 +52,5 @@ meta: {

docs: {
description: "disallow variable or `function` declarations in nested blocks",
category: "Possible Errors",
description: "Disallow variable or `function` declarations in nested blocks",
recommended: true,
url: "https://eslint.org/docs/rules/no-inner-declarations"
url: "https://eslint.org/docs/latest/rules/no-inner-declarations"
},

@@ -65,4 +89,2 @@

const upperFunction = astUtils.getUpperFunction(parent);
context.report({

@@ -73,3 +95,3 @@ node,

type: (node.type === "FunctionDeclaration" ? "function" : "variable"),
body: (upperFunction === null ? "program" : "function body")
body: getAllowedBodyDescription(node)
}

@@ -76,0 +98,0 @@ });

@@ -11,5 +11,5 @@ /**

const RegExpValidator = require("regexpp").RegExpValidator;
const RegExpValidator = require("@eslint-community/regexpp").RegExpValidator;
const validator = new RegExpValidator();
const validFlags = /[gimuys]/gu;
const validFlags = /[dgimsuvy]/gu;
const undefined1 = void 0;

@@ -21,2 +21,3 @@

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -27,6 +28,5 @@ meta: {

docs: {
description: "disallow invalid regular expression strings in `RegExp` constructors",
category: "Possible Errors",
description: "Disallow invalid regular expression strings in `RegExp` constructors",
recommended: true,
url: "https://eslint.org/docs/rules/no-invalid-regexp"
url: "https://eslint.org/docs/latest/rules/no-invalid-regexp"
},

@@ -66,2 +66,16 @@

/**
* Reports error with the provided message.
* @param {ASTNode} node The node holding the invalid RegExp
* @param {string} message The message to report.
* @returns {void}
*/
function report(node, message) {
context.report({
node,
messageId: "regexMessage",
data: { message }
});
}
/**
* Check if node is a string

@@ -101,8 +115,10 @@ * @param {ASTNode} node node to evaluate

* @param {string} pattern The RegExp pattern to validate.
* @param {boolean} uFlag The Unicode flag.
* @param {Object} flags The RegExp flags to validate.
* @param {boolean} [flags.unicode] The Unicode flag.
* @param {boolean} [flags.unicodeSets] The UnicodeSets flag.
* @returns {string|null} The syntax error.
*/
function validateRegExpPattern(pattern, uFlag) {
function validateRegExpPattern(pattern, flags) {
try {
validator.validatePattern(pattern, undefined1, undefined1, uFlag);
validator.validatePattern(pattern, undefined1, undefined1, flags);
return null;

@@ -116,12 +132,24 @@ } catch (err) {

* Check syntax error in a given flags.
* @param {string} flags The RegExp flags to validate.
* @param {string|null} flags The RegExp flags to validate.
* @returns {string|null} The syntax error.
*/
function validateRegExpFlags(flags) {
if (!flags) {
return null;
}
try {
validator.validateFlags(flags);
return null;
} catch {
return `Invalid flags supplied to RegExp constructor '${flags}'`;
}
/*
* `regexpp` checks the combination of `u` and `v` flags when parsing `Pattern` according to `ecma262`,
* but this rule may check only the flag when the pattern is unidentifiable, so check it here.
* https://tc39.es/ecma262/multipage/text-processing.html#sec-parsepattern
*/
if (flags.includes("u") && flags.includes("v")) {
return "Regex 'u' and 'v' flags cannot be used together";
}
return null;
}

@@ -131,6 +159,6 @@

"CallExpression, NewExpression"(node) {
if (node.callee.type !== "Identifier" || node.callee.name !== "RegExp" || !isString(node.arguments[0])) {
if (node.callee.type !== "Identifier" || node.callee.name !== "RegExp") {
return;
}
const pattern = node.arguments[0].value;
let flags = getFlags(node);

@@ -142,20 +170,29 @@

const message =
(
flags && validateRegExpFlags(flags)
) ||
(
let message = validateRegExpFlags(flags);
// If flags are unknown, report the regex only if its pattern is invalid both with and without the "u" flag
flags === null
? validateRegExpPattern(pattern, true) && validateRegExpPattern(pattern, false)
: validateRegExpPattern(pattern, flags.includes("u"))
);
if (message) {
report(node, message);
return;
}
if (!isString(node.arguments[0])) {
return;
}
const pattern = node.arguments[0].value;
message = (
// If flags are unknown, report the regex only if its pattern is invalid both with and without the "u" flag
flags === null
? (
validateRegExpPattern(pattern, { unicode: true, unicodeSets: false }) &&
validateRegExpPattern(pattern, { unicode: false, unicodeSets: true }) &&
validateRegExpPattern(pattern, { unicode: false, unicodeSets: false })
)
: validateRegExpPattern(pattern, { unicode: flags.includes("u"), unicodeSets: flags.includes("v") })
);
if (message) {
context.report({
node,
messageId: "regexMessage",
data: { message }
});
report(node, message);
}

@@ -162,0 +199,0 @@ }

/**
* @fileoverview A rule to disallow `this` keywords outside of classes or class-like objects.
* @fileoverview A rule to disallow `this` keywords in contexts where the value of `this` is `undefined`.
* @author Toru Nagashima

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

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
/**
* Determines if the given code path is a code path with lexical `this` binding.
* That is, if `this` within the code path refers to `this` of surrounding code path.
* @param {CodePath} codePath Code path.
* @param {ASTNode} node Node that started the code path.
* @returns {boolean} `true` if it is a code path with lexical `this` binding.
*/
function isCodePathWithLexicalThis(codePath, node) {
return codePath.origin === "function" && node.type === "ArrowFunctionExpression";
}
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,6 +40,5 @@ meta: {

docs: {
description: "disallow `this` keywords outside of classes or class-like objects",
category: "Best Practices",
description: "Disallow use of `this` in contexts where the value of `this` is `undefined`",
recommended: false,
url: "https://eslint.org/docs/rules/no-invalid-this"
url: "https://eslint.org/docs/latest/rules/no-invalid-this"
},

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

const stack = [],
sourceCode = context.getSourceCode();
sourceCode = context.sourceCode;

@@ -78,59 +93,49 @@ /**

/**
* Pushs new checking context into the stack.
*
* The checking context is not initialized yet.
* Because most functions don't have `this` keyword.
* When `this` keyword was found, the checking context is initialized.
* @param {ASTNode} node A function node that was entered.
* @returns {void}
*/
function enterFunction(node) {
return {
// `this` can be invalid only under strict mode.
stack.push({
init: !context.getScope().isStrict,
node,
valid: true
});
}
onCodePathStart(codePath, node) {
if (isCodePathWithLexicalThis(codePath, node)) {
return;
}
/**
* Pops the current checking context from the stack.
* @returns {void}
*/
function exitFunction() {
stack.pop();
}
if (codePath.origin === "program") {
const scope = sourceCode.getScope(node);
const features = context.parserOptions.ecmaFeatures || {};
return {
// `this` at the top level of scripts always refers to the global object
stack.push({
init: true,
node,
valid: !(
node.sourceType === "module" ||
(features.globalReturn && scope.childScopes[0].isStrict)
)
});
/*
* `this` is invalid only under strict mode.
* Modules is always strict mode.
*/
Program(node) {
const scope = context.getScope(),
features = context.parserOptions.ecmaFeatures || {};
return;
}
/*
* `init: false` means that `valid` isn't determined yet.
* Most functions don't use `this`, and the calculation for `valid`
* is relatively costly, so we'll calculate it lazily when the first
* `this` within the function is traversed. A special case are non-strict
* functions, because `this` refers to the global object and therefore is
* always valid, so we can set `init: true` right away.
*/
stack.push({
init: true,
init: !sourceCode.getScope(node).isStrict,
node,
valid: !(
scope.isStrict ||
node.sourceType === "module" ||
(features.globalReturn && scope.childScopes[0].isStrict)
)
valid: true
});
},
"Program:exit"() {
onCodePathEnd(codePath, node) {
if (isCodePathWithLexicalThis(codePath, node)) {
return;
}
stack.pop();
},
FunctionDeclaration: enterFunction,
"FunctionDeclaration:exit": exitFunction,
FunctionExpression: enterFunction,
"FunctionExpression:exit": exitFunction,
// Reports if `this` of the current context is invalid.

@@ -137,0 +142,0 @@ ThisExpression(node) {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -34,6 +35,5 @@ meta: {

docs: {
description: "disallow irregular whitespace",
category: "Possible Errors",
description: "Disallow irregular whitespace",
recommended: true,
url: "https://eslint.org/docs/rules/no-irregular-whitespace"
url: "https://eslint.org/docs/latest/rules/no-irregular-whitespace"
},

@@ -60,2 +60,6 @@

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

@@ -83,4 +87,5 @@ },

const skipTemplates = !!options.skipTemplates;
const skipJSXText = !!options.skipJSXText;
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const commentNodes = sourceCode.getAllComments();

@@ -107,3 +112,3 @@

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

@@ -113,3 +118,3 @@ * @returns {void}

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

@@ -154,2 +159,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

@@ -247,5 +264,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() {

@@ -252,0 +269,0 @@ if (skipComments) {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,6 +25,5 @@ meta: {

docs: {
description: "disallow the use of the `__iterator__` property",
category: "Best Practices",
description: "Disallow the use of the `__iterator__` property",
recommended: false,
url: "https://eslint.org/docs/rules/no-iterator"
url: "https://eslint.org/docs/latest/rules/no-iterator"
},

@@ -30,0 +30,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,6 +25,5 @@ meta: {

docs: {
description: "disallow labels that share a name with a variable",
category: "Variables",
description: "Disallow labels that share a name with a variable",
recommended: false,
url: "https://eslint.org/docs/rules/no-label-var"
url: "https://eslint.org/docs/latest/rules/no-label-var"
},

@@ -39,2 +39,3 @@

create(context) {
const sourceCode = context.sourceCode;

@@ -65,3 +66,3 @@ //--------------------------------------------------------------------------

// Fetch the innermost scope.
const scope = context.getScope();
const scope = sourceCode.getScope(node);

@@ -68,0 +69,0 @@ /*

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -23,6 +24,5 @@ meta: {

docs: {
description: "disallow labeled statements",
category: "Best Practices",
description: "Disallow labeled statements",
recommended: false,
url: "https://eslint.org/docs/rules/no-labels"
url: "https://eslint.org/docs/latest/rules/no-labels"
},

@@ -103,3 +103,3 @@

/* istanbul ignore next: syntax error */
/* c8 ignore next */
return "other";

@@ -106,0 +106,0 @@ }

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "disallow unnecessary nested blocks",
category: "Best Practices",
description: "Disallow unnecessary nested blocks",
recommended: false,
url: "https://eslint.org/docs/rules/no-lone-blocks"
url: "https://eslint.org/docs/latest/rules/no-lone-blocks"
},

@@ -38,2 +38,3 @@

let ruleDef;
const sourceCode = context.sourceCode;

@@ -46,3 +47,5 @@ /**

function report(node) {
const messageId = node.parent.type === "BlockStatement" ? "redundantNestedBlock" : "redundantBlock";
const messageId = node.parent.type === "BlockStatement" || node.parent.type === "StaticBlock"
? "redundantNestedBlock"
: "redundantBlock";

@@ -62,2 +65,3 @@ context.report({

return node.parent.type === "BlockStatement" ||
node.parent.type === "StaticBlock" ||
node.parent.type === "Program" ||

@@ -72,5 +76,6 @@

* and "marks it" as valid if any.
* @param {ASTNode} node The current node to check.
* @returns {void}
*/
function markLoneBlock() {
function markLoneBlock(node) {
if (loneBlocks.length === 0) {

@@ -80,3 +85,3 @@ return;

const block = context.getAncestors().pop();
const block = node.parent;

@@ -98,3 +103,3 @@ if (loneBlocks[loneBlocks.length - 1] === block) {

// ES6: report blocks without block-level bindings, or that's only child of another block
if (context.parserOptions.ecmaVersion >= 6) {
if (context.languageOptions.ecmaVersion >= 2015) {
ruleDef = {

@@ -111,3 +116,6 @@ BlockStatement(node) {

} else if (
node.parent.type === "BlockStatement" &&
(
node.parent.type === "BlockStatement" ||
node.parent.type === "StaticBlock"
) &&
node.parent.body.length === 1

@@ -122,9 +130,9 @@ ) {

if (node.kind === "let" || node.kind === "const") {
markLoneBlock();
markLoneBlock(node);
}
};
ruleDef.FunctionDeclaration = function() {
if (context.getScope().isStrict) {
markLoneBlock();
ruleDef.FunctionDeclaration = function(node) {
if (sourceCode.getScope(node).isStrict) {
markLoneBlock(node);
}

@@ -131,0 +139,0 @@ };

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -17,6 +18,5 @@ meta: {

docs: {
description: "disallow `if` statements as the only statement in `else` blocks",
category: "Stylistic Issues",
description: "Disallow `if` statements as the only statement in `else` blocks",
recommended: false,
url: "https://eslint.org/docs/rules/no-lonely-if"
url: "https://eslint.org/docs/latest/rules/no-lonely-if"
},

@@ -33,9 +33,8 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
return {
IfStatement(node) {
const ancestors = context.getAncestors(),
parent = ancestors.pop(),
grandparent = ancestors.pop();
const parent = node.parent,
grandparent = parent.parent;

@@ -42,0 +41,0 @@ if (parent && parent.type === "BlockStatement" &&

@@ -128,3 +128,3 @@ /**

*
* It's safeafe if the reference matches one of the following condition.
* It's safe if the reference matches one of the following condition.
* - is readonly.

@@ -152,2 +152,3 @@ * - doesn't exist inside a local function and after the border.

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -158,6 +159,5 @@ meta: {

docs: {
description: "disallow function declarations that contain unsafe references inside loop statements",
category: "Best Practices",
description: "Disallow function declarations that contain unsafe references inside loop statements",
recommended: false,
url: "https://eslint.org/docs/rules/no-loop-func"
url: "https://eslint.org/docs/latest/rules/no-loop-func"
},

@@ -174,2 +174,4 @@

const sourceCode = context.sourceCode;
/**

@@ -181,3 +183,3 @@ * Reports functions which match the following condition:

* @param {ASTNode} node The AST node to check.
* @returns {boolean} Whether or not the node is within a loop.
* @returns {void}
*/

@@ -191,4 +193,4 @@ function checkForLoops(node) {

const references = context.getScope().through;
const unsafeRefs = references.filter(r => !isSafe(loopNode, r)).map(r => r.identifier.name);
const references = sourceCode.getScope(node).through;
const unsafeRefs = references.filter(r => r.resolved && !isSafe(loopNode, r)).map(r => r.identifier.name);

@@ -195,0 +197,0 @@ if (unsafeRefs.length > 0) {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "disallow literal numbers that lose precision",
category: "Possible Errors",
recommended: false,
url: "https://eslint.org/docs/rules/no-loss-of-precision"
description: "Disallow literal numbers that lose precision",
recommended: true,
url: "https://eslint.org/docs/latest/rules/no-loss-of-precision"
},

@@ -88,3 +88,3 @@ schema: [],

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

@@ -98,3 +98,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;
}

@@ -108,9 +113,14 @@

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;
}
/**
* Converts an integer to to an object containing the the integer's coefficient and order of magnitude
* Converts an integer to an object containing the integer's coefficient and order of magnitude
* @param {string} stringInteger the string representation of the integer being converted
* @returns {Object} the object containing the the integer's coefficient and order of magnitude
* @returns {Object} the object containing the integer's coefficient and order of magnitude
*/

@@ -128,5 +138,5 @@ function normalizeInteger(stringInteger) {

*
* Converts a float to to an object containing the the floats's coefficient and order of magnitude
* Converts a float to an object containing the floats's coefficient and order of magnitude
* @param {string} stringFloat the string representation of the float being converted
* @returns {Object} the object containing the the integer's coefficient and order of magnitude
* @returns {Object} the object containing the integer's coefficient and order of magnitude
*/

@@ -137,3 +147,3 @@ function normalizeFloat(stringFloat) {

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

@@ -154,3 +164,2 @@

/**

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

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

@@ -174,0 +182,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -35,6 +36,5 @@ meta: {

docs: {
description: "disallow magic numbers",
category: "Best Practices",
description: "Disallow magic numbers",
recommended: false,
url: "https://eslint.org/docs/rules/no-magic-numbers"
url: "https://eslint.org/docs/latest/rules/no-magic-numbers"
},

@@ -70,2 +70,6 @@

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

@@ -86,5 +90,6 @@ },

enforceConst = !!config.enforceConst,
ignore = (config.ignore || []).map(normalizeIgnoreValue),
ignore = new Set((config.ignore || []).map(normalizeIgnoreValue)),
ignoreArrayIndexes = !!config.ignoreArrayIndexes,
ignoreDefaultValues = !!config.ignoreDefaultValues;
ignoreDefaultValues = !!config.ignoreDefaultValues,
ignoreClassFieldInitialValues = !!config.ignoreClassFieldInitialValues;

@@ -99,3 +104,3 @@ const okTypes = detectObjects ? [] : ["ObjectExpression", "Property", "AssignmentExpression"];

function isIgnoredValue(value) {
return ignore.indexOf(value) !== -1;
return ignore.has(value);
}

@@ -115,2 +120,13 @@

/**
* Returns whether the number is the initial value of a class field.
* @param {ASTNode} fullNumberNode `Literal` or `UnaryExpression` full number node
* @returns {boolean} true if the number is the initial value of a class field.
*/
function isClassFieldInitialValue(fullNumberNode) {
const parent = fullNumberNode.parent;
return parent.type === "PropertyDefinition" && parent.value === fullNumberNode;
}
/**
* Returns whether the given node is used as a radix within parseInt() or Number.parseInt()

@@ -203,2 +219,3 @@ * @param {ASTNode} fullNumberNode `Literal` or `UnaryExpression` full number node

(ignoreDefaultValues && isDefaultValue(fullNumberNode)) ||
(ignoreClassFieldInitialValues && isClassFieldInitialValue(fullNumberNode)) ||
isParseIntRadix(fullNumberNode) ||

@@ -219,3 +236,3 @@ isJSXNumber(fullNumberNode) ||

} else if (
okTypes.indexOf(parent.type) === -1 ||
!okTypes.includes(parent.type) ||
(parent.type === "AssignmentExpression" && parent.left.type === "Identifier")

@@ -222,0 +239,0 @@ ) {

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

const { CALL, CONSTRUCT, ReferenceTracker, getStringIfConstant } = require("eslint-utils");
const { RegExpParser, visitRegExpAST } = require("regexpp");
const { CALL, CONSTRUCT, ReferenceTracker, getStringIfConstant } = require("@eslint-community/eslint-utils");
const { RegExpParser, visitRegExpAST } = require("@eslint-community/regexpp");
const { isCombiningCharacter, isEmojiModifier, isRegionalIndicatorSymbol, isSurrogatePair } = require("./utils/unicode");
const astUtils = require("./utils/ast-utils.js");
const { isValidWithUnicodeFlag } = require("./utils/regular-expressions");

@@ -16,2 +18,7 @@ //------------------------------------------------------------------------------

/**
* @typedef {import('@eslint-community/regexpp').AST.Character} Character
* @typedef {import('@eslint-community/regexpp').AST.CharacterClassElement} CharacterClassElement
*/
/**
* Iterate character sequences of a given nodes.

@@ -21,6 +28,8 @@ *

* so this function reverts CharacterClassRange syntax and restore the sequence.
* @param {regexpp.AST.CharacterClassElement[]} nodes The node list to iterate character sequences.
* @returns {IterableIterator<number[]>} The list of character sequences.
* @param {CharacterClassElement[]} nodes The node list to iterate character sequences.
* @returns {IterableIterator<Character[]>} The list of character sequences.
*/
function *iterateCharacterSequence(nodes) {
/** @type {Character[]} */
let seq = [];

@@ -31,12 +40,15 @@

case "Character":
seq.push(node.value);
seq.push(node);
break;
case "CharacterClassRange":
seq.push(node.min.value);
seq.push(node.min);
yield seq;
seq = [node.max.value];
seq = [node.max];
break;
case "CharacterSet":
case "CharacterClass": // [[]] nesting character class
case "ClassStringDisjunction": // \q{...}
case "ExpressionCharacterClass": // [A--B]
if (seq.length > 0) {

@@ -57,12 +69,54 @@ yield seq;

/**
* Checks whether the given character node is a Unicode code point escape or not.
* @param {Character} char the character node to check.
* @returns {boolean} `true` if the character node is a Unicode code point escape.
*/
function isUnicodeCodePointEscape(char) {
return /^\\u\{[\da-f]+\}$/iu.test(char.raw);
}
/**
* Each function returns `true` if it detects that kind of problem.
* @type {Record<string, (chars: Character[]) => boolean>}
*/
const hasCharacterSequence = {
surrogatePairWithoutUFlag(chars) {
return chars.some((c, i) => i !== 0 && isSurrogatePair(chars[i - 1], c));
return chars.some((c, i) => {
if (i === 0) {
return false;
}
const c1 = chars[i - 1];
return (
isSurrogatePair(c1.value, c.value) &&
!isUnicodeCodePointEscape(c1) &&
!isUnicodeCodePointEscape(c)
);
});
},
surrogatePair(chars) {
return chars.some((c, i) => {
if (i === 0) {
return false;
}
const c1 = chars[i - 1];
return (
isSurrogatePair(c1.value, c.value) &&
(
isUnicodeCodePointEscape(c1) ||
isUnicodeCodePointEscape(c)
)
);
});
},
combiningClass(chars) {
return chars.some((c, i) => (
i !== 0 &&
isCombiningCharacter(c) &&
!isCombiningCharacter(chars[i - 1])
isCombiningCharacter(c.value) &&
!isCombiningCharacter(chars[i - 1].value)
));

@@ -74,4 +128,4 @@ },

i !== 0 &&
isEmojiModifier(c) &&
!isEmojiModifier(chars[i - 1])
isEmojiModifier(c.value) &&
!isEmojiModifier(chars[i - 1].value)
));

@@ -83,4 +137,4 @@ },

i !== 0 &&
isRegionalIndicatorSymbol(c) &&
isRegionalIndicatorSymbol(chars[i - 1])
isRegionalIndicatorSymbol(c.value) &&
isRegionalIndicatorSymbol(chars[i - 1].value)
));

@@ -95,5 +149,5 @@ },

i !== lastIndex &&
c === 0x200d &&
chars[i - 1] !== 0x200d &&
chars[i + 1] !== 0x200d
c.value === 0x200d &&
chars[i - 1].value !== 0x200d &&
chars[i + 1].value !== 0x200d
));

@@ -109,2 +163,3 @@ }

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -115,8 +170,9 @@ meta: {

docs: {
description: "disallow characters which are made with multiple code points in character class syntax",
category: "Possible Errors",
description: "Disallow characters which are made with multiple code points in character class syntax",
recommended: true,
url: "https://eslint.org/docs/rules/no-misleading-character-class"
url: "https://eslint.org/docs/latest/rules/no-misleading-character-class"
},
hasSuggestions: true,
schema: [],

@@ -126,9 +182,12 @@

surrogatePairWithoutUFlag: "Unexpected surrogate pair in character class. Use 'u' flag.",
surrogatePair: "Unexpected surrogate pair in character class.",
combiningClass: "Unexpected combined character in character class.",
emojiModifier: "Unexpected modified Emoji in character class.",
regionalIndicatorSymbol: "Unexpected national flag in character class.",
zwj: "Unexpected joined character sequence in character class."
zwj: "Unexpected joined character sequence in character class.",
suggestUnicodeFlag: "Add unicode 'u' flag to regex."
}
},
create(context) {
const sourceCode = context.sourceCode;
const parser = new RegExpParser();

@@ -141,13 +200,6 @@

* @param {string} flags The flags of the regular expression.
* @param {Function} unicodeFixer Fixer for missing "u" flag.
* @returns {void}
*/
function verify(node, pattern, flags) {
const has = {
surrogatePairWithoutUFlag: false,
combiningClass: false,
variationSelector: false,
emojiModifier: false,
regionalIndicatorSymbol: false,
zwj: false
};
function verify(node, pattern, flags, unicodeFixer) {
let patternNode;

@@ -160,3 +212,6 @@

pattern.length,
flags.includes("u")
{
unicode: flags.includes("u"),
unicodeSets: flags.includes("v")
}
);

@@ -169,2 +224,4 @@ } catch {

const foundKinds = new Set();
visitRegExpAST(patternNode, {

@@ -174,3 +231,5 @@ onCharacterClassEnter(ccNode) {

for (const kind of kinds) {
has[kind] = has[kind] || hasCharacterSequence[kind](chars);
if (hasCharacterSequence[kind](chars)) {
foundKinds.add(kind);
}
}

@@ -181,6 +240,17 @@ }

for (const kind of kinds) {
if (has[kind]) {
context.report({ node, messageId: kind });
for (const kind of foundKinds) {
let suggest;
if (kind === "surrogatePairWithoutUFlag") {
suggest = [{
messageId: "suggestUnicodeFlag",
fix: unicodeFixer
}];
}
context.report({
node,
messageId: kind,
suggest
});
}

@@ -191,6 +261,12 @@ }

"Literal[regex]"(node) {
verify(node, node.regex.pattern, node.regex.flags);
verify(node, node.regex.pattern, node.regex.flags, fixer => {
if (!isValidWithUnicodeFlag(context.languageOptions.ecmaVersion, node.regex.pattern)) {
return null;
}
return fixer.insertTextAfter(node, "u");
});
},
"Program"() {
const scope = context.getScope();
"Program"(node) {
const scope = sourceCode.getScope(node);
const tracker = new ReferenceTracker(scope);

@@ -203,6 +279,6 @@

*/
for (const { node } of tracker.iterateGlobalReferences({
for (const { node: refNode } of tracker.iterateGlobalReferences({
RegExp: { [CALL]: true, [CONSTRUCT]: true }
})) {
const [patternNode, flagsNode] = node.arguments;
const [patternNode, flagsNode] = refNode.arguments;
const pattern = getStringIfConstant(patternNode, scope);

@@ -212,3 +288,27 @@ const flags = getStringIfConstant(flagsNode, scope);

if (typeof pattern === "string") {
verify(node, pattern, flags || "");
verify(refNode, pattern, flags || "", fixer => {
if (!isValidWithUnicodeFlag(context.languageOptions.ecmaVersion, pattern)) {
return null;
}
if (refNode.arguments.length === 1) {
const penultimateToken = sourceCode.getLastToken(refNode, { skip: 1 }); // skip closing parenthesis
return fixer.insertTextAfter(
penultimateToken,
astUtils.isCommaToken(penultimateToken)
? ' "u",'
: ', "u"'
);
}
if ((flagsNode.type === "Literal" && typeof flagsNode.value === "string") || flagsNode.type === "TemplateLiteral") {
const range = [flagsNode.range[0], flagsNode.range[1] - 1];
return fixer.insertTextAfterRange(range, "u");
}
return null;
});
}

@@ -215,0 +315,0 @@ }

@@ -61,3 +61,3 @@ /**

* Checks whether any group which includes both given operator exists or not.
* @param {Array.<string[]>} groups A list of groups to check.
* @param {Array<string[]>} groups A list of groups to check.
* @param {string} left An operator.

@@ -68,3 +68,3 @@ * @param {string} right Another operator.

function includesBothInAGroup(groups, left, right) {
return groups.some(group => group.indexOf(left) !== -1 && group.indexOf(right) !== -1);
return groups.some(group => group.includes(left) && group.includes(right));
}

@@ -87,2 +87,3 @@

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -93,6 +94,5 @@ meta: {

docs: {
description: "disallow mixed binary operators",
category: "Stylistic Issues",
description: "Disallow mixed binary operators",
recommended: false,
url: "https://eslint.org/docs/rules/no-mixed-operators"
url: "https://eslint.org/docs/latest/rules/no-mixed-operators"
},

@@ -124,3 +124,3 @@

messages: {
unexpectedMixedOperator: "Unexpected mix of '{{leftOperator}}' and '{{rightOperator}}'."
unexpectedMixedOperator: "Unexpected mix of '{{leftOperator}}' and '{{rightOperator}}'. Use parentheses to clarify the intended order of operations."
}

@@ -130,3 +130,3 @@ },

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const options = normalizeOptions(context.options[0]);

@@ -171,13 +171,2 @@

/**
* Checks whether the operator of a given node is mixed with a
* conditional expression.
* @param {ASTNode} node A node to check. This is a conditional
* expression node
* @returns {boolean} `true` if the node was mixed.
*/
function isMixedWithConditionalParent(node) {
return !astUtils.isParenthesised(sourceCode, node) && !astUtils.isParenthesised(sourceCode, node.test);
}
/**
* Gets the operator token of a given node.

@@ -230,15 +219,9 @@ * @param {ASTNode} node A node to check. This is a BinaryExpression

function check(node) {
if (TARGET_NODE_TYPE.test(node.parent.type)) {
if (node.parent.type === "ConditionalExpression" && !shouldIgnore(node) && isMixedWithConditionalParent(node.parent)) {
reportBothOperators(node);
} else {
if (TARGET_NODE_TYPE.test(node.parent.type) &&
isMixedWithParent(node) &&
!shouldIgnore(node)
) {
reportBothOperators(node);
}
}
if (
TARGET_NODE_TYPE.test(node.parent.type) &&
isMixedWithParent(node) &&
!shouldIgnore(node)
) {
reportBothOperators(node);
}
}

@@ -245,0 +228,0 @@

/**
* @fileoverview Rule to enforce grouped require statements for Node.JS
* @author Raphael Pigulla
* @deprecated in ESLint v7.0.0
*/

@@ -12,2 +13,3 @@

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -22,6 +24,5 @@ meta: {

docs: {
description: "disallow `require` calls to be mixed with regular variable declarations",
category: "Node.js and CommonJS",
description: "Disallow `require` calls to be mixed with regular variable declarations",
recommended: false,
url: "https://eslint.org/docs/rules/no-mixed-requires"
url: "https://eslint.org/docs/latest/rules/no-mixed-requires"
},

@@ -164,3 +165,3 @@

if (BUILTIN_MODULES.indexOf(arg.value) !== -1) {
if (BUILTIN_MODULES.includes(arg.value)) {

@@ -167,0 +168,0 @@ // "var fs = require('fs');"

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -17,6 +18,5 @@ meta: {

docs: {
description: "disallow mixed spaces and tabs for indentation",
category: "Stylistic Issues",
description: "Disallow mixed spaces and tabs for indentation",
recommended: true,
url: "https://eslint.org/docs/rules/no-mixed-spaces-and-tabs"
url: "https://eslint.org/docs/latest/rules/no-mixed-spaces-and-tabs"
},

@@ -36,3 +36,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -39,0 +39,0 @@ let smartTabs;

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -19,9 +20,17 @@ meta: {

docs: {
description: "disallow use of chained assignment expressions",
category: "Stylistic Issues",
description: "Disallow use of chained assignment expressions",
recommended: false,
url: "https://eslint.org/docs/rules/no-multi-assign"
url: "https://eslint.org/docs/latest/rules/no-multi-assign"
},
schema: [],
schema: [{
type: "object",
properties: {
ignoreNonDeclaration: {
type: "boolean",
default: false
}
},
additionalProperties: false
}],

@@ -38,11 +47,20 @@ messages: {

//--------------------------------------------------------------------------
const options = context.options[0] || {
ignoreNonDeclaration: false
};
const selectors = [
"VariableDeclarator > AssignmentExpression.init",
"PropertyDefinition > AssignmentExpression.value"
];
if (!options.ignoreNonDeclaration) {
selectors.push("AssignmentExpression > AssignmentExpression.right");
}
return {
AssignmentExpression(node) {
if (["AssignmentExpression", "VariableDeclarator"].indexOf(node.parent.type) !== -1) {
context.report({
node,
messageId: "unexpectedChain"
});
}
[selectors](node) {
context.report({
node,
messageId: "unexpectedChain"
});
}

@@ -49,0 +67,0 @@ };

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -20,6 +21,5 @@ meta: {

docs: {
description: "disallow multiple spaces",
category: "Best Practices",
description: "Disallow multiple spaces",
recommended: false,
url: "https://eslint.org/docs/rules/no-multi-spaces"
url: "https://eslint.org/docs/latest/rules/no-multi-spaces"
},

@@ -57,7 +57,7 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const options = context.options[0] || {};
const ignoreEOLComments = options.ignoreEOLComments;
const exceptions = Object.assign({ Property: true }, options.exceptions);
const hasExceptions = Object.keys(exceptions).filter(key => exceptions[key]).length > 0;
const hasExceptions = Object.keys(exceptions).some(key => exceptions[key]);

@@ -64,0 +64,0 @@ /**

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,6 +25,5 @@ meta: {

docs: {
description: "disallow multiline strings",
category: "Best Practices",
description: "Disallow multiline strings",
recommended: false,
url: "https://eslint.org/docs/rules/no-multi-str"
url: "https://eslint.org/docs/latest/rules/no-multi-str"
},

@@ -30,0 +30,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "disallow multiple empty lines",
category: "Stylistic Issues",
description: "Disallow multiple empty lines",
recommended: false,
url: "https://eslint.org/docs/rules/no-multiple-empty-lines"
url: "https://eslint.org/docs/latest/rules/no-multiple-empty-lines"
},

@@ -69,3 +69,3 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -72,0 +72,0 @@ // Swallow the final newline, as some editors add it automatically and we don't want it to cause an issue

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -19,6 +20,5 @@ meta: {

docs: {
description: "disallow assignments to native objects or read-only global variables",
category: "Best Practices",
description: "Disallow assignments to native objects or read-only global variables",
recommended: false,
url: "https://eslint.org/docs/rules/no-native-reassign"
url: "https://eslint.org/docs/latest/rules/no-native-reassign"
},

@@ -52,2 +52,3 @@

const exceptions = (config && config.exceptions) || [];
const sourceCode = context.sourceCode;

@@ -87,3 +88,3 @@ /**

function checkVariable(variable) {
if (variable.writeable === false && exceptions.indexOf(variable.name) === -1) {
if (variable.writeable === false && !exceptions.includes(variable.name)) {
variable.references.forEach(checkReference);

@@ -94,4 +95,4 @@ }

return {
Program() {
const globalScope = context.getScope();
Program(node) {
const globalScope = sourceCode.getScope(node);

@@ -98,0 +99,0 @@ globalScope.variables.forEach(checkVariable);

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -17,6 +18,5 @@ meta: {

docs: {
description: "disallow negated conditions",
category: "Stylistic Issues",
description: "Disallow negated conditions",
recommended: false,
url: "https://eslint.org/docs/rules/no-negated-condition"
url: "https://eslint.org/docs/latest/rules/no-negated-condition"
},

@@ -23,0 +23,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -19,6 +20,5 @@ meta: {

docs: {
description: "disallow negating the left operand in `in` expressions",
category: "Possible Errors",
description: "Disallow negating the left operand in `in` expressions",
recommended: false,
url: "https://eslint.org/docs/rules/no-negated-in-lhs"
url: "https://eslint.org/docs/latest/rules/no-negated-in-lhs"
},

@@ -25,0 +25,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "disallow nested ternary expressions",
category: "Stylistic Issues",
description: "Disallow nested ternary expressions",
recommended: false,
url: "https://eslint.org/docs/rules/no-nested-ternary"
url: "https://eslint.org/docs/latest/rules/no-nested-ternary"
},

@@ -24,0 +24,0 @@

@@ -9,5 +9,18 @@ /**

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const astUtils = require("./utils/ast-utils");
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
const callMethods = new Set(["apply", "bind", "call"]);
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +31,5 @@ meta: {

docs: {
description: "disallow `new` operators with the `Function` object",
category: "Best Practices",
description: "Disallow `new` operators with the `Function` object",
recommended: false,
url: "https://eslint.org/docs/rules/no-new-func"
url: "https://eslint.org/docs/latest/rules/no-new-func"
},

@@ -33,6 +45,7 @@

create(context) {
const sourceCode = context.sourceCode;
return {
"Program:exit"() {
const globalScope = context.getScope();
"Program:exit"(node) {
const globalScope = sourceCode.getScope(node);
const variable = globalScope.set.get("Function");

@@ -42,12 +55,28 @@

variable.references.forEach(ref => {
const node = ref.identifier;
const { parent } = node;
const idNode = ref.identifier;
const { parent } = idNode;
let evalNode;
if (
parent &&
(parent.type === "NewExpression" || parent.type === "CallExpression") &&
node === parent.callee
) {
if (parent) {
if (idNode === parent.callee && (
parent.type === "NewExpression" ||
parent.type === "CallExpression"
)) {
evalNode = parent;
} else if (
parent.type === "MemberExpression" &&
idNode === parent.object &&
callMethods.has(astUtils.getStaticPropertyName(parent))
) {
const maybeCallee = parent.parent.type === "ChainExpression" ? parent.parent : parent;
if (maybeCallee.parent.type === "CallExpression" && maybeCallee.parent.callee === maybeCallee) {
evalNode = maybeCallee.parent;
}
}
}
if (evalNode) {
context.report({
node: parent,
node: evalNode,
messageId: "noFunctionConstructor"

@@ -54,0 +83,0 @@ });

/**
* @fileoverview A rule to disallow calls to the Object constructor
* @author Matt DuVall <http://www.mattduvall.com/>
* @deprecated in ESLint v8.50.0
*/

@@ -18,2 +19,3 @@

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,12 +26,17 @@ meta: {

docs: {
description: "disallow `Object` constructors",
category: "Stylistic Issues",
description: "Disallow `Object` constructors",
recommended: false,
url: "https://eslint.org/docs/rules/no-new-object"
url: "https://eslint.org/docs/latest/rules/no-new-object"
},
deprecated: true,
replacedBy: [
"no-object-constructor"
],
schema: [],
messages: {
preferLiteral: "The object literal notation {} is preferrable."
preferLiteral: "The object literal notation {} is preferable."
}

@@ -39,6 +46,9 @@ },

create(context) {
const sourceCode = context.sourceCode;
return {
NewExpression(node) {
const variable = astUtils.getVariableByName(
context.getScope(),
sourceCode.getScope(node),
node.callee.name

@@ -45,0 +55,0 @@ );

/**
* @fileoverview Rule to disallow use of new operator with the `require` function
* @author Wil Moore III
* @deprecated in ESLint v7.0.0
*/

@@ -12,2 +13,3 @@

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -22,6 +24,5 @@ meta: {

docs: {
description: "disallow `new` operators with calls to `require`",
category: "Node.js and CommonJS",
description: "Disallow `new` operators with calls to `require`",
recommended: false,
url: "https://eslint.org/docs/rules/no-new-require"
url: "https://eslint.org/docs/latest/rules/no-new-require"
},

@@ -28,0 +29,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "disallow `new` operators with the `Symbol` object",
category: "ECMAScript 6",
description: "Disallow `new` operators with the `Symbol` object",
recommended: true,
url: "https://eslint.org/docs/rules/no-new-symbol"
url: "https://eslint.org/docs/latest/rules/no-new-symbol"
},

@@ -34,5 +34,7 @@

const sourceCode = context.sourceCode;
return {
"Program:exit"() {
const globalScope = context.getScope();
"Program:exit"(node) {
const globalScope = sourceCode.getScope(node);
const variable = globalScope.set.get("Symbol");

@@ -42,8 +44,8 @@

variable.references.forEach(ref => {
const node = ref.identifier;
const parent = node.parent;
const idNode = ref.identifier;
const parent = idNode.parent;
if (parent && parent.type === "NewExpression" && parent.callee === node) {
if (parent && parent.type === "NewExpression" && parent.callee === idNode) {
context.report({
node,
node: idNode,
messageId: "noNewSymbol"

@@ -50,0 +52,0 @@ });

@@ -9,5 +9,12 @@ /**

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const { getVariableByName } = require("./utils/ast-utils");
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +25,5 @@ meta: {

docs: {
description: "disallow `new` operators with the `String`, `Number`, and `Boolean` objects",
category: "Best Practices",
description: "Disallow `new` operators with the `String`, `Number`, and `Boolean` objects",
recommended: false,
url: "https://eslint.org/docs/rules/no-new-wrappers"
url: "https://eslint.org/docs/latest/rules/no-new-wrappers"
},

@@ -33,2 +39,3 @@

create(context) {
const { sourceCode } = context;

@@ -39,9 +46,14 @@ return {

const wrapperObjects = ["String", "Number", "Boolean"];
const { name } = node.callee;
if (wrapperObjects.indexOf(node.callee.name) > -1) {
context.report({
node,
messageId: "noConstructor",
data: { fn: node.callee.name }
});
if (wrapperObjects.includes(name)) {
const variable = getVariableByName(sourceCode.getScope(node), name);
if (variable && variable.identifiers.length === 0) {
context.report({
node,
messageId: "noConstructor",
data: { fn: name }
});
}
}

@@ -48,0 +60,0 @@ }

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -19,6 +20,5 @@ meta: {

docs: {
description: "disallow `new` operators outside of assignments or comparisons",
category: "Best Practices",
description: "Disallow `new` operators outside of assignments or comparisons",
recommended: false,
url: "https://eslint.org/docs/rules/no-new"
url: "https://eslint.org/docs/latest/rules/no-new"
},

@@ -25,0 +25,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -33,9 +34,9 @@ meta: {

docs: {
description: "disallow `\\8` and `\\9` escape sequences in string literals",
category: "Best Practices",
recommended: false,
url: "https://eslint.org/docs/rules/no-nonoctal-decimal-escape",
suggestion: true
description: "Disallow `\\8` and `\\9` escape sequences in string literals",
recommended: true,
url: "https://eslint.org/docs/latest/rules/no-nonoctal-decimal-escape"
},
hasSuggestions: true,
schema: [],

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

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -56,0 +57,0 @@ /**

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

const { CALL, CONSTRUCT, ReferenceTracker } = require("eslint-utils");
const { CALL, CONSTRUCT, ReferenceTracker } = require("@eslint-community/eslint-utils");
const getPropertyName = require("./utils/ast-utils").getStaticPropertyName;

@@ -20,3 +20,3 @@

const nonCallableGlobals = ["Atomics", "JSON", "Math", "Reflect"];
const nonCallableGlobals = ["Atomics", "JSON", "Math", "Reflect", "Intl"];

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -48,6 +49,5 @@ meta: {

docs: {
description: "disallow calling global object properties as functions",
category: "Possible Errors",
description: "Disallow calling global object properties as functions",
recommended: true,
url: "https://eslint.org/docs/rules/no-obj-calls"
url: "https://eslint.org/docs/latest/rules/no-obj-calls"
},

@@ -65,5 +65,7 @@

const sourceCode = context.sourceCode;
return {
Program() {
const scope = context.getScope();
Program(node) {
const scope = sourceCode.getScope(node);
const tracker = new ReferenceTracker(scope);

@@ -79,8 +81,8 @@ const traceMap = {};

for (const { node, path } of tracker.iterateGlobalReferences(traceMap)) {
const name = getReportNodeName(node.callee);
for (const { node: refNode, path } of tracker.iterateGlobalReferences(traceMap)) {
const name = getReportNodeName(refNode.callee);
const ref = path[0];
const messageId = name === ref ? "unexpectedCall" : "unexpectedRefCall";
context.report({ node, messageId, data: { name, ref } });
context.report({ node: refNode, messageId, data: { name, ref } });
}

@@ -87,0 +89,0 @@ }

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "disallow octal escape sequences in string literals",
category: "Best Practices",
description: "Disallow octal escape sequences in string literals",
recommended: false,
url: "https://eslint.org/docs/rules/no-octal-escape"
url: "https://eslint.org/docs/latest/rules/no-octal-escape"
},

@@ -24,0 +24,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "disallow octal literals",
category: "Best Practices",
description: "Disallow octal literals",
recommended: true,
url: "https://eslint.org/docs/rules/no-octal"
url: "https://eslint.org/docs/latest/rules/no-octal"
},

@@ -28,3 +28,3 @@

messages: {
noOcatal: "Octal literals should not be used."
noOctal: "Octal literals should not be used."
}

@@ -41,3 +41,3 @@ },

node,
messageId: "noOcatal"
messageId: "noOctal"
});

@@ -44,0 +44,0 @@ }

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -19,6 +20,5 @@ meta: {

docs: {
description: "disallow reassigning `function` parameters",
category: "Best Practices",
description: "Disallow reassigning `function` parameters",
recommended: false,
url: "https://eslint.org/docs/rules/no-param-reassign"
url: "https://eslint.org/docs/latest/rules/no-param-reassign"
},

@@ -75,2 +75,3 @@

const ignoredPropertyAssignmentsForRegex = context.options[0] && context.options[0].ignorePropertyModificationsForRegex || [];
const sourceCode = context.sourceCode;

@@ -220,3 +221,3 @@ /**

function checkForFunction(node) {
context.getDeclaredVariables(node).forEach(checkVariable);
sourceCode.getDeclaredVariables(node).forEach(checkVariable);
}

@@ -223,0 +224,0 @@

/**
* @fileoverview Disallow string concatenation when using __dirname and __filename
* @author Nicholas C. Zakas
* @deprecated in ESLint v7.0.0
*/

@@ -11,2 +12,3 @@ "use strict";

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -21,6 +23,5 @@ meta: {

docs: {
description: "disallow string concatenation with `__dirname` and `__filename`",
category: "Node.js and CommonJS",
description: "Disallow string concatenation with `__dirname` and `__filename`",
recommended: false,
url: "https://eslint.org/docs/rules/no-path-concat"
url: "https://eslint.org/docs/latest/rules/no-path-concat"
},

@@ -27,0 +28,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -54,6 +55,5 @@ meta: {

docs: {
description: "disallow the unary operators `++` and `--`",
category: "Stylistic Issues",
description: "Disallow the unary operators `++` and `--`",
recommended: false,
url: "https://eslint.org/docs/rules/no-plusplus"
url: "https://eslint.org/docs/latest/rules/no-plusplus"
},

@@ -60,0 +60,0 @@

/**
* @fileoverview Disallow the use of process.env()
* @author Vignesh Anand
* @deprecated in ESLint v7.0.0
*/

@@ -11,2 +12,3 @@ "use strict";

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -21,6 +23,5 @@ meta: {

docs: {
description: "disallow the use of `process.env`",
category: "Node.js and CommonJS",
description: "Disallow the use of `process.env`",
recommended: false,
url: "https://eslint.org/docs/rules/no-process-env"
url: "https://eslint.org/docs/latest/rules/no-process-env"
},

@@ -27,0 +28,0 @@

/**
* @fileoverview Disallow the use of process.exit()
* @author Nicholas C. Zakas
* @deprecated in ESLint v7.0.0
*/

@@ -11,2 +12,3 @@ "use strict";

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -21,6 +23,5 @@ meta: {

docs: {
description: "disallow the use of `process.exit()`",
category: "Node.js and CommonJS",
description: "Disallow the use of `process.exit()`",
recommended: false,
url: "https://eslint.org/docs/rules/no-process-exit"
url: "https://eslint.org/docs/latest/rules/no-process-exit"
},

@@ -27,0 +28,0 @@

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

const { findVariable } = require("eslint-utils");
const { findVariable } = require("@eslint-community/eslint-utils");
const astUtils = require("./utils/ast-utils");

@@ -63,2 +64,74 @@ //------------------------------------------------------------------------------

/**
* Checks if the given node is a void expression.
* @param {ASTNode} node The node to check.
* @returns {boolean} - `true` if the node is a void expression
*/
function expressionIsVoid(node) {
return node.type === "UnaryExpression" && node.operator === "void";
}
/**
* Fixes the linting error by prepending "void " to the given node
* @param {Object} sourceCode context given by context.sourceCode
* @param {ASTNode} node The node to fix.
* @param {Object} fixer The fixer object provided by ESLint.
* @returns {Array<Object>} - An array of fix objects to apply to the node.
*/
function voidPrependFixer(sourceCode, node, fixer) {
const requiresParens =
// prepending `void ` will fail if the node has a lower precedence than void
astUtils.getPrecedence(node) < astUtils.getPrecedence({ type: "UnaryExpression", operator: "void" }) &&
// check if there are parentheses around the node to avoid redundant parentheses
!astUtils.isParenthesised(sourceCode, node);
// avoid parentheses issues
const returnOrArrowToken = sourceCode.getTokenBefore(
node,
node.parent.type === "ArrowFunctionExpression"
? astUtils.isArrowToken
// isReturnToken
: token => token.type === "Keyword" && token.value === "return"
);
const firstToken = sourceCode.getTokenAfter(returnOrArrowToken);
const prependSpace =
// is return token, as => allows void to be adjacent
returnOrArrowToken.value === "return" &&
// If two tokens (return and "(") are adjacent
returnOrArrowToken.range[1] === firstToken.range[0];
return [
fixer.insertTextBefore(firstToken, `${prependSpace ? " " : ""}void ${requiresParens ? "(" : ""}`),
fixer.insertTextAfter(node, requiresParens ? ")" : "")
];
}
/**
* Fixes the linting error by `wrapping {}` around the given node's body.
* @param {Object} sourceCode context given by context.sourceCode
* @param {ASTNode} node The node to fix.
* @param {Object} fixer The fixer object provided by ESLint.
* @returns {Array<Object>} - An array of fix objects to apply to the node.
*/
function curlyWrapFixer(sourceCode, node, fixer) {
// https://github.com/eslint/eslint/pull/17282#issuecomment-1592795923
const arrowToken = sourceCode.getTokenBefore(node.body, astUtils.isArrowToken);
const firstToken = sourceCode.getTokenAfter(arrowToken);
const lastToken = sourceCode.getLastToken(node);
return [
fixer.insertTextBefore(firstToken, "{"),
fixer.insertTextAfter(lastToken, "}")
];
}
//------------------------------------------------------------------------------

@@ -68,2 +141,3 @@ // Rule Definition

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -74,12 +148,28 @@ meta: {

docs: {
description: "disallow returning values from Promise executor functions",
category: "Possible Errors",
description: "Disallow returning values from Promise executor functions",
recommended: false,
url: "https://eslint.org/docs/rules/no-promise-executor-return"
url: "https://eslint.org/docs/latest/rules/no-promise-executor-return"
},
schema: [],
hasSuggestions: true,
schema: [{
type: "object",
properties: {
allowVoid: {
type: "boolean",
default: false
}
},
additionalProperties: false
}],
messages: {
returnsValue: "Return values from promise executor functions cannot be read."
returnsValue: "Return values from promise executor functions cannot be read.",
// arrow and function suggestions
prependVoid: "Prepend `void` to the expression.",
// only arrow suggestions
wrapBraces: "Wrap the expression in `{}`."
}

@@ -91,12 +181,7 @@ },

let funcInfo = null;
const sourceCode = context.sourceCode;
const {
allowVoid = false
} = context.options[0] || {};
/**
* Reports the given node.
* @param {ASTNode} node Node to report.
* @returns {void}
*/
function report(node) {
context.report({ node, messageId: "returnsValue" });
}
return {

@@ -107,7 +192,39 @@

upper: funcInfo,
shouldCheck: functionTypesToCheck.has(node.type) && isPromiseExecutor(node, context.getScope())
shouldCheck:
functionTypesToCheck.has(node.type) &&
isPromiseExecutor(node, sourceCode.getScope(node))
};
if (funcInfo.shouldCheck && node.type === "ArrowFunctionExpression" && node.expression) {
report(node.body);
if (// Is a Promise executor
funcInfo.shouldCheck &&
node.type === "ArrowFunctionExpression" &&
node.expression &&
// Except void
!(allowVoid && expressionIsVoid(node.body))
) {
const suggest = [];
// prevent useless refactors
if (allowVoid) {
suggest.push({
messageId: "prependVoid",
fix(fixer) {
return voidPrependFixer(sourceCode, node.body, fixer);
}
});
}
suggest.push({
messageId: "wrapBraces",
fix(fixer) {
return curlyWrapFixer(sourceCode, node, fixer);
}
});
context.report({
node: node.body,
messageId: "returnsValue",
suggest
});
}

@@ -121,5 +238,27 @@ },

ReturnStatement(node) {
if (funcInfo.shouldCheck && node.argument) {
report(node);
if (!(funcInfo.shouldCheck && node.argument)) {
return;
}
// node is `return <expression>`
if (!allowVoid) {
context.report({ node, messageId: "returnsValue" });
return;
}
if (expressionIsVoid(node.argument)) {
return;
}
// allowVoid && !expressionIsVoid
context.report({
node,
messageId: "returnsValue",
suggest: [{
messageId: "prependVoid",
fix(fixer) {
return voidPrependFixer(sourceCode, node.argument, fixer);
}
}]
});
}

@@ -126,0 +265,0 @@ };

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,6 +25,5 @@ meta: {

docs: {
description: "disallow the use of the `__proto__` property",
category: "Best Practices",
description: "Disallow the use of the `__proto__` property",
recommended: false,
url: "https://eslint.org/docs/rules/no-proto"
url: "https://eslint.org/docs/latest/rules/no-proto"
},

@@ -30,0 +30,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -23,6 +24,5 @@ meta: {

docs: {
description: "disallow calling some `Object.prototype` methods directly on objects",
category: "Possible Errors",
description: "Disallow calling some `Object.prototype` methods directly on objects",
recommended: true,
url: "https://eslint.org/docs/rules/no-prototype-builtins"
url: "https://eslint.org/docs/latest/rules/no-prototype-builtins"
},

@@ -38,7 +38,7 @@

create(context) {
const DISALLOWED_PROPS = [
const DISALLOWED_PROPS = new Set([
"hasOwnProperty",
"isPrototypeOf",
"propertyIsEnumerable"
];
]);

@@ -60,3 +60,3 @@ /**

if (propName !== null && DISALLOWED_PROPS.indexOf(propName) > -1) {
if (propName !== null && DISALLOWED_PROPS.has(propName)) {
context.report({

@@ -63,0 +63,0 @@ messageId: "prototypeBuildIn",

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,6 +25,5 @@ meta: {

docs: {
description: "disallow variable redeclaration",
category: "Best Practices",
description: "Disallow variable redeclaration",
recommended: true,
url: "https://eslint.org/docs/rules/no-redeclare"
url: "https://eslint.org/docs/latest/rules/no-redeclare"
},

@@ -55,3 +55,3 @@

};
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -135,3 +135,3 @@ /**

function checkForBlock(node) {
const scope = context.getScope();
const scope = sourceCode.getScope(node);

@@ -148,4 +148,4 @@ /*

return {
Program() {
const scope = context.getScope();
Program(node) {
const scope = sourceCode.getScope(node);

@@ -170,2 +170,4 @@ findVariablesInScope(scope);

StaticBlock: checkForBlock,
BlockStatement: checkForBlock,

@@ -172,0 +174,0 @@ ForStatement: checkForBlock,

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

const astUtils = require("./utils/ast-utils");
const regexpp = require("regexpp");
const regexpp = require("@eslint-community/regexpp");

@@ -37,2 +37,3 @@ //------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -43,6 +44,5 @@ meta: {

docs: {
description: "disallow multiple spaces in regular expressions",
category: "Possible Errors",
description: "Disallow multiple spaces in regular expressions",
recommended: true,
url: "https://eslint.org/docs/rules/no-regex-spaces"
url: "https://eslint.org/docs/latest/rules/no-regex-spaces"
},

@@ -60,2 +60,4 @@

const sourceCode = context.sourceCode;
/**

@@ -82,3 +84,3 @@ * Validate regular expression

try {
regExpAST = regExpParser.parsePattern(pattern, 0, pattern.length, flags.includes("u"));
regExpAST = regExpParser.parsePattern(pattern, 0, pattern.length, { unicode: flags.includes("u"), unicodeSets: flags.includes("v") });
} catch {

@@ -157,7 +159,6 @@

function checkFunction(node) {
const scope = context.getScope();
const scope = sourceCode.getScope(node);
const regExpVar = astUtils.getVariableByName(scope, "RegExp");
const shadowed = regExpVar && regExpVar.defs.length > 0;
const patternNode = node.arguments[0];
const flagsNode = node.arguments[1];

@@ -168,4 +169,20 @@ if (node.callee.type === "Identifier" && node.callee.name === "RegExp" && isString(patternNode) && !shadowed) {

const rawPatternStartRange = patternNode.range[0] + 1;
const flags = isString(flagsNode) ? flagsNode.value : "";
let flags;
if (node.arguments.length < 2) {
// It has no flags.
flags = "";
} else {
const flagsNode = node.arguments[1];
if (isString(flagsNode)) {
flags = flagsNode.value;
} else {
// The flags cannot be determined.
return;
}
}
checkRegex(

@@ -172,0 +189,0 @@ node,

@@ -9,5 +9,12 @@ /**

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const astUtils = require("./utils/ast-utils");
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,24 +25,73 @@ meta: {

docs: {
description: "disallow specified names in exports",
category: "ECMAScript 6",
description: "Disallow specified names in exports",
recommended: false,
url: "https://eslint.org/docs/rules/no-restricted-exports"
url: "https://eslint.org/docs/latest/rules/no-restricted-exports"
},
schema: [{
type: "object",
properties: {
restrictedNamedExports: {
type: "array",
items: {
type: "string"
anyOf: [
{
type: "object",
properties: {
restrictedNamedExports: {
type: "array",
items: {
type: "string"
},
uniqueItems: true
}
},
uniqueItems: true
additionalProperties: false
},
{
type: "object",
properties: {
restrictedNamedExports: {
type: "array",
items: {
type: "string",
pattern: "^(?!default$)"
},
uniqueItems: true
},
restrictDefaultExports: {
type: "object",
properties: {
// Allow/Disallow `export default foo; export default 42; export default function foo() {}` format
direct: {
type: "boolean"
},
// Allow/Disallow `export { foo as default };` declarations
named: {
type: "boolean"
},
// Allow/Disallow `export { default } from "mod"; export { default as default } from "mod";` declarations
defaultFrom: {
type: "boolean"
},
// Allow/Disallow `export { foo as default } from "mod";` declarations
namedFrom: {
type: "boolean"
},
// Allow/Disallow `export * as default from "mod"`; declarations
namespaceFrom: {
type: "boolean"
}
},
additionalProperties: false
}
},
additionalProperties: false
}
},
additionalProperties: false
]
}],
messages: {
restrictedNamed: "'{{name}}' is restricted from being used as an exported name."
restrictedNamed: "'{{name}}' is restricted from being used as an exported name.",
restrictedDefault: "Exporting 'default' is restricted."
}

@@ -47,10 +103,12 @@ },

const restrictedNames = new Set(context.options[0] && context.options[0].restrictedNamedExports);
const restrictDefaultExports = context.options[0] && context.options[0].restrictDefaultExports;
const sourceCode = context.sourceCode;
/**
* Checks and reports given exported identifier.
* @param {ASTNode} node exported `Identifier` node to check.
* Checks and reports given exported name.
* @param {ASTNode} node exported `Identifier` or string `Literal` node to check.
* @returns {void}
*/
function checkExportedName(node) {
const name = node.name;
const name = astUtils.getModuleExportName(node);

@@ -63,3 +121,39 @@ if (restrictedNames.has(name)) {

});
return;
}
if (name === "default") {
if (node.parent.type === "ExportAllDeclaration") {
if (restrictDefaultExports && restrictDefaultExports.namespaceFrom) {
context.report({
node,
messageId: "restrictedDefault"
});
}
} else { // ExportSpecifier
const isSourceSpecified = !!node.parent.parent.source;
const specifierLocalName = astUtils.getModuleExportName(node.parent.local);
if (!isSourceSpecified && restrictDefaultExports && restrictDefaultExports.named) {
context.report({
node,
messageId: "restrictedDefault"
});
return;
}
if (isSourceSpecified && restrictDefaultExports) {
if (
(specifierLocalName === "default" && restrictDefaultExports.defaultFrom) ||
(specifierLocalName !== "default" && restrictDefaultExports.namedFrom)
) {
context.report({
node,
messageId: "restrictedDefault"
});
}
}
}
}
}

@@ -74,2 +168,11 @@

ExportDefaultDeclaration(node) {
if (restrictDefaultExports && restrictDefaultExports.direct) {
context.report({
node,
messageId: "restrictedDefault"
});
}
},
ExportNamedDeclaration(node) {

@@ -82,3 +185,3 @@ const declaration = node.declaration;

} else if (declaration.type === "VariableDeclaration") {
context.getDeclaredVariables(declaration)
sourceCode.getDeclaredVariables(declaration)
.map(v => v.defs.find(d => d.parent === declaration))

@@ -85,0 +188,0 @@ .map(d => d.name) // Identifier nodes

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -17,6 +18,5 @@ meta: {

docs: {
description: "disallow specified global variables",
category: "Variables",
description: "Disallow specified global variables",
recommended: false,
url: "https://eslint.org/docs/rules/no-restricted-globals"
url: "https://eslint.org/docs/latest/rules/no-restricted-globals"
},

@@ -48,3 +48,3 @@

defaultMessage: "Unexpected use of '{{name}}'.",
// eslint-disable-next-line eslint-plugin/report-message-format
// eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
customMessage: "Unexpected use of '{{name}}'. {{customMessage}}"

@@ -56,2 +56,4 @@ }

const sourceCode = context.sourceCode;
// If no globals are restricted, we don't need to do anything

@@ -106,4 +108,4 @@ if (context.options.length === 0) {

return {
Program() {
const scope = context.getScope();
Program(node) {
const scope = sourceCode.getScope(node);

@@ -110,0 +112,0 @@ // Report variables declared elsewhere (ex: variables defined as "global" by eslint)

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

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

@@ -14,8 +20,2 @@ //------------------------------------------------------------------------------

const arrayOfStrings = {
type: "array",
items: { type: "string" },
uniqueItems: true
};
const arrayOfStringsOrObjects = {

@@ -49,2 +49,49 @@ type: "array",

const arrayOfStringsOrObjectPatterns = {
anyOf: [
{
type: "array",
items: {
type: "string"
},
uniqueItems: true
},
{
type: "array",
items: {
type: "object",
properties: {
importNames: {
type: "array",
items: {
type: "string"
},
minItems: 1,
uniqueItems: true
},
group: {
type: "array",
items: {
type: "string"
},
minItems: 1,
uniqueItems: true
},
message: {
type: "string",
minLength: 1
},
caseSensitive: {
type: "boolean"
}
},
additionalProperties: false,
required: ["group"]
},
uniqueItems: true
}
]
};
/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -55,6 +102,5 @@ meta: {

docs: {
description: "disallow specified modules when loaded by `import`",
category: "ECMAScript 6",
description: "Disallow specified modules when loaded by `import`",
recommended: false,
url: "https://eslint.org/docs/rules/no-restricted-imports"
url: "https://eslint.org/docs/latest/rules/no-restricted-imports"
},

@@ -64,13 +110,23 @@

path: "'{{importSource}}' import is restricted from being used.",
// eslint-disable-next-line eslint-plugin/report-message-format
// eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
pathWithCustomMessage: "'{{importSource}}' import is restricted from being used. {{customMessage}}",
patterns: "'{{importSource}}' import is restricted from being used by a pattern.",
// eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
patternWithCustomMessage: "'{{importSource}}' import is restricted from being used by a pattern. {{customMessage}}",
patternAndImportName: "'{{importName}}' import from '{{importSource}}' is restricted from being used by a pattern.",
// eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
patternAndImportNameWithCustomMessage: "'{{importName}}' import from '{{importSource}}' is restricted from being used by a pattern. {{customMessage}}",
patternAndEverything: "* import is invalid because '{{importNames}}' from '{{importSource}}' is restricted from being used by a pattern.",
// eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
patternAndEverythingWithCustomMessage: "* import is invalid because '{{importNames}}' from '{{importSource}}' is restricted from being used by a pattern. {{customMessage}}",
everything: "* import is invalid because '{{importNames}}' from '{{importSource}}' is restricted.",
// eslint-disable-next-line eslint-plugin/report-message-format
// eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
everythingWithCustomMessage: "* import is invalid because '{{importNames}}' from '{{importSource}}' is restricted. {{customMessage}}",
importName: "'{{importName}}' import from '{{importSource}}' is restricted.",
// eslint-disable-next-line eslint-plugin/report-message-format
// eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
importNameWithCustomMessage: "'{{importName}}' import from '{{importSource}}' is restricted. {{customMessage}}"

@@ -88,3 +144,3 @@ },

paths: arrayOfStringsOrObjects,
patterns: arrayOfStrings
patterns: arrayOfStringsOrObjectPatterns
},

@@ -100,3 +156,3 @@ additionalProperties: false

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const options = Array.isArray(context.options) ? context.options : [];

@@ -108,9 +164,2 @@ const isPathAndPatternsObject =

const restrictedPaths = (isPathAndPatternsObject ? options[0].paths : context.options) || [];
const restrictedPatterns = (isPathAndPatternsObject ? options[0].patterns : []) || [];
// if no imports are restricted we don"t need to check
if (Object.keys(restrictedPaths).length === 0 && restrictedPatterns.length === 0) {
return {};
}
const restrictedPathMessages = restrictedPaths.reduce((memo, importSource) => {

@@ -128,4 +177,22 @@ if (typeof importSource === "string") {

const restrictedPatternsMatcher = ignore().add(restrictedPatterns);
// Handle patterns too, either as strings or groups
let restrictedPatterns = (isPathAndPatternsObject ? options[0].patterns : []) || [];
// standardize to array of objects if we have an array of strings
if (restrictedPatterns.length > 0 && typeof restrictedPatterns[0] === "string") {
restrictedPatterns = [{ group: restrictedPatterns }];
}
// relative paths are supported for this rule
const restrictedPatternGroups = restrictedPatterns.map(({ group, message, caseSensitive, importNames }) => ({
matcher: ignore({ allowRelativePaths: true, ignorecase: !caseSensitive }).add(group),
customMessage: message,
importNames
}));
// if no imports are restricted we don't need to check
if (Object.keys(restrictedPaths).length === 0 && restrictedPatternGroups.length === 0) {
return {};
}
/**

@@ -196,14 +263,64 @@ * Report a restricted path.

* @param {node} node representing the restricted path reference
* @param {Object} group contains an Ignore instance for paths, the customMessage to show on failure,
* and any restricted import names that have been specified in the config
* @param {Map<string,Object[]>} importNames Map of import names that are being imported
* @returns {void}
* @private
*/
function reportPathForPatterns(node) {
function reportPathForPatterns(node, group, importNames) {
const importSource = node.source.value.trim();
context.report({
node,
messageId: "patterns",
data: {
importSource
const customMessage = group.customMessage;
const restrictedImportNames = group.importNames;
/*
* If we are not restricting to any specific import names and just the pattern itself,
* report the error and move on
*/
if (!restrictedImportNames) {
context.report({
node,
messageId: customMessage ? "patternWithCustomMessage" : "patterns",
data: {
importSource,
customMessage
}
});
return;
}
if (importNames.has("*")) {
const specifierData = importNames.get("*")[0];
context.report({
node,
messageId: customMessage ? "patternAndEverythingWithCustomMessage" : "patternAndEverything",
loc: specifierData.loc,
data: {
importSource,
importNames: restrictedImportNames,
customMessage
}
});
}
restrictedImportNames.forEach(importName => {
if (!importNames.has(importName)) {
return;
}
const specifiers = importNames.get(importName);
specifiers.forEach(specifier => {
context.report({
node,
messageId: customMessage ? "patternAndImportNameWithCustomMessage" : "patternAndImportName",
loc: specifier.loc,
data: {
importSource,
customMessage,
importName
}
});
});
});

@@ -215,7 +332,8 @@ }

* @param {string} importSource path of the import
* @param {Object} group contains a Ignore instance for paths, and the customMessage to show if it fails
* @returns {boolean} whether the variable is a restricted pattern or not
* @private
*/
function isRestrictedPattern(importSource) {
return restrictedPatterns.length > 0 && restrictedPatternsMatcher.ignores(importSource);
function isRestrictedPattern(importSource, group) {
return group.matcher.ignores(importSource);
}

@@ -247,8 +365,8 @@

} else if (specifier.imported) {
name = specifier.imported.name;
name = astUtils.getModuleExportName(specifier.imported);
} else if (specifier.local) {
name = specifier.local.name;
name = astUtils.getModuleExportName(specifier.local);
}
if (name) {
if (typeof name === "string") {
if (importNames.has(name)) {

@@ -264,6 +382,7 @@ importNames.get(name).push(specifierData);

checkRestrictedPathAndReport(importSource, importNames, node);
if (isRestrictedPattern(importSource)) {
reportPathForPatterns(node);
}
restrictedPatternGroups.forEach(group => {
if (isRestrictedPattern(importSource, group)) {
reportPathForPatterns(node, group, importNames);
}
});
}

@@ -270,0 +389,0 @@

/**
* @fileoverview Restrict usage of specified node modules.
* @author Christian Schulz
* @deprecated in ESLint v7.0.0
*/

@@ -8,2 +9,8 @@ "use strict";

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

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -52,6 +60,5 @@ meta: {

docs: {
description: "disallow specified modules when loaded by `require`",
category: "Node.js and CommonJS",
description: "Disallow specified modules when loaded by `require`",
recommended: false,
url: "https://eslint.org/docs/rules/no-restricted-modules"
url: "https://eslint.org/docs/latest/rules/no-restricted-modules"
},

@@ -79,3 +86,3 @@

defaultMessage: "'{{name}}' module is restricted from being used.",
// eslint-disable-next-line eslint-plugin/report-message-format
// eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
customMessage: "'{{name}}' module is restricted from being used. {{customMessage}}",

@@ -104,3 +111,3 @@ patternMessage: "'{{name}}' module is restricted from being used by a pattern."

// if no imports are restricted we don"t need to check
// if no imports are restricted we don't need to check
if (Object.keys(restrictedPaths).length === 0 && restrictedPatterns.length === 0) {

@@ -110,3 +117,4 @@ return {};

const ig = ignore().add(restrictedPatterns);
// relative paths are supported for this rule
const ig = ignore({ allowRelativePaths: true }).add(restrictedPatterns);

@@ -124,11 +132,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.

@@ -152,3 +151,3 @@ * @param {ASTNode} node The node to check.

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

@@ -155,0 +154,0 @@ }

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -20,6 +21,5 @@ meta: {

docs: {
description: "disallow certain properties on certain objects",
category: "Best Practices",
description: "Disallow certain properties on certain objects",
recommended: false,
url: "https://eslint.org/docs/rules/no-restricted-properties"
url: "https://eslint.org/docs/latest/rules/no-restricted-properties"
},

@@ -69,5 +69,5 @@

messages: {
// eslint-disable-next-line eslint-plugin/report-message-format
// eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
restrictedObjectProperty: "'{{objectName}}.{{propertyName}}' is restricted from being used.{{message}}",
// eslint-disable-next-line eslint-plugin/report-message-format
// eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
restrictedProperty: "'{{propertyName}}' is restricted from being used.{{message}}"

@@ -74,0 +74,0 @@ }

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -17,6 +18,5 @@ meta: {

docs: {
description: "disallow specified syntax",
category: "Stylistic Issues",
description: "Disallow specified syntax",
recommended: false,
url: "https://eslint.org/docs/rules/no-restricted-syntax"
url: "https://eslint.org/docs/latest/rules/no-restricted-syntax"
},

@@ -47,3 +47,3 @@

messages: {
// eslint-disable-next-line eslint-plugin/report-message-format
// eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
restrictedSyntax: "{{message}}"

@@ -50,0 +50,0 @@ }

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -29,6 +30,5 @@ meta: {

docs: {
description: "disallow assignment operators in `return` statements",
category: "Best Practices",
description: "Disallow assignment operators in `return` statements",
recommended: false,
url: "https://eslint.org/docs/rules/no-return-assign"
url: "https://eslint.org/docs/latest/rules/no-return-assign"
},

@@ -50,3 +50,3 @@

const always = (context.options[0] || "except-parens") !== "except-parens";
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -53,0 +53,0 @@ return {

/**
* @fileoverview Disallows unnecessary `return await`
* @author Jordan Harband
* @deprecated in ESLint v8.46.0
*/

@@ -13,13 +14,14 @@ "use strict";

/** @type {import('../shared/types').Rule} */
module.exports = {
meta: {
hasSuggestions: true,
type: "suggestion",
docs: {
description: "disallow unnecessary `return await`",
category: "Best Practices",
description: "Disallow unnecessary `return await`",
recommended: false,
url: "https://eslint.org/docs/rules/no-return-await"
url: "https://eslint.org/docs/latest/rules/no-return-await"
},

@@ -29,2 +31,6 @@

deprecated: true,
replacedBy: [],
schema: [

@@ -34,2 +40,3 @@ ],

messages: {
removeAwait: "Remove redundant `await`.",
redundantUseOfAwait: "Redundant use of `await` on a return value."

@@ -48,5 +55,30 @@ }

context.report({
node: context.getSourceCode().getFirstToken(node),
node: context.sourceCode.getFirstToken(node),
loc: node.loc,
messageId: "redundantUseOfAwait"
messageId: "redundantUseOfAwait",
suggest: [
{
messageId: "removeAwait",
fix(fixer) {
const sourceCode = context.sourceCode;
const [awaitToken, tokenAfterAwait] = sourceCode.getFirstTokens(node, 2);
const areAwaitAndAwaitedExpressionOnTheSameLine = awaitToken.loc.start.line === tokenAfterAwait.loc.start.line;
if (!areAwaitAndAwaitedExpressionOnTheSameLine) {
return null;
}
const [startOfAwait, endOfAwait] = awaitToken.range;
const characterAfterAwait = sourceCode.text[endOfAwait];
const trimLength = characterAfterAwait === " " ? 1 : 0;
const range = [startOfAwait, endOfAwait + trimLength];
return fixer.removeRange(range);
}
}
]
});

@@ -53,0 +85,0 @@ }

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

*/
/* jshint scripturl: true */
/* eslint no-script-url: 0 */
/* eslint no-script-url: 0 -- Code is checking to report such URLs */

@@ -17,2 +16,3 @@ "use strict";

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -23,6 +23,5 @@ meta: {

docs: {
description: "disallow `javascript:` urls",
category: "Best Practices",
description: "Disallow `javascript:` urls",
recommended: false,
url: "https://eslint.org/docs/rules/no-script-url"
url: "https://eslint.org/docs/latest/rules/no-script-url"
},

@@ -29,0 +28,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -133,6 +134,5 @@ meta: {

docs: {
description: "disallow assignments where both sides are exactly the same",
category: "Best Practices",
description: "Disallow assignments where both sides are exactly the same",
recommended: true,
url: "https://eslint.org/docs/rules/no-self-assign"
url: "https://eslint.org/docs/latest/rules/no-self-assign"
},

@@ -159,3 +159,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const [{ props = true } = {}] = context.options;

@@ -180,3 +180,3 @@

AssignmentExpression(node) {
if (node.operator === "=") {
if (["=", "&&=", "||=", "??="].includes(node.operator)) {
eachSelfAssignment(node.left, node.right, props, report);

@@ -183,0 +183,0 @@ }

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -19,6 +20,5 @@ meta: {

docs: {
description: "disallow comparisons where both sides are exactly the same",
category: "Best Practices",
description: "Disallow comparisons where both sides are exactly the same",
recommended: false,
url: "https://eslint.org/docs/rules/no-self-compare"
url: "https://eslint.org/docs/latest/rules/no-self-compare"
},

@@ -34,3 +34,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -37,0 +37,0 @@ /**

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

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
const DEFAULT_OPTIONS = {
allowInParentheses: true
};
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,9 +33,16 @@ meta: {

docs: {
description: "disallow comma operators",
category: "Best Practices",
description: "Disallow comma operators",
recommended: false,
url: "https://eslint.org/docs/rules/no-sequences"
url: "https://eslint.org/docs/latest/rules/no-sequences"
},
schema: [],
schema: [{
properties: {
allowInParentheses: {
type: "boolean",
default: true
}
},
additionalProperties: false
}],

@@ -39,3 +55,4 @@ messages: {

create(context) {
const sourceCode = context.getSourceCode();
const options = Object.assign({}, DEFAULT_OPTIONS, context.options[0]);
const sourceCode = context.sourceCode;

@@ -105,10 +122,12 @@ /**

// Wrapping a sequence in extra parens indicates intent
if (requiresExtraParens(node)) {
if (isParenthesisedTwice(node)) {
return;
if (options.allowInParentheses) {
if (requiresExtraParens(node)) {
if (isParenthesisedTwice(node)) {
return;
}
} else {
if (isParenthesised(node)) {
return;
}
}
} else {
if (isParenthesised(node)) {
return;
}
}

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

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

const astUtils = require("./utils/ast-utils");
const { findVariable } = require("eslint-utils");
const { findVariable } = require("@eslint-community/eslint-utils");

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

if (
(parent.type === "Property" || parent.type === "MethodDefinition") &&
parent.kind === "set" &&

@@ -140,2 +141,3 @@ parent.value === node

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -146,6 +148,5 @@ meta: {

docs: {
description: "disallow returning values from setters",
category: "Possible Errors",
description: "Disallow returning values from setters",
recommended: true,
url: "https://eslint.org/docs/rules/no-setter-return"
url: "https://eslint.org/docs/latest/rules/no-setter-return"
},

@@ -162,2 +163,3 @@

let funcInfo = null;
const sourceCode = context.sourceCode;

@@ -170,3 +172,3 @@ /**

function enterFunction(node) {
const outerScope = getOuterScope(context.getScope());
const outerScope = getOuterScope(sourceCode.getScope(node));

@@ -173,0 +175,0 @@ funcInfo = {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -30,6 +31,5 @@ meta: {

docs: {
description: "disallow identifiers from shadowing restricted names",
category: "Variables",
description: "Disallow identifiers from shadowing restricted names",
recommended: true,
url: "https://eslint.org/docs/rules/no-shadow-restricted-names"
url: "https://eslint.org/docs/latest/rules/no-shadow-restricted-names"
},

@@ -48,6 +48,7 @@

const RESTRICTED = new Set(["undefined", "NaN", "Infinity", "arguments", "eval"]);
const sourceCode = context.sourceCode;
return {
"VariableDeclaration, :function, CatchClause"(node) {
for (const variable of context.getDeclaredVariables(node)) {
for (const variable of sourceCode.getDeclaredVariables(node)) {
if (variable.defs.length > 0 && RESTRICTED.has(variable.name) && !safelyShadowsUndefined(variable)) {

@@ -54,0 +55,0 @@ context.report({

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

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
const FUNC_EXPR_NODE_TYPES = new Set(["ArrowFunctionExpression", "FunctionExpression"]);
const CALL_EXPR_NODE_TYPE = new Set(["CallExpression"]);
const FOR_IN_OF_TYPE = /^For(?:In|Of)Statement$/u;
const SENTINEL_TYPE = /^(?:(?:Function|Class)(?:Declaration|Expression)|ArrowFunctionExpression|CatchClause|ImportDeclaration|ExportNamedDeclaration)$/u;
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,6 +34,5 @@ meta: {

docs: {
description: "disallow variable declarations from shadowing variables declared in the outer scope",
category: "Variables",
description: "Disallow variable declarations from shadowing variables declared in the outer scope",
recommended: false,
url: "https://eslint.org/docs/rules/no-shadow"
url: "https://eslint.org/docs/latest/rules/no-shadow"
},

@@ -42,3 +51,4 @@

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

@@ -60,12 +70,113 @@ additionalProperties: false

hoist: (context.options[0] && context.options[0].hoist) || "functions",
allow: (context.options[0] && context.options[0].allow) || []
allow: (context.options[0] && context.options[0].allow) || [],
ignoreOnInitialization: context.options[0] && context.options[0].ignoreOnInitialization
};
const sourceCode = context.sourceCode;
/**
* Checks whether or not a given location is inside of the range of a given node.
* @param {ASTNode} node An node to check.
* @param {number} location A location to check.
* @returns {boolean} `true` if the location is inside of the range of the node.
*/
function isInRange(node, location) {
return node && node.range[0] <= location && location <= node.range[1];
}
/**
* Searches from the current node through its ancestry to find a matching node.
* @param {ASTNode} node a node to get.
* @param {(node: ASTNode) => boolean} match a callback that checks whether or not the node verifies its condition or not.
* @returns {ASTNode|null} the matching node.
*/
function findSelfOrAncestor(node, match) {
let currentNode = node;
while (currentNode && !match(currentNode)) {
currentNode = currentNode.parent;
}
return currentNode;
}
/**
* Finds function's outer scope.
* @param {Scope} scope Function's own scope.
* @returns {Scope} Function's outer scope.
*/
function getOuterScope(scope) {
const upper = scope.upper;
if (upper.type === "function-expression-name") {
return upper.upper;
}
return upper;
}
/**
* Checks if a variable and a shadowedVariable have the same init pattern ancestor.
* @param {Object} variable a variable to check.
* @param {Object} shadowedVariable a shadowedVariable to check.
* @returns {boolean} Whether or not the variable and the shadowedVariable have the same init pattern ancestor.
*/
function isInitPatternNode(variable, shadowedVariable) {
const outerDef = shadowedVariable.defs[0];
if (!outerDef) {
return false;
}
const { variableScope } = variable.scope;
if (!(FUNC_EXPR_NODE_TYPES.has(variableScope.block.type) && getOuterScope(variableScope) === shadowedVariable.scope)) {
return false;
}
const fun = variableScope.block;
const { parent } = fun;
const callExpression = findSelfOrAncestor(
parent,
node => CALL_EXPR_NODE_TYPE.has(node.type)
);
if (!callExpression) {
return false;
}
let node = outerDef.name;
const location = callExpression.range[1];
while (node) {
if (node.type === "VariableDeclarator") {
if (isInRange(node.init, location)) {
return true;
}
if (FOR_IN_OF_TYPE.test(node.parent.parent.type) &&
isInRange(node.parent.parent.right, location)
) {
return true;
}
break;
} else if (node.type === "AssignmentPattern") {
if (isInRange(node.right, location)) {
return true;
}
} else if (SENTINEL_TYPE.test(node.type)) {
break;
}
node = node.parent;
}
return false;
}
/**
* Check if variable name is allowed.
* @param {ASTNode} variable The variable to check.
* @param {ASTNode} variable The variable to check.
* @returns {boolean} Whether or not the variable name is allowed.
*/
function isAllowed(variable) {
return options.allow.indexOf(variable.name) !== -1;
return options.allow.includes(variable.name);
}

@@ -106,7 +217,7 @@

outer &&
inner &&
outer[0] < inner[0] &&
inner[1] < outer[1] &&
((innerDef.type === "FunctionName" && innerDef.node.type === "FunctionExpression") || innerDef.node.type === "ClassExpression") &&
outerScope === innerScope.upper
inner &&
outer[0] < inner[0] &&
inner[1] < outer[1] &&
((innerDef.type === "FunctionName" && innerDef.node.type === "FunctionExpression") || innerDef.node.type === "ClassExpression") &&
outerScope === innerScope.upper
);

@@ -162,7 +273,7 @@ }

inner &&
outer &&
inner[1] < outer[0] &&
outer &&
inner[1] < outer[0] &&
// Excepts FunctionDeclaration if is {"hoist":"function"}.
(options.hoist !== "functions" || !outerDef || outerDef.node.type !== "FunctionDeclaration")
// Excepts FunctionDeclaration if is {"hoist":"function"}.
(options.hoist !== "functions" || !outerDef || outerDef.node.type !== "FunctionDeclaration")
);

@@ -184,4 +295,4 @@ }

if (variable.identifiers.length === 0 ||
isDuplicatedClassNameVariable(variable) ||
isAllowed(variable)
isDuplicatedClassNameVariable(variable) ||
isAllowed(variable)
) {

@@ -195,5 +306,6 @@ continue;

if (shadowed &&
(shadowed.identifiers.length > 0 || (options.builtinGlobals && "writeable" in shadowed)) &&
!isOnInitializer(variable, shadowed) &&
!(options.hoist !== "all" && isInTdz(variable, shadowed))
(shadowed.identifiers.length > 0 || (options.builtinGlobals && "writeable" in shadowed)) &&
!isOnInitializer(variable, shadowed) &&
!(options.ignoreOnInitialization && isInitPatternNode(variable, shadowed)) &&
!(options.hoist !== "all" && isInTdz(variable, shadowed))
) {

@@ -218,4 +330,4 @@ const location = getDeclaredLocation(shadowed);

return {
"Program:exit"() {
const globalScope = context.getScope();
"Program:exit"(node) {
const globalScope = sourceCode.getScope(node);
const stack = globalScope.childScopes.slice();

@@ -222,0 +334,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -19,6 +20,5 @@ meta: {

docs: {
description: "disallow spacing between function identifiers and their applications (deprecated)",
category: "Stylistic Issues",
description: "Disallow spacing between function identifiers and their applications (deprecated)",
recommended: false,
url: "https://eslint.org/docs/rules/no-spaced-func"
url: "https://eslint.org/docs/latest/rules/no-spaced-func"
},

@@ -40,3 +40,3 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -43,0 +43,0 @@ /**

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -17,6 +18,5 @@ meta: {

docs: {
description: "disallow sparse arrays",
category: "Possible Errors",
description: "Disallow sparse arrays",
recommended: true,
url: "https://eslint.org/docs/rules/no-sparse-arrays"
url: "https://eslint.org/docs/latest/rules/no-sparse-arrays"
},

@@ -42,3 +42,3 @@

const emptySpot = node.elements.indexOf(null) > -1;
const emptySpot = node.elements.includes(null);

@@ -45,0 +45,0 @@ if (emptySpot) {

/**
* @fileoverview Rule to check for properties whose identifier ends with the string Sync
* @author Matt DuVall<http://mattduvall.com/>
* @deprecated in ESLint v7.0.0
*/
/* jshint node:true */
"use strict";

@@ -14,2 +13,3 @@

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,6 +24,5 @@ meta: {

docs: {
description: "disallow synchronous methods",
category: "Node.js and CommonJS",
description: "Disallow synchronous methods",
recommended: false,
url: "https://eslint.org/docs/rules/no-sync"
url: "https://eslint.org/docs/latest/rules/no-sync"
},

@@ -30,0 +29,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -25,6 +26,5 @@ meta: {

docs: {
description: "disallow all tabs",
category: "Stylistic Issues",
description: "Disallow all tabs",
recommended: false,
url: "https://eslint.org/docs/rules/no-tabs"
url: "https://eslint.org/docs/latest/rules/no-tabs"
},

@@ -48,3 +48,3 @@ schema: [{

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const allowIndentationTabs = context.options && context.options[0] && context.options[0].allowIndentationTabs;

@@ -51,0 +51,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -17,6 +18,5 @@ meta: {

docs: {
description: "disallow template literal placeholder syntax in regular strings",
category: "Possible Errors",
description: "Disallow template literal placeholder syntax in regular strings",
recommended: false,
url: "https://eslint.org/docs/rules/no-template-curly-in-string"
url: "https://eslint.org/docs/latest/rules/no-template-curly-in-string"
},

@@ -23,0 +23,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "disallow ternary operators",
category: "Stylistic Issues",
description: "Disallow ternary operators",
recommended: false,
url: "https://eslint.org/docs/rules/no-ternary"
url: "https://eslint.org/docs/latest/rules/no-ternary"
},

@@ -24,0 +24,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -43,6 +44,5 @@ meta: {

docs: {
description: "disallow `this`/`super` before calling `super()` in constructors",
category: "ECMAScript 6",
description: "Disallow `this`/`super` before calling `super()` in constructors",
recommended: true,
url: "https://eslint.org/docs/rules/no-this-before-super"
url: "https://eslint.org/docs/latest/rules/no-this-before-super"
},

@@ -96,2 +96,17 @@

/**
* Determines if every segment in a set has been called.
* @param {Set<CodePathSegment>} segments The segments to search.
* @returns {boolean} True if every segment has been called; false otherwise.
*/
function isEverySegmentCalled(segments) {
for (const segment of segments) {
if (!isCalled(segment)) {
return false;
}
}
return true;
}
/**
* Checks whether or not this is before `super()` is called.

@@ -103,3 +118,3 @@ * @returns {boolean} `true` if this is before `super()` is called.

isInConstructorOfDerivedClass() &&
!funcInfo.codePath.currentSegments.every(isCalled)
!isEverySegmentCalled(funcInfo.currentSegments)
);

@@ -115,7 +130,5 @@ }

function setInvalid(node) {
const segments = funcInfo.codePath.currentSegments;
const segments = funcInfo.currentSegments;
for (let i = 0; i < segments.length; ++i) {
const segment = segments[i];
for (const segment of segments) {
if (segment.reachable) {

@@ -132,7 +145,5 @@ segInfoMap[segment.id].invalidNodes.push(node);

function setSuperCalled() {
const segments = funcInfo.codePath.currentSegments;
const segments = funcInfo.currentSegments;
for (let i = 0; i < segments.length; ++i) {
const segment = segments[i];
for (const segment of segments) {
if (segment.reachable) {

@@ -165,3 +176,4 @@ segInfoMap[segment.id].superCalled = true;

),
codePath
codePath,
currentSegments: new Set()
};

@@ -173,3 +185,4 @@ } else {

hasExtends: false,
codePath
codePath,
currentSegments: new Set()
};

@@ -222,2 +235,4 @@ }

onCodePathSegmentStart(segment) {
funcInfo.currentSegments.add(segment);
if (!isInConstructorOfDerivedClass()) {

@@ -237,2 +252,14 @@ return;

onUnreachableCodePathSegmentStart(segment) {
funcInfo.currentSegments.add(segment);
},
onUnreachableCodePathSegmentEnd(segment) {
funcInfo.currentSegments.delete(segment);
},
onCodePathSegmentEnd(segment) {
funcInfo.currentSegments.delete(segment);
},
/**

@@ -239,0 +266,0 @@ * Update information of the code path segment when a code path was

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -20,6 +21,5 @@ meta: {

docs: {
description: "disallow throwing literals as exceptions",
category: "Best Practices",
description: "Disallow throwing literals as exceptions",
recommended: false,
url: "https://eslint.org/docs/rules/no-throw-literal"
url: "https://eslint.org/docs/latest/rules/no-throw-literal"
},

@@ -26,0 +26,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -23,6 +24,5 @@ meta: {

docs: {
description: "disallow trailing whitespace at the end of lines",
category: "Stylistic Issues",
description: "Disallow trailing whitespace at the end of lines",
recommended: false,
url: "https://eslint.org/docs/rules/no-trailing-spaces"
url: "https://eslint.org/docs/latest/rules/no-trailing-spaces"
},

@@ -55,3 +55,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -58,0 +58,0 @@ const BLANK_CLASS = "[ \t\u00a0\u2000-\u200b\u3000]",

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -20,6 +21,5 @@ meta: {

docs: {
description: "disallow initializing variables to `undefined`",
category: "Variables",
description: "Disallow initializing variables to `undefined`",
recommended: false,
url: "https://eslint.org/docs/rules/no-undef-init"
url: "https://eslint.org/docs/latest/rules/no-undef-init"
},

@@ -37,3 +37,3 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -45,3 +45,3 @@ return {

init = node.init && node.init.name,
scope = context.getScope(),
scope = sourceCode.getScope(node),
undefinedVar = astUtils.getVariableByName(scope, "undefined"),

@@ -48,0 +48,0 @@ shadowed = undefinedVar && undefinedVar.defs.length > 0,

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -32,6 +33,5 @@ meta: {

docs: {
description: "disallow the use of undeclared variables unless mentioned in `/*global */` comments",
category: "Variables",
description: "Disallow the use of undeclared variables unless mentioned in `/*global */` comments",
recommended: true,
url: "https://eslint.org/docs/rules/no-undef"
url: "https://eslint.org/docs/latest/rules/no-undef"
},

@@ -59,6 +59,7 @@

const considerTypeOf = options && options.typeof === true || false;
const sourceCode = context.sourceCode;
return {
"Program:exit"(/* node */) {
const globalScope = context.getScope();
"Program:exit"(node) {
const globalScope = sourceCode.getScope(node);

@@ -65,0 +66,0 @@ globalScope.through.forEach(ref => {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -17,6 +18,5 @@ meta: {

docs: {
description: "disallow the use of `undefined` as an identifier",
category: "Variables",
description: "Disallow the use of `undefined` as an identifier",
recommended: false,
url: "https://eslint.org/docs/rules/no-undefined"
url: "https://eslint.org/docs/latest/rules/no-undefined"
},

@@ -33,2 +33,4 @@

const sourceCode = context.sourceCode;
/**

@@ -72,4 +74,4 @@ * Report an invalid "undefined" identifier node.

return {
"Program:exit"() {
const globalScope = context.getScope();
"Program:exit"(node) {
const globalScope = sourceCode.getScope(node);

@@ -76,0 +78,0 @@ const stack = [globalScope];

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "disallow dangling underscores in identifiers",
category: "Stylistic Issues",
description: "Disallow dangling underscores in identifiers",
recommended: false,
url: "https://eslint.org/docs/rules/no-underscore-dangle"
url: "https://eslint.org/docs/latest/rules/no-underscore-dangle"
},

@@ -54,2 +54,14 @@

default: true
},
enforceInClassFields: {
type: "boolean",
default: false
},
allowInArrayDestructuring: {
type: "boolean",
default: true
},
allowInObjectDestructuring: {
type: "boolean",
default: true
}

@@ -74,3 +86,7 @@ },

const enforceInMethodNames = typeof options.enforceInMethodNames !== "undefined" ? options.enforceInMethodNames : false;
const enforceInClassFields = typeof options.enforceInClassFields !== "undefined" ? options.enforceInClassFields : false;
const allowFunctionParams = typeof options.allowFunctionParams !== "undefined" ? options.allowFunctionParams : true;
const allowInArrayDestructuring = typeof options.allowInArrayDestructuring !== "undefined" ? options.allowInArrayDestructuring : true;
const allowInObjectDestructuring = typeof options.allowInObjectDestructuring !== "undefined" ? options.allowInObjectDestructuring : true;
const sourceCode = context.sourceCode;

@@ -88,3 +104,3 @@ //-------------------------------------------------------------------------

function isAllowed(identifier) {
return ALLOWED_VARIABLES.some(ident => ident === identifier);
return ALLOWED_VARIABLES.includes(identifier);
}

@@ -198,2 +214,3 @@

/**

@@ -206,14 +223,28 @@ * Check if variable expression has a dangling underscore

function checkForDanglingUnderscoreInVariableExpression(node) {
const identifier = node.id.name;
sourceCode.getDeclaredVariables(node).forEach(variable => {
const definition = variable.defs.find(def => def.node === node);
const identifierNode = definition.name;
const identifier = identifierNode.name;
let parent = identifierNode.parent;
if (typeof identifier !== "undefined" && hasDanglingUnderscore(identifier) &&
!isSpecialCaseIdentifierInVariableExpression(identifier) && !isAllowed(identifier)) {
context.report({
node,
messageId: "unexpectedUnderscore",
data: {
identifier
}
});
}
while (!["VariableDeclarator", "ArrayPattern", "ObjectPattern"].includes(parent.type)) {
parent = parent.parent;
}
if (
hasDanglingUnderscore(identifier) &&
!isSpecialCaseIdentifierInVariableExpression(identifier) &&
!isAllowed(identifier) &&
!(allowInArrayDestructuring && parent.type === "ArrayPattern") &&
!(allowInObjectDestructuring && parent.type === "ObjectPattern")
) {
context.report({
node,
messageId: "unexpectedUnderscore",
data: {
identifier
}
});
}
});
}

@@ -263,3 +294,5 @@

data: {
identifier
identifier: node.key.type === "PrivateIdentifier"
? `#${identifier}`
: identifier
}

@@ -270,2 +303,26 @@ });

/**
* Check if a class field has a dangling underscore
* @param {ASTNode} node node to evaluate
* @returns {void}
* @private
*/
function checkForDanglingUnderscoreInClassField(node) {
const identifier = node.key.name;
if (typeof identifier !== "undefined" && hasDanglingUnderscore(identifier) &&
enforceInClassFields &&
!isAllowed(identifier)) {
context.report({
node,
messageId: "unexpectedUnderscore",
data: {
identifier: node.key.type === "PrivateIdentifier"
? `#${identifier}`
: identifier
}
});
}
}
//--------------------------------------------------------------------------

@@ -280,2 +337,3 @@ // Public API

MethodDefinition: checkForDanglingUnderscoreInMethod,
PropertyDefinition: checkForDanglingUnderscoreInClassField,
Property: checkForDanglingUnderscoreInMethod,

@@ -282,0 +340,0 @@ FunctionExpression: checkForDanglingUnderscoreInFunction,

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -23,6 +24,5 @@ meta: {

docs: {
description: "disallow confusing multiline expressions",
category: "Possible Errors",
description: "Disallow confusing multiline expressions",
recommended: true,
url: "https://eslint.org/docs/rules/no-unexpected-multiline"
url: "https://eslint.org/docs/latest/rules/no-unexpected-multiline"
},

@@ -43,3 +43,3 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -46,0 +46,0 @@ /**

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -165,6 +166,5 @@ meta: {

docs: {
description: "disallow unmodified loop conditions",
category: "Best Practices",
description: "Disallow unmodified loop conditions",
recommended: false,
url: "https://eslint.org/docs/rules/no-unmodified-loop-condition"
url: "https://eslint.org/docs/latest/rules/no-unmodified-loop-condition"
},

@@ -180,3 +180,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
let groupMap = null;

@@ -346,4 +346,4 @@

return {
"Program:exit"() {
const queue = [context.getScope()];
"Program:exit"(node) {
const queue = [sourceCode.getScope(node)];

@@ -350,0 +350,0 @@ groupMap = new Map();

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -32,6 +33,5 @@ meta: {

docs: {
description: "disallow ternary operators when simpler alternatives exist",
category: "Stylistic Issues",
description: "Disallow ternary operators when simpler alternatives exist",
recommended: false,
url: "https://eslint.org/docs/rules/no-unneeded-ternary"
url: "https://eslint.org/docs/latest/rules/no-unneeded-ternary"
},

@@ -63,3 +63,3 @@

const defaultAssignment = options.defaultAssignment !== false;
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -150,3 +150,3 @@ /**

messageId: "unnecessaryConditionalAssignment",
fix: fixer => {
fix(fixer) {
const shouldParenthesizeAlternate =

@@ -153,0 +153,0 @@ (

@@ -15,2 +15,18 @@ /**

/**
* Checks all segments in a set and returns true if any are reachable.
* @param {Set<CodePathSegment>} segments The segments to check.
* @returns {boolean} True if any segment is reachable; false otherwise.
*/
function isAnySegmentReachable(segments) {
for (const segment of segments) {
if (segment.reachable) {
return true;
}
}
return false;
}
/**
* Determines whether the given node is the first node in the code path to which a loop statement

@@ -57,2 +73,3 @@ * 'loops' for the next iteration.

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -63,6 +80,5 @@ meta: {

docs: {
description: "disallow loops with a body that allows only one iteration",
category: "Possible Errors",
description: "Disallow loops with a body that allows only one iteration",
recommended: false,
url: "https://eslint.org/docs/rules/no-unreachable-loop"
url: "https://eslint.org/docs/latest/rules/no-unreachable-loop"
},

@@ -96,25 +112,32 @@

let currentCodePath = null;
const codePathSegments = [];
let currentCodePathSegments = new Set();
return {
onCodePathStart(codePath) {
currentCodePath = codePath;
onCodePathStart() {
codePathSegments.push(currentCodePathSegments);
currentCodePathSegments = new Set();
},
onCodePathEnd() {
currentCodePath = currentCodePath.upper;
currentCodePathSegments = codePathSegments.pop();
},
[loopSelector](node) {
onUnreachableCodePathSegmentStart(segment) {
currentCodePathSegments.add(segment);
},
/**
* Ignore unreachable loop statements to avoid unnecessary complexity in the implementation, or false positives otherwise.
* For unreachable segments, the code path analysis does not raise events required for this implementation.
*/
if (currentCodePath.currentSegments.some(segment => segment.reachable)) {
loopsToReport.add(node);
}
onUnreachableCodePathSegmentEnd(segment) {
currentCodePathSegments.delete(segment);
},
onCodePathSegmentEnd(segment) {
currentCodePathSegments.delete(segment);
},
onCodePathSegmentStart(segment, node) {
currentCodePathSegments.add(segment);
if (isLoopingTarget(node)) {

@@ -147,2 +170,14 @@ const loop = node.parent;

[loopSelector](node) {
/**
* Ignore unreachable loop statements to avoid unnecessary complexity in the implementation, or false positives otherwise.
* For unreachable segments, the code path analysis does not raise events required for this implementation.
*/
if (isAnySegmentReachable(currentCodePathSegments)) {
loopsToReport.add(node);
}
},
"Program:exit"() {

@@ -149,0 +184,0 @@ loopsToReport.forEach(

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

/**
* @typedef {Object} ConstructorInfo
* @property {ConstructorInfo | null} upper Info about the constructor that encloses this constructor.
* @property {boolean} hasSuperCall The flag about having `super()` expressions.
*/
/**
* Checks whether or not a given variable declarator has the initializer.

@@ -22,8 +28,15 @@ * @param {ASTNode} node A VariableDeclarator node to check.

/**
* Checks whether or not a given code path segment is unreachable.
* @param {CodePathSegment} segment A CodePathSegment to check.
* @returns {boolean} `true` if the segment is unreachable.
* Checks all segments in a set and returns true if all are unreachable.
* @param {Set<CodePathSegment>} segments The segments to check.
* @returns {boolean} True if all segments are unreachable; false otherwise.
*/
function isUnreachable(segment) {
return !segment.reachable;
function areAllSegmentsUnreachable(segments) {
for (const segment of segments) {
if (segment.reachable) {
return false;
}
}
return true;
}

@@ -104,2 +117,3 @@

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -110,6 +124,5 @@ meta: {

docs: {
description: "disallow unreachable code after `return`, `throw`, `continue`, and `break` statements",
category: "Possible Errors",
description: "Disallow unreachable code after `return`, `throw`, `continue`, and `break` statements",
recommended: true,
url: "https://eslint.org/docs/rules/no-unreachable"
url: "https://eslint.org/docs/latest/rules/no-unreachable"
},

@@ -125,6 +138,15 @@

create(context) {
let currentCodePath = null;
const range = new ConsecutiveRange(context.getSourceCode());
/** @type {ConstructorInfo | null} */
let constructorInfo = null;
/** @type {ConsecutiveRange} */
const range = new ConsecutiveRange(context.sourceCode);
/** @type {Array<Set<CodePathSegment>>} */
const codePathSegments = [];
/** @type {Set<CodePathSegment>} */
let currentCodePathSegments = new Set();
/**

@@ -138,3 +160,3 @@ * Reports a given node if it's unreachable.

if (node && currentCodePath.currentSegments.every(isUnreachable)) {
if (node && (node.type === "PropertyDefinition" || areAllSegmentsUnreachable(currentCodePathSegments))) {

@@ -180,10 +202,27 @@ // Store this statement to distinguish consecutive statements.

// Manages the current code path.
onCodePathStart(codePath) {
currentCodePath = codePath;
onCodePathStart() {
codePathSegments.push(currentCodePathSegments);
currentCodePathSegments = new Set();
},
onCodePathEnd() {
currentCodePath = currentCodePath.upper;
currentCodePathSegments = codePathSegments.pop();
},
onUnreachableCodePathSegmentStart(segment) {
currentCodePathSegments.add(segment);
},
onUnreachableCodePathSegmentEnd(segment) {
currentCodePathSegments.delete(segment);
},
onCodePathSegmentEnd(segment) {
currentCodePathSegments.delete(segment);
},
onCodePathSegmentStart(segment) {
currentCodePathSegments.add(segment);
},
// Registers for all statement nodes (excludes FunctionDeclaration).

@@ -222,2 +261,38 @@ BlockStatement: reportIfUnreachable,

reportIfUnreachable();
},
/*
* Instance fields defined in a subclass are never created if the constructor of the subclass
* doesn't call `super()`, so their definitions are unreachable code.
*/
"MethodDefinition[kind='constructor']"() {
constructorInfo = {
upper: constructorInfo,
hasSuperCall: false
};
},
"MethodDefinition[kind='constructor']:exit"(node) {
const { hasSuperCall } = constructorInfo;
constructorInfo = constructorInfo.upper;
// skip typescript constructors without the body
if (!node.value.body) {
return;
}
const classDefinition = node.parent.parent;
if (classDefinition.superClass && !hasSuperCall) {
for (const element of classDefinition.body.body) {
if (element.type === "PropertyDefinition" && !element.static) {
reportIfUnreachable(element);
}
}
}
},
"CallExpression > Super.callee"() {
if (constructorInfo) {
constructorInfo.hasSuperCall = true;
}
}

@@ -224,0 +299,0 @@ };

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -27,6 +28,5 @@ meta: {

docs: {
description: "disallow control flow statements in `finally` blocks",
category: "Possible Errors",
description: "Disallow control flow statements in `finally` blocks",
recommended: true,
url: "https://eslint.org/docs/rules/no-unsafe-finally"
url: "https://eslint.org/docs/latest/rules/no-unsafe-finally"
},

@@ -33,0 +33,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -55,9 +56,9 @@ meta: {

docs: {
description: "disallow negating the left operand of relational operators",
category: "Possible Errors",
description: "Disallow negating the left operand of relational operators",
recommended: true,
url: "https://eslint.org/docs/rules/no-unsafe-negation",
suggestion: true
url: "https://eslint.org/docs/latest/rules/no-unsafe-negation"
},
hasSuggestions: true,
schema: [

@@ -86,3 +87,3 @@ {

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const options = context.options[0] || {};

@@ -89,0 +90,0 @@ const enforceForOrderingRelations = options.enforceForOrderingRelations === true;

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -27,6 +28,5 @@ meta: {

docs: {
description: "disallow use of optional chaining in contexts where the `undefined` value is not allowed",
category: "Possible Errors",
recommended: false,
url: "https://eslint.org/docs/rules/no-unsafe-optional-chaining"
description: "Disallow use of optional chaining in contexts where the `undefined` value is not allowed",
recommended: true,
url: "https://eslint.org/docs/latest/rules/no-unsafe-optional-chaining"
},

@@ -33,0 +33,0 @@ schema: [{

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

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

@@ -28,2 +30,3 @@ // Rule Definition

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -34,6 +37,5 @@ meta: {

docs: {
description: "disallow unused expressions",
category: "Best Practices",
description: "Disallow unused expressions",
recommended: false,
url: "https://eslint.org/docs/rules/no-unused-expressions"
url: "https://eslint.org/docs/latest/rules/no-unused-expressions"
},

@@ -78,4 +80,4 @@

// eslint-disable-next-line jsdoc/require-description
/**
* Has AST suggesting a directive.
* @param {ASTNode} node any node

@@ -89,4 +91,4 @@ * @returns {boolean} whether the given node structurally represents a directive

// eslint-disable-next-line jsdoc/require-description
/**
* Gets the leading sequence of members in a list that pass the predicate.
* @param {Function} predicate ([a] -> Boolean) the function used to make the determination

@@ -105,4 +107,4 @@ * @param {a[]} list the input list

// eslint-disable-next-line jsdoc/require-description
/**
* Gets leading directives nodes in a Node body.
* @param {ASTNode} node a Program or BlockStatement node

@@ -115,15 +117,16 @@ * @returns {ASTNode[]} the leading sequence of directive nodes in the given node's body

// eslint-disable-next-line jsdoc/require-description
/**
* Detect if a Node is a directive.
* @param {ASTNode} node any node
* @param {ASTNode[]} ancestors the given node's ancestors
* @returns {boolean} whether the given node is considered a directive in its current position
*/
function isDirective(node, ancestors) {
const parent = ancestors[ancestors.length - 1],
grandparent = ancestors[ancestors.length - 2];
function isDirective(node) {
return (parent.type === "Program" || parent.type === "BlockStatement" &&
(/Function/u.test(grandparent.type))) &&
directives(parent).indexOf(node) >= 0;
/**
* https://tc39.es/ecma262/#directive-prologue
*
* Only `FunctionBody`, `ScriptBody` and `ModuleBody` can have directive prologue.
* Class static blocks do not have directive prologue.
*/
return astUtils.isTopLevelExpressionStatement(node) && directives(node.parent).includes(node);
}

@@ -184,3 +187,3 @@

ExpressionStatement(node) {
if (Checker.isDisallowed(node.expression) && !isDirective(node, context.getAncestors())) {
if (Checker.isDisallowed(node.expression) && !isDirective(node)) {
context.report({ node, messageId: "unusedExpression" });

@@ -187,0 +190,0 @@ }

@@ -9,5 +9,12 @@ /**

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const astUtils = require("./utils/ast-utils");
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +25,5 @@ meta: {

docs: {
description: "disallow unused labels",
category: "Best Practices",
description: "Disallow unused labels",
recommended: true,
url: "https://eslint.org/docs/rules/no-unused-labels"
url: "https://eslint.org/docs/latest/rules/no-unused-labels"
},

@@ -35,3 +41,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
let scopeInfo = null;

@@ -53,2 +59,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.

@@ -65,15 +110,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
});

@@ -80,0 +113,0 @@ }

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -36,6 +37,5 @@ meta: {

docs: {
description: "disallow unused variables",
category: "Variables",
description: "Disallow unused variables",
recommended: true,
url: "https://eslint.org/docs/rules/no-unused-vars"
url: "https://eslint.org/docs/latest/rules/no-unused-vars"
},

@@ -72,2 +72,5 @@

type: "string"
},
destructuredArrayIgnorePattern: {
type: "string"
}

@@ -87,3 +90,3 @@ },

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -121,2 +124,6 @@ const REST_PROPERTY_TYPE = /^(?:RestElement|(?:Experimental)?RestProperty)$/u;

}
if (firstOption.destructuredArrayIgnorePattern) {
config.destructuredArrayIgnorePattern = new RegExp(firstOption.destructuredArrayIgnorePattern, "u");
}
}

@@ -163,4 +170,11 @@ }

function getAssignedMessageData(unusedVar) {
const additional = config.varsIgnorePattern ? `. Allowed unused vars must match ${config.varsIgnorePattern.toString()}` : "";
const def = unusedVar.defs[0];
let additional = "";
if (config.destructuredArrayIgnorePattern && def && def.name.parent.type === "ArrayPattern") {
additional = `. Allowed unused elements of array destructuring patterns must match ${config.destructuredArrayIgnorePattern.toString()}`;
} else if (config.varsIgnorePattern) {
additional = `. Allowed unused vars must match ${config.varsIgnorePattern.toString()}`;
}
return {

@@ -206,2 +220,13 @@ varName: unusedVar.name,

/**
* Checks whether a node is a sibling of the rest property or not.
* @param {ASTNode} node a node to check
* @returns {boolean} True if the node is a sibling of the rest property, otherwise false.
*/
function hasRestSibling(node) {
return node.type === "Property" &&
node.parent.type === "ObjectPattern" &&
REST_PROPERTY_TYPE.test(node.parent.properties[node.parent.properties.length - 1].type);
}
/**
* Determines if a variable has a sibling rest property

@@ -214,12 +239,6 @@ * @param {Variable} variable eslint-scope variable object.

if (config.ignoreRestSiblings) {
return variable.defs.some(def => {
const propertyNode = def.name.parent;
const patternNode = propertyNode.parent;
const hasRestSiblingDefinition = variable.defs.some(def => hasRestSibling(def.name.parent));
const hasRestSiblingReference = variable.references.some(ref => hasRestSibling(ref.identifier.parent));
return (
propertyNode.type === "Property" &&
patternNode.type === "ObjectPattern" &&
REST_PROPERTY_TYPE.test(patternNode.properties[patternNode.properties.length - 1].type)
);
});
return hasRestSiblingDefinition || hasRestSiblingReference;
}

@@ -251,3 +270,3 @@

while (scope) {
if (nodes.indexOf(scope.block) >= 0) {
if (nodes.includes(scope.block)) {
return true;

@@ -303,2 +322,27 @@ }

/**
* Checks whether a given node is unused expression or not.
* @param {ASTNode} node The node itself
* @returns {boolean} The node is an unused expression.
* @private
*/
function isUnusedExpression(node) {
const parent = node.parent;
if (parent.type === "ExpressionStatement") {
return true;
}
if (parent.type === "SequenceExpression") {
const isLastExpression = parent.expressions[parent.expressions.length - 1] === node;
if (!isLastExpression) {
return true;
}
return isUnusedExpression(parent);
}
return false;
}
/**
* If a given reference is left-hand side of an assignment, this gets

@@ -321,3 +365,2 @@ * the right-hand side node of the assignment.

const parent = id.parent;
const grandparent = parent.parent;
const refScope = ref.from.variableScope;

@@ -336,3 +379,3 @@ const varScope = ref.resolved.scope.variableScope;

if (parent.type === "AssignmentExpression" &&
grandparent.type === "ExpressionStatement" &&
isUnusedExpression(parent) &&
id === parent.left &&

@@ -430,3 +473,2 @@ !canBeUsedLater

const parent = id.parent;
const grandparent = parent.parent;

@@ -436,14 +478,21 @@ return ref.isRead() && (

// self update. e.g. `a += 1`, `a++`
(// in RHS of an assignment for itself. e.g. `a = a + 1`
((
(
(
parent.type === "AssignmentExpression" &&
grandparent.type === "ExpressionStatement" &&
parent.left === id
parent.left === id &&
isUnusedExpression(parent) &&
!astUtils.isLogicalAssignmentOperator(parent.operator)
) ||
(
parent.type === "UpdateExpression" &&
isUnusedExpression(parent)
)
) ||
// in RHS of an assignment for itself. e.g. `a = a + 1`
(
parent.type === "UpdateExpression" &&
grandparent.type === "ExpressionStatement"
) || rhsNode &&
isInside(id, rhsNode) &&
!isInsideOfStorableFunction(id, rhsNode)))
rhsNode &&
isInside(id, rhsNode) &&
!isInsideOfStorableFunction(id, rhsNode)
)
);

@@ -453,3 +502,3 @@ }

/**
* Determine if an identifier is used either in for-in loops.
* Determine if an identifier is used either in for-in or for-of loops.
* @param {Reference} ref The reference to check.

@@ -459,3 +508,3 @@ * @returns {boolean} whether reference is used in the for-in loops

*/
function isForInRef(ref) {
function isForInOfRef(ref) {
let target = ref.identifier.parent;

@@ -469,3 +518,3 @@

if (target.type !== "ForInStatement") {
if (target.type !== "ForInStatement" && target.type !== "ForOfStatement") {
return false;

@@ -503,3 +552,3 @@ }

return variable.references.some(ref => {
if (isForInRef(ref)) {
if (isForInOfRef(ref)) {
return true;

@@ -528,3 +577,3 @@ }

const def = variable.defs[0];
const params = context.getDeclaredVariables(def.node);
const params = sourceCode.getDeclaredVariables(def.node);
const posteriorParams = params.slice(params.indexOf(variable) + 1);

@@ -572,3 +621,16 @@

const type = def.type;
const refUsedInArrayPatterns = variable.references.some(ref => ref.identifier.parent.type === "ArrayPattern");
// skip elements of array destructuring patterns
if (
(
def.name.parent.type === "ArrayPattern" ||
refUsedInArrayPatterns
) &&
config.destructuredArrayIgnorePattern &&
config.destructuredArrayIgnorePattern.test(def.name.name)
) {
continue;
}
// skip catch variables

@@ -635,3 +697,3 @@ if (type === "CatchClause") {

"Program:exit"(programNode) {
const unusedVars = collectUnusedVariables(context.getScope(), []);
const unusedVars = collectUnusedVariables(sourceCode.getScope(programNode), []);

@@ -643,6 +705,14 @@ for (let i = 0, l = unusedVars.length; i < l; ++i) {

if (unusedVar.defs.length > 0) {
// report last write reference, https://github.com/eslint/eslint/issues/14324
const writeReferences = unusedVar.references.filter(ref => ref.isWrite() && ref.from.variableScope === unusedVar.scope.variableScope);
let referenceToReport;
if (writeReferences.length > 0) {
referenceToReport = writeReferences[writeReferences.length - 1];
}
context.report({
node: unusedVar.references.length ? unusedVar.references[
unusedVar.references.length - 1
].identifier : unusedVar.identifiers[0],
node: referenceToReport ? referenceToReport.identifier : unusedVar.identifiers[0],
messageId: "unusedVar",

@@ -649,0 +719,0 @@ data: unusedVar.references.some(ref => ref.isWrite())

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

let variables = true;
let allowNamedExports = false;

@@ -32,54 +33,116 @@ if (typeof options === "string") {

variables = options.variables !== false;
allowNamedExports = !!options.allowNamedExports;
}
return { functions, classes, variables };
return { functions, classes, variables, allowNamedExports };
}
/**
* Checks whether or not a given variable is a function declaration.
* @param {eslint-scope.Variable} variable A variable to check.
* @returns {boolean} `true` if the variable is a function declaration.
* Checks whether or not a given location is inside of the range of a given node.
* @param {ASTNode} node An node to check.
* @param {number} location A location to check.
* @returns {boolean} `true` if the location is inside of the range of the node.
*/
function isFunction(variable) {
return variable.defs[0].type === "FunctionName";
function isInRange(node, location) {
return node && node.range[0] <= location && location <= node.range[1];
}
/**
* Checks whether or not a given variable is a class declaration in an upper function scope.
* @param {eslint-scope.Variable} variable A variable to check.
* @param {eslint-scope.Reference} reference A reference to check.
* @returns {boolean} `true` if the variable is a class declaration.
* Checks whether or not a given location is inside of the range of a class static initializer.
* Static initializers are static blocks and initializers of static fields.
* @param {ASTNode} node `ClassBody` node to check static initializers.
* @param {number} location A location to check.
* @returns {boolean} `true` if the location is inside of a class static initializer.
*/
function isOuterClass(variable, reference) {
return (
variable.defs[0].type === "ClassName" &&
variable.scope.variableScope !== reference.from.variableScope
);
function isInClassStaticInitializerRange(node, location) {
return node.body.some(classMember => (
(
classMember.type === "StaticBlock" &&
isInRange(classMember, location)
) ||
(
classMember.type === "PropertyDefinition" &&
classMember.static &&
classMember.value &&
isInRange(classMember.value, location)
)
));
}
/**
* Checks whether or not a given variable is a variable declaration in an upper function scope.
* @param {eslint-scope.Variable} variable A variable to check.
* @param {eslint-scope.Reference} reference A reference to check.
* @returns {boolean} `true` if the variable is a variable declaration.
* Checks whether a given scope is the scope of a class static initializer.
* Static initializers are static blocks and initializers of static fields.
* @param {eslint-scope.Scope} scope A scope to check.
* @returns {boolean} `true` if the scope is a class static initializer scope.
*/
function isOuterVariable(variable, reference) {
return (
variable.defs[0].type === "Variable" &&
variable.scope.variableScope !== reference.from.variableScope
);
function isClassStaticInitializerScope(scope) {
if (scope.type === "class-static-block") {
return true;
}
if (scope.type === "class-field-initializer") {
// `scope.block` is PropertyDefinition#value node
const propertyDefinition = scope.block.parent;
return propertyDefinition.static;
}
return false;
}
/**
* Checks whether or not a given location is inside of the range of a given node.
* @param {ASTNode} node An node to check.
* @param {number} location A location to check.
* @returns {boolean} `true` if the location is inside of the range of the node.
* Checks whether a given reference is evaluated in an execution context
* that isn't the one where the variable it refers to is defined.
* Execution contexts are:
* - top-level
* - functions
* - class field initializers (implicit functions)
* - class static blocks (implicit functions)
* Static class field initializers and class static blocks are automatically run during the class definition evaluation,
* and therefore we'll consider them as a part of the parent execution context.
* Example:
*
* const x = 1;
*
* x; // returns `false`
* () => x; // returns `true`
*
* class C {
* field = x; // returns `true`
* static field = x; // returns `false`
*
* method() {
* x; // returns `true`
* }
*
* static method() {
* x; // returns `true`
* }
*
* static {
* x; // returns `false`
* }
* }
* @param {eslint-scope.Reference} reference A reference to check.
* @returns {boolean} `true` if the reference is from a separate execution context.
*/
function isInRange(node, location) {
return node && node.range[0] <= location && location <= node.range[1];
function isFromSeparateExecutionContext(reference) {
const variable = reference.resolved;
let scope = reference.from;
// Scope#variableScope represents execution context
while (variable.scope.variableScope !== scope.variableScope) {
if (isClassStaticInitializerScope(scope.variableScope)) {
scope = scope.variableScope.upper;
} else {
return true;
}
}
return false;
}
/**
* Checks whether or not a given reference is inside of the initializers of a given variable.
* Checks whether or not a given reference is evaluated during the initialization of its variable.
*

@@ -93,14 +156,42 @@ * This returns `true` in the following cases:

* for (var a of a) {}
* @param {Variable} variable A variable to check.
* var C = class { [C]; };
* var C = class { static foo = C; };
* var C = class { static { foo = C; } };
* class C extends C {}
* class C extends (class { static foo = C; }) {}
* class C { [C]; }
* @param {Reference} reference A reference to check.
* @returns {boolean} `true` if the reference is inside of the initializers.
* @returns {boolean} `true` if the reference is evaluated during the initialization.
*/
function isInInitializer(variable, reference) {
if (variable.scope !== reference.from) {
function isEvaluatedDuringInitialization(reference) {
if (isFromSeparateExecutionContext(reference)) {
/*
* Even if the reference appears in the initializer, it isn't evaluated during the initialization.
* For example, `const x = () => x;` is valid.
*/
return false;
}
let node = variable.identifiers[0].parent;
const location = reference.identifier.range[1];
const definition = reference.resolved.defs[0];
if (definition.type === "ClassName") {
// `ClassDeclaration` or `ClassExpression`
const classDefinition = definition.node;
return (
isInRange(classDefinition, location) &&
/*
* Class binding is initialized before running static initializers.
* For example, `class C { static foo = C; static { bar = C; } }` is valid.
*/
!isInClassStaticInitializerRange(classDefinition.body, location)
);
}
let node = definition.name.parent;
while (node) {

@@ -135,2 +226,3 @@ if (node.type === "VariableDeclarator") {

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -141,6 +233,5 @@ meta: {

docs: {
description: "disallow the use of variables before they are defined",
category: "Variables",
description: "Disallow the use of variables before they are defined",
recommended: false,
url: "https://eslint.org/docs/rules/no-use-before-define"
url: "https://eslint.org/docs/latest/rules/no-use-before-define"
},

@@ -159,3 +250,4 @@

classes: { type: "boolean" },
variables: { type: "boolean" }
variables: { type: "boolean" },
allowNamedExports: { type: "boolean" }
},

@@ -175,19 +267,55 @@ additionalProperties: false

const options = parseOptions(context.options[0]);
const sourceCode = context.sourceCode;
/**
* Determines whether a given use-before-define case should be reported according to the options.
* @param {eslint-scope.Variable} variable The variable that gets used before being defined
* @param {eslint-scope.Reference} reference The reference to the variable
* @returns {boolean} `true` if the usage should be reported
* Determines whether a given reference should be checked.
*
* Returns `false` if the reference is:
* - initialization's (e.g., `let a = 1`).
* - referring to an undefined variable (i.e., if it's an unresolved reference).
* - referring to a variable that is defined, but not in the given source code
* (e.g., global environment variable or `arguments` in functions).
* - allowed by options.
* @param {eslint-scope.Reference} reference The reference
* @returns {boolean} `true` if the reference should be checked
*/
function isForbidden(variable, reference) {
if (isFunction(variable)) {
return options.functions;
function shouldCheck(reference) {
if (reference.init) {
return false;
}
if (isOuterClass(variable, reference)) {
return options.classes;
const { identifier } = reference;
if (
options.allowNamedExports &&
identifier.parent.type === "ExportSpecifier" &&
identifier.parent.local === identifier
) {
return false;
}
if (isOuterVariable(variable, reference)) {
return options.variables;
const variable = reference.resolved;
if (!variable || variable.defs.length === 0) {
return false;
}
const definitionType = variable.defs[0].type;
if (!options.functions && definitionType === "FunctionName") {
return false;
}
if (
(
!options.variables && definitionType === "Variable" ||
!options.classes && definitionType === "ClassName"
) &&
// don't skip checking the reference if it's in the same execution context, because of TDZ
isFromSeparateExecutionContext(reference)
) {
return false;
}
return true;

@@ -197,42 +325,29 @@ }

/**
* Finds and validates all variables in a given scope.
* @param {Scope} scope The scope object.
* Finds and validates all references in a given scope and its child scopes.
* @param {eslint-scope.Scope} scope The scope object.
* @returns {void}
* @private
*/
function findVariablesInScope(scope) {
scope.references.forEach(reference => {
function checkReferencesInScope(scope) {
scope.references.filter(shouldCheck).forEach(reference => {
const variable = reference.resolved;
const definitionIdentifier = variable.defs[0].name;
/*
* Skips when the reference is:
* - initialization's.
* - referring to an undefined variable.
* - referring to a global environment variable (there're no identifiers).
* - located preceded by the variable (except in initializers).
* - allowed by options.
*/
if (reference.init ||
!variable ||
variable.identifiers.length === 0 ||
(variable.identifiers[0].range[1] < reference.identifier.range[1] && !isInInitializer(variable, reference)) ||
!isForbidden(variable, reference)
if (
reference.identifier.range[1] < definitionIdentifier.range[1] ||
isEvaluatedDuringInitialization(reference)
) {
return;
context.report({
node: reference.identifier,
messageId: "usedBeforeDefined",
data: reference.identifier
});
}
// Reports.
context.report({
node: reference.identifier,
messageId: "usedBeforeDefined",
data: reference.identifier
});
});
scope.childScopes.forEach(findVariablesInScope);
scope.childScopes.forEach(checkReferencesInScope);
}
return {
Program() {
findVariablesInScope(context.getScope());
Program(node) {
checkReferencesInScope(sourceCode.getScope(node));
}

@@ -239,0 +354,0 @@ };

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

const { CALL, CONSTRUCT, ReferenceTracker, getStringIfConstant } = require("eslint-utils");
const { RegExpParser, visitRegExpAST } = require("regexpp");
const lodash = require("lodash");
const { CALL, CONSTRUCT, ReferenceTracker, getStringIfConstant } = require("@eslint-community/eslint-utils");
const { RegExpParser, visitRegExpAST } = require("@eslint-community/regexpp");

@@ -63,2 +62,3 @@ //------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -69,6 +69,5 @@ meta: {

docs: {
description: "disallow useless backreferences in regular expressions",
category: "Possible Errors",
recommended: false,
url: "https://eslint.org/docs/rules/no-useless-backreference"
description: "Disallow useless backreferences in regular expressions",
recommended: true,
url: "https://eslint.org/docs/latest/rules/no-useless-backreference"
},

@@ -89,2 +88,4 @@

const sourceCode = context.sourceCode;
/**

@@ -101,3 +102,3 @@ * Checks and reports useless backreferences in the given regular expression.

try {
regExpAST = parser.parsePattern(pattern, 0, pattern.length, flags.includes("u"));
regExpAST = parser.parsePattern(pattern, 0, pattern.length, { unicode: flags.includes("u"), unicodeSets: flags.includes("v") });
} catch {

@@ -145,3 +146,3 @@

messageId = "backward";
} else if (lodash.last(groupCut).type === "Alternative") {
} else if (groupCut[groupCut.length - 1].type === "Alternative") {

@@ -177,4 +178,4 @@ // group's and bref's ancestor nodes below the lowest common ancestor are sibling alternatives => they're disjunctive.

},
Program() {
const scope = context.getScope(),
Program(node) {
const scope = sourceCode.getScope(node),
tracker = new ReferenceTracker(scope),

@@ -188,4 +189,4 @@ traceMap = {

for (const { node } of tracker.iterateGlobalReferences(traceMap)) {
const [patternNode, flagsNode] = node.arguments,
for (const { node: refNode } of tracker.iterateGlobalReferences(traceMap)) {
const [patternNode, flagsNode] = refNode.arguments,
pattern = getStringIfConstant(patternNode, scope),

@@ -195,3 +196,3 @@ flags = getStringIfConstant(flagsNode, scope);

if (typeof pattern === "string") {
checkRegex(node, pattern, flags || "");
checkRegex(refNode, pattern, flags || "");
}

@@ -198,0 +199,0 @@ }

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -58,6 +59,5 @@ meta: {

docs: {
description: "disallow unnecessary calls to `.call()` and `.apply()`",
category: "Best Practices",
description: "Disallow unnecessary calls to `.call()` and `.apply()`",
recommended: false,
url: "https://eslint.org/docs/rules/no-useless-call"
url: "https://eslint.org/docs/latest/rules/no-useless-call"
},

@@ -73,3 +73,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -76,0 +76,0 @@ return {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "disallow unnecessary `catch` clauses",
category: "Best Practices",
description: "Disallow unnecessary `catch` clauses",
recommended: true,
url: "https://eslint.org/docs/rules/no-useless-catch"
url: "https://eslint.org/docs/latest/rules/no-useless-catch"
},

@@ -24,0 +24,0 @@

@@ -11,9 +11,80 @@ /**

const lodash = require("lodash");
const astUtils = require("./utils/ast-utils");
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
/**
* Determines whether the computed key syntax is unnecessarily used for the given node.
* In particular, it determines whether removing the square brackets and using the content between them
* directly as the key (e.g. ['foo'] -> 'foo') would produce valid syntax and preserve the same behavior.
* Valid non-computed keys are only: identifiers, number literals and string literals.
* Only literals can preserve the same behavior, with a few exceptions for specific node types:
* Property
* - { ["__proto__"]: foo } defines a property named "__proto__"
* { "__proto__": foo } defines object's prototype
* PropertyDefinition
* - class C { ["constructor"]; } defines an instance field named "constructor"
* class C { "constructor"; } produces a parsing error
* - class C { static ["constructor"]; } defines a static field named "constructor"
* class C { static "constructor"; } produces a parsing error
* - class C { static ["prototype"]; } produces a runtime error (doesn't break the whole script)
* class C { static "prototype"; } produces a parsing error (breaks the whole script)
* MethodDefinition
* - class C { ["constructor"]() {} } defines a prototype method named "constructor"
* class C { "constructor"() {} } defines the constructor
* - class C { static ["prototype"]() {} } produces a runtime error (doesn't break the whole script)
* class C { static "prototype"() {} } produces a parsing error (breaks the whole script)
* @param {ASTNode} node The node to check. It can be `Property`, `PropertyDefinition` or `MethodDefinition`.
* @throws {Error} (Unreachable.)
* @returns {void} `true` if the node has useless computed key.
*/
function hasUselessComputedKey(node) {
if (!node.computed) {
return false;
}
const { key } = node;
if (key.type !== "Literal") {
return false;
}
const { value } = key;
if (typeof value !== "number" && typeof value !== "string") {
return false;
}
switch (node.type) {
case "Property":
return value !== "__proto__";
case "PropertyDefinition":
if (node.static) {
return value !== "constructor" && value !== "prototype";
}
return value !== "constructor";
case "MethodDefinition":
if (node.static) {
return value !== "prototype";
}
return value !== "constructor";
/* c8 ignore next */
default:
throw new Error(`Unexpected node type: ${node.type}`);
}
}
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,6 +95,5 @@ meta: {

docs: {
description: "disallow unnecessary computed property keys in objects and classes",
category: "ECMAScript 6",
description: "Disallow unnecessary computed property keys in objects and classes",
recommended: false,
url: "https://eslint.org/docs/rules/no-useless-computed-key"
url: "https://eslint.org/docs/latest/rules/no-useless-computed-key"
},

@@ -48,3 +118,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const enforceForClassMembers = context.options[0] && context.options[0].enforceForClassMembers;

@@ -58,18 +128,5 @@

function check(node) {
if (!node.computed) {
return;
}
if (hasUselessComputedKey(node)) {
const { key } = node;
const key = node.key,
nodeType = typeof key.value;
let allowedKey;
if (node.type === "MethodDefinition") {
allowedKey = node.static ? "prototype" : "constructor";
} else {
allowedKey = "__proto__";
}
if (key.type === "Literal" && (nodeType === "string" || nodeType === "number") && key.value !== allowedKey) {
context.report({

@@ -102,7 +159,15 @@ node,

/**
* A no-op function to act as placeholder for checking a node when the `enforceForClassMembers` option is `false`.
* @returns {void}
* @private
*/
function noop() {}
return {
Property: check,
MethodDefinition: enforceForClassMembers ? check : lodash.noop
MethodDefinition: enforceForClassMembers ? check : noop,
PropertyDefinition: enforceForClassMembers ? check : noop
};
}
};

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -73,6 +74,5 @@ meta: {

docs: {
description: "disallow unnecessary concatenation of literals or template literals",
category: "Best Practices",
description: "Disallow unnecessary concatenation of literals or template literals",
recommended: false,
url: "https://eslint.org/docs/rules/no-useless-concat"
url: "https://eslint.org/docs/latest/rules/no-useless-concat"
},

@@ -88,3 +88,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -91,0 +91,0 @@ return {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -141,6 +142,5 @@ meta: {

docs: {
description: "disallow unnecessary constructors",
category: "ECMAScript 6",
description: "Disallow unnecessary constructors",
recommended: false,
url: "https://eslint.org/docs/rules/no-useless-constructor"
url: "https://eslint.org/docs/latest/rules/no-useless-constructor"
},

@@ -147,0 +147,0 @@

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

const astUtils = require("./utils/ast-utils");
const { RegExpParser, visitRegExpAST } = require("@eslint-community/regexpp");
/**
* @typedef {import('@eslint-community/regexpp').AST.CharacterClass} CharacterClass
* @typedef {import('@eslint-community/regexpp').AST.ExpressionCharacterClass} ExpressionCharacterClass
*/
//------------------------------------------------------------------------------

@@ -32,51 +37,15 @@ // Rule Definition

/**
* Parses a regular expression into a list of characters with character class info.
* @param {string} regExpText The raw text used to create the regular expression
* @returns {Object[]} A list of characters, each with info on escaping and whether they're in a character class.
* @example
*
* parseRegExp('a\\b[cd-]')
*
* returns:
* [
* {text: 'a', index: 0, escaped: false, inCharClass: false, startsCharClass: false, endsCharClass: false},
* {text: 'b', index: 2, escaped: true, inCharClass: false, startsCharClass: false, endsCharClass: false},
* {text: 'c', index: 4, escaped: false, inCharClass: true, startsCharClass: true, endsCharClass: false},
* {text: 'd', index: 5, escaped: false, inCharClass: true, startsCharClass: false, endsCharClass: false},
* {text: '-', index: 6, escaped: false, inCharClass: true, startsCharClass: false, endsCharClass: false}
* ]
/*
* Set of characters that require escaping in character classes in `unicodeSets` mode.
* ( ) [ ] { } / - \ | are ClassSetSyntaxCharacter
*/
function parseRegExp(regExpText) {
const charList = [];
const REGEX_CLASSSET_CHARACTER_ESCAPES = union(REGEX_GENERAL_ESCAPES, new Set("q/[{}|()-"));
regExpText.split("").reduce((state, char, index) => {
if (!state.escapeNextChar) {
if (char === "\\") {
return Object.assign(state, { escapeNextChar: true });
}
if (char === "[" && !state.inCharClass) {
return Object.assign(state, { inCharClass: true, startingCharClass: true });
}
if (char === "]" && state.inCharClass) {
if (charList.length && charList[charList.length - 1].inCharClass) {
charList[charList.length - 1].endsCharClass = true;
}
return Object.assign(state, { inCharClass: false, startingCharClass: false });
}
}
charList.push({
text: char,
index,
escaped: state.escapeNextChar,
inCharClass: state.inCharClass,
startsCharClass: state.startingCharClass,
endsCharClass: false
});
return Object.assign(state, { escapeNextChar: false, startingCharClass: false });
}, { escapeNextChar: false, inCharClass: false, startingCharClass: false });
/*
* A single character set of ClassSetReservedDoublePunctuator.
* && !! ## $$ %% ** ++ ,, .. :: ;; << == >> ?? @@ ^^ `` ~~ are ClassSetReservedDoublePunctuator
*/
const REGEX_CLASS_SET_RESERVED_DOUBLE_PUNCTUATOR = new Set("!#$%&*+,.:;<=>?@^`~");
return charList;
}
/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -87,12 +56,13 @@ meta: {

docs: {
description: "disallow unnecessary escape characters",
category: "Best Practices",
description: "Disallow unnecessary escape characters",
recommended: true,
url: "https://eslint.org/docs/rules/no-useless-escape",
suggestion: true
url: "https://eslint.org/docs/latest/rules/no-useless-escape"
},
hasSuggestions: true,
messages: {
unnecessaryEscape: "Unnecessary escape character: \\{{character}}.",
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."

@@ -105,3 +75,4 @@ },

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const parser = new RegExpParser();

@@ -113,5 +84,6 @@ /**

* @param {string} character The uselessly escaped character (not including the backslash)
* @param {boolean} [disableEscapeBackslashSuggest] `true` if escapeBackslash suggestion should be turned off.
* @returns {void}
*/
function report(node, startOffset, character) {
function report(node, startOffset, character, disableEscapeBackslashSuggest) {
const rangeStart = node.range[0] + startOffset;

@@ -131,3 +103,6 @@ const range = [rangeStart, rangeStart + 1];

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

@@ -137,8 +112,12 @@ return fixer.removeRange(range);

},
{
messageId: "escapeBackslash",
fix(fixer) {
return fixer.insertTextBeforeRange(range, "\\");
}
}
...disableEscapeBackslashSuggest
? []
: [
{
messageId: "escapeBackslash",
fix(fixer) {
return fixer.insertTextBeforeRange(range, "\\");
}
}
]
]

@@ -187,2 +166,129 @@ });

/**
* Checks if the escape character in given regexp is unnecessary.
* @private
* @param {ASTNode} node node to validate.
* @returns {void}
*/
function validateRegExp(node) {
const { pattern, flags } = node.regex;
let patternNode;
const unicode = flags.includes("u");
const unicodeSets = flags.includes("v");
try {
patternNode = parser.parsePattern(pattern, 0, pattern.length, { unicode, unicodeSets });
} catch {
// Ignore regular expressions with syntax errors
return;
}
/** @type {(CharacterClass | ExpressionCharacterClass)[]} */
const characterClassStack = [];
visitRegExpAST(patternNode, {
onCharacterClassEnter: characterClassNode => characterClassStack.unshift(characterClassNode),
onCharacterClassLeave: () => characterClassStack.shift(),
onExpressionCharacterClassEnter: characterClassNode => characterClassStack.unshift(characterClassNode),
onExpressionCharacterClassLeave: () => characterClassStack.shift(),
onCharacterEnter(characterNode) {
if (!characterNode.raw.startsWith("\\")) {
// It's not an escaped character.
return;
}
const escapedChar = characterNode.raw.slice(1);
if (escapedChar !== String.fromCodePoint(characterNode.value)) {
// It's a valid escape.
return;
}
let allowedEscapes;
if (characterClassStack.length) {
allowedEscapes = unicodeSets ? REGEX_CLASSSET_CHARACTER_ESCAPES : REGEX_GENERAL_ESCAPES;
} else {
allowedEscapes = REGEX_NON_CHARCLASS_ESCAPES;
}
if (allowedEscapes.has(escapedChar)) {
return;
}
const reportedIndex = characterNode.start + 1;
let disableEscapeBackslashSuggest = false;
if (characterClassStack.length) {
const characterClassNode = characterClassStack[0];
if (escapedChar === "^") {
/*
* The '^' character is also a special case; it must always be escaped outside of character classes, but
* it only needs to be escaped in character classes if it's at the beginning of the character class. To
* account for this, consider it to be a valid escape character outside of character classes, and filter
* out '^' characters that appear at the start of a character class.
*/
if (characterClassNode.start + 1 === characterNode.start) {
return;
}
}
if (!unicodeSets) {
if (escapedChar === "-") {
/*
* The '-' character is a special case, because it's only valid to escape it if it's in a character
* class, and is not at either edge of the character class. To account for this, don't consider '-'
* characters to be valid in general, and filter out '-' characters that appear in the middle of a
* character class.
*/
if (characterClassNode.start + 1 !== characterNode.start && characterNode.end !== characterClassNode.end - 1) {
return;
}
}
} else { // unicodeSets mode
if (REGEX_CLASS_SET_RESERVED_DOUBLE_PUNCTUATOR.has(escapedChar)) {
// Escaping is valid if it is a ClassSetReservedDoublePunctuator.
if (pattern[characterNode.end] === escapedChar) {
return;
}
if (pattern[characterNode.start - 1] === escapedChar) {
if (escapedChar !== "^") {
return;
}
// If the previous character is a `negate` caret(`^`), escape to caret is unnecessary.
if (!characterClassNode.negate) {
return;
}
const negateCaretIndex = characterClassNode.start + 1;
if (negateCaretIndex < characterNode.start - 1) {
return;
}
}
}
if (characterNode.parent.type === "ClassIntersection" || characterNode.parent.type === "ClassSubtraction") {
disableEscapeBackslashSuggest = true;
}
}
}
report(
node,
reportedIndex,
escapedChar,
disableEscapeBackslashSuggest
);
}
});
}
/**
* Checks if a node has an escape.

@@ -225,28 +331,3 @@ * @param {ASTNode} node node to check.

} else if (node.regex) {
parseRegExp(node.regex.pattern)
/*
* The '-' character is a special case, because it's only valid to escape it if it's in a character
* class, and is not at either edge of the character class. To account for this, don't consider '-'
* characters to be valid in general, and filter out '-' characters that appear in the middle of a
* character class.
*/
.filter(charInfo => !(charInfo.text === "-" && charInfo.inCharClass && !charInfo.startsCharClass && !charInfo.endsCharClass))
/*
* The '^' character is also a special case; it must always be escaped outside of character classes, but
* it only needs to be escaped in character classes if it's at the beginning of the character class. To
* account for this, consider it to be a valid escape character outside of character classes, and filter
* out '^' characters that appear at the start of a character class.
*/
.filter(charInfo => !(charInfo.text === "^" && charInfo.startsCharClass))
// Filter out characters that aren't escaped.
.filter(charInfo => charInfo.escaped)
// Filter out characters that are valid to escape, based on their position in the regular expression.
.filter(charInfo => !(charInfo.inCharClass ? REGEX_GENERAL_ESCAPES : REGEX_NON_CHARCLASS_ESCAPES).has(charInfo.text))
// Report all the remaining characters.
.forEach(charInfo => report(node, charInfo.index, charInfo.text));
validateRegExp(node);
}

@@ -253,0 +334,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,6 +25,5 @@ meta: {

docs: {
description: "disallow renaming import, export, and destructured assignments to the same name",
category: "ECMAScript 6",
description: "Disallow renaming import, export, and destructured assignments to the same name",
recommended: false,
url: "https://eslint.org/docs/rules/no-useless-rename"
url: "https://eslint.org/docs/latest/rules/no-useless-rename"
},

@@ -51,3 +51,3 @@

create(context) {
const sourceCode = context.getSourceCode(),
const sourceCode = context.sourceCode,
options = context.options[0] || {},

@@ -138,4 +138,6 @@ ignoreDestructuring = options.ignoreDestructuring === true,

if (node.imported.name === node.local.name &&
node.imported.range[0] !== node.local.range[0]) {
if (
node.imported.range[0] !== node.local.range[0] &&
astUtils.getModuleExportName(node.imported) === node.local.name
) {
reportError(node, node.imported, "Import");

@@ -155,4 +157,6 @@ }

if (node.local.name === node.exported.name &&
node.local.range[0] !== node.exported.range[0]) {
if (
node.local.range[0] !== node.exported.range[0] &&
astUtils.getModuleExportName(node.local) === astUtils.getModuleExportName(node.exported)
) {
reportError(node, node.local, "Export");

@@ -159,0 +163,0 @@ }

@@ -60,2 +60,18 @@ /**

/**
* Checks all segments in a set and returns true if any are reachable.
* @param {Set<CodePathSegment>} segments The segments to check.
* @returns {boolean} True if any segment is reachable; false otherwise.
*/
function isAnySegmentReachable(segments) {
for (const segment of segments) {
if (segment.reachable) {
return true;
}
}
return false;
}
//------------------------------------------------------------------------------

@@ -65,2 +81,3 @@ // Rule Definition

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -71,6 +88,5 @@ meta: {

docs: {
description: "disallow redundant return statements",
category: "Best Practices",
description: "Disallow redundant return statements",
recommended: false,
url: "https://eslint.org/docs/rules/no-useless-return"
url: "https://eslint.org/docs/latest/rules/no-useless-return"
},

@@ -88,4 +104,3 @@

const segmentInfoMap = new WeakMap();
const usedUnreachableSegments = new WeakSet();
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
let scopeInfo = null;

@@ -159,5 +174,6 @@

* @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) {

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

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

@@ -175,6 +191,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;
});
}

@@ -196,5 +231,4 @@

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

@@ -208,3 +242,3 @@

// Makes and pushs a new scope information.
// Makes and pushes a new scope information.
onCodePathStart(codePath) {

@@ -214,3 +248,5 @@ scopeInfo = {

uselessReturns: [],
codePath
traversedTryBlockStatements: [],
codePath,
currentSegments: new Set()
};

@@ -252,2 +288,5 @@ },

onCodePathSegmentStart(segment) {
scopeInfo.currentSegments.add(segment);
const info = {

@@ -262,2 +301,14 @@ uselessReturns: getUselessReturns([], segment.allPrevSegments),

onUnreachableCodePathSegmentStart(segment) {
scopeInfo.currentSegments.add(segment);
},
onUnreachableCodePathSegmentEnd(segment) {
scopeInfo.currentSegments.delete(segment);
},
onCodePathSegmentEnd(segment) {
scopeInfo.currentSegments.delete(segment);
},
// Adds ReturnStatement node to check whether it's useless or not.

@@ -274,3 +325,3 @@ ReturnStatement(node) {

// Ignore `return` statements in unreachable places (https://github.com/eslint/eslint/issues/11647).
!scopeInfo.codePath.currentSegments.some(s => s.reachable)
!isAnySegmentReachable(scopeInfo.currentSegments)
) {

@@ -280,3 +331,3 @@ return;

for (const segment of scopeInfo.codePath.currentSegments) {
for (const segment of scopeInfo.currentSegments) {
const info = segmentInfoMap.get(segment);

@@ -292,2 +343,10 @@

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

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

@@ -93,3 +93,3 @@ /**

/* istanbul ignore next : unreachable */
/* c8 ignore next */
return null;

@@ -163,3 +163,3 @@ }

(defaultValue !== null && start >= defaultStart && end <= defaultEnd) ||
(start >= initStart && end <= initEnd)
(!astUtils.isFunction(node) && start >= initStart && end <= initEnd)
);

@@ -184,2 +184,3 @@ });

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -190,6 +191,5 @@ meta: {

docs: {
description: "require `let` or `const` instead of `var`",
category: "ECMAScript 6",
description: "Require `let` or `const` instead of `var`",
recommended: false,
url: "https://eslint.org/docs/rules/no-var"
url: "https://eslint.org/docs/latest/rules/no-var"
},

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

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -218,3 +218,3 @@ /**

}
const variables = context.getDeclaredVariables(declarator);
const variables = sourceCode.getDeclaredVariables(declarator);

@@ -277,3 +277,3 @@ return variables.some(hasReferenceInTDZ(declarator.init));

function canFix(node) {
const variables = context.getDeclaredVariables(node);
const variables = sourceCode.getDeclaredVariables(node);
const scopeNode = getScopeNode(node);

@@ -280,0 +280,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -17,6 +18,5 @@ meta: {

docs: {
description: "disallow `void` operators",
category: "Best Practices",
description: "Disallow `void` operators",
recommended: false,
url: "https://eslint.org/docs/rules/no-void"
url: "https://eslint.org/docs/latest/rules/no-void"
},

@@ -23,0 +23,0 @@

@@ -8,3 +8,3 @@ /**

const { escapeRegExp } = require("lodash");
const escapeRegExp = require("escape-string-regexp");
const astUtils = require("./utils/ast-utils");

@@ -18,2 +18,3 @@

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,6 +25,5 @@ meta: {

docs: {
description: "disallow specified warning terms in comments",
category: "Best Practices",
description: "Disallow specified warning terms in comments",
recommended: false,
url: "https://eslint.org/docs/rules/no-warning-comments"
url: "https://eslint.org/docs/latest/rules/no-warning-comments"
},

@@ -43,2 +43,11 @@

enum: ["start", "anywhere"]
},
decoration: {
type: "array",
items: {
type: "string",
pattern: "^\\S$"
},
minItems: 1,
uniqueItems: true
}

@@ -56,6 +65,7 @@ },

create(context) {
const sourceCode = context.getSourceCode(),
const sourceCode = context.sourceCode,
configuration = context.options[0] || {},
warningTerms = configuration.terms || ["todo", "fixme", "xxx"],
location = configuration.location || "start",
decoration = [...configuration.decoration || []].join(""),
selfConfigRegEx = /\bno-warning-comments\b/u;

@@ -72,55 +82,50 @@

const escaped = escapeRegExp(term);
const wordBoundary = "\\b";
const eitherOrWordBoundary = `|${wordBoundary}`;
let prefix;
const escapedDecoration = escapeRegExp(decoration);
/*
* If the term ends in a word character (a-z0-9_), ensure a word
* boundary at the end, so that substrings do not get falsely
* matched. eg "todo" in a string such as "mastodon".
* If the term ends in a non-word character, then \b won't match on
* the boundary to the next non-word character, which would likely
* be a space. For example `/\bFIX!\b/.test('FIX! blah') === false`.
* In these cases, use no bounding match. Same applies for the
* prefix, handled below.
* When matching at the start, ignore leading whitespace, and
* there's no need to worry about word boundaries.
*
* These expressions for the prefix and suffix are designed as follows:
* ^ handles any terms at the beginning of a comment.
* e.g. terms ["TODO"] matches `//TODO something`
* $ handles any terms at the end of a comment
* e.g. terms ["TODO"] matches `// something TODO`
* \b handles terms preceded/followed by word boundary
* e.g. terms: ["!FIX", "FIX!"] matches `// FIX!something` or `// something!FIX`
* terms: ["FIX"] matches `// FIX!` or `// !FIX`, but not `// fixed or affix`
*
* For location start:
* [\s]* handles optional leading spaces
* e.g. terms ["TODO"] matches `// TODO something`
* [\s\*]* (where "\*" is the escaped string of decoration)
* handles optional leading spaces or decoration characters (for "start" location only)
* e.g. terms ["TODO"] matches `/**** TODO something ... `
*/
const suffix = /\w$/u.test(term) ? "\\b" : "";
const wordBoundary = "\\b";
let prefix = "";
if (location === "start") {
/*
* When matching at the start, ignore leading whitespace, and
* there's no need to worry about word boundaries.
*/
prefix = "^\\s*";
prefix = `^[\\s${escapedDecoration}]*`;
} else if (/^\w/u.test(term)) {
prefix = wordBoundary;
} else {
prefix = "";
}
if (location === "start") {
const suffix = /\w$/u.test(term) ? wordBoundary : "";
const flags = "iu"; // Case-insensitive with Unicode case folding.
/*
* For location "start" the regex should be
* ^\s*TERM\b. This checks the word boundary
* at the beginning of the comment.
*/
return new RegExp(prefix + escaped + suffix, "iu");
}
/*
* For location "anywhere" the regex should be
* \bTERM\b|\bTERM\b, this checks the entire comment
* for the term.
* For location "start", the typical regex is:
* /^[\s]*ESCAPED_TERM\b/iu.
* Or if decoration characters are specified (e.g. "*"), then any of
* those characters may appear in any order at the start:
* /^[\s\*]*ESCAPED_TERM\b/iu.
*
* For location "anywhere" the typical regex is
* /\bESCAPED_TERM\b/iu
*
* If it starts or ends with non-word character, the prefix and suffix are empty, respectively.
*/
return new RegExp(
prefix +
escaped +
suffix +
eitherOrWordBoundary +
term +
wordBoundary,
"iu"
);
return new RegExp(`${prefix}${escaped}${suffix}`, flags);
}

@@ -127,0 +132,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -23,6 +24,5 @@ meta: {

docs: {
description: "disallow whitespace before properties",
category: "Stylistic Issues",
description: "Disallow whitespace before properties",
recommended: false,
url: "https://eslint.org/docs/rules/no-whitespace-before-property"
url: "https://eslint.org/docs/latest/rules/no-whitespace-before-property"
},

@@ -39,3 +39,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -42,0 +42,0 @@ //--------------------------------------------------------------------------

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "disallow `with` statements",
category: "Best Practices",
description: "Disallow `with` statements",
recommended: true,
url: "https://eslint.org/docs/rules/no-with"
url: "https://eslint.org/docs/latest/rules/no-with"
},

@@ -24,0 +24,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -19,6 +20,5 @@ meta: {

docs: {
description: "enforce the location of single-line statements",
category: "Stylistic Issues",
description: "Enforce the location of single-line statements",
recommended: false,
url: "https://eslint.org/docs/rules/nonblock-statement-body-position"
url: "https://eslint.org/docs/latest/rules/nonblock-statement-body-position"
},

@@ -54,3 +54,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -57,0 +57,0 @@ //----------------------------------------------------------------------

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

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

@@ -74,2 +73,20 @@ //------------------------------------------------------------------------------

/**
* Checks if a value is an object.
* @param {any} value The value to check
* @returns {boolean} `true` if the value is an object, otherwise `false`
*/
function isObject(value) {
return typeof value === "object" && value !== null;
}
/**
* Checks if an option is a node-specific option
* @param {any} option The option to check
* @returns {boolean} `true` if the option is node-specific, otherwise `false`
*/
function isNodeSpecificOption(option) {
return isObject(option) || typeof option === "string";
}
/**
* Normalizes a given option value.

@@ -85,5 +102,3 @@ * @param {string|Object|undefined} options An option value to parse.

function normalizeOptions(options) {
const isNodeSpecificOption = lodash.overSome([lodash.isPlainObject, lodash.isString]);
if (lodash.isPlainObject(options) && lodash.some(options, isNodeSpecificOption)) {
if (isObject(options) && Object.values(options).some(isNodeSpecificOption)) {
return {

@@ -135,2 +150,3 @@ ObjectExpression: normalizeOptionValue(options.ObjectExpression),

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -141,6 +157,5 @@ meta: {

docs: {
description: "enforce consistent line breaks inside braces",
category: "Stylistic Issues",
description: "Enforce consistent line breaks after opening and before closing braces",
recommended: false,
url: "https://eslint.org/docs/rules/object-curly-newline"
url: "https://eslint.org/docs/latest/rules/object-curly-newline"
},

@@ -178,3 +193,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const normalizedOptions = normalizeOptions(context.options[0]);

@@ -181,0 +196,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -19,6 +20,5 @@ meta: {

docs: {
description: "enforce consistent spacing inside braces",
category: "Stylistic Issues",
description: "Enforce consistent spacing inside braces",
recommended: false,
url: "https://eslint.org/docs/rules/object-curly-spacing"
url: "https://eslint.org/docs/latest/rules/object-curly-spacing"
},

@@ -56,3 +56,3 @@

const spaced = context.options[0] === "always",
sourceCode = context.getSourceCode();
sourceCode = context.sourceCode;

@@ -87,3 +87,3 @@ /**

function reportNoBeginningSpace(node, token) {
const nextToken = context.getSourceCode().getTokenAfter(token, { includeComments: true });
const nextToken = context.sourceCode.getTokenAfter(token, { includeComments: true });

@@ -110,3 +110,3 @@ context.report({

function reportNoEndingSpace(node, token) {
const previousToken = context.getSourceCode().getTokenBefore(token, { includeComments: true });
const previousToken = context.sourceCode.getTokenBefore(token, { includeComments: true });

@@ -113,0 +113,0 @@ context.report({

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "enforce placing object properties on separate lines",
category: "Stylistic Issues",
description: "Enforce placing object properties on separate lines",
recommended: false,
url: "https://eslint.org/docs/rules/object-property-newline"
url: "https://eslint.org/docs/latest/rules/object-property-newline"
},

@@ -58,3 +58,3 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -61,0 +61,0 @@ return {

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

//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -31,6 +32,5 @@ meta: {

docs: {
description: "require or disallow method and property shorthand syntax for object literals",
category: "ECMAScript 6",
description: "Require or disallow method and property shorthand syntax for object literals",
recommended: false,
url: "https://eslint.org/docs/rules/object-shorthand"
url: "https://eslint.org/docs/latest/rules/object-shorthand"
},

@@ -83,2 +83,5 @@

},
methodsIgnorePattern: {
type: "string"
},
avoidQuotes: {

@@ -121,5 +124,8 @@ type: "boolean"

const IGNORE_CONSTRUCTORS = PARAMS.ignoreConstructors;
const METHODS_IGNORE_PATTERN = PARAMS.methodsIgnorePattern
? new RegExp(PARAMS.methodsIgnorePattern, "u")
: null;
const AVOID_QUOTES = PARAMS.avoidQuotes;
const AVOID_EXPLICIT_RETURN_ARROWS = !!PARAMS.avoidExplicitReturnArrows;
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -156,3 +162,2 @@ //--------------------------------------------------------------------------

* @private
*
*/

@@ -165,3 +170,3 @@ function canHaveShorthand(property) {

* Checks whether a node is a string literal.
* @param {ASTNode} node Any AST node.
* @param {ASTNode} node Any AST node.
* @returns {boolean} `true` if it is a string literal.

@@ -178,3 +183,2 @@ */

* @private
*
*/

@@ -192,3 +196,2 @@ function isShorthand(property) {

* @private
*
*/

@@ -210,6 +213,5 @@ function isRedundant(property) {

* Ensures that an object's properties are consistently shorthand, or not shorthand at all.
* @param {ASTNode} node Property AST node
* @param {boolean} checkRedundancy Whether to check longform redundancy
* @param {ASTNode} node Property AST node
* @param {boolean} checkRedundancy Whether to check longform redundancy
* @returns {void}
*
*/

@@ -364,7 +366,8 @@ function checkConsistency(node, checkRedundancy) {

* Also, this marks all `arguments` identifiers so that they can be detected later.
* @param {ASTNode} node The node representing the function.
* @returns {void}
*/
function enterFunction() {
function enterFunction(node) {
lexicalScopeStack.unshift(new Set());
context.getScope().variables.filter(variable => variable.name === "arguments").forEach(variable => {
sourceCode.getScope(node).variables.filter(variable => variable.name === "arguments").forEach(variable => {
variable.references.map(ref => ref.identifier).forEach(identifier => argumentsIdentifiers.add(identifier));

@@ -474,2 +477,11 @@ });

}
if (METHODS_IGNORE_PATTERN) {
const propertyName = astUtils.getStaticPropertyName(node);
if (propertyName !== null && METHODS_IGNORE_PATTERN.test(propertyName)) {
return;
}
}
if (AVOID_QUOTES && isStringLiteral(node.key)) {

@@ -476,0 +488,0 @@ return;

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -17,6 +18,5 @@ meta: {

docs: {
description: "require or disallow newlines around variable declarations",
category: "Stylistic Issues",
description: "Require or disallow newlines around variable declarations",
recommended: false,
url: "https://eslint.org/docs/rules/one-var-declaration-per-line"
url: "https://eslint.org/docs/latest/rules/one-var-declaration-per-line"
},

@@ -23,0 +23,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -37,6 +38,5 @@ meta: {

docs: {
description: "enforce variables to be declared either together or separately in functions",
category: "Stylistic Issues",
description: "Enforce variables to be declared either together or separately in functions",
recommended: false,
url: "https://eslint.org/docs/rules/one-var"
url: "https://eslint.org/docs/latest/rules/one-var"
},

@@ -126,3 +126,3 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -215,3 +215,3 @@ //--------------------------------------------------------------------------

* Determines the current scope (function or block)
* @param {string} statementType node.kind, one of: "var", "let", or "const"
* @param {string} statementType node.kind, one of: "var", "let", or "const"
* @returns {Object} The scope associated with statementType

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

ArrowFunctionExpression: startFunction,
StaticBlock: startFunction, // StaticBlock creates a new scope for `var` variables
BlockStatement: startBlock,

@@ -561,6 +563,8 @@ ForStatement: startBlock,

"BlockStatement:exit": endBlock,
"Program:exit": endFunction,
"FunctionDeclaration:exit": endFunction,
"FunctionExpression:exit": endFunction,
"ArrowFunctionExpression:exit": endFunction
"ArrowFunctionExpression:exit": endFunction,
"StaticBlock:exit": endFunction
};

@@ -567,0 +571,0 @@

@@ -20,8 +20,8 @@ /**

* shorthand form.
* @param {string} operator Operator to check.
* @returns {boolean} True if the operator is commutative and has a
* @param {string} operator Operator to check.
* @returns {boolean} True if the operator is commutative and has a
* shorthand form.
*/
function isCommutativeOperatorWithShorthand(operator) {
return ["*", "&", "^", "|"].indexOf(operator) >= 0;
return ["*", "&", "^", "|"].includes(operator);
}

@@ -32,8 +32,8 @@

* shorthand form.
* @param {string} operator Operator to check.
* @returns {boolean} True if the operator is not commutative and has
* @param {string} operator Operator to check.
* @returns {boolean} True if the operator is not commutative and has
* a shorthand form.
*/
function isNonCommutativeOperatorWithShorthand(operator) {
return ["+", "-", "/", "%", "<<", ">>", ">>>", "**"].indexOf(operator) >= 0;
return ["+", "-", "/", "%", "<<", ">>", ">>>", "**"].includes(operator);
}

@@ -62,2 +62,3 @@

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -68,6 +69,5 @@ meta: {

docs: {
description: "require or disallow assignment operator shorthand where possible",
category: "Stylistic Issues",
description: "Require or disallow assignment operator shorthand where possible",
recommended: false,
url: "https://eslint.org/docs/rules/operator-assignment"
url: "https://eslint.org/docs/latest/rules/operator-assignment"
},

@@ -83,4 +83,4 @@

messages: {
replaced: "Assignment can be replaced with operator assignment.",
unexpected: "Unexpected operator assignment shorthand."
replaced: "Assignment (=) can be replaced with operator assignment ({{operator}}).",
unexpected: "Unexpected operator assignment ({{operator}}) shorthand."
}

@@ -91,3 +91,3 @@ },

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -105,3 +105,3 @@ /**

* Ensures that an assignment uses the shorthand form where possible.
* @param {ASTNode} node An AssignmentExpression node.
* @param {ASTNode} node An AssignmentExpression node.
* @returns {void}

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

if (isCommutativeOperatorWithShorthand(operator) || isNonCommutativeOperatorWithShorthand(operator)) {
const replacementOperator = `${operator}=`;
if (astUtils.isSameReference(left, expr.left, true)) {

@@ -124,2 +126,3 @@ context.report({

messageId: "replaced",
data: { operator: replacementOperator },
fix(fixer) {

@@ -137,3 +140,3 @@ if (canBeFixed(left) && canBeFixed(expr.left)) {

return fixer.replaceText(node, `${leftText}${expr.operator}=${rightText}`);
return fixer.replaceText(node, `${leftText}${replacementOperator}${rightText}`);
}

@@ -152,3 +155,4 @@ return null;

node,
messageId: "replaced"
messageId: "replaced",
data: { operator: replacementOperator }
});

@@ -161,3 +165,3 @@ }

* Warns if an assignment expression uses operator assignment shorthand.
* @param {ASTNode} node An AssignmentExpression node.
* @param {ASTNode} node An AssignmentExpression node.
* @returns {void}

@@ -170,2 +174,3 @@ */

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

@@ -172,0 +177,0 @@ if (canBeFixed(node.left)) {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,6 +25,5 @@ meta: {

docs: {
description: "enforce consistent linebreak style for operators",
category: "Stylistic Issues",
description: "Enforce consistent linebreak style for operators",
recommended: false,
url: "https://eslint.org/docs/rules/operator-linebreak"
url: "https://eslint.org/docs/latest/rules/operator-linebreak"
},

@@ -74,3 +74,3 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -142,19 +142,17 @@ //--------------------------------------------------------------------------

* @param {ASTNode} node The node to check
* @param {ASTNode} leftSide The node that comes before the operator in `node`
* @param {ASTNode} rightSide The node that comes after the operator in `node`
* @param {string} operator The operator
* @private
* @returns {void}
*/
function validateNode(node, leftSide) {
function validateNode(node, rightSide, operator) {
/*
* When the left part of a binary expression is a single expression wrapped in
* parentheses (ex: `(a) + b`), leftToken will be the last token of the expression
* and operatorToken will be the closing parenthesis.
* The leftToken should be the last closing parenthesis, and the operatorToken
* should be the token right after that.
* Find the operator token by searching from the right side, because between the left side and the operator
* there could be additional tokens from type annotations. Search specifically for the token which
* value equals the operator, in order to skip possible opening parentheses before the right side node.
*/
const operatorToken = sourceCode.getTokenAfter(leftSide, astUtils.isNotClosingParenToken);
const operatorToken = sourceCode.getTokenBefore(rightSide, token => token.value === operator);
const leftToken = sourceCode.getTokenBefore(operatorToken);
const rightToken = sourceCode.getTokenAfter(operatorToken);
const operator = operatorToken.value;
const operatorStyleOverride = styleOverrides[operator];

@@ -229,3 +227,3 @@ const style = operatorStyleOverride || globalStyle;

function validateBinaryExpression(node) {
validateNode(node, node.left);
validateNode(node, node.right, node.operator);
}

@@ -243,8 +241,13 @@

if (node.init) {
validateNode(node, node.id);
validateNode(node, node.init, "=");
}
},
PropertyDefinition(node) {
if (node.value) {
validateNode(node, node.value, "=");
}
},
ConditionalExpression(node) {
validateNode(node, node.test);
validateNode(node, node.consequent);
validateNode(node, node.consequent, "?");
validateNode(node, node.alternate, ":");
}

@@ -251,0 +254,0 @@ };

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,6 +25,5 @@ meta: {

docs: {
description: "require or disallow padding within blocks",
category: "Stylistic Issues",
description: "Require or disallow padding within blocks",
recommended: false,
url: "https://eslint.org/docs/rules/padded-blocks"
url: "https://eslint.org/docs/latest/rules/padded-blocks"
},

@@ -101,3 +101,3 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

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

}
if (node.type === "StaticBlock") {
return sourceCode.getFirstToken(node, { skip: 1 }); // skip the `static` token
}
// `BlockStatement` or `ClassBody`
return sourceCode.getFirstToken(node);

@@ -174,2 +180,3 @@ }

* @param {ASTNode} node The AST node to check.
* @throws {Error} (Unreachable)
* @returns {boolean} True if the node should be padded, false otherwise.

@@ -180,2 +187,3 @@ */

case "BlockStatement":
case "StaticBlock":
return options.blocks;

@@ -187,3 +195,3 @@ case "SwitchStatement":

/* istanbul ignore next */
/* c8 ignore next */
default:

@@ -292,2 +300,3 @@ throw new Error("unreachable");

};
rule.StaticBlock = rule.BlockStatement;
}

@@ -294,0 +303,0 @@

@@ -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.

@@ -257,3 +214,3 @@ *

const end = nextToken.range[0];
const text = context.getSourceCode().text
const text = context.sourceCode.text
.slice(start, end)

@@ -289,3 +246,3 @@ .replace(PADDING_LINE_SEQUENCE, replacerToRemovePaddingLines);

fix(fixer) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
let prevToken = getActualLastToken(sourceCode, prevNode);

@@ -372,8 +329,6 @@ const nextToken = sourceCode.getFirstTokenBetween(

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

@@ -389,6 +344,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)
},

@@ -433,2 +388,3 @@

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -439,6 +395,5 @@ meta: {

docs: {
description: "require or disallow padding lines between statements",
category: "Stylistic Issues",
description: "Require or disallow padding lines between statements",
recommended: false,
url: "https://eslint.org/docs/rules/padding-line-between-statements"
url: "https://eslint.org/docs/latest/rules/padding-line-between-statements"
},

@@ -460,4 +415,3 @@

minItems: 1,
uniqueItems: true,
additionalItems: false
uniqueItems: true
}

@@ -477,4 +431,3 @@ ]

required: ["blankLine", "prev", "next"]
},
additionalItems: false
}
},

@@ -489,3 +442,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const configureList = context.options || [];

@@ -632,5 +585,7 @@ let scopeInfo = null;

SwitchStatement: enterScope,
StaticBlock: enterScope,
"Program:exit": exitScope,
"BlockStatement:exit": exitScope,
"SwitchStatement:exit": exitScope,
"StaticBlock:exit": exitScope,

@@ -637,0 +592,0 @@ ":statement": verify,

@@ -56,3 +56,3 @@ /**

/* istanbul ignore next */
/* c8 ignore next */
return null;

@@ -64,2 +64,3 @@ }

* @param {ASTNode} node A node to check.
* @throws {Error} (Unreachable.)
* @returns {Object}

@@ -130,3 +131,3 @@ * {boolean} retv.isCallback - `true` if the node is a callback.

/* istanbul ignore next */
/* c8 ignore next */
throw new Error("unreachable");

@@ -150,2 +151,3 @@ }

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -156,6 +158,5 @@ meta: {

docs: {
description: "require using arrow functions for callbacks",
category: "ECMAScript 6",
description: "Require using arrow functions for callbacks",
recommended: false,
url: "https://eslint.org/docs/rules/prefer-arrow-callback"
url: "https://eslint.org/docs/latest/rules/prefer-arrow-callback"
},

@@ -192,3 +193,3 @@

const allowNamedFunctions = options.allowNamedFunctions;
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -271,3 +272,3 @@ /*

// Skip recursive functions.
const nameVar = context.getDeclaredVariables(node)[0];
const nameVar = sourceCode.getDeclaredVariables(node)[0];

@@ -279,3 +280,3 @@ if (isFunctionName(nameVar) && nameVar.references.length > 0) {

// Skip if it's using arguments.
const variable = getVariableOfArguments(context.getScope());
const variable = getVariableOfArguments(sourceCode.getScope(node));

@@ -306,3 +307,3 @@ if (variable && variable.references.length > 0) {

*/
return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
return;
}

@@ -319,3 +320,3 @@

if (memberNode.type !== "MemberExpression") {
return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
return;
}

@@ -333,3 +334,3 @@

if (astUtils.isParenthesised(sourceCode, memberNode)) {
return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
return;
}

@@ -339,3 +340,3 @@

if (sourceCode.commentsExistBetween(firstTokenToRemove, lastTokenToRemove)) {
return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
return;
}

@@ -349,2 +350,3 @@

const leftParenToken = sourceCode.getTokenAfter(functionToken, astUtils.isOpeningParenToken);
const tokenBeforeBody = sourceCode.getTokenBefore(node.body);

@@ -363,3 +365,3 @@ if (sourceCode.commentsExistBetween(functionToken, leftParenToken)) {

}
yield fixer.insertTextBefore(node.body, "=> ");
yield fixer.insertTextAfter(tokenBeforeBody, " =>");

@@ -366,0 +368,0 @@ // Get the node that will become the new arrow function.

@@ -20,3 +20,3 @@ /**

const PATTERN_TYPE = /^(?:.+?Pattern|RestElement|SpreadProperty|ExperimentalRestProperty|Property)$/u;
const DECLARATION_HOST_TYPE = /^(?:Program|BlockStatement|SwitchCase)$/u;
const DECLARATION_HOST_TYPE = /^(?:Program|BlockStatement|StaticBlock|SwitchCase)$/u;
const DESTRUCTURING_HOST_TYPE = /^(?:VariableDeclarator|AssignmentExpression)$/u;

@@ -64,3 +64,3 @@

if (initScope.through.find(ref => ref.resolved && ref.resolved.name === name)) {
if (initScope.through.some(ref => ref.resolved && ref.resolved.name === name)) {
return true;

@@ -331,2 +331,3 @@ }

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -337,6 +338,5 @@ meta: {

docs: {
description: "require `const` declarations for variables that are never reassigned after declared",
category: "ECMAScript 6",
description: "Require `const` declarations for variables that are never reassigned after declared",
recommended: false,
url: "https://eslint.org/docs/rules/prefer-const"
url: "https://eslint.org/docs/latest/rules/prefer-const"
},

@@ -363,3 +363,3 @@

const options = context.options[0] || {};
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const shouldMatchAnyDestructuredVariable = options.destructuring !== "all";

@@ -454,3 +454,15 @@ const ignoreReadBeforeAssign = options.ignoreReadBeforeAssign === true;

shouldFix = shouldFix && (reportCount === varDeclParent.declarations.length);
let totalDeclarationsCount = 0;
varDeclParent.declarations.forEach(declaration => {
if (declaration.id.type === "ObjectPattern") {
totalDeclarationsCount += declaration.id.properties.length;
} else if (declaration.id.type === "ArrayPattern") {
totalDeclarationsCount += declaration.id.elements.length;
} else {
totalDeclarationsCount += 1;
}
});
shouldFix = shouldFix && (reportCount === totalDeclarationsCount);
}

@@ -490,3 +502,3 @@ }

if (node.kind === "let" && !isInitOfForStatement(node)) {
variables.push(...context.getDeclaredVariables(node));
variables.push(...sourceCode.getDeclaredVariables(node));
}

@@ -493,0 +505,0 @@ }

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -29,6 +30,5 @@ meta: {

docs: {
description: "require destructuring from arrays and/or objects",
category: "ECMAScript 6",
description: "Require destructuring from arrays and/or objects",
recommended: false,
url: "https://eslint.org/docs/rules/prefer-destructuring"
url: "https://eslint.org/docs/latest/rules/prefer-destructuring"
},

@@ -124,4 +124,4 @@

// eslint-disable-next-line jsdoc/require-description
/**
* Checks if destructuring type should be checked.
* @param {string} nodeType "AssignmentExpression" or "VariableDeclarator"

@@ -174,3 +174,3 @@ * @param {string} destructuringType "array" or "object"

* Renamed property is not fixed.
* @param {ASTNode} node the the node to evaluate
* @param {ASTNode} node the node to evaluate
* @returns {boolean} whether or not the node should be fixed

@@ -197,3 +197,3 @@ */

const rightNode = node.init;
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -229,3 +229,7 @@ // Don't fix if that would remove any comments. Only comments inside `rightNode.object` can be preserved.

function performCheck(leftNode, rightNode, reportNode) {
if (rightNode.type !== "MemberExpression" || rightNode.object.type === "Super") {
if (
rightNode.type !== "MemberExpression" ||
rightNode.object.type === "Super" ||
rightNode.property.type === "PrivateIdentifier"
) {
return;

@@ -232,0 +236,0 @@ }

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

const astUtils = require("./utils/ast-utils");
const { CALL, ReferenceTracker } = require("eslint-utils");
const { CALL, ReferenceTracker } = require("@eslint-community/eslint-utils");

@@ -59,2 +59,3 @@ //------------------------------------------------------------------------------

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

@@ -64,3 +65,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) &&

@@ -90,2 +91,3 @@ !((parent.type === "CallExpression" || parent.type === "NewExpression") && parent.arguments.includes(node)) &&

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -96,6 +98,5 @@ meta: {

docs: {
description: "disallow the use of `Math.pow` in favor of the `**` operator",
category: "Stylistic Issues",
description: "Disallow the use of `Math.pow` in favor of the `**` operator",
recommended: false,
url: "https://eslint.org/docs/rules/prefer-exponentiation-operator"
url: "https://eslint.org/docs/latest/rules/prefer-exponentiation-operator"
},

@@ -112,3 +113,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -181,4 +182,4 @@ /**

return {
Program() {
const scope = context.getScope();
Program(node) {
const scope = sourceCode.getScope(node);
const tracker = new ReferenceTracker(scope);

@@ -191,4 +192,4 @@ const trackMap = {

for (const { node } of tracker.iterateGlobalReferences(trackMap)) {
report(node);
for (const { node: refNode } of tracker.iterateGlobalReferences(trackMap)) {
report(refNode);
}

@@ -195,0 +196,0 @@ }

@@ -17,4 +17,4 @@ /**

getStringIfConstant
} = require("eslint-utils");
const regexpp = require("regexpp");
} = require("@eslint-community/eslint-utils");
const regexpp = require("@eslint-community/regexpp");

@@ -27,2 +27,57 @@ //------------------------------------------------------------------------------

/**
* Creates fixer suggestions for the regex, if statically determinable.
* @param {number} groupStart Starting index of the regex group.
* @param {string} pattern The regular expression pattern to be checked.
* @param {string} rawText Source text of the regexNode.
* @param {ASTNode} regexNode AST node which contains the regular expression.
* @returns {Array<SuggestionResult>} Fixer suggestions for the regex, if statically determinable.
*/
function suggestIfPossible(groupStart, pattern, rawText, regexNode) {
switch (regexNode.type) {
case "Literal":
if (typeof regexNode.value === "string" && rawText.includes("\\")) {
return null;
}
break;
case "TemplateLiteral":
if (regexNode.expressions.length || rawText.slice(1, -1) !== pattern) {
return null;
}
break;
default:
return null;
}
const start = regexNode.range[0] + groupStart + 2;
return [
{
fix(fixer) {
const existingTemps = pattern.match(/temp\d+/gu) || [];
const highestTempCount = existingTemps.reduce(
(previous, next) =>
Math.max(previous, Number(next.slice("temp".length))),
0
);
return fixer.insertTextBeforeRange(
[start, start],
`?<temp${highestTempCount + 1}>`
);
},
messageId: "addGroupName"
},
{
fix(fixer) {
return fixer.insertTextBeforeRange(
[start, start],
"?:"
);
},
messageId: "addNonCapture"
}
];
}
//------------------------------------------------------------------------------

@@ -32,2 +87,3 @@ // Rule Definition

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -38,11 +94,14 @@ meta: {

docs: {
description: "enforce using named capture group in regular expression",
category: "Best Practices",
description: "Enforce using named capture group in regular expression",
recommended: false,
url: "https://eslint.org/docs/rules/prefer-named-capture-group"
url: "https://eslint.org/docs/latest/rules/prefer-named-capture-group"
},
hasSuggestions: true,
schema: [],
messages: {
addGroupName: "Add name to capture group.",
addNonCapture: "Convert group to non-capturing.",
required: "Capture group '{{group}}' should be converted to a named or non-capturing group."

@@ -53,15 +112,20 @@ }

create(context) {
const sourceCode = context.sourceCode;
/**
* Function to check regular expression.
* @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.
* @param {string} pattern The regular expression pattern to be checked.
* @param {ASTNode} node AST node which contains the regular expression or a call/new expression.
* @param {ASTNode} regexNode AST node which contains the regular expression.
* @param {string|null} flags The regular expression flags to be checked.
* @returns {void}
*/
function checkRegex(pattern, node, uFlag) {
function checkRegex(pattern, node, regexNode, flags) {
let ast;
try {
ast = parser.parsePattern(pattern, 0, pattern.length, uFlag);
ast = parser.parsePattern(pattern, 0, pattern.length, {
unicode: Boolean(flags && flags.includes("u")),
unicodeSets: Boolean(flags && flags.includes("v"))
});
} catch {

@@ -76,2 +140,5 @@

if (!group.name) {
const rawText = sourceCode.getText(regexNode);
const suggest = suggestIfPossible(group.start, pattern, rawText, regexNode);
context.report({

@@ -82,3 +149,4 @@ node,

group: group.raw
}
},
suggest
});

@@ -93,7 +161,7 @@ }

if (node.regex) {
checkRegex(node.regex.pattern, node, node.regex.flags.includes("u"));
checkRegex(node.regex.pattern, node, node, node.regex.flags);
}
},
Program() {
const scope = context.getScope();
Program(node) {
const scope = sourceCode.getScope(node);
const tracker = new ReferenceTracker(scope);

@@ -107,8 +175,8 @@ const traceMap = {

for (const { node } of tracker.iterateGlobalReferences(traceMap)) {
const regex = getStringIfConstant(node.arguments[0]);
const flags = getStringIfConstant(node.arguments[1]);
for (const { node: refNode } of tracker.iterateGlobalReferences(traceMap)) {
const regex = getStringIfConstant(refNode.arguments[0]);
const flags = getStringIfConstant(refNode.arguments[1]);
if (regex) {
checkRegex(regex, node, flags && flags.includes("u"));
checkRegex(regex, refNode, refNode.arguments[0], flags);
}

@@ -115,0 +183,0 @@ }

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -48,6 +49,5 @@ meta: {

docs: {
description: "disallow `parseInt()` and `Number.parseInt()` in favor of binary, octal, and hexadecimal literals",
category: "ECMAScript 6",
description: "Disallow `parseInt()` and `Number.parseInt()` in favor of binary, octal, and hexadecimal literals",
recommended: false,
url: "https://eslint.org/docs/rules/prefer-numeric-literals"
url: "https://eslint.org/docs/latest/rules/prefer-numeric-literals"
},

@@ -65,3 +65,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -68,0 +68,0 @@ //----------------------------------------------------------------------

/**
* @fileoverview Prefers object spread property over Object.assign
* @author Sharmila Jesupaul
* See LICENSE file in root directory for full license.
*/

@@ -9,3 +8,3 @@

const { CALL, ReferenceTracker } = require("eslint-utils");
const { CALL, ReferenceTracker } = require("@eslint-community/eslint-utils");
const {

@@ -244,2 +243,3 @@ isCommaToken,

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -251,6 +251,5 @@ meta: {

description:
"disallow using Object.assign with an object literal as the first argument and prefer the use of object spread instead.",
category: "Stylistic Issues",
"Disallow using Object.assign with an object literal as the first argument and prefer the use of object spread instead",
recommended: false,
url: "https://eslint.org/docs/rules/prefer-object-spread"
url: "https://eslint.org/docs/latest/rules/prefer-object-spread"
},

@@ -268,7 +267,7 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
return {
Program() {
const scope = context.getScope();
Program(node) {
const scope = sourceCode.getScope(node);
const tracker = new ReferenceTracker(scope);

@@ -282,18 +281,18 @@ const trackMap = {

// Iterate all calls of `Object.assign` (only of the global variable `Object`).
for (const { node } of tracker.iterateGlobalReferences(trackMap)) {
for (const { node: refNode } of tracker.iterateGlobalReferences(trackMap)) {
if (
node.arguments.length >= 1 &&
node.arguments[0].type === "ObjectExpression" &&
!hasArraySpread(node) &&
refNode.arguments.length >= 1 &&
refNode.arguments[0].type === "ObjectExpression" &&
!hasArraySpread(refNode) &&
!(
node.arguments.length > 1 &&
hasArgumentsWithAccessors(node)
refNode.arguments.length > 1 &&
hasArgumentsWithAccessors(refNode)
)
) {
const messageId = node.arguments.length === 1
const messageId = refNode.arguments.length === 1
? "useLiteralMessage"
: "useSpreadMessage";
const fix = defineFixer(node, sourceCode);
const fix = defineFixer(refNode, sourceCode);
context.report({ node, messageId, fix });
context.report({ node: refNode, messageId, fix });
}

@@ -300,0 +299,0 @@ }

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -19,6 +20,5 @@ meta: {

docs: {
description: "require using Error objects as Promise rejection reasons",
category: "Best Practices",
description: "Require using Error objects as Promise rejection reasons",
recommended: false,
url: "https://eslint.org/docs/rules/prefer-promise-reject-errors"
url: "https://eslint.org/docs/latest/rules/prefer-promise-reject-errors"
},

@@ -46,2 +46,3 @@

const ALLOW_EMPTY_REJECT = context.options.length && context.options[0].allowEmptyReject;
const sourceCode = context.sourceCode;

@@ -106,3 +107,3 @@ //----------------------------------------------------------------------

) {
context.getDeclaredVariables(node.arguments[0])
sourceCode.getDeclaredVariables(node.arguments[0])

@@ -109,0 +110,0 @@ /*

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "require `Reflect` methods where applicable",
category: "ECMAScript 6",
description: "Require `Reflect` methods where applicable",
recommended: false,
url: "https://eslint.org/docs/rules/prefer-reflect"
url: "https://eslint.org/docs/latest/rules/prefer-reflect"
},

@@ -111,3 +111,3 @@

const hasReflectSubstitute = Object.prototype.hasOwnProperty.call(reflectSubstitutes, methodName);
const userConfiguredException = exceptions.indexOf(methodName) !== -1;
const userConfiguredException = exceptions.includes(methodName);

@@ -121,3 +121,3 @@ if (hasReflectSubstitute && !isReflectCall && !userConfiguredException) {

const targetsIdentifier = node.argument.type === "Identifier";
const userConfiguredException = exceptions.indexOf("delete") !== -1;
const userConfiguredException = exceptions.includes("delete");

@@ -124,0 +124,0 @@ if (isDeleteOperator && !targetsIdentifier && !userConfiguredException) {

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

const astUtils = require("./utils/ast-utils");
const { CALL, CONSTRUCT, ReferenceTracker, findVariable } = require("eslint-utils");
const { CALL, CONSTRUCT, ReferenceTracker, findVariable } = require("@eslint-community/eslint-utils");
const { RegExpValidator, visitRegExpAST, RegExpParser } = require("@eslint-community/regexpp");
const { canTokensBeAdjacent } = require("./utils/ast-utils");
const { REGEXPP_LATEST_ECMA_VERSION } = require("./utils/regular-expressions");

@@ -38,10 +41,66 @@ //------------------------------------------------------------------------------

/**
* 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([
"(",
";",
"[",
",",
"=",
"+",
"*",
"-",
"?",
"~",
"%",
"**",
"!",
"typeof",
"instanceof",
"&&",
"||",
"??",
"return",
"...",
"delete",
"void",
"in",
"<",
">",
"<=",
">=",
"==",
"===",
"!=",
"!==",
"<<",
">>",
">>>",
"&",
"|",
"^",
":",
"{",
"=>",
"*=",
"<<=",
">>=",
">>>=",
"^=",
"|=",
"&=",
"??=",
"||=",
"&&=",
"**=",
"+=",
"-=",
"/=",
"%=",
"/",
"do",
"break",
"continue",
"debugger",
"case",
"throw"
]);

@@ -53,2 +112,3 @@

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -59,8 +119,9 @@ meta: {

docs: {
description: "disallow use of the `RegExp` constructor in favor of regular expression literals",
category: "Best Practices",
description: "Disallow use of the `RegExp` constructor in favor of regular expression literals",
recommended: false,
url: "https://eslint.org/docs/rules/prefer-regex-literals"
url: "https://eslint.org/docs/latest/rules/prefer-regex-literals"
},
hasSuggestions: true,
schema: [

@@ -81,2 +142,5 @@ {

unexpectedRegExp: "Use a regular expression literal instead of the 'RegExp' constructor.",
replaceWithLiteral: "Replace with an equivalent regular expression literal.",
replaceWithLiteralAndFlags: "Replace with an equivalent regular expression literal with flags '{{ flags }}'.",
replaceWithIntendedLiteralAndFlags: "Replace with a regular expression literal with flags '{{ flags }}'.",
unexpectedRedundantRegExp: "Regular expression literal is unnecessarily wrapped within a 'RegExp' constructor.",

@@ -89,2 +153,3 @@ unexpectedRedundantRegExpWithFlags: "Use regular expression literal with flags instead of the 'RegExp' constructor."

const [{ disallowRedundantWrapping = false } = {}] = context.options;
const sourceCode = context.sourceCode;

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

function isGlobalReference(node) {
const scope = context.getScope();
const scope = sourceCode.getScope(node);
const variable = findVariable(scope, node);

@@ -114,6 +179,27 @@

isGlobalReference(astUtils.skipChainExpression(node.tag).object) &&
isStaticTemplateLiteral(node.quasi);
astUtils.isStaticTemplateLiteral(node.quasi);
}
/**
* Gets the value of a string
* @param {ASTNode} node The node to get the string of.
* @returns {string|null} The value of the node.
*/
function getStringValue(node) {
if (isStringLiteral(node)) {
return node.value;
}
if (astUtils.isStaticTemplateLiteral(node)) {
return node.quasis[0].value.cooked;
}
if (isStringRawTaggedStaticTemplateLiteral(node)) {
return node.quasi.quasis[0].value.raw;
}
return null;
}
/**
* Determines whether the given node is considered to be a static string by the logic of this rule.

@@ -125,3 +211,3 @@ * @param {ASTNode} node Node to check.

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

@@ -164,5 +250,134 @@ }

/**
* Returns a ecmaVersion compatible for regexpp.
* @param {number} ecmaVersion The ecmaVersion to convert.
* @returns {import("@eslint-community/regexpp/ecma-versions").EcmaVersion} The resulting ecmaVersion compatible for regexpp.
*/
function getRegexppEcmaVersion(ecmaVersion) {
if (ecmaVersion <= 5) {
return 5;
}
return Math.min(ecmaVersion, REGEXPP_LATEST_ECMA_VERSION);
}
const regexppEcmaVersion = getRegexppEcmaVersion(context.languageOptions.ecmaVersion);
/**
* Makes a character escaped or else returns null.
* @param {string} character The character to escape.
* @returns {string} The resulting escaped character.
*/
function resolveEscapes(character) {
switch (character) {
case "\n":
case "\\\n":
return "\\n";
case "\r":
case "\\\r":
return "\\r";
case "\t":
case "\\\t":
return "\\t";
case "\v":
case "\\\v":
return "\\v";
case "\f":
case "\\\f":
return "\\f";
case "/":
return "\\/";
default:
return null;
}
}
/**
* Checks whether the given regex and flags are valid for the ecma version or not.
* @param {string} pattern The regex pattern to check.
* @param {string | undefined} flags The regex flags to check.
* @returns {boolean} True if the given regex pattern and flags are valid for the ecma version.
*/
function isValidRegexForEcmaVersion(pattern, flags) {
const validator = new RegExpValidator({ ecmaVersion: regexppEcmaVersion });
try {
validator.validatePattern(pattern, 0, pattern.length, {
unicode: flags ? flags.includes("u") : false,
unicodeSets: flags ? flags.includes("v") : false
});
if (flags) {
validator.validateFlags(flags);
}
return true;
} catch {
return false;
}
}
/**
* Checks whether two given regex flags contain the same flags or not.
* @param {string} flagsA The regex flags.
* @param {string} flagsB The regex flags.
* @returns {boolean} True if two regex flags contain same flags.
*/
function areFlagsEqual(flagsA, flagsB) {
return [...flagsA].sort().join("") === [...flagsB].sort().join("");
}
/**
* Merges two regex flags.
* @param {string} flagsA The regex flags.
* @param {string} flagsB The regex flags.
* @returns {string} The merged regex flags.
*/
function mergeRegexFlags(flagsA, flagsB) {
const flagsSet = new Set([
...flagsA,
...flagsB
]);
return [...flagsSet].join("");
}
/**
* Checks whether a give node can be fixed to the given regex pattern and flags.
* @param {ASTNode} node The node to check.
* @param {string} pattern The regex pattern to check.
* @param {string} flags The regex flags
* @returns {boolean} True if a node can be fixed to the given regex pattern and flags.
*/
function canFixTo(node, pattern, flags) {
const tokenBefore = sourceCode.getTokenBefore(node);
return sourceCode.getCommentsInside(node).length === 0 &&
(!tokenBefore || validPrecedingTokens.has(tokenBefore.value)) &&
isValidRegexForEcmaVersion(pattern, flags);
}
/**
* Returns a safe output code considering the before and after tokens.
* @param {ASTNode} node The regex node.
* @param {string} newRegExpValue The new regex expression value.
* @returns {string} The output code.
*/
function getSafeOutput(node, newRegExpValue) {
const tokenBefore = sourceCode.getTokenBefore(node);
const tokenAfter = sourceCode.getTokenAfter(node);
return (tokenBefore && !canTokensBeAdjacent(tokenBefore, newRegExpValue) && tokenBefore.range[1] === node.range[0] ? " " : "") +
newRegExpValue +
(tokenAfter && !canTokensBeAdjacent(newRegExpValue, tokenAfter) && node.range[1] === tokenAfter.range[0] ? " " : "");
}
return {
Program() {
const scope = context.getScope();
Program(node) {
const scope = sourceCode.getScope(node);
const tracker = new ReferenceTracker(scope);

@@ -176,11 +391,123 @@ const traceMap = {

for (const { node } of tracker.iterateGlobalReferences(traceMap)) {
if (disallowRedundantWrapping && isUnnecessarilyWrappedRegexLiteral(node)) {
if (node.arguments.length === 2) {
context.report({ node, messageId: "unexpectedRedundantRegExpWithFlags" });
for (const { node: refNode } of tracker.iterateGlobalReferences(traceMap)) {
if (disallowRedundantWrapping && isUnnecessarilyWrappedRegexLiteral(refNode)) {
const regexNode = refNode.arguments[0];
if (refNode.arguments.length === 2) {
const suggests = [];
const argFlags = getStringValue(refNode.arguments[1]) || "";
if (canFixTo(refNode, regexNode.regex.pattern, argFlags)) {
suggests.push({
messageId: "replaceWithLiteralAndFlags",
pattern: regexNode.regex.pattern,
flags: argFlags
});
}
const literalFlags = regexNode.regex.flags || "";
const mergedFlags = mergeRegexFlags(literalFlags, argFlags);
if (
!areFlagsEqual(mergedFlags, argFlags) &&
canFixTo(refNode, regexNode.regex.pattern, mergedFlags)
) {
suggests.push({
messageId: "replaceWithIntendedLiteralAndFlags",
pattern: regexNode.regex.pattern,
flags: mergedFlags
});
}
context.report({
node: refNode,
messageId: "unexpectedRedundantRegExpWithFlags",
suggest: suggests.map(({ flags, pattern, messageId }) => ({
messageId,
data: {
flags
},
fix(fixer) {
return fixer.replaceText(refNode, getSafeOutput(refNode, `/${pattern}/${flags}`));
}
}))
});
} else {
context.report({ node, messageId: "unexpectedRedundantRegExp" });
const outputs = [];
if (canFixTo(refNode, regexNode.regex.pattern, regexNode.regex.flags)) {
outputs.push(sourceCode.getText(regexNode));
}
context.report({
node: refNode,
messageId: "unexpectedRedundantRegExp",
suggest: outputs.map(output => ({
messageId: "replaceWithLiteral",
fix(fixer) {
return fixer.replaceText(
refNode,
getSafeOutput(refNode, output)
);
}
}))
});
}
} else if (hasOnlyStaticStringArguments(node)) {
context.report({ node, messageId: "unexpectedRegExp" });
} else if (hasOnlyStaticStringArguments(refNode)) {
let regexContent = getStringValue(refNode.arguments[0]);
let noFix = false;
let flags;
if (refNode.arguments[1]) {
flags = getStringValue(refNode.arguments[1]);
}
if (!canFixTo(refNode, regexContent, flags)) {
noFix = true;
}
if (!/^[-a-zA-Z0-9\\[\](){} \t\r\n\v\f!@#$%^&*+^_=/~`.><?,'"|:;]*$/u.test(regexContent)) {
noFix = true;
}
if (regexContent && !noFix) {
let charIncrease = 0;
const ast = new RegExpParser({ ecmaVersion: regexppEcmaVersion }).parsePattern(regexContent, 0, regexContent.length, {
unicode: flags ? flags.includes("u") : false,
unicodeSets: flags ? flags.includes("v") : false
});
visitRegExpAST(ast, {
onCharacterEnter(characterNode) {
const escaped = resolveEscapes(characterNode.raw);
if (escaped) {
regexContent =
regexContent.slice(0, characterNode.start + charIncrease) +
escaped +
regexContent.slice(characterNode.end + charIncrease);
if (characterNode.raw.length === 1) {
charIncrease += 1;
}
}
}
});
}
const newRegExpValue = `/${regexContent || "(?:)"}/${flags || ""}`;
context.report({
node: refNode,
messageId: "unexpectedRegExp",
suggest: noFix ? [] : [{
messageId: "replaceWithLiteral",
fix(fixer) {
return fixer.replaceText(refNode, getSafeOutput(refNode, newRegExpValue));
}
}]
});
}

@@ -187,0 +514,0 @@ }

@@ -33,3 +33,3 @@ /**

/* istanbul ignore next : unreachable */
/* c8 ignore next */
return null;

@@ -63,2 +63,3 @@ }

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -69,6 +70,5 @@ meta: {

docs: {
description: "require rest parameters instead of `arguments`",
category: "ECMAScript 6",
description: "Require rest parameters instead of `arguments`",
recommended: false,
url: "https://eslint.org/docs/rules/prefer-rest-params"
url: "https://eslint.org/docs/latest/rules/prefer-rest-params"
},

@@ -85,2 +85,4 @@

const sourceCode = context.sourceCode;
/**

@@ -101,6 +103,7 @@ * Reports a given reference.

* Reports references of the implicit `arguments` variable if exist.
* @param {ASTNode} node The node representing the function.
* @returns {void}
*/
function checkForArguments() {
const argumentsVar = getVariableOfArguments(context.getScope());
function checkForArguments(node) {
const argumentsVar = getVariableOfArguments(sourceCode.getScope(node));

@@ -107,0 +110,0 @@ if (argumentsVar) {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -52,6 +53,5 @@ meta: {

docs: {
description: "require spread operators instead of `.apply()`",
category: "ECMAScript 6",
description: "Require spread operators instead of `.apply()`",
recommended: false,
url: "https://eslint.org/docs/rules/prefer-spread"
url: "https://eslint.org/docs/latest/rules/prefer-spread"
},

@@ -68,3 +68,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -71,0 +71,0 @@ return {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -131,6 +132,5 @@ meta: {

docs: {
description: "require template literals instead of string concatenation",
category: "ECMAScript 6",
description: "Require template literals instead of string concatenation",
recommended: false,
url: "https://eslint.org/docs/rules/prefer-template"
url: "https://eslint.org/docs/latest/rules/prefer-template"
},

@@ -147,3 +147,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
let done = Object.create(null);

@@ -194,3 +194,3 @@

if (isConcatenation(currentNode) && hasStringLiteral(currentNode) && hasNonStringLiteral(currentNode)) {
if (isConcatenation(currentNode) && hasStringLiteral(currentNode)) {
const plusSign = sourceCode.getFirstTokenBetween(currentNode.left, currentNode.right, token => token.value === "+");

@@ -197,0 +197,0 @@ const textBeforePlus = getTextBetween(currentNode.left, plusSign);

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -25,6 +26,5 @@ meta: {

docs: {
description: "require quotes around object literal property names",
category: "Stylistic Issues",
description: "Require quotes around object literal property names",
recommended: false,
url: "https://eslint.org/docs/rules/quote-props"
url: "https://eslint.org/docs/latest/rules/quote-props"
},

@@ -91,3 +91,3 @@

sourceCode = context.getSourceCode();
sourceCode = context.sourceCode;

@@ -97,7 +97,7 @@

* Checks whether a certain string constitutes an ES3 token
* @param {string} tokenStr The string to be checked.
* @param {string} tokenStr The string to be checked.
* @returns {boolean} `true` if it is an ES3 token.
*/
function isKeyword(tokenStr) {
return keywords.indexOf(tokenStr) >= 0;
return keywords.includes(tokenStr);
}

@@ -107,5 +107,5 @@

* Checks if an espree-tokenized key has redundant quotes (i.e. whether quotes are unnecessary)
* @param {string} rawKey The raw key value from the source
* @param {espreeTokens} tokens The espree-tokenized node key
* @param {boolean} [skipNumberLiterals=false] Indicates whether number literals should be checked
* @param {string} rawKey The raw key value from the source
* @param {espreeTokens} tokens The espree-tokenized node key
* @param {boolean} [skipNumberLiterals=false] Indicates whether number literals should be checked
* @returns {boolean} Whether or not a key has redundant quotes.

@@ -116,3 +116,3 @@ * @private

return tokens.length === 1 && tokens[0].start === 0 && tokens[0].end === rawKey.length &&
(["Identifier", "Keyword", "Null", "Boolean"].indexOf(tokens[0].type) >= 0 ||
(["Identifier", "Keyword", "Null", "Boolean"].includes(tokens[0].type) ||
(tokens[0].type === "Numeric" && !skipNumberLiterals && String(+tokens[0].value) === tokens[0].value));

@@ -148,3 +148,3 @@ }

* Ensures that a property's key is quoted only when necessary
* @param {ASTNode} node Property AST node
* @param {ASTNode} node Property AST node
* @returns {void}

@@ -205,3 +205,3 @@ */

* Ensures that a property's key is quoted
* @param {ASTNode} node Property AST node
* @param {ASTNode} node Property AST node
* @returns {void}

@@ -224,4 +224,4 @@ */

* Ensures that an object's keys are consistently quoted, optionally checks for redundancy of quotes
* @param {ASTNode} node Property AST node
* @param {boolean} checkQuotesRedundancy Whether to check quotes' redundancy
* @param {ASTNode} node Property AST node
* @param {boolean} checkQuotesRedundancy Whether to check quotes' redundancy
* @returns {void}

@@ -228,0 +228,0 @@ */

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -83,6 +84,5 @@ meta: {

docs: {
description: "enforce the consistent use of either backticks, double, or single quotes",
category: "Stylistic Issues",
description: "Enforce the consistent use of either backticks, double, or single quotes",
recommended: false,
url: "https://eslint.org/docs/rules/quotes"
url: "https://eslint.org/docs/latest/rules/quotes"
},

@@ -128,3 +128,3 @@

allowTemplateLiterals = options && options.allowTemplateLiterals === true,
sourceCode = context.getSourceCode();
sourceCode = context.sourceCode;
let avoidEscape = options && options.avoidEscape === true;

@@ -163,3 +163,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.

@@ -173,3 +174,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)
);

@@ -179,14 +181,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;

@@ -221,6 +222,7 @@ // Check the node is at a prologue.

case "ExpressionStatement":
return isPartOfDirectivePrologue(node);
return !astUtils.isParenthesised(sourceCode, node) && isExpressionInOrJustAfterDirectivePrologue(node);
// LiteralPropertyName.
case "Property":
case "PropertyDefinition":
case "MethodDefinition":

@@ -232,5 +234,16 @@ return parent.key === node && !parent.computed;

case "ExportNamedDeclaration":
case "ExportAllDeclaration":
return parent.source === node;
// ModuleExportName or ModuleSpecifier.
case "ExportAllDeclaration":
return parent.exported === node || parent.source === node;
// ModuleExportName.
case "ImportSpecifier":
return parent.imported === node;
// ModuleExportName.
case "ExportSpecifier":
return parent.local === node || parent.exported === node;
// Others don't allow.

@@ -282,3 +295,3 @@ default:

if (!isValid && avoidEscape) {
isValid = astUtils.isSurroundedBy(rawVal, settings.alternateQuote) && rawVal.indexOf(settings.quote) >= 0;
isValid = astUtils.isSurroundedBy(rawVal, settings.alternateQuote) && rawVal.includes(settings.quote);
}

@@ -328,8 +341,7 @@

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

@@ -336,0 +348,0 @@ return null;

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -83,8 +84,9 @@ meta: {

docs: {
description: "enforce the consistent use of the radix argument when using `parseInt()`",
category: "Best Practices",
description: "Enforce the consistent use of the radix argument when using `parseInt()`",
recommended: false,
url: "https://eslint.org/docs/rules/radix"
url: "https://eslint.org/docs/latest/rules/radix"
},
hasSuggestions: true,
schema: [

@@ -100,3 +102,4 @@ {

missingRadix: "Missing radix parameter.",
invalidRadix: "Invalid radix parameter, must be an integer between 2 and 36."
invalidRadix: "Invalid radix parameter, must be an integer between 2 and 36.",
addRadixParameter10: "Add radix parameter `10` for parsing decimal numbers."
}

@@ -107,2 +110,3 @@ },

const mode = context.options[0] || MODE_ALWAYS;
const sourceCode = context.sourceCode;

@@ -130,3 +134,16 @@ /**

node,
messageId: "missingRadix"
messageId: "missingRadix",
suggest: [
{
messageId: "addRadixParameter10",
fix(fixer) {
const tokens = sourceCode.getTokens(node);
const lastToken = tokens[tokens.length - 1]; // Parenthesis.
const secondToLastToken = tokens[tokens.length - 2]; // May or may not be a comma.
const hasTrailingComma = secondToLastToken.type === "Punctuator" && secondToLastToken.value === ",";
return fixer.insertTextBefore(lastToken, hasTrailingComma ? " 10," : ", 10");
}
}
]
});

@@ -153,4 +170,4 @@ }

return {
"Program:exit"() {
const scope = context.getScope();
"Program:exit"(node) {
const scope = sourceCode.getScope(node);
let variable;

@@ -162,6 +179,6 @@

variable.references.forEach(reference => {
const node = reference.identifier;
const idNode = reference.identifier;
if (astUtils.isCallee(node)) {
checkArguments(node.parent);
if (astUtils.isCallee(idNode)) {
checkArguments(idNode.parent);
}

@@ -175,8 +192,8 @@ });

variable.references.forEach(reference => {
const node = reference.identifier.parent;
const maybeCallee = node.parent.type === "ChainExpression"
? node.parent
: node;
const parentNode = reference.identifier.parent;
const maybeCallee = parentNode.parent.type === "ChainExpression"
? parentNode.parent
: parentNode;
if (isParseIntMethod(node) && astUtils.isCallee(maybeCallee)) {
if (isParseIntMethod(parentNode) && astUtils.isCallee(maybeCallee)) {
checkArguments(maybeCallee.parent);

@@ -183,0 +200,0 @@ }

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

for (const reference of scope.references) {
if (reference.resolved === null) {
continue;
}
outReferenceMap.set(reference.identifier, reference);

@@ -79,2 +83,5 @@ }

/**
* Represents segment information.
*/
class SegmentInfo {

@@ -91,4 +98,4 @@ constructor() {

initialize(segment) {
const outdatedReadVariableNames = new Set();
const freshReadVariableNames = new Set();
const outdatedReadVariables = new Set();
const freshReadVariables = new Set();

@@ -99,8 +106,8 @@ for (const prevSegment of segment.prevSegments) {

if (info) {
info.outdatedReadVariableNames.forEach(Set.prototype.add, outdatedReadVariableNames);
info.freshReadVariableNames.forEach(Set.prototype.add, freshReadVariableNames);
info.outdatedReadVariables.forEach(Set.prototype.add, outdatedReadVariables);
info.freshReadVariables.forEach(Set.prototype.add, freshReadVariables);
}
}
this.info.set(segment, { outdatedReadVariableNames, freshReadVariableNames });
this.info.set(segment, { outdatedReadVariables, freshReadVariables });
}

@@ -111,6 +118,6 @@

* @param {PathSegment[]} segments The segments that it read the variable on.
* @param {string} variableName The variable name to be read.
* @param {Variable} variable The variable to be read.
* @returns {void}
*/
markAsRead(segments, variableName) {
markAsRead(segments, variable) {
for (const segment of segments) {

@@ -120,6 +127,6 @@ const info = this.info.get(segment);

if (info) {
info.freshReadVariableNames.add(variableName);
info.freshReadVariables.add(variable);
// If a variable is freshly read again, then it's no more out-dated.
info.outdatedReadVariableNames.delete(variableName);
info.outdatedReadVariables.delete(variable);
}

@@ -130,3 +137,3 @@ }

/**
* Move `freshReadVariableNames` to `outdatedReadVariableNames`.
* Move `freshReadVariables` to `outdatedReadVariables`.
* @param {PathSegment[]} segments The segments to process.

@@ -140,4 +147,4 @@ * @returns {void}

if (info) {
info.freshReadVariableNames.forEach(Set.prototype.add, info.outdatedReadVariableNames);
info.freshReadVariableNames.clear();
info.freshReadVariables.forEach(Set.prototype.add, info.outdatedReadVariables);
info.freshReadVariables.clear();
}

@@ -150,10 +157,10 @@ }

* @param {PathSegment[]} segments The current segments.
* @param {string} variableName The variable name to check.
* @param {Variable} variable The variable to check.
* @returns {boolean} `true` if the variable is outdated on the segments.
*/
isOutdated(segments, variableName) {
isOutdated(segments, variable) {
for (const segment of segments) {
const info = this.info.get(segment);
if (info && info.outdatedReadVariableNames.has(variableName)) {
if (info && info.outdatedReadVariables.has(variable)) {
return true;

@@ -170,2 +177,3 @@ }

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -176,13 +184,23 @@ meta: {

docs: {
description: "disallow assignments that can lead to race conditions due to usage of `await` or `yield`",
category: "Possible Errors",
description: "Disallow assignments that can lead to race conditions due to usage of `await` or `yield`",
recommended: false,
url: "https://eslint.org/docs/rules/require-atomic-updates"
url: "https://eslint.org/docs/latest/rules/require-atomic-updates"
},
fixable: null,
schema: [],
schema: [{
type: "object",
properties: {
allowProperties: {
type: "boolean",
default: false
}
},
additionalProperties: false
}],
messages: {
nonAtomicUpdate: "Possible race condition: `{{value}}` might be reassigned based on an outdated value of `{{value}}`."
nonAtomicUpdate: "Possible race condition: `{{value}}` might be reassigned based on an outdated value of `{{value}}`.",
nonAtomicObjectUpdate: "Possible race condition: `{{value}}` might be assigned based on an outdated state of `{{object}}`."
}

@@ -192,3 +210,5 @@ },

create(context) {
const sourceCode = context.getSourceCode();
const allowProperties = !!context.options[0] && context.options[0].allowProperties;
const sourceCode = context.sourceCode;
const assignmentReferences = new Map();

@@ -199,4 +219,4 @@ const segmentInfo = new SegmentInfo();

return {
onCodePathStart(codePath) {
const scope = context.getScope();
onCodePathStart(codePath, node) {
const scope = sourceCode.getScope(node);
const shouldVerify =

@@ -209,3 +229,4 @@ scope.type === "function" &&

codePath,
referenceMap: shouldVerify ? createReferenceMap(scope) : null
referenceMap: shouldVerify ? createReferenceMap(scope) : null,
currentSegments: new Set()
};

@@ -220,7 +241,21 @@ },

segmentInfo.initialize(segment);
stack.currentSegments.add(segment);
},
onUnreachableCodePathSegmentStart(segment) {
stack.currentSegments.add(segment);
},
onUnreachableCodePathSegmentEnd(segment) {
stack.currentSegments.delete(segment);
},
onCodePathSegmentEnd(segment) {
stack.currentSegments.delete(segment);
},
// Handle references to prepare verification.
Identifier(node) {
const { codePath, referenceMap } = stack;
const { referenceMap } = stack;
const reference = referenceMap && referenceMap.get(node);

@@ -232,3 +267,2 @@

}
const name = reference.identifier.name;
const variable = reference.resolved;

@@ -240,3 +274,3 @@ const writeExpr = getWriteExpr(reference);

if (reference.isRead() && !(writeExpr && writeExpr.parent.operator === "=")) {
segmentInfo.markAsRead(codePath.currentSegments, name);
segmentInfo.markAsRead(stack.currentSegments, variable);
}

@@ -265,9 +299,8 @@

* Verify assignments.
* If the reference exists in `outdatedReadVariableNames` list, report it.
* If the reference exists in `outdatedReadVariables` list, report it.
*/
":expression:exit"(node) {
const { codePath, referenceMap } = stack;
// referenceMap exists if this is in a resumable function scope.
if (!referenceMap) {
if (!stack.referenceMap) {
return;

@@ -278,3 +311,3 @@ }

if (node.type === "AwaitExpression" || node.type === "YieldExpression") {
segmentInfo.makeOutdated(codePath.currentSegments);
segmentInfo.makeOutdated(stack.currentSegments);
}

@@ -289,12 +322,24 @@

for (const reference of references) {
const name = reference.identifier.name;
const variable = reference.resolved;
if (segmentInfo.isOutdated(codePath.currentSegments, name)) {
context.report({
node: node.parent,
messageId: "nonAtomicUpdate",
data: {
value: sourceCode.getText(node.parent.left)
}
});
if (segmentInfo.isOutdated(stack.currentSegments, variable)) {
if (node.parent.left === reference.identifier) {
context.report({
node: node.parent,
messageId: "nonAtomicUpdate",
data: {
value: variable.name
}
});
} else if (!allowProperties) {
context.report({
node: node.parent,
messageId: "nonAtomicObjectUpdate",
data: {
value: sourceCode.getText(node.parent.left),
object: variable.name
}
});
}
}

@@ -301,0 +346,0 @@ }

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -37,6 +38,5 @@ meta: {

docs: {
description: "disallow async functions which have no `await` expression",
category: "Best Practices",
description: "Disallow async functions which have no `await` expression",
recommended: false,
url: "https://eslint.org/docs/rules/require-await"
url: "https://eslint.org/docs/latest/rules/require-await"
},

@@ -52,3 +52,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
let scopeInfo = null;

@@ -55,0 +55,0 @@

/**
* @fileoverview Rule to check for jsdoc presence.
* @author Gyandeep Singh
* @deprecated in ESLint v5.10.0
*/
"use strict";
/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -12,6 +14,5 @@ meta: {

docs: {
description: "require JSDoc comments",
category: "Stylistic Issues",
description: "Require JSDoc comments",
recommended: false,
url: "https://eslint.org/docs/rules/require-jsdoc"
url: "https://eslint.org/docs/latest/rules/require-jsdoc"
},

@@ -64,3 +65,3 @@

create(context) {
const source = context.getSourceCode();
const source = context.sourceCode;
const DEFAULT_OPTIONS = {

@@ -67,0 +68,0 @@ FunctionDeclaration: true,

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

getStringIfConstant
} = require("eslint-utils");
} = require("@eslint-community/eslint-utils");
const astUtils = require("./utils/ast-utils.js");
const { isValidWithUnicodeFlag } = require("./utils/regular-expressions");

@@ -24,2 +26,3 @@ //------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -30,9 +33,11 @@ meta: {

docs: {
description: "enforce the use of `u` flag on RegExp",
category: "Best Practices",
description: "Enforce the use of `u` or `v` flag on RegExp",
recommended: false,
url: "https://eslint.org/docs/rules/require-unicode-regexp"
url: "https://eslint.org/docs/latest/rules/require-unicode-regexp"
},
hasSuggestions: true,
messages: {
addUFlag: "Add the 'u' flag.",
requireUFlag: "Use the 'u' flag."

@@ -45,2 +50,5 @@ },

create(context) {
const sourceCode = context.sourceCode;
return {

@@ -50,9 +58,22 @@ "Literal[regex]"(node) {

if (!flags.includes("u")) {
context.report({ node, messageId: "requireUFlag" });
if (!flags.includes("u") && !flags.includes("v")) {
context.report({
messageId: "requireUFlag",
node,
suggest: isValidWithUnicodeFlag(context.languageOptions.ecmaVersion, node.regex.pattern)
? [
{
fix(fixer) {
return fixer.insertTextAfter(node, "u");
},
messageId: "addUFlag"
}
]
: null
});
}
},
Program() {
const scope = context.getScope();
Program(node) {
const scope = sourceCode.getScope(node);
const tracker = new ReferenceTracker(scope);

@@ -63,8 +84,47 @@ const trackMap = {

for (const { node } of tracker.iterateGlobalReferences(trackMap)) {
const flagsNode = node.arguments[1];
for (const { node: refNode } of tracker.iterateGlobalReferences(trackMap)) {
const [patternNode, flagsNode] = refNode.arguments;
if (patternNode && patternNode.type === "SpreadElement") {
continue;
}
const pattern = getStringIfConstant(patternNode, scope);
const flags = getStringIfConstant(flagsNode, scope);
if (!flagsNode || (typeof flags === "string" && !flags.includes("u"))) {
context.report({ node, messageId: "requireUFlag" });
if (!flagsNode || (typeof flags === "string" && !flags.includes("u") && !flags.includes("v"))) {
context.report({
messageId: "requireUFlag",
node: refNode,
suggest: typeof pattern === "string" && isValidWithUnicodeFlag(context.languageOptions.ecmaVersion, pattern)
? [
{
fix(fixer) {
if (flagsNode) {
if ((flagsNode.type === "Literal" && typeof flagsNode.value === "string") || flagsNode.type === "TemplateLiteral") {
const flagsNodeText = sourceCode.getText(flagsNode);
return fixer.replaceText(flagsNode, [
flagsNodeText.slice(0, flagsNodeText.length - 1),
flagsNodeText.slice(flagsNodeText.length - 1)
].join("u"));
}
// We intentionally don't suggest concatenating + "u" to non-literals
return null;
}
const penultimateToken = sourceCode.getLastToken(refNode, { skip: 1 }); // skip closing parenthesis
return fixer.insertTextAfter(
penultimateToken,
astUtils.isCommaToken(penultimateToken)
? ' "u",'
: ', "u"'
);
},
messageId: "addUFlag"
}
]
: null
});
}

@@ -71,0 +131,0 @@ }

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "require generator functions to contain `yield`",
category: "ECMAScript 6",
description: "Require generator functions to contain `yield`",
recommended: true,
url: "https://eslint.org/docs/rules/require-yield"
url: "https://eslint.org/docs/latest/rules/require-yield"
},

@@ -73,3 +73,2 @@

/* istanbul ignore else */
if (stack.length > 0) {

@@ -76,0 +75,0 @@ stack[stack.length - 1] += 1;

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "enforce spacing between rest and spread operators and their expressions",
category: "ECMAScript 6",
description: "Enforce spacing between rest and spread operators and their expressions",
recommended: false,
url: "https://eslint.org/docs/rules/rest-spread-spacing"
url: "https://eslint.org/docs/latest/rules/rest-spread-spacing"
},

@@ -40,3 +40,3 @@

create(context) {
const sourceCode = context.getSourceCode(),
const sourceCode = context.sourceCode,
alwaysSpace = context.options[0] === "always";

@@ -43,0 +43,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -20,6 +21,5 @@ meta: {

docs: {
description: "enforce consistent spacing before and after semicolons",
category: "Stylistic Issues",
description: "Enforce consistent spacing before and after semicolons",
recommended: false,
url: "https://eslint.org/docs/rules/semi-spacing"
url: "https://eslint.org/docs/latest/rules/semi-spacing"
},

@@ -57,3 +57,3 @@

const config = context.options[0],
sourceCode = context.getSourceCode();
sourceCode = context.sourceCode;
let requireSpaceBefore = false,

@@ -244,5 +244,6 @@ requireSpaceAfter = true;

}
}
},
PropertyDefinition: checkNode
};
}
};

@@ -18,15 +18,14 @@ /**

const SELECTOR = `:matches(${
[
"BreakStatement", "ContinueStatement", "DebuggerStatement",
"DoWhileStatement", "ExportAllDeclaration",
"ExportDefaultDeclaration", "ExportNamedDeclaration",
"ExpressionStatement", "ImportDeclaration", "ReturnStatement",
"ThrowStatement", "VariableDeclaration"
].join(",")
})`;
const SELECTOR = [
"BreakStatement", "ContinueStatement", "DebuggerStatement",
"DoWhileStatement", "ExportAllDeclaration",
"ExportDefaultDeclaration", "ExportNamedDeclaration",
"ExpressionStatement", "ImportDeclaration", "ReturnStatement",
"ThrowStatement", "VariableDeclaration", "PropertyDefinition"
].join(",");
/**
* Get the child node list of a given node.
* This returns `Program#body`, `BlockStatement#body`, or `SwitchCase#consequent`.
* This returns `BlockStatement#body`, `StaticBlock#body`, `Program#body`,
* `ClassBody#body`, or `SwitchCase#consequent`.
* This is used to check whether a node is the first/last child.

@@ -39,3 +38,8 @@ * @param {Node} node A node to get child node list.

if (t === "BlockStatement" || t === "Program") {
if (
t === "BlockStatement" ||
t === "StaticBlock" ||
t === "Program" ||
t === "ClassBody"
) {
return node.body;

@@ -68,2 +72,3 @@ }

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -74,6 +79,5 @@ meta: {

docs: {
description: "enforce location of semicolons",
category: "Stylistic Issues",
description: "Enforce location of semicolons",
recommended: false,
url: "https://eslint.org/docs/rules/semi-style"
url: "https://eslint.org/docs/latest/rules/semi-style"
},

@@ -90,3 +94,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const option = context.options[0] || "last";

@@ -93,0 +97,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,6 +25,5 @@ meta: {

docs: {
description: "require or disallow semicolons instead of ASI",
category: "Stylistic Issues",
description: "Require or disallow semicolons instead of ASI",
recommended: false,
url: "https://eslint.org/docs/rules/semi"
url: "https://eslint.org/docs/latest/rules/semi"
},

@@ -63,3 +63,4 @@

properties: {
omitLastInOneLineBlock: { type: "boolean" }
omitLastInOneLineBlock: { type: "boolean" },
omitLastInOneLineClassBody: { type: "boolean" }
},

@@ -84,7 +85,10 @@ additionalProperties: false

const OPT_OUT_PATTERN = /^[-[(/+`]/u; // One of [(/+-`
const unsafeClassFieldNames = new Set(["get", "set", "static"]);
const unsafeClassFieldFollowers = new Set(["*", "in", "instanceof"]);
const options = context.options[1];
const never = context.options[0] === "never";
const exceptOneLine = Boolean(options && options.omitLastInOneLineBlock);
const exceptOneLineClassBody = Boolean(options && options.omitLastInOneLineClassBody);
const beforeStatementContinuationChars = options && options.beforeStatementContinuationChars || "any";
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -174,2 +178,51 @@ //--------------------------------------------------------------------------

/**
* Checks if a given PropertyDefinition node followed by a semicolon
* can safely remove that semicolon. It is not to safe to remove if
* the class field name is "get", "set", or "static", or if
* followed by a generator method.
* @param {ASTNode} node The node to check.
* @returns {boolean} `true` if the node cannot have the semicolon
* removed.
*/
function maybeClassFieldAsiHazard(node) {
if (node.type !== "PropertyDefinition") {
return false;
}
/*
* Computed property names and non-identifiers are always safe
* as they can be distinguished from keywords easily.
*/
const needsNameCheck = !node.computed && node.key.type === "Identifier";
/*
* Certain names are problematic unless they also have a
* a way to distinguish between keywords and property
* names.
*/
if (needsNameCheck && unsafeClassFieldNames.has(node.key.name)) {
/*
* Special case: If the field name is `static`,
* it is only valid if the field is marked as static,
* so "static static" is okay but "static" is not.
*/
const isStaticStatic = node.static && node.key.name === "static";
/*
* For other unsafe names, we only care if there is no
* initializer. No initializer = hazard.
*/
if (!isStaticStatic && !node.value) {
return true;
}
}
const followingToken = sourceCode.getTokenAfter(node);
return unsafeClassFieldFollowers.has(followingToken.value);
}
/**
* Check whether a given node is on the same line with the next token.

@@ -241,6 +294,15 @@ * @param {Node} node A statement node to check.

}
if (maybeClassFieldAsiHazard(node)) {
return false;
}
if (isOnSameLineWithNextToken(node)) {
return false; // One liner.
}
if (beforeStatementContinuationChars === "never" && !maybeAsiHazardAfter(node)) {
// continuation characters should not apply to class fields
if (
node.type !== "PropertyDefinition" &&
beforeStatementContinuationChars === "never" &&
!maybeAsiHazardAfter(node)
) {
return true; // ASI works. This statement doesn't connect to the next.

@@ -256,7 +318,9 @@ }

/**
* Checks a node to see if it's in a one-liner block statement.
* Checks a node to see if it's the last item in a one-liner block.
* Block is any `BlockStatement` or `StaticBlock` node. Block is a one-liner if its
* braces (and consequently everything between them) are on the same line.
* @param {ASTNode} node The node to check.
* @returns {boolean} whether the node is in a one-liner block statement.
* @returns {boolean} whether the node is the last item in a one-liner block.
*/
function isOneLinerBlock(node) {
function isLastInOneLinerBlock(node) {
const parent = node.parent;

@@ -268,10 +332,38 @@ const nextToken = sourceCode.getTokenAfter(node);

}
return (
!!parent &&
parent.type === "BlockStatement" &&
parent.loc.start.line === parent.loc.end.line
);
if (parent.type === "BlockStatement") {
return parent.loc.start.line === parent.loc.end.line;
}
if (parent.type === "StaticBlock") {
const openingBrace = sourceCode.getFirstToken(parent, { skip: 1 }); // skip the `static` token
return openingBrace.loc.start.line === parent.loc.end.line;
}
return false;
}
/**
* Checks a node to see if it's the last item in a one-liner `ClassBody` node.
* ClassBody is a one-liner if its braces (and consequently everything between them) are on the same line.
* @param {ASTNode} node The node to check.
* @returns {boolean} whether the node is the last item in a one-liner ClassBody.
*/
function isLastInOneLinerClassBody(node) {
const parent = node.parent;
const nextToken = sourceCode.getTokenAfter(node);
if (!nextToken || nextToken.value !== "}") {
return false;
}
if (parent.type === "ClassBody") {
return parent.loc.start.line === parent.loc.end.line;
}
return false;
}
/**
* Checks a node to see if it's followed by a semicolon.

@@ -287,11 +379,17 @@ * @param {ASTNode} node The node to check.

report(node, true);
} else if (!isSemi && beforeStatementContinuationChars === "always" && maybeAsiHazardBefore(sourceCode.getTokenAfter(node))) {
} else if (
!isSemi && beforeStatementContinuationChars === "always" &&
node.type !== "PropertyDefinition" &&
maybeAsiHazardBefore(sourceCode.getTokenAfter(node))
) {
report(node);
}
} else {
const oneLinerBlock = (exceptOneLine && isOneLinerBlock(node));
const oneLinerBlock = (exceptOneLine && isLastInOneLinerBlock(node));
const oneLinerClassBody = (exceptOneLineClassBody && isLastInOneLinerClassBody(node));
const oneLinerBlockOrClassBody = oneLinerBlock || oneLinerClassBody;
if (isSemi && oneLinerBlock) {
if (isSemi && oneLinerBlockOrClassBody) {
report(node, true);
} else if (!isSemi && !oneLinerBlock) {
} else if (!isSemi && !oneLinerBlockOrClassBody) {
report(node);

@@ -341,3 +439,4 @@ }

}
}
},
PropertyDefinition: checkForSemicolon
};

@@ -344,0 +443,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "enforce sorted import declarations within modules",
category: "ECMAScript 6",
description: "Enforce sorted import declarations within modules",
recommended: false,
url: "https://eslint.org/docs/rules/sort-imports"
url: "https://eslint.org/docs/latest/rules/sort-imports"
},

@@ -76,3 +76,3 @@

allowSeparatedGroups = configuration.allowSeparatedGroups || false,
sourceCode = context.getSourceCode();
sourceCode = context.sourceCode;
let previousDeclaration = null;

@@ -79,0 +79,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -84,6 +85,5 @@ meta: {

docs: {
description: "require object keys to be sorted",
category: "Stylistic Issues",
description: "Require object keys to be sorted",
recommended: false,
url: "https://eslint.org/docs/rules/sort-keys"
url: "https://eslint.org/docs/latest/rules/sort-keys"
},

@@ -110,2 +110,6 @@

default: 2
},
allowLineSeparatedGroups: {
type: "boolean",
default: false
}

@@ -130,2 +134,3 @@ },

const minKeys = options && options.minKeys;
const allowLineSeparatedGroups = options && options.allowLineSeparatedGroups || false;
const isValidOrder = isValidOrders[

@@ -137,2 +142,3 @@ order + (insensitive ? "I" : "") + (natural ? "N" : "")

let stack = null;
const sourceCode = context.sourceCode;

@@ -143,2 +149,4 @@ return {

upper: stack,
prevNode: null,
prevBlankLine: false,
prevName: null,

@@ -168,2 +176,32 @@ numKeys: node.properties.length

// Get tokens between current node and previous node
const tokens = stack.prevNode && sourceCode
.getTokensBetween(stack.prevNode, node, { includeComments: true });
let isBlankLineBetweenNodes = stack.prevBlankLine;
if (tokens) {
// check blank line between tokens
tokens.forEach((token, index) => {
const previousToken = tokens[index - 1];
if (previousToken && (token.loc.start.line - previousToken.loc.end.line > 1)) {
isBlankLineBetweenNodes = true;
}
});
// check blank line between the current node and the last token
if (!isBlankLineBetweenNodes && (node.loc.start.line - tokens[tokens.length - 1].loc.end.line > 1)) {
isBlankLineBetweenNodes = true;
}
// check blank line between the first token and the previous node
if (!isBlankLineBetweenNodes && (tokens[0].loc.start.line - stack.prevNode.loc.end.line > 1)) {
isBlankLineBetweenNodes = true;
}
}
stack.prevNode = node;
if (thisName !== null) {

@@ -173,2 +211,7 @@ stack.prevName = thisName;

if (allowLineSeparatedGroups && isBlankLineBetweenNodes) {
stack.prevBlankLine = thisName === null;
return;
}
if (prevName === null || thisName === null || numKeys < minKeys) {

@@ -175,0 +218,0 @@ return;

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "require variables within the same declaration block to be sorted",
category: "Stylistic Issues",
description: "Require variables within the same declaration block to be sorted",
recommended: false,
url: "https://eslint.org/docs/rules/sort-vars"
url: "https://eslint.org/docs/latest/rules/sort-vars"
},

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

ignoreCase = configuration.ignoreCase || false,
sourceCode = context.getSourceCode();
sourceCode = context.sourceCode;

@@ -52,0 +52,0 @@ return {

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -43,6 +44,5 @@ meta: {

docs: {
description: "enforce consistent spacing before blocks",
category: "Stylistic Issues",
description: "Enforce consistent spacing before blocks",
recommended: false,
url: "https://eslint.org/docs/rules/space-before-blocks"
url: "https://eslint.org/docs/latest/rules/space-before-blocks"
},

@@ -85,3 +85,3 @@

const config = context.options[0],
sourceCode = context.getSourceCode();
sourceCode = context.sourceCode;
let alwaysFunctions = true,

@@ -114,2 +114,3 @@ alwaysKeywords = true,

* - `keyword-spacing` checks spaces after keywords in certain contexts.
* - `switch-colon-spacing` checks spaces after `:` of switch cases.
* @param {Token} precedingToken first token before the block.

@@ -120,4 +121,15 @@ * @param {ASTNode|Token} node `BlockStatement` node or `{` token of a `SwitchStatement` node.

function isConflicted(precedingToken, node) {
return astUtils.isArrowToken(precedingToken) ||
astUtils.isKeywordToken(precedingToken) && !isFunctionBody(node);
return (
astUtils.isArrowToken(precedingToken) ||
(
astUtils.isKeywordToken(precedingToken) &&
!isFunctionBody(node)
) ||
(
astUtils.isColonToken(precedingToken) &&
node.parent &&
node.parent.type === "SwitchCase" &&
precedingToken === astUtils.getSwitchCaseColonToken(node.parent, sourceCode)
)
);
}

@@ -124,0 +136,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -23,6 +24,5 @@ meta: {

docs: {
description: "enforce consistent spacing before `function` definition opening parenthesis",
category: "Stylistic Issues",
description: "Enforce consistent spacing before `function` definition opening parenthesis",
recommended: false,
url: "https://eslint.org/docs/rules/space-before-function-paren"
url: "https://eslint.org/docs/latest/rules/space-before-function-paren"
},

@@ -64,3 +64,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const baseConfig = typeof context.options[0] === "string" ? context.options[0] : "always";

@@ -67,0 +67,0 @@ const overrideConfig = typeof context.options[0] === "object" ? context.options[0] : {};

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -19,6 +20,5 @@ meta: {

docs: {
description: "enforce consistent spacing inside parentheses",
category: "Stylistic Issues",
description: "Enforce consistent spacing inside parentheses",
recommended: false,
url: "https://eslint.org/docs/rules/space-in-parens"
url: "https://eslint.org/docs/latest/rules/space-in-parens"
},

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

//--------------------------------------------------------------------------
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -110,0 +110,0 @@ /**

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

const { isEqToken } = require("./utils/ast-utils");
//------------------------------------------------------------------------------

@@ -12,2 +14,3 @@ // Rule Definition

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +21,5 @@ meta: {

docs: {
description: "require spacing around infix operators",
category: "Stylistic Issues",
description: "Require spacing around infix operators",
recommended: false,
url: "https://eslint.org/docs/rules/space-infix-ops"
url: "https://eslint.org/docs/latest/rules/space-infix-ops"
},

@@ -47,3 +49,3 @@

const int32Hint = context.options[0] ? context.options[0].int32Hint === true : false;
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

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

ConditionalExpression: checkConditional,
VariableDeclarator: checkVar
VariableDeclarator: checkVar,
PropertyDefinition(node) {
if (!node.value) {
return;
}
/*
* Because of computed properties and type annotations, some
* tokens may exist between `node.key` and `=`.
* Therefore, find the `=` from the right.
*/
const operatorToken = sourceCode.getTokenBefore(node.value, isEqToken);
const leftToken = sourceCode.getTokenBefore(operatorToken);
const rightToken = sourceCode.getTokenAfter(operatorToken);
if (
!sourceCode.isSpaceBetweenTokens(leftToken, operatorToken) ||
!sourceCode.isSpaceBetweenTokens(operatorToken, rightToken)
) {
report(node, operatorToken);
}
}
};

@@ -174,0 +198,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -23,6 +24,5 @@ meta: {

docs: {
description: "enforce consistent spacing before or after unary operators",
category: "Stylistic Issues",
description: "Enforce consistent spacing before or after unary operators",
recommended: false,
url: "https://eslint.org/docs/rules/space-unary-ops"
url: "https://eslint.org/docs/latest/rules/space-unary-ops"
},

@@ -67,3 +67,3 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -70,0 +70,0 @@ //--------------------------------------------------------------------------

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

const lodash = require("lodash");
const escapeRegExp = require("escape-string-regexp");
const astUtils = require("./utils/ast-utils");

@@ -21,3 +21,3 @@

function escape(s) {
return `(?:${lodash.escapeRegExp(s)})`;
return `(?:${escapeRegExp(s)})`;
}

@@ -44,3 +44,3 @@

// `*` is a marker for JSDoc comments.
if (markers.indexOf("*") === -1) {
if (!markers.includes("*")) {
return markers.concat("*");

@@ -152,2 +152,3 @@ }

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -158,6 +159,5 @@ meta: {

docs: {
description: "enforce consistent spacing after the `//` or `/*` in a comment",
category: "Stylistic Issues",
description: "Enforce consistent spacing after the `//` or `/*` in a comment",
recommended: false,
url: "https://eslint.org/docs/rules/spaced-comment"
url: "https://eslint.org/docs/latest/rules/spaced-comment"
},

@@ -243,3 +243,3 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -246,0 +246,0 @@ // Unless the first option is never, require a space

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -72,6 +73,5 @@ meta: {

docs: {
description: "require or disallow strict mode directives",
category: "Strict Mode",
description: "Require or disallow strict mode directives",
recommended: false,
url: "https://eslint.org/docs/rules/strict"
url: "https://eslint.org/docs/latest/rules/strict"
},

@@ -110,3 +110,3 @@

} else if (mode === "safe") {
mode = ecmaFeatures.globalReturn ? "global" : "function";
mode = ecmaFeatures.globalReturn || context.languageOptions.sourceType === "commonjs" ? "global" : "function";
}

@@ -113,0 +113,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,6 +25,5 @@ meta: {

docs: {
description: "enforce spacing around colons of switch statements",
category: "Stylistic Issues",
description: "Enforce spacing around colons of switch statements",
recommended: false,
url: "https://eslint.org/docs/rules/switch-colon-spacing"
url: "https://eslint.org/docs/latest/rules/switch-colon-spacing"
},

@@ -51,3 +51,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const options = context.options[0] || {};

@@ -58,14 +58,2 @@ const beforeSpacing = options.before === true; // false by default

/**
* Get the colon token of the given SwitchCase node.
* @param {ASTNode} node The SwitchCase node to get.
* @returns {Token} The colon token of the node.
*/
function getColonToken(node) {
if (node.test) {
return sourceCode.getTokenAfter(node.test, astUtils.isColonToken);
}
return sourceCode.getFirstToken(node, 1);
}
/**
* Check whether the spacing between the given 2 tokens is valid or not.

@@ -122,3 +110,3 @@ * @param {Token} left The left token to check.

SwitchCase(node) {
const colonToken = getColonToken(node);
const colonToken = astUtils.getSwitchCaseColonToken(node, sourceCode);
const beforeToken = sourceCode.getTokenBefore(colonToken);

@@ -125,0 +113,0 @@ const afterToken = sourceCode.getTokenAfter(colonToken);

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -25,6 +26,5 @@ meta: {

docs: {
description: "require symbol descriptions",
category: "ECMAScript 6",
description: "Require symbol descriptions",
recommended: false,
url: "https://eslint.org/docs/rules/symbol-description"
url: "https://eslint.org/docs/latest/rules/symbol-description"
},

@@ -40,2 +40,4 @@ fixable: null,

const sourceCode = context.sourceCode;
/**

@@ -57,4 +59,4 @@ * Reports if node does not conform the rule in case rule is set to

return {
"Program:exit"() {
const scope = context.getScope();
"Program:exit"(node) {
const scope = sourceCode.getScope(node);
const variable = astUtils.getVariableByName(scope, "Symbol");

@@ -64,6 +66,6 @@

variable.references.forEach(reference => {
const node = reference.identifier;
const idNode = reference.identifier;
if (astUtils.isCallee(node)) {
checkArgument(node.parent);
if (astUtils.isCallee(idNode)) {
checkArgument(idNode.parent);
}

@@ -70,0 +72,0 @@ });

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -24,6 +25,5 @@ meta: {

docs: {
description: "require or disallow spacing around embedded expressions of template strings",
category: "ECMAScript 6",
description: "Require or disallow spacing around embedded expressions of template strings",
recommended: false,
url: "https://eslint.org/docs/rules/template-curly-spacing"
url: "https://eslint.org/docs/latest/rules/template-curly-spacing"
},

@@ -45,3 +45,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const always = context.options[0] === "always";

@@ -48,0 +48,0 @@

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "require or disallow spacing between template tags and their literals",
category: "Stylistic Issues",
description: "Require or disallow spacing between template tags and their literals",
recommended: false,
url: "https://eslint.org/docs/rules/template-tag-spacing"
url: "https://eslint.org/docs/latest/rules/template-tag-spacing"
},

@@ -38,3 +38,3 @@

const never = context.options[0] !== "always";
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -41,0 +41,0 @@ /**

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -17,6 +18,5 @@ meta: {

docs: {
description: "require or disallow Unicode byte order mark (BOM)",
category: "Stylistic Issues",
description: "Require or disallow Unicode byte order mark (BOM)",
recommended: false,
url: "https://eslint.org/docs/rules/unicode-bom"
url: "https://eslint.org/docs/latest/rules/unicode-bom"
},

@@ -47,3 +47,3 @@

const sourceCode = context.getSourceCode(),
const sourceCode = context.sourceCode,
location = { column: 0, line: 1 },

@@ -50,0 +50,0 @@ requireBOM = context.options[0] || "never";

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

function isNaNIdentifier(node) {
return Boolean(node) && node.type === "Identifier" && node.name === "NaN";
return Boolean(node) && (
astUtils.isSpecificId(node, "NaN") ||
astUtils.isSpecificMemberAccess(node, "Number", "NaN")
);
}

@@ -32,2 +35,3 @@

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -38,6 +42,5 @@ meta: {

docs: {
description: "require calls to `isNaN()` when checking for `NaN`",
category: "Possible Errors",
description: "Require calls to `isNaN()` when checking for `NaN`",
recommended: true,
url: "https://eslint.org/docs/rules/use-isnan"
url: "https://eslint.org/docs/latest/rules/use-isnan"
},

@@ -107,3 +110,3 @@

/**
* Checks the the given `CallExpression` node for `.indexOf(NaN)` and `.lastIndexOf(NaN)`.
* Checks the given `CallExpression` node for `.indexOf(NaN)` and `.lastIndexOf(NaN)`.
* @param {ASTNode} node The node to check.

@@ -110,0 +113,0 @@ * @returns {void}

@@ -17,6 +17,6 @@ /**

* ["semi", () => require("semi")],
* ["no-unused-vars", () => require("no-unused-vars")],
* ])
* ["no-unused-vars", () => require("no-unused-vars")]
* ]);
*
* rules.get("semi") // call `() => require("semi")` here.
* rules.get("semi"); // call `() => require("semi")` here.
*

@@ -23,0 +23,0 @@ * @extends {Map<string, () => Rule>}

/**
* @fileoverview Validates JSDoc comments are syntactically correct
* @author Nicholas C. Zakas
* @deprecated in ESLint v5.10.0
*/

@@ -17,2 +18,3 @@ "use strict";

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -23,6 +25,5 @@ meta: {

docs: {
description: "enforce valid JSDoc comments",
category: "Possible Errors",
description: "Enforce valid JSDoc comments",
recommended: false,
url: "https://eslint.org/docs/rules/valid-jsdoc"
url: "https://eslint.org/docs/latest/rules/valid-jsdoc"
},

@@ -100,3 +101,3 @@

prefer = options.prefer || {},
sourceCode = context.getSourceCode(),
sourceCode = context.sourceCode,

@@ -410,3 +411,3 @@ // these both default to true, so you have to explicitly make them false

});
} else if (param.name.indexOf(".") === -1) {
} else if (!param.name.includes(".")) {
paramTagsByName[param.name] = param;

@@ -413,0 +414,0 @@ }

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

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const astUtils = require("./utils/ast-utils");
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -17,8 +24,9 @@ meta: {

docs: {
description: "enforce comparing `typeof` expressions against valid strings",
category: "Possible Errors",
description: "Enforce comparing `typeof` expressions against valid strings",
recommended: true,
url: "https://eslint.org/docs/rules/valid-typeof"
url: "https://eslint.org/docs/latest/rules/valid-typeof"
},
hasSuggestions: true,
schema: [

@@ -38,3 +46,4 @@ {

invalidValue: "Invalid typeof comparison value.",
notString: "Typeof comparisons should be to string literals."
notString: "Typeof comparisons should be to string literals.",
suggestString: 'Use `"{{type}}"` instead of `{{type}}`.'
}

@@ -45,8 +54,23 @@ },

const VALID_TYPES = ["symbol", "undefined", "object", "boolean", "number", "string", "function", "bigint"],
OPERATORS = ["==", "===", "!=", "!=="];
const VALID_TYPES = new Set(["symbol", "undefined", "object", "boolean", "number", "string", "function", "bigint"]),
OPERATORS = new Set(["==", "===", "!=", "!=="]);
const sourceCode = context.sourceCode;
const requireStringLiterals = context.options[0] && context.options[0].requireStringLiterals;
let globalScope;
/**
* Checks whether the given node represents a reference to a global variable that is not declared in the source code.
* These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables.
* @param {ASTNode} node `Identifier` node to check.
* @returns {boolean} `true` if the node is a reference to a global variable.
*/
function isReferenceToGlobalVariable(node) {
const variable = globalScope.set.get(node.name);
return variable && variable.defs.length === 0 &&
variable.references.some(ref => ref.identifier === node);
}
/**
* Determines whether a node is a typeof expression.

@@ -66,15 +90,33 @@ * @param {ASTNode} node The node

Program(node) {
globalScope = sourceCode.getScope(node);
},
UnaryExpression(node) {
if (isTypeofExpression(node)) {
const parent = context.getAncestors().pop();
const { parent } = node;
if (parent.type === "BinaryExpression" && OPERATORS.indexOf(parent.operator) !== -1) {
if (parent.type === "BinaryExpression" && OPERATORS.has(parent.operator)) {
const sibling = parent.left === node ? parent.right : parent.left;
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;
if (VALID_TYPES.indexOf(value) === -1) {
if (!VALID_TYPES.has(value)) {
context.report({ node: sibling, messageId: "invalidValue" });
}
} else if (sibling.type === "Identifier" && sibling.name === "undefined" && isReferenceToGlobalVariable(sibling)) {
context.report({
node: sibling,
messageId: requireStringLiterals ? "notString" : "invalidValue",
suggest: [
{
messageId: "suggestString",
data: { type: "undefined" },
fix(fixer) {
return fixer.replaceText(sibling, '"undefined"');
}
}
]
});
} else if (requireStringLiterals && !isTypeofExpression(sibling)) {

@@ -81,0 +123,0 @@ context.report({ node: sibling, messageId: "notString" });

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "require `var` declarations be placed at the top of their containing scope",
category: "Best Practices",
description: "Require `var` declarations be placed at the top of their containing scope",
recommended: false,
url: "https://eslint.org/docs/rules/vars-on-top"
url: "https://eslint.org/docs/latest/rules/vars-on-top"
},

@@ -37,4 +37,4 @@

// eslint-disable-next-line jsdoc/require-description
/**
* Has AST suggesting a directive.
* @param {ASTNode} node any node

@@ -84,6 +84,8 @@ * @returns {boolean} whether the given node structurally represents a directive

// skip over directives
for (; i < l; ++i) {
if (!looksLikeDirective(statements[i]) && !looksLikeImport(statements[i])) {
break;
// Skip over directives and imports. Static blocks don't have either.
if (node.parent.type !== "StaticBlock") {
for (; i < l; ++i) {
if (!looksLikeDirective(statements[i]) && !looksLikeImport(statements[i])) {
break;
}
}

@@ -119,12 +121,23 @@ }

* @param {ASTNode} node The node to check
* @param {ASTNode} parent Parent of the node
* @param {ASTNode} grandParent Parent of the node's parent
* @returns {void}
*/
function blockScopeVarCheck(node, parent, grandParent) {
if (!(/Function/u.test(grandParent.type) &&
parent.type === "BlockStatement" &&
isVarOnTop(node, parent.body))) {
context.report({ node, messageId: "top" });
function blockScopeVarCheck(node) {
const { parent } = node;
if (
parent.type === "BlockStatement" &&
/Function/u.test(parent.parent.type) &&
isVarOnTop(node, parent.body)
) {
return;
}
if (
parent.type === "StaticBlock" &&
isVarOnTop(node, parent.body)
) {
return;
}
context.report({ node, messageId: "top" });
}

@@ -143,3 +156,3 @@

} else {
blockScopeVarCheck(node, node.parent, node.parent.parent);
blockScopeVarCheck(node);
}

@@ -146,0 +159,0 @@ }

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

const astUtils = require("./utils/ast-utils");
const eslintUtils = require("eslint-utils");
const eslintUtils = require("@eslint-community/eslint-utils");

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -47,6 +48,5 @@ meta: {

docs: {
description: "require parentheses around immediate `function` invocations",
category: "Best Practices",
description: "Require parentheses around immediate `function` invocations",
recommended: false,
url: "https://eslint.org/docs/rules/wrap-iife"
url: "https://eslint.org/docs/latest/rules/wrap-iife"
},

@@ -83,3 +83,3 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -86,0 +86,0 @@ /**

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "require parenthesis around regex literals",
category: "Stylistic Issues",
description: "Require parenthesis around regex literals",
recommended: false,
url: "https://eslint.org/docs/rules/wrap-regex"
url: "https://eslint.org/docs/latest/rules/wrap-regex"
},

@@ -34,3 +34,3 @@

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -46,6 +46,5 @@ return {

const afterToken = sourceCode.getTokenAfter(node);
const ancestors = context.getAncestors();
const grandparent = ancestors[ancestors.length - 1];
const { parent } = node;
if (grandparent.type === "MemberExpression" && grandparent.object === node &&
if (parent.type === "MemberExpression" && parent.object === node &&
!(beforeToken && beforeToken.value === "(" && afterToken && afterToken.value === ")")) {

@@ -52,0 +51,0 @@ context.report({

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

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -18,6 +19,5 @@ meta: {

docs: {
description: "require or disallow spacing around the `*` in `yield*` expressions",
category: "ECMAScript 6",
description: "Require or disallow spacing around the `*` in `yield*` expressions",
recommended: false,
url: "https://eslint.org/docs/rules/yield-star-spacing"
url: "https://eslint.org/docs/latest/rules/yield-star-spacing"
},

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

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -56,0 +56,0 @@ const mode = (function(option) {

@@ -42,3 +42,3 @@ /**

function isRangeTestOperator(operator) {
return ["<", "<="].indexOf(operator) >= 0;
return ["<", "<="].includes(operator);
}

@@ -63,11 +63,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.

@@ -78,3 +69,3 @@ * @param {ASTNode} node Node to test

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

@@ -106,3 +97,3 @@

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

@@ -122,2 +113,3 @@ type: "Literal",

/** @type {import('../shared/types').Rule} */
module.exports = {

@@ -128,6 +120,5 @@ meta: {

docs: {
description: 'require or disallow "Yoda" conditions',
category: "Best Practices",
description: 'Require or disallow "Yoda" conditions',
recommended: false,
url: "https://eslint.org/docs/rules/yoda"
url: "https://eslint.org/docs/latest/rules/yoda"
},

@@ -171,3 +162,3 @@

const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;

@@ -353,3 +344,3 @@ /**

isComparisonOperator(node.operator) &&
!(exceptRange && isRangeTest(context.getAncestors().pop()))
!(exceptRange && isRangeTest(node.parent))
) {

@@ -356,0 +347,0 @@ context.report({

@@ -30,3 +30,3 @@ /**

ajv.addMetaSchema(metaSchema);
// eslint-disable-next-line no-underscore-dangle
// eslint-disable-next-line no-underscore-dangle -- Ajv's API
ajv._opts.defaultMeta = metaSchema.id;

@@ -33,0 +33,0 @@

@@ -27,5 +27,9 @@ /*

configSchema = require("../../conf/config-schema"),
BuiltInEnvironments = require("@eslint/eslintrc/conf/environments"),
BuiltInRules = require("../rules"),
ConfigOps = require("@eslint/eslintrc/lib/shared/config-ops"),
{
Legacy: {
ConfigOps,
environments: BuiltInEnvironments
}
} = require("@eslint/eslintrc"),
{ emitDeprecationWarning } = require("./deprecation-warnings");

@@ -84,2 +88,3 @@

* @param {options} options The given options for the rule.
* @throws {Error} Wrong severity value.
* @returns {number|string} The rule's severity value

@@ -103,2 +108,3 @@ */

* @param {Array} localOptions The options for the rule, excluding severity
* @throws {Error} Any rule validation errors.
* @returns {void}

@@ -134,2 +140,3 @@ */

* no source is prepended to the message.
* @throws {Error} Upon any bad rule configuration.
* @returns {void}

@@ -159,3 +166,3 @@ */

* @param {string} source The name of the configuration source to report in any errors.
* @param {function(envId:string): Object} [getAdditionalEnv] A map from strings to loaded environments.
* @param {(envId:string) => Object} [getAdditionalEnv] A map from strings to loaded environments.
* @returns {void}

@@ -189,3 +196,3 @@ */

* @param {string} source The name of the configuration source to report in any errors.
* @param {function(ruleId:string): Object} getAdditionalRule A map from strings to loaded rules
* @param {(ruleId:string) => Object} getAdditionalRule A map from strings to loaded rules
* @returns {void}

@@ -234,3 +241,4 @@ */

* @param {string} source The name of config file.
* @param {function(id:string): Processor} getProcessor The getter of defined processors.
* @param {(id:string) => Processor} getProcessor The getter of defined processors.
* @throws {Error} For invalid processor configuration.
* @returns {void}

@@ -274,2 +282,3 @@ */

* @param {string} source The name of the configuration source to report in any errors.
* @throws {Error} For any config invalid per the schema.
* @returns {void}

@@ -293,4 +302,4 @@ */

* @param {string} source The name of the configuration source to report in any errors.
* @param {function(ruleId:string): Object} [getAdditionalRule] A map from strings to loaded rules.
* @param {function(envId:string): Object} [getAdditionalEnv] A map from strings to loaded envs.
* @param {(ruleId:string) => Object} [getAdditionalRule] A map from strings to loaded rules.
* @param {(envId:string) => Object} [getAdditionalEnv] A map from strings to loaded envs.
* @returns {void}

@@ -297,0 +306,0 @@ */

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

const path = require("path");
const lodash = require("lodash");

@@ -22,12 +21,7 @@ //------------------------------------------------------------------------------

ESLINT_LEGACY_ECMAFEATURES:
"The 'ecmaFeatures' config file property is deprecated and has no effect.",
ESLINT_PERSONAL_CONFIG_LOAD:
"'~/.eslintrc.*' config files have been deprecated. " +
"Please use a config file per project or the '--config' option.",
ESLINT_PERSONAL_CONFIG_SUPPRESS:
"'~/.eslintrc.*' config files have been deprecated. " +
"Please remove it or add 'root:true' to the config files in your " +
"projects in order to avoid loading '~/.eslintrc.*' accidentally."
"The 'ecmaFeatures' config file property is deprecated and has no effect."
};
const sourceFileErrorCache = new Set();
/**

@@ -41,3 +35,11 @@ * Emits a deprecation warning containing a given filepath. A new deprecation warning is emitted

*/
const emitDeprecationWarning = lodash.memoize((source, errorCode) => {
function emitDeprecationWarning(source, errorCode) {
const cacheKey = JSON.stringify({ source, errorCode });
if (sourceFileErrorCache.has(cacheKey)) {
return;
}
sourceFileErrorCache.add(cacheKey);
const rel = path.relative(process.cwd(), source);

@@ -51,3 +53,3 @@ const message = deprecationWarningMessages[errorCode];

);
}, (...args) => JSON.stringify(args));
}

@@ -54,0 +56,0 @@ //------------------------------------------------------------------------------

@@ -8,5 +8,5 @@ /**

/* eslint no-console: "off" */
/* eslint no-console: "off" -- Logging util */
/* istanbul ignore next */
/* c8 ignore next */
module.exports = {

@@ -13,0 +13,0 @@

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

const Module = require("module");
const { createRequire } = require("module");
/*
* `Module.createRequire` is added in v12.2.0. It supports URL as well.
* We only support the case where the argument is a filepath, not a URL.
*/
// eslint-disable-next-line node/no-unsupported-features/node-builtins, node/no-deprecated-api
const createRequire = Module.createRequire || Module.createRequireFromPath;
module.exports = {

@@ -37,2 +30,3 @@

* a file rather than a directory, but the file need not actually exist.
* @throws {Error} Any error from `module.createRequire` or its `resolve`.
* @returns {string} The absolute path that would result from calling `require.resolve(moduleName)` in a file located at `relativeToPath`

@@ -39,0 +33,0 @@ */

@@ -14,3 +14,3 @@ /**

const spawn = require("cross-spawn");
const { isEmpty } = require("lodash");
const os = require("os");
const log = require("../shared/logging");

@@ -44,2 +44,3 @@ const packageJson = require("../../package.json");

* @param {Array} args The arguments to be executed with the command.
* @throws {Error} As may be collected by `cross-spawn.sync`.
* @returns {string} The version returned by the command.

@@ -78,2 +79,3 @@ */

* @param {string} bin The bin to check.
* @throws {Error} As may be collected by `cross-spawn.sync`.
* @returns {string} The normalized version returned by the command.

@@ -96,2 +98,3 @@ */

* @param {boolean} global Whether to check globally or not.
* @throws {Error} As may be collected by `cross-spawn.sync`.
* @returns {string} The normalized version returned by the command.

@@ -101,3 +104,3 @@ */

const npmBinArgs = ["bin", "-g"];
const npmLsArgs = ["ls", "--depth=0", "--json", "eslint"];
const npmLsArgs = ["ls", "--depth=0", "--json", pkg];

@@ -115,3 +118,3 @@ if (global) {

*/
if (isEmpty(parsedStdout) || !(parsedStdout.dependencies && parsedStdout.dependencies.eslint)) {
if (Object.keys(parsedStdout).length === 0 || !(parsedStdout.dependencies && parsedStdout.dependencies.eslint)) {
return "Not found";

@@ -150,3 +153,4 @@ }

`Local ESLint version: ${getNpmPackageVersion("eslint", { global: false })}`,
`Global ESLint version: ${getNpmPackageVersion("eslint", { global: true })}`
`Global ESLint version: ${getNpmPackageVersion("eslint", { global: true })}`,
`Operating System: ${os.platform()} ${os.release()}`
].join("\n");

@@ -153,0 +157,0 @@ }

@@ -68,4 +68,4 @@ /**

// eslint-disable-next-line jsdoc/require-description
/**
* Gives current node.
* @returns {ASTNode} The current node.

@@ -77,4 +77,4 @@ */

// eslint-disable-next-line jsdoc/require-description
/**
* Gives a copy of the ancestor nodes.
* @returns {ASTNode[]} The ancestor nodes.

@@ -81,0 +81,0 @@ */

@@ -24,7 +24,17 @@ /**

* @property {EcmaFeatures} [ecmaFeatures] The optional features.
* @property {3|5|6|7|8|9|10|11|12|2015|2016|2017|2018|2019|2020|2021} [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.
* @property {boolean} [allowReserved] Allowing the use of reserved words as identifiers in ES3.
*/
/**
* @typedef {Object} LanguageOptions
* @property {number|"latest"} [ecmaVersion] The ECMAScript version (or revision number).
* @property {Record<string, GlobalConf>} [globals] The global variable settings.
* @property {"script"|"module"|"commonjs"} [sourceType] The source code type.
* @property {string|Object} [parser] The parser to use.
* @property {Object} [parserOptions] The parser options to use.
*/
/**
* @typedef {Object} ConfigData

@@ -87,9 +97,11 @@ * @property {Record<string, boolean>} [env] The environment settings.

* @typedef {Object} LintMessage
* @property {number} column The 1-based column number.
* @property {number|undefined} column The 1-based column number.
* @property {number} [endColumn] The 1-based column number of the end location.
* @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} line The 1-based line number.
* @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.

@@ -101,2 +113,19 @@ * @property {0|1|2} severity The severity of this message.

/**
* @typedef {Object} SuppressedLintMessage
* @property {number|undefined} column The 1-based column number.
* @property {number} [endColumn] The 1-based column number of the end location.
* @property {number} [endLine] The 1-based line number of the end location.
* @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.
* @property {0|1|2} severity The severity of this message.
* @property {Array<{kind: string, justification: string}>} suppressions The suppression info.
* @property {Array<{desc?: string, messageId?: string, fix: {range: [number, number], text: string}}>} [suggestions] Information for suggestions.
*/
/**
* @typedef {Object} SuggestionResult

@@ -117,3 +146,2 @@ * @property {string} desc A short description.

* @typedef {Object} RuleMetaDocs
* @property {string} category The category of the rule.
* @property {string} description The description of the rule.

@@ -129,2 +157,3 @@ * @property {boolean} recommended If `true` then the rule is included in `eslint:recommended` preset.

* @property {"code"|"whitespace"} [fixable] The autofix type.
* @property {boolean} [hasSuggestions] If `true` then the rule provides suggestions.
* @property {Record<string,string>} [messages] The messages the rule reports.

@@ -156,1 +185,38 @@ * @property {string[]} [replacedBy] The IDs of the alternative rules.

*/
/**
* A linting result.
* @typedef {Object} LintResult
* @property {string} filePath The path to the file that was linted.
* @property {LintMessage[]} messages All of the messages for the result.
* @property {SuppressedLintMessage[]} suppressedMessages All of the suppressed messages for the result.
* @property {number} errorCount Number of errors for the result.
* @property {number} fatalErrorCount Number of fatal errors for the result.
* @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 {DeprecatedRuleInfo[]} usedDeprecatedRules The list of used deprecated rules.
*/
/**
* Information provided when the maximum warning threshold is exceeded.
* @typedef {Object} MaxWarningsExceeded
* @property {number} maxWarnings Number of warnings to trigger nonzero exit code.
* @property {number} foundWarnings Number of warnings found while linting.
*/
/**
* Metadata about results for formatters.
* @typedef {Object} ResultsMeta
* @property {MaxWarningsExceeded} [maxWarningsExceeded] Present if the maxWarnings threshold was exceeded.
*/
/**
* A formatter function.
* @callback FormatterFunction
* @param {LintResult[]} results The list of linting results.
* @param {{cwd: string, maxWarningsExceeded?: MaxWarningsExceeded, rulesMeta: Record<string, RuleMeta>}} [context] A context object.
* @returns {string | Promise<string>} Formatted text.
*/

@@ -12,12 +12,27 @@ /**

const
{ isCommentToken } = require("eslint-utils"),
{ isCommentToken } = require("@eslint-community/eslint-utils"),
TokenStore = require("./token-store"),
astUtils = require("../shared/ast-utils"),
Traverser = require("../shared/traverser"),
lodash = require("lodash");
globals = require("../../conf/globals"),
{
directivesPattern
} = require("../shared/directives"),
/* eslint-disable-next-line n/no-restricted-require -- Too messy to figure out right now. */
ConfigCommentParser = require("../linter/config-comment-parser"),
eslintScope = require("eslint-scope");
//------------------------------------------------------------------------------
// Type Definitions
//------------------------------------------------------------------------------
/** @typedef {import("eslint-scope").Variable} Variable */
//------------------------------------------------------------------------------
// Private
//------------------------------------------------------------------------------
const commentParser = new ConfigCommentParser();
/**

@@ -49,2 +64,25 @@ * Validates that the given AST has the required information.

/**
* Retrieves globals for the given ecmaVersion.
* @param {number} ecmaVersion The version to retrieve globals for.
* @returns {Object} The globals for the given ecmaVersion.
*/
function getGlobalsForEcmaVersion(ecmaVersion) {
switch (ecmaVersion) {
case 3:
return globals.es3;
case 5:
return globals.es5;
default:
if (ecmaVersion < 2015) {
return globals[`es${ecmaVersion + 2009}`];
}
return globals[`es${ecmaVersion}`];
}
}
/**
* Check to see if its a ES6 export declaration.

@@ -84,2 +122,32 @@ * @param {ASTNode} astNode An AST node.

/**
* Normalizes a value for a global in a config
* @param {(boolean|string|null)} configuredValue The value given for a global in configuration or in
* a global directive comment
* @returns {("readable"|"writeable"|"off")} The value normalized as a string
* @throws Error if global value is invalid
*/
function normalizeConfigGlobal(configuredValue) {
switch (configuredValue) {
case "off":
return "off";
case true:
case "true":
case "writeable":
case "writable":
return "writable";
case null:
case false:
case "false":
case "readable":
case "readonly":
return "readonly";
default:
throw new Error(`'${configuredValue}' is not a valid configuration for a global (use 'readonly', 'writable', or 'off')`);
}
}
/**
* Determines if two nodes or tokens overlap.

@@ -146,2 +214,112 @@ * @param {ASTNode|Token} first The first node or token to check.

//-----------------------------------------------------------------------------
// Directive Comments
//-----------------------------------------------------------------------------
/**
* Extract the directive and the justification from a given directive comment and trim them.
* @param {string} value The comment text to extract.
* @returns {{directivePart: string, justificationPart: string}} The extracted directive and justification.
*/
function extractDirectiveComment(value) {
const match = /\s-{2,}\s/u.exec(value);
if (!match) {
return { directivePart: value.trim(), justificationPart: "" };
}
const directive = value.slice(0, match.index).trim();
const justification = value.slice(match.index + match[0].length).trim();
return { directivePart: directive, justificationPart: justification };
}
/**
* Ensures that variables representing built-in properties of the Global Object,
* and any globals declared by special block comments, are present in the global
* scope.
* @param {Scope} globalScope The global scope.
* @param {Object|undefined} configGlobals The globals declared in configuration
* @param {Object|undefined} inlineGlobals The globals declared in the source code
* @returns {void}
*/
function addDeclaredGlobals(globalScope, configGlobals = {}, inlineGlobals = {}) {
// Define configured global variables.
for (const id of new Set([...Object.keys(configGlobals), ...Object.keys(inlineGlobals)])) {
/*
* `normalizeConfigGlobal` will throw an error if a configured global value is invalid. However, these errors would
* typically be caught when validating a config anyway (validity for inline global comments is checked separately).
*/
const configValue = configGlobals[id] === void 0 ? void 0 : normalizeConfigGlobal(configGlobals[id]);
const commentValue = inlineGlobals[id] && inlineGlobals[id].value;
const value = commentValue || configValue;
const sourceComments = inlineGlobals[id] && inlineGlobals[id].comments;
if (value === "off") {
continue;
}
let variable = globalScope.set.get(id);
if (!variable) {
variable = new eslintScope.Variable(id, globalScope);
globalScope.variables.push(variable);
globalScope.set.set(id, variable);
}
variable.eslintImplicitGlobalSetting = configValue;
variable.eslintExplicitGlobal = sourceComments !== void 0;
variable.eslintExplicitGlobalComments = sourceComments;
variable.writeable = (value === "writable");
}
/*
* "through" contains all references which definitions cannot be found.
* Since we augment the global scope using configuration, we need to update
* references and remove the ones that were added by configuration.
*/
globalScope.through = globalScope.through.filter(reference => {
const name = reference.identifier.name;
const variable = globalScope.set.get(name);
if (variable) {
/*
* Links the variable and the reference.
* And this reference is removed from `Scope#through`.
*/
reference.resolved = variable;
variable.references.push(reference);
return false;
}
return true;
});
}
/**
* Sets the given variable names as exported so they won't be triggered by
* the `no-unused-vars` rule.
* @param {eslint.Scope} globalScope The global scope to define exports in.
* @param {Record<string,string>} variables An object whose keys are the variable
* names to export.
* @returns {void}
*/
function markExportedVariables(globalScope, variables) {
Object.keys(variables).forEach(name => {
const variable = globalScope.set.get(name);
if (variable) {
variable.eslintUsed = true;
variable.eslintExported = true;
}
});
}
//------------------------------------------------------------------------------

@@ -151,6 +329,10 @@ // Public Interface

const caches = Symbol("caches");
/**
* Represents parsed source code.
*/
class SourceCode extends TokenStore {
/**
* Represents parsed source code.
* @param {string|Object} textOrConfig The source code text or config object.

@@ -183,4 +365,13 @@ * @param {string} textOrConfig.text The source code text.

/**
* General purpose caching for the class.
*/
this[caches] = new Map([
["scopes", new WeakMap()],
["vars", new Map()],
["configNodes", void 0]
]);
/**
* The flag to indicate that the source code has Unicode BOM.
* @type boolean
* @type {boolean}
*/

@@ -192,3 +383,3 @@ this.hasBOM = (text.charCodeAt(0) === 0xFEFF);

* BOM was stripped from this text.
* @type string
* @type {string}
*/

@@ -199,3 +390,3 @@ this.text = (this.hasBOM ? text.slice(1) : text);

* The parsed AST for the source code.
* @type ASTNode
* @type {ASTNode}
*/

@@ -235,3 +426,3 @@ this.ast = ast;

* This is done to avoid each rule needing to do so separately.
* @type string[]
* @type {string[]}
*/

@@ -262,3 +453,3 @@ this.lines = [];

// don't allow modification of this object
// don't allow further modification of this object
Object.freeze(this);

@@ -363,3 +554,3 @@ Object.freeze(this.lines);

while (currentToken && isCommentToken(currentToken)) {
if (node.parent && (currentToken.start < node.parent.start)) {
if (node.parent && node.parent.type !== "Program" && (currentToken.start < node.parent.start)) {
break;

@@ -376,3 +567,3 @@ }

while (currentToken && isCommentToken(currentToken)) {
if (node.parent && (currentToken.end > node.parent.end)) {
if (node.parent && node.parent.type !== "Program" && (currentToken.end > node.parent.end)) {
break;

@@ -522,2 +713,3 @@ }

* @param {number} index The index of a character in a file
* @throws {TypeError} If non-numeric index or index out of range.
* @returns {Object} A {line, column} location object with a 0-indexed column

@@ -547,6 +739,8 @@ * @public

/*
* To figure out which line rangeIndex is on, determine the last index at which rangeIndex could
* be inserted into lineIndices to keep the list sorted.
* To figure out which line index is on, determine the last place at which index could
* be inserted into lineStartIndices to keep the list sorted.
*/
const lineNumber = lodash.sortedLastIndex(this.lineStartIndices, index);
const lineNumber = index >= this.lineStartIndices[this.lineStartIndices.length - 1]
? this.lineStartIndices.length
: this.lineStartIndices.findIndex(el => index < el);

@@ -561,2 +755,5 @@ return { line: lineNumber, column: index - this.lineStartIndices[lineNumber - 1] };

* @param {number} loc.column The column number of the location (0-indexed)
* @throws {TypeError|RangeError} If `loc` is not an object with a numeric
* `line` and `column`, if the `line` is less than or equal to zero or
* the line or column is out of the expected range.
* @returns {number} The range index of the location in the file.

@@ -599,4 +796,297 @@ * @public

}
/**
* Gets the scope for the given node
* @param {ASTNode} currentNode The node to get the scope of
* @returns {eslint-scope.Scope} The scope information for this node
* @throws {TypeError} If the `currentNode` argument is missing.
*/
getScope(currentNode) {
if (!currentNode) {
throw new TypeError("Missing required argument: node.");
}
// check cache first
const cache = this[caches].get("scopes");
const cachedScope = cache.get(currentNode);
if (cachedScope) {
return cachedScope;
}
// On Program node, get the outermost scope to avoid return Node.js special function scope or ES modules scope.
const inner = currentNode.type !== "Program";
for (let node = currentNode; node; node = node.parent) {
const scope = this.scopeManager.acquire(node, inner);
if (scope) {
if (scope.type === "function-expression-name") {
cache.set(currentNode, scope.childScopes[0]);
return scope.childScopes[0];
}
cache.set(currentNode, scope);
return scope;
}
}
cache.set(currentNode, this.scopeManager.scopes[0]);
return this.scopeManager.scopes[0];
}
/**
* Get the variables that `node` defines.
* This is a convenience method that passes through
* to the same method on the `scopeManager`.
* @param {ASTNode} node The node for which the variables are obtained.
* @returns {Array<Variable>} An array of variable nodes representing
* the variables that `node` defines.
*/
getDeclaredVariables(node) {
return this.scopeManager.getDeclaredVariables(node);
}
/* eslint-disable class-methods-use-this -- node is owned by SourceCode */
/**
* Gets all the ancestors of a given node
* @param {ASTNode} node The node
* @returns {Array<ASTNode>} All the ancestor nodes in the AST, not including the provided node, starting
* from the root node at index 0 and going inwards to the parent node.
* @throws {TypeError} When `node` is missing.
*/
getAncestors(node) {
if (!node) {
throw new TypeError("Missing required argument: node.");
}
const ancestorsStartingAtParent = [];
for (let ancestor = node.parent; ancestor; ancestor = ancestor.parent) {
ancestorsStartingAtParent.push(ancestor);
}
return ancestorsStartingAtParent.reverse();
}
/* eslint-enable class-methods-use-this -- node is owned by SourceCode */
/**
* Marks a variable as used in the current scope
* @param {string} name The name of the variable to mark as used.
* @param {ASTNode} [refNode] The closest node to the variable reference.
* @returns {boolean} True if the variable was found and marked as used, false if not.
*/
markVariableAsUsed(name, refNode = this.ast) {
const currentScope = this.getScope(refNode);
let initialScope = currentScope;
/*
* When we are in an ESM or CommonJS module, we need to start searching
* from the top-level scope, not the global scope. For ESM the top-level
* scope is the module scope; for CommonJS the top-level scope is the
* outer function scope.
*
* Without this check, we might miss a variable declared with `var` at
* the top-level because it won't exist in the global scope.
*/
if (
currentScope.type === "global" &&
currentScope.childScopes.length > 0 &&
// top-level scopes refer to a `Program` node
currentScope.childScopes[0].block === this.ast
) {
initialScope = currentScope.childScopes[0];
}
for (let scope = initialScope; scope; scope = scope.upper) {
const variable = scope.variables.find(scopeVar => scopeVar.name === name);
if (variable) {
variable.eslintUsed = true;
return true;
}
}
return false;
}
/**
* Returns an array of all inline configuration nodes found in the
* source code.
* @returns {Array<Token>} An array of all inline configuration nodes.
*/
getInlineConfigNodes() {
// check the cache first
let configNodes = this[caches].get("configNodes");
if (configNodes) {
return configNodes;
}
// calculate fresh config nodes
configNodes = this.ast.comments.filter(comment => {
// shebang comments are never directives
if (comment.type === "Shebang") {
return false;
}
const { directivePart } = extractDirectiveComment(comment.value);
const directiveMatch = directivesPattern.exec(directivePart);
if (!directiveMatch) {
return false;
}
// only certain comment types are supported as line comments
return comment.type !== "Line" || !!/^eslint-disable-(next-)?line$/u.test(directiveMatch[1]);
});
this[caches].set("configNodes", configNodes);
return configNodes;
}
/**
* Applies language options sent in from the core.
* @param {Object} languageOptions The language options for this run.
* @returns {void}
*/
applyLanguageOptions(languageOptions) {
/*
* Add configured globals and language globals
*
* Using Object.assign instead of object spread for performance reasons
* https://github.com/eslint/eslint/issues/16302
*/
const configGlobals = Object.assign(
{},
getGlobalsForEcmaVersion(languageOptions.ecmaVersion),
languageOptions.sourceType === "commonjs" ? globals.commonjs : void 0,
languageOptions.globals
);
const varsCache = this[caches].get("vars");
varsCache.set("configGlobals", configGlobals);
}
/**
* Applies configuration found inside of the source code. This method is only
* called when ESLint is running with inline configuration allowed.
* @returns {{problems:Array<Problem>,configs:{config:FlatConfigArray,node:ASTNode}}} Information
* that ESLint needs to further process the inline configuration.
*/
applyInlineConfig() {
const problems = [];
const configs = [];
const exportedVariables = {};
const inlineGlobals = Object.create(null);
this.getInlineConfigNodes().forEach(comment => {
const { directivePart } = extractDirectiveComment(comment.value);
const match = directivesPattern.exec(directivePart);
const directiveText = match[1];
const directiveValue = directivePart.slice(match.index + directiveText.length);
switch (directiveText) {
case "exported":
Object.assign(exportedVariables, commentParser.parseStringConfig(directiveValue, comment));
break;
case "globals":
case "global":
for (const [id, { value }] of Object.entries(commentParser.parseStringConfig(directiveValue, comment))) {
let normalizedValue;
try {
normalizedValue = normalizeConfigGlobal(value);
} catch (err) {
problems.push({
ruleId: null,
loc: comment.loc,
message: err.message
});
continue;
}
if (inlineGlobals[id]) {
inlineGlobals[id].comments.push(comment);
inlineGlobals[id].value = normalizedValue;
} else {
inlineGlobals[id] = {
comments: [comment],
value: normalizedValue
};
}
}
break;
case "eslint": {
const parseResult = commentParser.parseJsonConfig(directiveValue, comment.loc);
if (parseResult.success) {
configs.push({
config: {
rules: parseResult.config
},
node: comment
});
} else {
problems.push(parseResult.error);
}
break;
}
// no default
}
});
// save all the new variables for later
const varsCache = this[caches].get("vars");
varsCache.set("inlineGlobals", inlineGlobals);
varsCache.set("exportedVariables", exportedVariables);
return {
configs,
problems
};
}
/**
* Called by ESLint core to indicate that it has finished providing
* information. We now add in all the missing variables and ensure that
* state-changing methods cannot be called by rules.
* @returns {void}
*/
finalize() {
// Step 1: ensure that all of the necessary variables are up to date
const varsCache = this[caches].get("vars");
const globalScope = this.scopeManager.scopes[0];
const configGlobals = varsCache.get("configGlobals");
const inlineGlobals = varsCache.get("inlineGlobals");
const exportedVariables = varsCache.get("exportedVariables");
addDeclaredGlobals(globalScope, configGlobals, inlineGlobals);
if (exportedVariables) {
markExportedVariables(globalScope, exportedVariables);
}
}
}
module.exports = SourceCode;

@@ -72,6 +72,6 @@ /**

*/
/* istanbul ignore next */
moveNext() { // eslint-disable-line class-methods-use-this
/* c8 ignore next */
moveNext() { // eslint-disable-line class-methods-use-this -- Unused
throw new Error("Not implemented.");
}
};

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

const assert = require("assert");
const { isCommentToken } = require("eslint-utils");
const { isCommentToken } = require("@eslint-community/eslint-utils");
const cursors = require("./cursors");

@@ -15,0 +15,0 @@ const ForwardTokenCursor = require("./forward-token-cursor");

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

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const lodash = require("lodash");
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
/**
* Gets `token.range[0]` from the given token.
* @param {Node|Token|Comment} token The token to get.
* @returns {number} The start location.
* @private
*/
function getStartLocation(token) {
return token.range[0];
}
//------------------------------------------------------------------------------
// Exports

@@ -33,3 +13,3 @@ //------------------------------------------------------------------------------

/**
* Binary-searches the index of the first token which is after the given location.
* Finds the index of the first token which is after the given location.
* If it was not found, this returns `tokens.length`.

@@ -41,7 +21,24 @@ * @param {(Token|Comment)[]} tokens It searches the token in this list.

exports.search = function search(tokens, location) {
return lodash.sortedIndexBy(
tokens,
{ range: [location] },
getStartLocation
);
for (let minIndex = 0, maxIndex = tokens.length - 1; minIndex <= maxIndex;) {
/*
* Calculate the index in the middle between minIndex and maxIndex.
* `| 0` is used to round a fractional value down to the nearest integer: this is similar to
* using `Math.trunc()` or `Math.floor()`, but performance tests have shown this method to
* be faster.
*/
const index = (minIndex + maxIndex) / 2 | 0;
const token = tokens[index];
const tokenStartLocation = token.range[0];
if (location <= tokenStartLocation) {
if (index === minIndex) {
return index;
}
maxIndex = index;
} else {
minIndex = index + 1;
}
}
return tokens.length;
};

@@ -63,4 +60,9 @@

const index = indexMap[startLoc - 1];
const token = (index >= 0 && index < tokens.length) ? tokens[index] : null;
const token = tokens[index];
// If the mapped index is out of bounds, the returned cursor index will point after the end of the tokens array.
if (!token) {
return tokens.length;
}
/*

@@ -70,3 +72,3 @@ * For the map of "comment's location -> token's index", it points the next token of a comment.

*/
if (token && token.range[0] >= startLoc) {
if (token.range[0] >= startLoc) {
return index;

@@ -93,4 +95,9 @@ }

const index = indexMap[endLoc - 1];
const token = (index >= 0 && index < tokens.length) ? tokens[index] : null;
const token = tokens[index];
// If the mapped index is out of bounds, the returned cursor index will point before the end of the tokens array.
if (!token) {
return tokens.length - 1;
}
/*

@@ -100,3 +107,3 @@ * For the map of "comment's location -> token's index", it points the next token of a comment.

*/
if (token && token.range[1] > endLoc) {
if (token.range[1] > endLoc) {
return index - 1;

@@ -103,0 +110,0 @@ }

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

@@ -10,17 +10,25 @@ "description": "An AST-based pattern checker for JavaScript.",

"main": "./lib/api.js",
"exports": {
"./package.json": "./package.json",
".": "./lib/api.js",
"./use-at-your-own-risk": "./lib/unsupported-api.js"
},
"scripts": {
"build:docs:update-links": "node tools/fetch-docs-links.js",
"build:site": "node Makefile.js gensite",
"build:webpack": "node Makefile.js webpack",
"build:readme": "node tools/update-readme.js",
"lint": "node Makefile.js lint",
"lint:docs:js": "node Makefile.js lintDocsJS",
"lint:fix": "node Makefile.js lint -- fix",
"lint:fix:docs:js": "node Makefile.js lintDocsJS -- fix",
"release:generate:alpha": "node Makefile.js generatePrerelease -- alpha",
"release:generate:beta": "node Makefile.js generatePrerelease -- beta",
"release:generate:latest": "node Makefile.js generateRelease",
"release:generate:rc": "node Makefile.js generatePrerelease -- rc",
"release:publish": "node Makefile.js publishRelease",
"test": "node Makefile.js test",
"test:cli": "mocha",
"lint": "node Makefile.js lint",
"fix": "node Makefile.js lint -- fix",
"fuzz": "node Makefile.js fuzz",
"generate-release": "node Makefile.js generateRelease",
"generate-alpharelease": "node Makefile.js generatePrerelease -- alpha",
"generate-betarelease": "node Makefile.js generatePrerelease -- beta",
"generate-rcrelease": "node Makefile.js generatePrerelease -- rc",
"publish-release": "node Makefile.js publishRelease",
"docs": "node Makefile.js docs",
"gensite": "node Makefile.js gensite",
"webpack": "node Makefile.js webpack",
"perf": "node Makefile.js perf"
"test:fuzz": "node Makefile.js fuzz",
"test:performance": "node Makefile.js perf"
},

@@ -31,7 +39,13 @@ "gitHooks": {

"lint-staged": {
"*.js": [
"eslint --fix",
"git add"
"*.js": "eslint --fix",
"*.md": "markdownlint --fix",
"lib/rules/*.js": [
"node tools/update-eslint-all.js",
"git add packages/js/src/configs/eslint-all.js"
],
"*.md": "markdownlint"
"docs/src/rules/*.md": [
"node tools/fetch-docs-links.js",
"git add docs/src/_data/further_reading_links.json"
],
"docs/**/*.svg": "npx svgo -r --multipass"
},

@@ -51,39 +65,39 @@ "files": [

"dependencies": {
"@babel/code-frame": "7.12.11",
"@eslint/eslintrc": "^0.3.0",
"ajv": "^6.10.0",
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
"@eslint/eslintrc": "^2.1.2",
"@eslint/js": "8.50.0",
"@humanwhocodes/config-array": "^0.11.11",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
"ajv": "^6.12.4",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
"debug": "^4.0.1",
"debug": "^4.3.2",
"doctrine": "^3.0.0",
"enquirer": "^2.3.5",
"eslint-scope": "^5.1.1",
"eslint-utils": "^2.1.0",
"eslint-visitor-keys": "^2.0.0",
"espree": "^7.3.1",
"esquery": "^1.4.0",
"escape-string-regexp": "^4.0.0",
"eslint-scope": "^7.2.2",
"eslint-visitor-keys": "^3.4.3",
"espree": "^9.6.1",
"esquery": "^1.4.2",
"esutils": "^2.0.2",
"file-entry-cache": "^6.0.0",
"functional-red-black-tree": "^1.0.1",
"glob-parent": "^5.0.0",
"globals": "^12.1.0",
"ignore": "^4.0.6",
"import-fresh": "^3.0.0",
"fast-deep-equal": "^3.1.3",
"file-entry-cache": "^6.0.1",
"find-up": "^5.0.0",
"glob-parent": "^6.0.2",
"globals": "^13.19.0",
"graphemer": "^1.4.0",
"ignore": "^5.2.0",
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
"js-yaml": "^3.13.1",
"is-path-inside": "^3.0.3",
"js-yaml": "^4.1.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1",
"lodash": "^4.17.20",
"minimatch": "^3.0.4",
"lodash.merge": "^4.6.2",
"minimatch": "^3.1.2",
"natural-compare": "^1.4.0",
"optionator": "^0.9.1",
"progress": "^2.0.0",
"regexpp": "^3.1.0",
"semver": "^7.2.1",
"strip-ansi": "^6.0.0",
"strip-json-comments": "^3.1.0",
"table": "^6.0.4",
"text-table": "^0.2.0",
"v8-compile-cache": "^2.0.3"
"optionator": "^0.9.3",
"strip-ansi": "^6.0.1",
"text-table": "^0.2.0"
},

@@ -93,4 +107,9 @@ "devDependencies": {

"@babel/preset-env": "^7.4.3",
"acorn": "^7.2.0",
"@wdio/browser-runner": "^8.14.6",
"@wdio/cli": "^8.14.6",
"@wdio/concise-reporter": "^8.14.0",
"@wdio/globals": "^8.14.6",
"@wdio/mocha-framework": "^8.14.0",
"babel-loader": "^8.0.5",
"c8": "^7.12.0",
"chai": "^4.0.1",

@@ -100,40 +119,48 @@ "cheerio": "^0.22.0",

"core-js": "^3.1.3",
"dateformat": "^3.0.3",
"ejs": "^3.0.2",
"escape-string-regexp": "^3.0.0",
"eslint": "file:.",
"eslint-config-eslint": "file:packages/eslint-config-eslint",
"eslint-plugin-eslint-plugin": "^2.2.1",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-eslint-plugin": "^5.1.0",
"eslint-plugin-internal-rules": "file:tools/internal-rules",
"eslint-plugin-jsdoc": "^22.1.0",
"eslint-plugin-node": "^11.1.0",
"eslint-release": "^2.0.0",
"eslump": "^2.0.0",
"eslint-plugin-jsdoc": "^46.2.5",
"eslint-plugin-n": "^16.0.0",
"eslint-plugin-unicorn": "^42.0.0",
"eslint-release": "^3.2.0",
"eslump": "^3.0.0",
"esprima": "^4.0.1",
"fs-teardown": "^0.1.0",
"fast-glob": "^3.2.11",
"fs-teardown": "^0.1.3",
"glob": "^7.1.6",
"jsdoc": "^3.5.5",
"karma": "^4.0.1",
"karma-chrome-launcher": "^3.1.0",
"karma-mocha": "^1.3.0",
"karma-mocha-reporter": "^2.2.3",
"karma-webpack": "^4.0.0-rc.6",
"lint-staged": "^10.1.2",
"got": "^11.8.3",
"gray-matter": "^4.0.3",
"lint-staged": "^11.0.0",
"load-perf": "^0.2.0",
"markdownlint": "^0.19.0",
"markdownlint-cli": "^0.22.0",
"markdownlint": "^0.25.1",
"markdownlint-cli": "^0.31.1",
"marked": "^4.0.8",
"memfs": "^3.0.1",
"mocha": "^7.1.1",
"mocha-junit-reporter": "^1.23.0",
"metascraper": "^5.25.7",
"metascraper-description": "^5.25.7",
"metascraper-image": "^5.29.3",
"metascraper-logo": "^5.25.7",
"metascraper-logo-favicon": "^5.25.7",
"metascraper-title": "^5.25.7",
"mocha": "^8.3.2",
"mocha-junit-reporter": "^2.0.0",
"node-polyfill-webpack-plugin": "^1.0.3",
"npm-license": "^0.3.3",
"nyc": "^15.0.1",
"pirates": "^4.0.5",
"progress": "^2.0.3",
"proxyquire": "^2.0.1",
"puppeteer": "^4.0.0",
"recast": "^0.19.0",
"recast": "^0.20.4",
"regenerator-runtime": "^0.13.2",
"rollup-plugin-node-polyfills": "^0.2.1",
"semver": "^7.5.3",
"shelljs": "^0.8.2",
"sinon": "^9.0.1",
"temp": "^0.9.0",
"webpack": "^4.35.0",
"webpack-cli": "^3.3.5",
"sinon": "^11.0.0",
"vite-plugin-commonjs": "^0.8.2",
"webdriverio": "^8.14.6",
"webpack": "^5.23.0",
"webpack-cli": "^4.5.0",
"yorkie": "^2.0.0"

@@ -150,4 +177,4 @@ },

"engines": {
"node": "^10.12.0 || >=12.0.0"
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
}

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

[Website](https://eslint.org) |
[Configuring](https://eslint.org/docs/user-guide/configuring) |
[Configure ESLint](https://eslint.org/docs/latest/use/configure) |
[Rules](https://eslint.org/docs/rules/) |
[Contributing](https://eslint.org/docs/developer-guide/contributing) |
[Reporting Bugs](https://eslint.org/docs/developer-guide/contributing/reporting-bugs) |
[Contribute to ESLint](https://eslint.org/docs/latest/contribute) |
[Report Bugs](https://eslint.org/docs/latest/contribute/report-bugs) |
[Code of Conduct](https://eslint.org/conduct) |
[Twitter](https://twitter.com/geteslint) |
[Mailing List](https://groups.google.com/group/eslint) |
[Chat Room](https://eslint.org/chat)
[Discord](https://eslint.org/chat) |
[Mastodon](https://fosstodon.org/@eslint)

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

4. [Filing Issues](#filing-issues)
5. [Frequently Asked Questions](#faq)
5. [Frequently Asked Questions](#frequently-asked-questions)
6. [Releases](#releases)

@@ -46,27 +46,21 @@ 7. [Security Policy](#security-policy)

## <a name="installation-and-usage"></a>Installation and Usage
## Installation and Usage
Prerequisites: [Node.js](https://nodejs.org/) (`^10.12.0`, or `>=12.0.0`) built with SSL support. (If you are using an official Node.js distribution, SSL is always built in.)
Prerequisites: [Node.js](https://nodejs.org/) (`^12.22.0`, `^14.17.0`, or `>=16.0.0`) built with SSL support. (If you are using an official Node.js distribution, SSL is always built in.)
You can install ESLint using npm:
You can install and configure ESLint using this command:
```shell
npm init @eslint/config
```
$ npm install eslint --save-dev
```
You should then set up a configuration file:
```
$ ./node_modules/.bin/eslint --init
```
After that, you can run ESLint on any file or directory like this:
```shell
./node_modules/.bin/eslint yourfile.js
```
$ ./node_modules/.bin/eslint yourfile.js
```
## <a name="configuration"></a>Configuration
## Configuration
After running `eslint --init`, you'll have a `.eslintrc` file in your directory. In it, you'll see some rules configured like this:
After running `npm init @eslint/config`, you'll have an `.eslintrc` file in your directory. In it, you'll see some rules configured like this:

@@ -88,18 +82,18 @@ ```json

The three error levels allow you fine-grained control over how ESLint applies rules (for more configuration options and details, see the [configuration docs](https://eslint.org/docs/user-guide/configuring)).
The three error levels allow you fine-grained control over how ESLint applies rules (for more configuration options and details, see the [configuration docs](https://eslint.org/docs/latest/use/configure)).
## <a name="code-of-conduct"></a>Code of Conduct
## Code of Conduct
ESLint adheres to the [JS Foundation Code of Conduct](https://eslint.org/conduct).
## <a name="filing-issues"></a>Filing Issues
## Filing Issues
Before filing an issue, please be sure to read the guidelines for what you're reporting:
* [Bug Report](https://eslint.org/docs/developer-guide/contributing/reporting-bugs)
* [Propose a New Rule](https://eslint.org/docs/developer-guide/contributing/new-rules)
* [Proposing a Rule Change](https://eslint.org/docs/developer-guide/contributing/rule-changes)
* [Request a Change](https://eslint.org/docs/developer-guide/contributing/changes)
* [Bug Report](https://eslint.org/docs/latest/contribute/report-bugs)
* [Propose a New Rule](https://eslint.org/docs/latest/contribute/propose-new-rule)
* [Proposing a Rule Change](https://eslint.org/docs/latest/contribute/propose-rule-change)
* [Request a Change](https://eslint.org/docs/latest/contribute/request-change)
## <a name="faq"></a>Frequently Asked Questions
## Frequently Asked Questions

@@ -110,3 +104,3 @@ ### I'm using JSCS, should I migrate to ESLint?

We have prepared a [migration guide](https://eslint.org/docs/user-guide/migrating-from-jscs) to help you convert your JSCS settings to an ESLint configuration.
We have prepared a [migration guide](https://eslint.org/docs/latest/use/migrating-from-jscs) to help you convert your JSCS settings to an ESLint configuration.

@@ -127,7 +121,7 @@ We are now at or near 100% compatibility with JSCS. If you try ESLint and believe we are not yet compatible with a JSCS rule/configuration, please create an issue (mentioning that it is a JSCS compatibility issue) and we will evaluate it as per our normal process.

Yes, ESLint natively supports parsing JSX syntax (this must be enabled in [configuration](https://eslint.org/docs/user-guide/configuring)). Please note that supporting JSX syntax *is not* the same as supporting React. React applies specific semantics to JSX syntax that ESLint doesn't recognize. We recommend using [eslint-plugin-react](https://www.npmjs.com/package/eslint-plugin-react) if you are using React and want React semantics.
Yes, ESLint natively supports parsing JSX syntax (this must be enabled in [configuration](https://eslint.org/docs/latest/use/configure)). Please note that supporting JSX syntax *is not* the same as supporting React. React applies specific semantics to JSX syntax that ESLint doesn't recognize. We recommend using [eslint-plugin-react](https://www.npmjs.com/package/eslint-plugin-react) if you are using React and want React semantics.
### What ECMAScript versions does ESLint support?
ESLint has full support for ECMAScript 3, 5 (default), 2015, 2016, 2017, 2018, 2019, and 2020. 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, 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).

@@ -138,9 +132,9 @@ ### What about experimental features?

In other cases (including if rules need to warn on more or fewer cases due to new syntax, rather than just not crashing), we recommend you use other parsers and/or rule plugins. If you are using Babel, you can use the [babel-eslint](https://github.com/babel/babel-eslint) parser and [eslint-plugin-babel](https://github.com/babel/eslint-plugin-babel) to use any option available in Babel.
In other cases (including if rules need to warn on more or fewer cases due to new syntax, rather than just not crashing), we recommend you use other parsers and/or rule plugins. If you are using Babel, you can use [@babel/eslint-parser](https://www.npmjs.com/package/@babel/eslint-parser) and [@babel/eslint-plugin](https://www.npmjs.com/package/@babel/eslint-plugin) to use any option available in Babel.
Once a language feature has been adopted into the ECMAScript standard (stage 4 according to the [TC39 process](https://tc39.github.io/process-document/)), we will accept issues and pull requests related to the new feature, subject to our [contributing guidelines](https://eslint.org/docs/developer-guide/contributing). Until then, please use the appropriate parser and plugin(s) for your experimental feature.
Once a language feature has been adopted into the ECMAScript standard (stage 4 according to the [TC39 process](https://tc39.github.io/process-document/)), we will accept issues and pull requests related to the new feature, subject to our [contributing guidelines](https://eslint.org/docs/latest/contribute). Until then, please use the appropriate parser and plugin(s) for your experimental feature.
### Where to ask for help?
Join our [Mailing List](https://groups.google.com/group/eslint) or [Chatroom](https://eslint.org/chat).
Open a [discussion](https://github.com/eslint/eslint/discussions) or stop by our [Discord server](https://eslint.org/chat).

@@ -157,11 +151,11 @@ ### Why doesn't ESLint lock dependency versions?

## <a name="releases"></a>Releases
## Releases
We have scheduled releases every two weeks on Friday or Saturday. You can follow a [release issue](https://github.com/eslint/eslint/issues?q=is%3Aopen+is%3Aissue+label%3Arelease) for updates about the scheduling of any particular release.
## <a name="security-policy"></a>Security Policy
## Security Policy
ESLint takes security seriously. We work hard to ensure that ESLint is safe for everyone and that security issues are addressed quickly and responsibly. Read the full [security policy](https://github.com/eslint/.github/blob/master/SECURITY.md).
## <a name="semantic-versioning-policy"></a>Semantic Versioning Policy
## Semantic Versioning Policy

@@ -199,3 +193,3 @@ ESLint follows [semantic versioning](https://semver.org). However, due to the nature of ESLint as a code quality tool, it's not always clear when a minor or major version bump occurs. To help clarify this for everyone, we've defined the following semantic versioning policy for ESLint:

## <a name="stylistic-rule-updates"></a>Stylistic Rule Updates
## Stylistic Rule Updates

@@ -209,7 +203,7 @@ Stylistic rules are frozen according to [our policy](https://eslint.org/blog/2020/05/changes-to-rules-policies) on how we evaluate new rules and rule changes.

## <a name="license"></a>License
## License
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Feslint%2Feslint.svg?type=large)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Feslint%2Feslint?ref=badge_large)
## <a name="team"></a>Team
## Team

@@ -219,2 +213,3 @@ These folks keep the project moving and are resources for help.

<!-- NOTE: This section is autogenerated. Do not manually edit.-->
<!--teamstart-->

@@ -232,12 +227,2 @@

</td><td align="center" valign="top" width="11%">
<a href="https://github.com/btmills">
<img src="https://github.com/btmills.png?s=75" width="75" height="75"><br />
Brandon Mills
</a>
</td><td align="center" valign="top" width="11%">
<a href="https://github.com/mysticatea">
<img src="https://github.com/mysticatea.png?s=75" width="75" height="75"><br />
Toru Nagashima
</a>
</td><td align="center" valign="top" width="11%">
<a href="https://github.com/mdjermanovic">

@@ -249,3 +234,2 @@ <img src="https://github.com/mdjermanovic.png?s=75" width="75" height="75"><br />

### Reviewers

@@ -258,34 +242,56 @@

<img src="https://github.com/aladdin-add.png?s=75" width="75" height="75"><br />
薛定谔的猫
唯然
</a>
</td><td align="center" valign="top" width="11%">
<a href="https://github.com/snitin315">
<img src="https://github.com/snitin315.png?s=75" width="75" height="75"><br />
Nitin Kumar
</a>
</td></tr></tbody></table>
### Committers
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/bmish">
<img src="https://github.com/bmish.png?s=75" width="75" height="75"><br />
Bryan Mishkin
</a>
</td><td align="center" valign="top" width="11%">
<a href="https://github.com/fasttime">
<img src="https://github.com/fasttime.png?s=75" width="75" height="75"><br />
Francesco Trotta
</a>
</td><td align="center" valign="top" width="11%">
<a href="https://github.com/ota-meshi">
<img src="https://github.com/ota-meshi.png?s=75" width="75" height="75"><br />
Yosuke Ota
</a>
</td></tr></tbody></table>
### Committers
### Website Team
The people who review and fix bugs and help triage issues.
Team members who focus specifically on eslint.org
<table><tbody><tr><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
<a href="https://github.com/amareshsm">
<img src="https://github.com/amareshsm.png?s=75" width="75" height="75"><br />
Amaresh S M
</a>
</td><td align="center" valign="top" width="11%">
<a href="https://github.com/anikethsaha">
<img src="https://github.com/anikethsaha.png?s=75" width="75" height="75"><br />
Anix
<a href="https://github.com/harish-sethuraman">
<img src="https://github.com/harish-sethuraman.png?s=75" width="75" height="75"><br />
Strek
</a>
</td><td align="center" valign="top" width="11%">
<a href="https://github.com/yeonjuan">
<img src="https://github.com/yeonjuan.png?s=75" width="75" height="75"><br />
YeonJuan
<a href="https://github.com/kecrily">
<img src="https://github.com/kecrily.png?s=75" width="75" height="75"><br />
Percy Ma
</a>
</td></tr></tbody></table>
<!--teamend-->
## <a name="sponsors"></a>Sponsors
## Sponsors

@@ -297,9 +303,9 @@ The following companies, organizations, and individuals support ESLint's ongoing maintenance and development. [Become a Sponsor](https://opencollective.com/eslint) to get your logo on our README and website.

<h3>Platinum Sponsors</h3>
<p><a href="https://automattic.com"><img src="https://images.opencollective.com/photomatt/d0ef3e1/logo.png" alt="Automattic" height="undefined"></a></p><h3>Gold Sponsors</h3>
<p><a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="96"></a> <a href="https://google.com/chrome"><img src="https://images.opencollective.com/chrome/dc55bd4/logo.png" alt="Chrome's Web Framework & Tools Performance Fund" height="96"></a> <a href="https://www.shopify.com"><img src="https://images.opencollective.com/shopify/e780cd4/logo.png" alt="Shopify" height="96"></a> <a href="https://www.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" 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://aka.ms/microsoftfossfund"><img src="https://avatars.githubusercontent.com/u/67931232?u=7fddc652a464d7151b97e8f108392af7d54fa3e8&v=4" alt="Microsoft FOSS Fund Sponsorships" height="96"></a></p><h3>Silver Sponsors</h3>
<p><a href="https://retool.com/"><img src="https://images.opencollective.com/retool/98ea68e/logo.png" alt="Retool" 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://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://writersperhour.com"><img src="https://images.opencollective.com/writersperhour/5787d4b/logo.png" alt="Writers Per Hour" height="32"></a> <a href="https://www.betacalendars.com/march-calendar.html"><img src="https://images.opencollective.com/betacalendars/9334b33/logo.png" alt="March 2021 calendar" height="32"></a> <a href="https://buy.fineproxy.org/eng/"><img src="https://images.opencollective.com/buy-fineproxy-org/b282e39/logo.png" alt="Buy.Fineproxy.Org" 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="null"><img src="https://images.opencollective.com/bugsnag-stability-monitoring/c2cef36/logo.png" alt="Bugsnag Stability Monitoring" 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://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS Server" 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://discordapp.com"><img src="https://images.opencollective.com/discordapp/7e3d9a9/logo.png" alt="Discord" 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://www.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" height="32"></a></p>
<p><a href="#"><img src="https://images.opencollective.com/2021-frameworks-fund/logo.png" alt="Chrome Frameworks Fund" height="undefined"></a> <a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="undefined"></a></p><h3>Gold Sponsors</h3>
<p><a href="https://engineering.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a></p><h3>Silver Sponsors</h3>
<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://opensource.siemens.com"><img src="https://avatars.githubusercontent.com/u/624020?v=4" alt="Siemens" 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://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" 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>
<!--sponsorsend-->
## <a name="technology-sponsors"></a>Technology Sponsors
## Technology Sponsors

@@ -306,0 +312,0 @@ * Site search ([eslint.org](https://eslint.org)) is sponsored by [Algolia](https://www.algolia.com)

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

Sorry, the diff of this file is not supported yet

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