Socket
Socket
Sign inDemoInstall

eslint

Package Overview
Dependencies
97
Maintainers
4
Versions
356
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 8.56.0 to 9.0.0-alpha.0

lib/eslint/legacy-eslint.js

4

conf/rule-type-list.json

@@ -26,4 +26,6 @@ {

{ "removed": "space-unary-word-ops", "replacedBy": ["space-unary-ops"] },
{ "removed": "spaced-line-comment", "replacedBy": ["spaced-comment"] }
{ "removed": "spaced-line-comment", "replacedBy": ["spaced-comment"] },
{ "removed": "valid-jsdoc", "replacedBy": [] },
{ "removed": "require-jsdoc", "replacedBy": [] }
]
}

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

const { ESLint } = require("./eslint");
const { ESLint } = require("./eslint/eslint");
const { Linter } = require("./linter");

@@ -15,0 +15,0 @@ const { RuleTester } = require("./rule-tester");

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

const debug = require("debug")("eslint:cli-engine");
const removedFormatters = new Set([
"checkstyle",
"codeframe",
"compact",
"jslint-xml",
"junit",
"table",
"tap",
"unix",
"visualstudio"
]);
const validFixTypes = new Set(["directive", "problem", "suggestion", "layout"]);

@@ -643,3 +654,3 @@

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

@@ -1052,3 +1063,3 @@ /** @type {ConfigArray[]} */

} catch (ex) {
if (format === "table" || format === "codeframe") {
if (removedFormatters.has(format)) {
ex.message = `The ${format} formatter is no longer part of core ESLint. Install it manually with \`npm install -D eslint-formatter-${format}\``;

@@ -1055,0 +1066,0 @@ } else {

[
{
"name": "checkstyle",
"description": "Outputs results to the [Checkstyle](https://checkstyle.sourceforge.io/) format."
},
{
"name": "compact",
"description": "Human-readable output format. Mimics the default output of JSHint."
},
{
"name": "html",

@@ -15,6 +7,2 @@ "description": "Outputs results to HTML. The `html` formatter is useful for visual presentation in the browser."

{
"name": "jslint-xml",
"description": "Outputs results to format compatible with the [JSLint Jenkins plugin](https://plugins.jenkins.io/jslint/)."
},
{
"name": "json-with-metadata",

@@ -28,21 +16,5 @@ "description": "Outputs JSON-serialized results. The `json-with-metadata` provides the same linting results as the [`json`](#json) formatter with additional metadata about the rules applied. The linting results are included in the `results` property and the rules metadata is included in the `metadata` property.\n\nAlternatively, you can use the [ESLint Node.js API](../../integrate/nodejs-api) to programmatically use ESLint."

{
"name": "junit",
"description": "Outputs results to format compatible with the [JUnit Jenkins plugin](https://plugins.jenkins.io/junit/)."
},
{
"name": "stylish",
"description": "Human-readable output format. This is the default formatter."
},
{
"name": "tap",
"description": "Outputs results to the [Test Anything Protocol (TAP)](https://testanything.org/) specification format."
},
{
"name": "unix",
"description": "Outputs results to a format similar to many commands in UNIX-like systems. Parsable with tools such as [grep](https://www.gnu.org/software/grep/manual/grep.html), [sed](https://www.gnu.org/software/sed/manual/sed.html), and [awk](https://www.gnu.org/software/gawk/manual/gawk.html)."
},
{
"name": "visualstudio",
"description": "Outputs results to format compatible with the integrated terminal of the [Visual Studio](https://visualstudio.microsoft.com/) IDE. When using Visual Studio, you can click on the linting results in the integrated terminal to go to the issue in the source code."
}
]
]

@@ -21,4 +21,4 @@ /**

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

@@ -63,2 +63,12 @@ log = require("./shared/logging"),

/**
* Predicate function for whether or not to run a rule in quiet mode.
* If a rule is set to warning, do not run it.
* @param {{ ruleId: string; severity: number; }} rule The rule id and severity.
* @returns {boolean} True if the lint rule should run, false otherwise.
*/
function quietRuleFilter(rule) {
return rule.severity === 2;
}
/**
* Translates the CLI options into the options expected by the ESLint constructor.

@@ -99,3 +109,5 @@ * @param {ParsedCLIOptions} cliOptions The CLI options to translate.

rulesdir,
warnIgnored
warnIgnored,
passOnNoPatterns,
maxWarnings
}, configType) {

@@ -193,3 +205,4 @@

overrideConfig,
overrideConfigFile
overrideConfigFile,
passOnNoPatterns
};

@@ -200,2 +213,8 @@

options.warnIgnored = warnIgnored;
/*
* For performance reasons rules not marked as 'error' are filtered out in quiet mode. As maxWarnings
* requires rules set to 'warn' to be run, we only filter out 'warn' rules if maxWarnings is not specified.
*/
options.ruleFilter = quiet && maxWarnings === -1 ? quietRuleFilter : () => true;
} else {

@@ -312,6 +331,6 @@ options.resolvePluginsRelativeTo = resolvePluginsRelativeTo;

* @param {string} [text] The text to lint (used for TTY).
* @param {boolean} [allowFlatConfig] Whether or not to allow flat config.
* @param {boolean} [allowFlatConfig=true] Whether or not to allow flat config.
* @returns {Promise<number>} The exit code for the operation.
*/
async execute(args, text, allowFlatConfig) {
async execute(args, text, allowFlatConfig = true) {
if (Array.isArray(args)) {

@@ -332,2 +351,6 @@ debug("CLI args: %o", args.slice(2));

if (allowFlatConfig && !usingFlatConfig) {
process.emitWarning("You are using an eslintrc configuration file, which is deprecated and support will be removed in v10.0.0. Please migrate to an eslint.config.js file. See https://eslint.org/docs/latest/use/configure/migration-guide for details.", "ESLintRCWarning");
}
const CLIOptions = createCLIOptions(usingFlatConfig);

@@ -386,4 +409,4 @@

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

@@ -416,3 +439,3 @@ await engine.calculateConfigForFile(options.printConfig);

const ActiveESLint = usingFlatConfig ? FlatESLint : ESLint;
const ActiveESLint = usingFlatConfig ? ESLint : LegacyESLint;

@@ -419,0 +442,0 @@ const engine = new ActiveESLint(await translateOptions(options, usingFlatConfig ? "flat" : "eslintrc"));

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

parserOptions: {}
},
linterOptions: {
reportUnusedDisableDirectives: 1
}

@@ -47,0 +50,0 @@ },

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

const { defaultConfig } = require("./default-config");
const jsPlugin = require("@eslint/js");

@@ -138,22 +137,3 @@ //-----------------------------------------------------------------------------

[ConfigArraySymbol.preprocessConfig](config) {
if (config === "eslint:recommended") {
// if we are in a Node.js environment warn the user
if (typeof process !== "undefined" && process.emitWarning) {
process.emitWarning("The 'eslint:recommended' string configuration is deprecated and will be replaced by the @eslint/js package's 'recommended' config.");
}
return jsPlugin.configs.recommended;
}
if (config === "eslint:all") {
// if we are in a Node.js environment warn the user
if (typeof process !== "undefined" && process.emitWarning) {
process.emitWarning("The 'eslint:all' string configuration is deprecated and will be replaced by the @eslint/js package's 'all' config.");
}
return jsPlugin.configs.all;
}
/*

@@ -160,0 +140,0 @@ * If `shouldIgnore` is false, we remove any ignore patterns specified

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

//------------------------------------------------------------------------------
// Typedefs
//------------------------------------------------------------------------------
/** @typedef {import("../shared/types").Rule} Rule */
//------------------------------------------------------------------------------
// Private Members
//------------------------------------------------------------------------------
// JSON schema that disallows passing any options
const noOptionsSchema = Object.freeze({
type: "array",
minItems: 0,
maxItems: 0
});
//-----------------------------------------------------------------------------

@@ -56,12 +73,4 @@ // Functions

const plugin = config.plugins && config.plugins[pluginName];
let rule = plugin && plugin.rules && plugin.rules[ruleName];
const rule = plugin && plugin.rules && plugin.rules[ruleName];
// normalize function rules into objects
if (rule && typeof rule === "function") {
rule = {
create: rule
};
}
return rule;

@@ -72,13 +81,28 @@ }

* Gets a complete options schema for a rule.
* @param {{create: Function, schema: (Array|null)}} rule A new-style rule object
* @returns {Object} JSON Schema for the rule's options.
* @param {Rule} rule A rule object
* @throws {TypeError} If `meta.schema` is specified but is not an array, object or `false`.
* @returns {Object|null} JSON Schema for the rule's options. `null` if `meta.schema` is `false`.
*/
function getRuleOptionsSchema(rule) {
if (!rule) {
if (!rule.meta) {
return { ...noOptionsSchema }; // default if `meta.schema` is not specified
}
const schema = rule.meta.schema;
if (typeof schema === "undefined") {
return { ...noOptionsSchema }; // default if `meta.schema` is not specified
}
// `schema:false` is an allowed explicit opt-out of options validation for the rule
if (schema === false) {
return null;
}
const schema = rule.schema || rule.meta && rule.meta.schema;
if (typeof schema !== "object" || schema === null) {
throw new TypeError("Rule's `meta.schema` must be an array or object");
}
// ESLint-specific array form needs to be converted into a valid JSON Schema definition
if (Array.isArray(schema)) {

@@ -93,12 +117,9 @@ if (schema.length) {

}
return {
type: "array",
minItems: 0,
maxItems: 0
};
// `schema:[]` is an explicit way to specify that the rule does not accept any options
return { ...noOptionsSchema };
}
// Given a full schema, leave it alone
return schema || null;
// `schema:<object>` is assumed to be a valid JSON Schema definition
return schema;
}

@@ -105,0 +126,0 @@

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

/*
* Note: This can be removed in ESLint v9 because structuredClone is available globally
* starting in Node.js v17.
*/
const structuredClone = require("@ungap/structured-clone").default;
const { normalizeSeverityToNumber } = require("../shared/severity");

@@ -58,2 +53,11 @@

/**
* Check if a value is a non-null non-array object.
* @param {any} value The value to check.
* @returns {boolean} `true` if the value is a non-null non-array object.
*/
function isNonArrayObject(value) {
return isNonNullObject(value) && !Array.isArray(value);
}
/**
* Check if a value is undefined.

@@ -68,15 +72,23 @@ * @param {any} value The value to check.

/**
* Deeply merges two objects.
* Deeply merges two non-array objects.
* @param {Object} first The base object.
* @param {Object} second The overrides object.
* @param {Map<string, Map<string, Object>>} [mergeMap] Maps the combination of first and second arguments to a merged result.
* @returns {Object} An object with properties from both first and second.
*/
function deepMerge(first = {}, second = {}) {
function deepMerge(first, second, mergeMap = new Map()) {
/*
* If the second value is an array, just return it. We don't merge
* arrays because order matters and we can't know the correct order.
*/
if (Array.isArray(second)) {
return second;
let secondMergeMap = mergeMap.get(first);
if (secondMergeMap) {
const result = secondMergeMap.get(second);
if (result) {
// If this combination of first and second arguments has been already visited, return the previously created result.
return result;
}
} else {
secondMergeMap = new Map();
mergeMap.set(first, secondMergeMap);
}

@@ -95,6 +107,11 @@

delete result.__proto__; // eslint-disable-line no-proto -- don't merge own property "__proto__"
// Store the pending result for this combination of first and second arguments.
secondMergeMap.set(second, result);
for (const key of Object.keys(second)) {
// avoid hairy edge case
if (key === "__proto__") {
if (key === "__proto__" || !Object.prototype.propertyIsEnumerable.call(first, key)) {
continue;

@@ -106,13 +123,6 @@ }

if (isNonNullObject(firstValue)) {
result[key] = deepMerge(firstValue, secondValue);
} else if (isUndefined(firstValue)) {
if (isNonNullObject(secondValue)) {
result[key] = deepMerge(
Array.isArray(secondValue) ? [] : {},
secondValue
);
} else if (!isUndefined(secondValue)) {
result[key] = secondValue;
}
if (isNonArrayObject(firstValue) && isNonArrayObject(secondValue)) {
result[key] = deepMerge(firstValue, secondValue, mergeMap);
} else if (isUndefined(secondValue)) {
result[key] = firstValue;
}

@@ -119,0 +129,0 @@ }

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

/**
* The error type when a rule has an invalid `meta.schema`.
*/
class InvalidRuleOptionsSchemaError extends Error {
/**
* Creates a new instance.
* @param {string} ruleId Id of the rule that has an invalid `meta.schema`.
* @param {Error} processingError Error caught while processing the `meta.schema`.
*/
constructor(ruleId, processingError) {
super(
`Error while processing options validation schema of rule '${ruleId}': ${processingError.message}`,
{ cause: processingError }
);
this.code = "ESLINT_INVALID_RULE_OPTIONS_SCHEMA";
}
}
//-----------------------------------------------------------------------------

@@ -134,6 +153,10 @@ // Exports

if (!this.validators.has(rule)) {
const schema = getRuleOptionsSchema(rule);
try {
const schema = getRuleOptionsSchema(rule);
if (schema) {
this.validators.set(rule, ajv.compile(schema));
if (schema) {
this.validators.set(rule, ajv.compile(schema));
}
} catch (err) {
throw new InvalidRuleOptionsSchemaError(ruleId, err);
}

@@ -140,0 +163,0 @@ }

@@ -108,7 +108,7 @@ /**

* Check if a given value is a non-empty string or not.
* @param {any} x The value to check.
* @returns {boolean} `true` if `x` is a non-empty string.
* @param {any} value The value to check.
* @returns {boolean} `true` if `value` is a non-empty string.
*/
function isNonEmptyString(x) {
return typeof x === "string" && x.trim() !== "";
function isNonEmptyString(value) {
return typeof value === "string" && value.trim() !== "";
}

@@ -118,9 +118,19 @@

* 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 strings.
* @param {any} value The value to check.
* @returns {boolean} `true` if `value` is an array of non-empty strings.
*/
function isArrayOfNonEmptyString(x) {
return Array.isArray(x) && x.every(isNonEmptyString);
function isArrayOfNonEmptyString(value) {
return Array.isArray(value) && value.length && value.every(isNonEmptyString);
}
/**
* Check if a given value is an empty array or an array of non-empty strings.
* @param {any} value The value to check.
* @returns {boolean} `true` if `value` is an empty array or an array of non-empty
* strings.
*/
function isEmptyArrayOrArrayOfNonEmptyString(value) {
return Array.isArray(value) && value.every(isNonEmptyString);
}
//-----------------------------------------------------------------------------

@@ -660,5 +670,5 @@ // File-related Helpers

* Validates and normalizes options for the wrapped CLIEngine instance.
* @param {FlatESLintOptions} options The options to process.
* @param {ESLintOptions} options The options to process.
* @throws {ESLintInvalidOptionsError} If of any of a variety of type errors.
* @returns {FlatESLintOptions} The normalized options.
* @returns {ESLintOptions} The normalized options.
*/

@@ -682,2 +692,4 @@ function processOptions({

warnIgnored = true,
passOnNoPatterns = false,
ruleFilter = () => true,
...unknownOptions

@@ -766,3 +778,3 @@ }) {

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

@@ -776,2 +788,5 @@ }

}
if (typeof passOnNoPatterns !== "boolean") {
errors.push("'passOnNoPatterns' must be a boolean.");
}
if (typeof plugins !== "object") {

@@ -788,2 +803,5 @@ errors.push("'plugins' must be an object or null.");

}
if (typeof ruleFilter !== "function") {
errors.push("'ruleFilter' must be a function.");
}
if (errors.length > 0) {

@@ -810,3 +828,5 @@ throw new ESLintInvalidOptionsError(errors);

ignorePatterns,
warnIgnored
passOnNoPatterns,
warnIgnored,
ruleFilter
};

@@ -813,0 +833,0 @@ }

/**
* @fileoverview Main API Class
* @author Kai Cataldo
* @author Toru Nagashima
* @fileoverview Main class using flat config
* @author Nicholas C. Zakas
*/

@@ -13,7 +12,10 @@

// Note: Node.js 12 does not support fs/promises.
const fs = require("fs").promises;
const { existsSync } = require("fs");
const path = require("path");
const fs = require("fs");
const { promisify } = require("util");
const { CLIEngine, getCLIEngineInternalSlots } = require("../cli-engine/cli-engine");
const BuiltinRules = require("../rules");
const findUp = require("find-up");
const { version } = require("../../package.json");
const { Linter } = require("../linter");
const { getRuleFromConfig } = require("../config/flat-config-helpers");
const {

@@ -23,7 +25,29 @@ Legacy: {

getRuleSeverity
}
},
ModuleResolver,
naming
}
} = require("@eslint/eslintrc");
const { version } = require("../../package.json");
const {
findFiles,
getCacheFile,
isNonEmptyString,
isArrayOfNonEmptyString,
createIgnoreResult,
isErrorMessage,
processOptions
} = require("./eslint-helpers");
const { pathToFileURL } = require("url");
const { FlatConfigArray } = require("../config/flat-config-array");
const LintResultCache = require("../cli-engine/lint-result-cache");
/*
* This is necessary to allow overwriting writeFile for testing purposes.
* We can just use fs/promises once we drop Node.js 12 support.
*/
//------------------------------------------------------------------------------

@@ -33,19 +57,15 @@ // Typedefs

/** @typedef {import("../cli-engine/cli-engine").LintReport} CLIEngineLintReport */
// For VSCode IntelliSense
/** @typedef {import("../shared/types").ConfigData} ConfigData */
/** @typedef {import("../shared/types").DeprecatedRuleInfo} DeprecatedRuleInfo */
/** @typedef {import("../shared/types").ConfigData} ConfigData */
/** @typedef {import("../shared/types").LintMessage} LintMessage */
/** @typedef {import("../shared/types").SuppressedLintMessage} SuppressedLintMessage */
/** @typedef {import("../shared/types").LintResult} LintResult */
/** @typedef {import("../shared/types").ParserOptions} ParserOptions */
/** @typedef {import("../shared/types").Plugin} Plugin */
/** @typedef {import("../shared/types").ResultsMeta} ResultsMeta */
/** @typedef {import("../shared/types").RuleConf} RuleConf */
/** @typedef {import("../shared/types").Rule} Rule */
/** @typedef {import("../shared/types").LintResult} LintResult */
/** @typedef {import("../shared/types").ResultsMeta} ResultsMeta */
/** @typedef {ReturnType<ConfigArray.extractConfig>} ExtractedConfig */
/**
* 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.

@@ -60,31 +80,17 @@ * @typedef {Object} ESLintOptions

* @property {boolean} [errorOnUnmatchedPattern] If `false` then `ESLint#lintFiles()` doesn't throw even if no target files found. Defaults to `true`.
* @property {string[]} [extensions] An array of file extensions to check.
* @property {boolean|Function} [fix] Execute in autofix mode. If a function, should return a boolean.
* @property {string[]} [fixTypes] Array of rule types to apply fixes for.
* @property {boolean} [globInputPaths] Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file.
* @property {boolean} [ignore] False disables use of .eslintignore.
* @property {string} [ignorePath] The ignore file to use instead of .eslintignore.
* @property {boolean} [ignore] False disables all ignore patterns except for the default ones.
* @property {string[]} [ignorePatterns] Ignore file patterns to use in addition to config ignores. These patterns are relative to `cwd`.
* @property {ConfigData} [overrideConfig] Override config object, overrides all configs used with this instance
* @property {string} [overrideConfigFile] The configuration file to use.
* @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.
* @property {string} [resolvePluginsRelativeTo] The folder where plugins should be resolved from, defaulting to the CWD.
* @property {string[]} [rulePaths] An array of directories to load custom rules from.
* @property {boolean} [useEslintrc] False disables looking for .eslintrc.* files.
* @property {boolean|string} [overrideConfigFile] Searches for default config file when falsy;
* doesn't do any config file lookup when `true`; considered to be a config filename
* when a string.
* @property {Record<string,Plugin>} [plugins] An array of plugin implementations.
* @property {boolean} warnIgnored Show warnings when the file list includes ignored files
* @property {boolean} [passOnNoPatterns=false] When set to true, missing patterns cause
* the linting operation to short circuit and not report any failures.
*/
/**
* A rules metadata object.
* @typedef {Object} RulesMeta
* @property {string} id The plugin ID.
* @property {Object} definition The plugin definition.
*/
/**
* Private members for the `ESLint` instance.
* @typedef {Object} ESLintPrivateMembers
* @property {CLIEngine} cliEngine The wrapped CLIEngine instance.
* @property {ESLintOptions} options The options used to instantiate the ESLint instance.
*/
//------------------------------------------------------------------------------

@@ -94,233 +100,56 @@ // Helpers

const writeFile = promisify(fs.writeFile);
const FLAT_CONFIG_FILENAMES = [
"eslint.config.js",
"eslint.config.mjs",
"eslint.config.cjs"
];
const debug = require("debug")("eslint:eslint");
const privateMembers = new WeakMap();
const importedConfigFileModificationTime = new Map();
const removedFormatters = new Set([
"checkstyle",
"codeframe",
"compact",
"jslint-xml",
"junit",
"table",
"tap",
"unix",
"visualstudio"
]);
/**
* The map with which to store private class members.
* @type {WeakMap<ESLint, ESLintPrivateMembers>}
* It will calculate the error and warning count for collection of messages per file
* @param {LintMessage[]} messages Collection of messages
* @returns {Object} Contains the stats
* @private
*/
const privateMembersMap = new WeakMap();
function calculateStatsPerFile(messages) {
const stat = {
errorCount: 0,
fatalErrorCount: 0,
warningCount: 0,
fixableErrorCount: 0,
fixableWarningCount: 0
};
/**
* Check if a given value is a non-empty string or not.
* @param {any} x The value to check.
* @returns {boolean} `true` if `x` is a non-empty string.
*/
function isNonEmptyString(x) {
return typeof x === "string" && x.trim() !== "";
}
for (let i = 0; i < messages.length; i++) {
const message = messages[i];
/**
* 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 strings.
*/
function isArrayOfNonEmptyString(x) {
return Array.isArray(x) && x.every(isNonEmptyString);
}
/**
* Check if a given value is a valid fix type or not.
* @param {any} x The value to check.
* @returns {boolean} `true` if `x` is valid fix type.
*/
function isFixType(x) {
return x === "directive" || x === "problem" || x === "suggestion" || x === "layout";
}
/**
* Check if a given value is an array of fix types or not.
* @param {any} x The value to check.
* @returns {boolean} `true` if `x` is an array of fix types.
*/
function isFixTypeArray(x) {
return Array.isArray(x) && x.every(isFixType);
}
/**
* The error for invalid options.
*/
class ESLintInvalidOptionsError extends Error {
constructor(messages) {
super(`Invalid Options:\n- ${messages.join("\n- ")}`);
this.code = "ESLINT_INVALID_OPTIONS";
Error.captureStackTrace(this, ESLintInvalidOptionsError);
}
}
/**
* Validates and normalizes options for the wrapped CLIEngine instance.
* @param {ESLintOptions} options The options to process.
* @throws {ESLintInvalidOptionsError} If of any of a variety of type errors.
* @returns {ESLintOptions} The normalized options.
*/
function processOptions({
allowInlineConfig = true, // ← we cannot use `overrideConfig.noInlineConfig` instead because `allowInlineConfig` has side-effect that suppress warnings that show inline configs are ignored.
baseConfig = null,
cache = false,
cacheLocation = ".eslintcache",
cacheStrategy = "metadata",
cwd = process.cwd(),
errorOnUnmatchedPattern = true,
extensions = null, // ← should be null by default because if it's an array then it suppresses RFC20 feature.
fix = false,
fixTypes = null, // ← should be null by default because if it's an array then it suppresses rules that don't have the `meta.type` property.
globInputPaths = true,
ignore = true,
ignorePath = null, // ← should be null by default because if it's a string then it may throw ENOENT.
overrideConfig = null,
overrideConfigFile = null,
plugins = {},
reportUnusedDisableDirectives = null, // ← should be null by default because if it's a string then it overrides the 'reportUnusedDisableDirectives' setting in config files. And we cannot use `overrideConfig.reportUnusedDisableDirectives` instead because we cannot configure the `error` severity with that.
resolvePluginsRelativeTo = null, // ← should be null by default because if it's a string then it suppresses RFC47 feature.
rulePaths = [],
useEslintrc = true,
...unknownOptions
}) {
const errors = [];
const unknownOptionKeys = Object.keys(unknownOptions);
if (unknownOptionKeys.length >= 1) {
errors.push(`Unknown options: ${unknownOptionKeys.join(", ")}`);
if (unknownOptionKeys.includes("cacheFile")) {
errors.push("'cacheFile' has been removed. Please use the 'cacheLocation' option instead.");
}
if (unknownOptionKeys.includes("configFile")) {
errors.push("'configFile' has been removed. Please use the 'overrideConfigFile' option instead.");
}
if (unknownOptionKeys.includes("envs")) {
errors.push("'envs' has been removed. Please use the 'overrideConfig.env' option instead.");
}
if (unknownOptionKeys.includes("globals")) {
errors.push("'globals' has been removed. Please use the 'overrideConfig.globals' option instead.");
}
if (unknownOptionKeys.includes("ignorePattern")) {
errors.push("'ignorePattern' has been removed. Please use the 'overrideConfig.ignorePatterns' option instead.");
}
if (unknownOptionKeys.includes("parser")) {
errors.push("'parser' has been removed. Please use the 'overrideConfig.parser' option instead.");
}
if (unknownOptionKeys.includes("parserOptions")) {
errors.push("'parserOptions' has been removed. Please use the 'overrideConfig.parserOptions' option instead.");
}
if (unknownOptionKeys.includes("rules")) {
errors.push("'rules' has been removed. Please use the 'overrideConfig.rules' option instead.");
}
}
if (typeof allowInlineConfig !== "boolean") {
errors.push("'allowInlineConfig' must be a boolean.");
}
if (typeof baseConfig !== "object") {
errors.push("'baseConfig' must be an object or null.");
}
if (typeof cache !== "boolean") {
errors.push("'cache' must be a boolean.");
}
if (!isNonEmptyString(cacheLocation)) {
errors.push("'cacheLocation' must be a non-empty string.");
}
if (
cacheStrategy !== "metadata" &&
cacheStrategy !== "content"
) {
errors.push("'cacheStrategy' must be any of \"metadata\", \"content\".");
}
if (!isNonEmptyString(cwd) || !path.isAbsolute(cwd)) {
errors.push("'cwd' must be an absolute path.");
}
if (typeof errorOnUnmatchedPattern !== "boolean") {
errors.push("'errorOnUnmatchedPattern' must be a boolean.");
}
if (!isArrayOfNonEmptyString(extensions) && extensions !== null) {
errors.push("'extensions' must be an array of non-empty strings or null.");
}
if (typeof fix !== "boolean" && typeof fix !== "function") {
errors.push("'fix' must be a boolean or a function.");
}
if (fixTypes !== null && !isFixTypeArray(fixTypes)) {
errors.push("'fixTypes' must be an array of any of \"directive\", \"problem\", \"suggestion\", and \"layout\".");
}
if (typeof globInputPaths !== "boolean") {
errors.push("'globInputPaths' must be a boolean.");
}
if (typeof ignore !== "boolean") {
errors.push("'ignore' must be a boolean.");
}
if (!isNonEmptyString(ignorePath) && ignorePath !== null) {
errors.push("'ignorePath' must be a non-empty string or null.");
}
if (typeof overrideConfig !== "object") {
errors.push("'overrideConfig' must be an object or null.");
}
if (!isNonEmptyString(overrideConfigFile) && overrideConfigFile !== null) {
errors.push("'overrideConfigFile' must be a non-empty string or null.");
}
if (typeof plugins !== "object") {
errors.push("'plugins' must be an object or null.");
} else if (plugins !== null && Object.keys(plugins).includes("")) {
errors.push("'plugins' must not include an empty string.");
}
if (Array.isArray(plugins)) {
errors.push("'plugins' doesn't add plugins to configuration to load. Please use the 'overrideConfig.plugins' option instead.");
}
if (
reportUnusedDisableDirectives !== "error" &&
reportUnusedDisableDirectives !== "warn" &&
reportUnusedDisableDirectives !== "off" &&
reportUnusedDisableDirectives !== null
) {
errors.push("'reportUnusedDisableDirectives' must be any of \"error\", \"warn\", \"off\", and null.");
}
if (
!isNonEmptyString(resolvePluginsRelativeTo) &&
resolvePluginsRelativeTo !== null
) {
errors.push("'resolvePluginsRelativeTo' must be a non-empty string or null.");
}
if (!isArrayOfNonEmptyString(rulePaths)) {
errors.push("'rulePaths' must be an array of non-empty strings.");
}
if (typeof useEslintrc !== "boolean") {
errors.push("'useEslintrc' must be a boolean.");
}
if (errors.length > 0) {
throw new ESLintInvalidOptionsError(errors);
}
return {
allowInlineConfig,
baseConfig,
cache,
cacheLocation,
cacheStrategy,
configFile: overrideConfigFile,
cwd: path.normalize(cwd),
errorOnUnmatchedPattern,
extensions,
fix,
fixTypes,
globInputPaths,
ignore,
ignorePath,
reportUnusedDisableDirectives,
resolvePluginsRelativeTo,
rulePaths,
useEslintrc
};
}
/**
* Check if a value has one or more properties and that value is not undefined.
* @param {any} obj The value to check.
* @returns {boolean} `true` if `obj` has one or more properties that that value is not undefined.
*/
function hasDefinedProperty(obj) {
if (typeof obj === "object" && obj !== null) {
for (const key in obj) {
if (typeof obj[key] !== "undefined") {
return true;
if (message.fatal || message.severity === 2) {
stat.errorCount++;
if (message.fatal) {
stat.fatalErrorCount++;
}
if (message.fix) {
stat.fixableErrorCount++;
}
} else {
stat.warningCount++;
if (message.fix) {
stat.fixableWarningCount++;
}
}
}
return false;
return stat;
}

@@ -340,2 +169,12 @@

/**
* Return the absolute path of a file named `"__placeholder__.js"` in a given directory.
* This is used as a replacement for a missing file path.
* @param {string} cwd An absolute directory path.
* @returns {string} The absolute path of a file named `"__placeholder__.js"` in the given directory.
*/
function getPlaceholderPath(cwd) {
return path.join(cwd, "__placeholder__.js");
}
/** @type {WeakMap<ExtractedConfig, DeprecatedRuleInfo[]>} */

@@ -346,38 +185,39 @@ const usedDeprecatedRulesCache = new WeakMap();

* Create used deprecated rule list.
* @param {CLIEngine} cliEngine The CLIEngine instance.
* @param {CLIEngine} eslint The CLIEngine instance.
* @param {string} maybeFilePath The absolute path to a lint target file or `"<text>"`.
* @returns {DeprecatedRuleInfo[]} The used deprecated rule list.
*/
function getOrFindUsedDeprecatedRules(cliEngine, maybeFilePath) {
function getOrFindUsedDeprecatedRules(eslint, maybeFilePath) {
const {
configArrayFactory,
configs,
options: { cwd }
} = getCLIEngineInternalSlots(cliEngine);
} = privateMembers.get(eslint);
const filePath = path.isAbsolute(maybeFilePath)
? maybeFilePath
: path.join(cwd, "__placeholder__.js");
const configArray = configArrayFactory.getConfigArrayForFile(filePath);
const config = configArray.extractConfig(filePath);
: getPlaceholderPath(cwd);
const config = configs.getConfig(filePath);
// Most files use the same config, so cache it.
if (!usedDeprecatedRulesCache.has(config)) {
const pluginRules = configArray.pluginRules;
if (config && !usedDeprecatedRulesCache.has(config)) {
const retv = [];
for (const [ruleId, ruleConf] of Object.entries(config.rules)) {
if (getRuleSeverity(ruleConf) === 0) {
continue;
}
const rule = pluginRules.get(ruleId) || BuiltinRules.get(ruleId);
const meta = rule && rule.meta;
if (config.rules) {
for (const [ruleId, ruleConf] of Object.entries(config.rules)) {
if (getRuleSeverity(ruleConf) === 0) {
continue;
}
const rule = getRuleFromConfig(ruleId, config);
const meta = rule && rule.meta;
if (meta && meta.deprecated) {
retv.push({ ruleId, replacedBy: meta.replacedBy || [] });
if (meta && meta.deprecated) {
retv.push({ ruleId, replacedBy: meta.replacedBy || [] });
}
}
}
usedDeprecatedRulesCache.set(config, Object.freeze(retv));
}
return usedDeprecatedRulesCache.get(config);
return config ? usedDeprecatedRulesCache.get(config) : Object.freeze([]);
}

@@ -388,7 +228,7 @@

* match the ESLint class's API.
* @param {CLIEngine} cliEngine The CLIEngine instance.
* @param {CLIEngine} eslint The CLIEngine instance.
* @param {CLIEngineLintReport} report The CLIEngine linting report to process.
* @returns {LintResult[]} The processed linting results.
*/
function processCLIEngineLintReport(cliEngine, { results }) {
function processLintReport(eslint, { results }) {
const descriptor = {

@@ -398,3 +238,3 @@ configurable: true,

get() {
return getOrFindUsedDeprecatedRules(cliEngine, this.filePath);
return getOrFindUsedDeprecatedRules(eslint, this.filePath);
}

@@ -429,4 +269,308 @@ };

/**
* Main API.
* Searches from the current working directory up until finding the
* given flat config filename.
* @param {string} cwd The current working directory to search from.
* @returns {Promise<string|undefined>} The filename if found or `undefined` if not.
*/
function findFlatConfigFile(cwd) {
return findUp(
FLAT_CONFIG_FILENAMES,
{ cwd }
);
}
/**
* Load the config array from the given filename.
* @param {string} filePath The filename to load from.
* @returns {Promise<any>} The config loaded from the config file.
*/
async function loadFlatConfigFile(filePath) {
debug(`Loading config from ${filePath}`);
const fileURL = pathToFileURL(filePath);
debug(`Config file URL is ${fileURL}`);
const mtime = (await fs.stat(filePath)).mtime.getTime();
/*
* Append a query with the config file's modification time (`mtime`) in order
* to import the current version of the config file. Without the query, `import()` would
* cache the config file module by the pathname only, and then always return
* the same version (the one that was actual when the module was imported for the first time).
*
* This ensures that the config file module is loaded and executed again
* if it has been changed since the last time it was imported.
* If it hasn't been changed, `import()` will just return the cached version.
*
* Note that we should not overuse queries (e.g., by appending the current time
* to always reload the config file module) as that could cause memory leaks
* because entries are never removed from the import cache.
*/
fileURL.searchParams.append("mtime", mtime);
/*
* With queries, we can bypass the import cache. However, when import-ing a CJS module,
* Node.js uses the require infrastructure under the hood. That includes the require cache,
* which caches the config file module by its file path (queries have no effect).
* Therefore, we also need to clear the require cache before importing the config file module.
* In order to get the same behavior with ESM and CJS config files, in particular - to reload
* the config file only if it has been changed, we track file modification times and clear
* the require cache only if the file has been changed.
*/
if (importedConfigFileModificationTime.get(filePath) !== mtime) {
delete require.cache[filePath];
}
const config = (await import(fileURL)).default;
importedConfigFileModificationTime.set(filePath, mtime);
return config;
}
/**
* Determines which config file to use. This is determined by seeing if an
* override config file was passed, and if so, using it; otherwise, as long
* as override config file is not explicitly set to `false`, it will search
* upwards from the cwd for a file named `eslint.config.js`.
* @param {import("./eslint").ESLintOptions} options The ESLint instance options.
* @returns {{configFilePath:string|undefined,basePath:string,error:Error|null}} Location information for
* the config file.
*/
async function locateConfigFileToUse({ configFile, cwd }) {
// determine where to load config file from
let configFilePath;
let basePath = cwd;
let error = null;
if (typeof configFile === "string") {
debug(`Override config file path is ${configFile}`);
configFilePath = path.resolve(cwd, configFile);
} else if (configFile !== false) {
debug("Searching for eslint.config.js");
configFilePath = await findFlatConfigFile(cwd);
if (configFilePath) {
basePath = path.resolve(path.dirname(configFilePath));
} else {
error = new Error("Could not find config file.");
}
}
return {
configFilePath,
basePath,
error
};
}
/**
* Calculates the config array for this run based on inputs.
* @param {ESLint} eslint The instance to create the config array for.
* @param {import("./eslint").ESLintOptions} options The ESLint instance options.
* @returns {FlatConfigArray} The config array for `eslint``.
*/
async function calculateConfigArray(eslint, {
cwd,
baseConfig,
overrideConfig,
configFile,
ignore: shouldIgnore,
ignorePatterns
}) {
// check for cached instance
const slots = privateMembers.get(eslint);
if (slots.configs) {
return slots.configs;
}
const { configFilePath, basePath, error } = await locateConfigFileToUse({ configFile, cwd });
// config file is required to calculate config
if (error) {
throw error;
}
const configs = new FlatConfigArray(baseConfig || [], { basePath, shouldIgnore });
// load config file
if (configFilePath) {
const fileConfig = await loadFlatConfigFile(configFilePath);
if (Array.isArray(fileConfig)) {
configs.push(...fileConfig);
} else {
configs.push(fileConfig);
}
}
// add in any configured defaults
configs.push(...slots.defaultConfigs);
// append command line ignore patterns
if (ignorePatterns && ignorePatterns.length > 0) {
let relativeIgnorePatterns;
/*
* If the config file basePath is different than the cwd, then
* the ignore patterns won't work correctly. Here, we adjust the
* ignore pattern to include the correct relative path. Patterns
* passed as `ignorePatterns` are relative to the cwd, whereas
* the config file basePath can be an ancestor of the cwd.
*/
if (basePath === cwd) {
relativeIgnorePatterns = ignorePatterns;
} else {
const relativeIgnorePath = path.relative(basePath, cwd);
relativeIgnorePatterns = ignorePatterns.map(pattern => {
const negated = pattern.startsWith("!");
const basePattern = negated ? pattern.slice(1) : pattern;
return (negated ? "!" : "") +
path.posix.join(relativeIgnorePath, basePattern);
});
}
/*
* Ignore patterns are added to the end of the config array
* so they can override default ignores.
*/
configs.push({
ignores: relativeIgnorePatterns
});
}
if (overrideConfig) {
if (Array.isArray(overrideConfig)) {
configs.push(...overrideConfig);
} else {
configs.push(overrideConfig);
}
}
await configs.normalize();
// cache the config array for this instance
slots.configs = configs;
return configs;
}
/**
* Processes an source code using ESLint.
* @param {Object} config The config object.
* @param {string} config.text The source code to verify.
* @param {string} config.cwd The path to the current working directory.
* @param {string|undefined} config.filePath The path to the file of `text`. If this is undefined, it uses `<text>`.
* @param {FlatConfigArray} config.configs The config.
* @param {boolean} config.fix If `true` then it does fix.
* @param {boolean} config.allowInlineConfig If `true` then it uses directive comments.
* @param {Function} config.ruleFilter A predicate function to filter which rules should be run.
* @param {Linter} config.linter The linter instance to verify.
* @returns {LintResult} The result of linting.
* @private
*/
function verifyText({
text,
cwd,
filePath: providedFilePath,
configs,
fix,
allowInlineConfig,
ruleFilter,
linter
}) {
const filePath = providedFilePath || "<text>";
debug(`Lint ${filePath}`);
/*
* Verify.
* `config.extractConfig(filePath)` requires an absolute path, but `linter`
* doesn't know CWD, so it gives `linter` an absolute path always.
*/
const filePathToVerify = filePath === "<text>" ? getPlaceholderPath(cwd) : filePath;
const { fixed, messages, output } = linter.verifyAndFix(
text,
configs,
{
allowInlineConfig,
filename: filePathToVerify,
fix,
ruleFilter,
/**
* Check if the linter should adopt a given code block or not.
* @param {string} blockFilename The virtual filename of a code block.
* @returns {boolean} `true` if the linter should adopt the code block.
*/
filterCodeBlock(blockFilename) {
return configs.isExplicitMatch(blockFilename);
}
}
);
// Tweak and return.
const result = {
filePath: filePath === "<text>" ? filePath : path.resolve(filePath),
messages,
suppressedMessages: linter.getSuppressedMessages(),
...calculateStatsPerFile(messages)
};
if (fixed) {
result.output = output;
}
if (
result.errorCount + result.warningCount > 0 &&
typeof result.output === "undefined"
) {
result.source = text;
}
return result;
}
/**
* Checks whether a message's rule type should be fixed.
* @param {LintMessage} message The message to check.
* @param {FlatConfig} config The config for the file that generated the message.
* @param {string[]} fixTypes An array of fix types to check.
* @returns {boolean} Whether the message should be fixed.
*/
function shouldMessageBeFixed(message, config, fixTypes) {
if (!message.ruleId) {
return fixTypes.has("directive");
}
const rule = message.ruleId && getRuleFromConfig(message.ruleId, config);
return Boolean(rule && rule.meta && fixTypes.has(rule.meta.type));
}
/**
* Creates an error to be thrown when an array of results passed to `getRulesMetaForResults` was not created by the current engine.
* @returns {TypeError} An error object.
*/
function createExtraneousResultsError() {
return new TypeError("Results object was not created from this ESLint instance.");
}
//-----------------------------------------------------------------------------
// Main API
//-----------------------------------------------------------------------------
/**
* Primary Node.js API for ESLint.
*/
class ESLint {

@@ -439,31 +583,45 @@

constructor(options = {}) {
const defaultConfigs = [];
const processedOptions = processOptions(options);
const cliEngine = new CLIEngine(processedOptions, { preloadedPlugins: options.plugins });
const {
configArrayFactory,
lastConfigArrays
} = getCLIEngineInternalSlots(cliEngine);
let updated = false;
const linter = new Linter({
cwd: processedOptions.cwd,
configType: "flat"
});
/*
* Address `overrideConfig` to set override config.
* Operate the `configArrayFactory` internal slot directly because this
* functionality doesn't exist as the public API of CLIEngine.
const cacheFilePath = getCacheFile(
processedOptions.cacheLocation,
processedOptions.cwd
);
const lintResultCache = processedOptions.cache
? new LintResultCache(cacheFilePath, processedOptions.cacheStrategy)
: null;
privateMembers.set(this, {
options: processedOptions,
linter,
cacheFilePath,
lintResultCache,
defaultConfigs,
configs: null
});
/**
* If additional plugins are passed in, add that to the default
* configs for this instance.
*/
if (hasDefinedProperty(options.overrideConfig)) {
configArrayFactory.setOverrideConfig(options.overrideConfig);
updated = true;
}
if (options.plugins) {
// Update caches.
if (updated) {
configArrayFactory.clearCache();
lastConfigArrays[0] = configArrayFactory.getConfigArrayForFile();
const plugins = {};
for (const [pluginName, plugin] of Object.entries(options.plugins)) {
plugins[naming.getShorthandName(pluginName, "eslint-plugin")] = plugin;
}
defaultConfigs.push({
plugins
});
}
// Initialize private properties.
privateMembersMap.set(this, {
cliEngine,
options: processedOptions
});
}

@@ -500,3 +658,3 @@

})
.map(r => writeFile(r.filePath, r.output))
.map(r => fs.writeFile(r.filePath, r.output))
);

@@ -511,3 +669,22 @@ }

static getErrorResults(results) {
return CLIEngine.getErrorResults(results);
const filtered = [];
results.forEach(result => {
const filteredMessages = result.messages.filter(isErrorMessage);
const filteredSuppressedMessages = result.suppressedMessages.filter(isErrorMessage);
if (filteredMessages.length > 0) {
filtered.push({
...result,
messages: filteredMessages,
suppressedMessages: filteredSuppressedMessages,
errorCount: filteredMessages.length,
warningCount: 0,
fixableErrorCount: result.fixableErrorCount,
fixableWarningCount: 0
});
}
});
return filtered;
}

@@ -519,27 +696,57 @@

* @returns {Object} A mapping of ruleIds to rule meta objects.
* @throws {TypeError} When the results object wasn't created from this ESLint instance.
* @throws {TypeError} When a plugin or rule is missing.
*/
getRulesMetaForResults(results) {
const resultRuleIds = new Set();
// short-circuit simple case
if (results.length === 0) {
return {};
}
// first gather all ruleIds from all results
const resultRules = new Map();
const {
configs,
options: { cwd }
} = privateMembers.get(this);
for (const result of results) {
for (const { ruleId } of result.messages) {
resultRuleIds.add(ruleId);
}
for (const { ruleId } of result.suppressedMessages) {
resultRuleIds.add(ruleId);
}
/*
* We can only accurately return rules meta information for linting results if the
* results were created by this instance. Otherwise, the necessary rules data is
* not available. So if the config array doesn't already exist, just throw an error
* to let the user know we can't do anything here.
*/
if (!configs) {
throw createExtraneousResultsError();
}
// create a map of all rules in the results
for (const result of results) {
const { cliEngine } = privateMembersMap.get(this);
const rules = cliEngine.getRules();
const resultRules = new Map();
/*
* Normalize filename for <text>.
*/
const filePath = result.filePath === "<text>"
? getPlaceholderPath(cwd) : result.filePath;
const allMessages = result.messages.concat(result.suppressedMessages);
for (const [ruleId, rule] of rules) {
if (resultRuleIds.has(ruleId)) {
resultRules.set(ruleId, rule);
for (const { ruleId } of allMessages) {
if (!ruleId) {
continue;
}
/*
* All of the plugin and rule information is contained within the
* calculated config for the given file.
*/
const config = configs.getConfig(filePath);
if (!config) {
throw createExtraneousResultsError();
}
const rule = getRuleFromConfig(ruleId, config);
// ignore unknown rules
if (rule) {
resultRules.set(ruleId, rule);
}
}

@@ -549,3 +756,2 @@ }

return createRulesMeta(resultRules);
}

@@ -555,15 +761,193 @@

* Executes the current configuration on an array of file and directory names.
* @param {string[]} patterns An array of file and directory names.
* @param {string|string[]} patterns An array of file and directory names.
* @returns {Promise<LintResult[]>} The results of linting the file patterns given.
*/
async lintFiles(patterns) {
if (!isNonEmptyString(patterns) && !isArrayOfNonEmptyString(patterns)) {
throw new Error("'patterns' must be a non-empty string or an array of non-empty strings");
let normalizedPatterns = patterns;
const {
cacheFilePath,
lintResultCache,
linter,
options: eslintOptions
} = privateMembers.get(this);
/*
* Special cases:
* 1. `patterns` is an empty string
* 2. `patterns` is an empty array
*
* In both cases, we use the cwd as the directory to lint.
*/
if (patterns === "" || Array.isArray(patterns) && patterns.length === 0) {
/*
* Special case: If `passOnNoPatterns` is true, then we just exit
* without doing any work.
*/
if (eslintOptions.passOnNoPatterns) {
return [];
}
normalizedPatterns = ["."];
} else {
if (!isNonEmptyString(patterns) && !isArrayOfNonEmptyString(patterns)) {
throw new Error("'patterns' must be a non-empty string or an array of non-empty strings");
}
if (typeof patterns === "string") {
normalizedPatterns = [patterns];
}
}
const { cliEngine } = privateMembersMap.get(this);
return processCLIEngineLintReport(
cliEngine,
cliEngine.executeOnFiles(patterns)
debug(`Using file patterns: ${normalizedPatterns}`);
const configs = await calculateConfigArray(this, eslintOptions);
const {
allowInlineConfig,
cache,
cwd,
fix,
fixTypes,
ruleFilter,
globInputPaths,
errorOnUnmatchedPattern,
warnIgnored
} = eslintOptions;
const startTime = Date.now();
const fixTypesSet = fixTypes ? new Set(fixTypes) : null;
// Delete cache file; should this be done here?
if (!cache && cacheFilePath) {
debug(`Deleting cache file at ${cacheFilePath}`);
try {
await fs.unlink(cacheFilePath);
} catch (error) {
const errorCode = error && error.code;
// Ignore errors when no such file exists or file system is read only (and cache file does not exist)
if (errorCode !== "ENOENT" && !(errorCode === "EROFS" && !existsSync(cacheFilePath))) {
throw error;
}
}
}
const filePaths = await findFiles({
patterns: normalizedPatterns,
cwd,
globInputPaths,
configs,
errorOnUnmatchedPattern
});
debug(`${filePaths.length} files found in: ${Date.now() - startTime}ms`);
/*
* Because we need to process multiple files, including reading from disk,
* it is most efficient to start by reading each file via promises so that
* they can be done in parallel. Then, we can lint the returned text. This
* ensures we are waiting the minimum amount of time in between lints.
*/
const results = await Promise.all(
filePaths.map(({ filePath, ignored }) => {
/*
* If a filename was entered that matches an ignore
* pattern, then notify the user.
*/
if (ignored) {
if (warnIgnored) {
return createIgnoreResult(filePath, cwd);
}
return void 0;
}
const config = configs.getConfig(filePath);
/*
* Sometimes a file found through a glob pattern will
* be ignored. In this case, `config` will be undefined
* and we just silently ignore the file.
*/
if (!config) {
return void 0;
}
// Skip if there is cached result.
if (lintResultCache) {
const cachedResult =
lintResultCache.getCachedLintResults(filePath, config);
if (cachedResult) {
const hadMessages =
cachedResult.messages &&
cachedResult.messages.length > 0;
if (hadMessages && fix) {
debug(`Reprocessing cached file to allow autofix: ${filePath}`);
} else {
debug(`Skipping file since it hasn't changed: ${filePath}`);
return cachedResult;
}
}
}
// set up fixer for fixTypes if necessary
let fixer = fix;
if (fix && fixTypesSet) {
// save original value of options.fix in case it's a function
const originalFix = (typeof fix === "function")
? fix : () => true;
fixer = message => shouldMessageBeFixed(message, config, fixTypesSet) && originalFix(message);
}
return fs.readFile(filePath, "utf8")
.then(text => {
// do the linting
const result = verifyText({
text,
filePath,
configs,
cwd,
fix: fixer,
allowInlineConfig,
ruleFilter,
linter
});
/*
* Store the lint result in the LintResultCache.
* NOTE: The LintResultCache will remove the file source and any
* other properties that are difficult to serialize, and will
* hydrate those properties back in on future lint runs.
*/
if (lintResultCache) {
lintResultCache.setCachedLintResults(filePath, config, result);
}
return result;
});
})
);
// Persist the cache to disk.
if (lintResultCache) {
lintResultCache.reconcile();
}
const finalResults = results.filter(result => !!result);
return processLintReport(this, {
results: finalResults
});
}

@@ -580,11 +964,18 @@

async lintText(code, options = {}) {
// Parameter validation
if (typeof code !== "string") {
throw new Error("'code' must be a string");
}
if (typeof options !== "object") {
throw new Error("'options' must be an object, null, or undefined");
}
// Options validation
const {
filePath,
warnIgnored = false,
warnIgnored,
...unknownOptions

@@ -602,12 +993,53 @@ } = options || {};

}
if (typeof warnIgnored !== "boolean") {
if (typeof warnIgnored !== "boolean" && typeof warnIgnored !== "undefined") {
throw new Error("'options.warnIgnored' must be a boolean or undefined");
}
const { cliEngine } = privateMembersMap.get(this);
// Now we can get down to linting
return processCLIEngineLintReport(
cliEngine,
cliEngine.executeOnText(code, filePath, warnIgnored)
);
const {
linter,
options: eslintOptions
} = privateMembers.get(this);
const configs = await calculateConfigArray(this, eslintOptions);
const {
allowInlineConfig,
cwd,
fix,
warnIgnored: constructorWarnIgnored,
ruleFilter
} = eslintOptions;
const results = [];
const startTime = Date.now();
const resolvedFilename = path.resolve(cwd, filePath || "__placeholder__.js");
// Clear the last used config arrays.
if (resolvedFilename && await this.isPathIgnored(resolvedFilename)) {
const shouldWarnIgnored = typeof warnIgnored === "boolean" ? warnIgnored : constructorWarnIgnored;
if (shouldWarnIgnored) {
results.push(createIgnoreResult(resolvedFilename, cwd));
}
} else {
// Do lint.
results.push(verifyText({
text: code,
filePath: resolvedFilename.endsWith("__placeholder__.js") ? "<text>" : resolvedFilename,
configs,
cwd,
fix,
allowInlineConfig,
ruleFilter,
linter
}));
}
debug(`Linting complete in: ${Date.now() - startTime}ms`);
return processLintReport(this, {
results
});
}

@@ -626,3 +1058,3 @@

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

@@ -636,9 +1068,49 @@ * a function.

const { cliEngine, options } = privateMembersMap.get(this);
const formatter = cliEngine.getFormatter(name);
// replace \ with / for Windows compatibility
const normalizedFormatName = name.replace(/\\/gu, "/");
const namespace = naming.getNamespaceFromTerm(normalizedFormatName);
// grab our options
const { cwd } = privateMembers.get(this).options;
let formatterPath;
// if there's a slash, then it's a file (TODO: this check seems dubious for scoped npm packages)
if (!namespace && normalizedFormatName.includes("/")) {
formatterPath = path.resolve(cwd, normalizedFormatName);
} else {
try {
const npmFormat = naming.normalizePackageName(normalizedFormatName, "eslint-formatter");
// TODO: This is pretty dirty...would be nice to clean up at some point.
formatterPath = ModuleResolver.resolve(npmFormat, getPlaceholderPath(cwd));
} catch {
formatterPath = path.resolve(__dirname, "../", "cli-engine", "formatters", `${normalizedFormatName}.js`);
}
}
let formatter;
try {
formatter = (await import(pathToFileURL(formatterPath))).default;
} catch (ex) {
// check for formatters that have been removed
if (removedFormatters.has(name)) {
ex.message = `The ${name} formatter is no longer part of core ESLint. Install it manually with \`npm install -D eslint-formatter-${name}\``;
} else {
ex.message = `There was a problem loading formatter: ${formatterPath}\nError: ${ex.message}`;
}
throw ex;
}
if (typeof formatter !== "function") {
throw new Error(`Formatter must be a function, but got a ${typeof formatter}.`);
throw new TypeError(`Formatter must be a function, but got a ${typeof formatter}.`);
}
const eslint = this;
return {

@@ -648,5 +1120,5 @@

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

@@ -660,8 +1132,6 @@ format(results, resultsMeta) {

...resultsMeta,
get cwd() {
return options.cwd;
},
cwd,
get rulesMeta() {
if (!rulesMeta) {
rulesMeta = createRulesMeta(cliEngine.getRules());
rulesMeta = eslint.getRulesMetaForResults(results);
}

@@ -681,3 +1151,4 @@

* @param {string} filePath The path of the file to retrieve a config object for.
* @returns {Promise<ConfigData>} A configuration object for the file.
* @returns {Promise<ConfigData|undefined>} A configuration object for the file
* or `undefined` if there is no configuration data for the object.
*/

@@ -688,8 +1159,23 @@ async calculateConfigForFile(filePath) {

}
const { cliEngine } = privateMembersMap.get(this);
const options = privateMembers.get(this).options;
const absolutePath = path.resolve(options.cwd, filePath);
const configs = await calculateConfigArray(this, options);
return cliEngine.getConfigForFile(filePath);
return configs.getConfig(absolutePath);
}
/**
* Finds the config file being used by this instance based on the options
* passed to the constructor.
* @returns {string|undefined} The path to the config file being used or
* `undefined` if no config file is being used.
*/
async findConfigFile() {
const options = privateMembers.get(this).options;
const { configFilePath } = await locateConfigFileToUse(options);
return configFilePath;
}
/**
* Checks if a given path is ignored by ESLint.

@@ -700,11 +1186,16 @@ * @param {string} filePath The path of the file to check.

async isPathIgnored(filePath) {
if (!isNonEmptyString(filePath)) {
throw new Error("'filePath' must be a non-empty string");
}
const { cliEngine } = privateMembersMap.get(this);
const config = await this.calculateConfigForFile(filePath);
return cliEngine.isPathIgnored(filePath);
return config === void 0;
}
}
/**
* Returns whether flat config should be used.
* @returns {Promise<boolean>} Whether flat config should be used.
*/
async function shouldUseFlatConfig() {
return (process.env.ESLINT_USE_FLAT_CONFIG !== "false");
}
//------------------------------------------------------------------------------

@@ -716,11 +1207,3 @@ // Public Interface

ESLint,
/**
* Get the private class members of a given ESLint instance for tests.
* @param {ESLint} instance The ESLint instance to get.
* @returns {ESLintPrivateMembers} The instance's private class members.
*/
getESLintPrivateMembers(instance) {
return privateMembersMap.get(instance);
}
shouldUseFlatConfig
};
"use strict";
const { ESLint } = require("./eslint");
const { FlatESLint } = require("./flat-eslint");
const { LegacyESLint } = require("./legacy-eslint");
module.exports = {
ESLint,
FlatESLint
LegacyESLint
};

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

const escapeRegExp = require("escape-string-regexp");
const {
Legacy: {
ConfigOps
}
} = require("@eslint/eslintrc/universal");

@@ -349,7 +354,7 @@ /**

const unusedDisableDirectivesToReport = options.directives
.filter(directive => directive.type === "disable" && !usedDisableDirectives.has(directive));
.filter(directive => directive.type === "disable" && !usedDisableDirectives.has(directive) && !options.rulesToIgnore.has(directive.ruleId));
const unusedEnableDirectivesToReport = new Set(
options.directives.filter(directive => directive.unprocessedDirective.type === "enable")
options.directives.filter(directive => directive.unprocessedDirective.type === "enable" && !options.rulesToIgnore.has(directive.ruleId))
);

@@ -415,2 +420,4 @@

* @param {"off" | "warn" | "error"} options.reportUnusedDisableDirectives If `"warn"` or `"error"`, adds additional problems for unused directives
* @param {Object} options.configuredRules The rules configuration.
* @param {Function} options.ruleFilter A predicate function to filter which rules should be executed.
* @param {boolean} options.disableFixes If true, it doesn't make `fix` properties.

@@ -420,3 +427,3 @@ * @returns {{ruleId: (string|null), line: number, column: number, suppressions?: {kind: string, justification: string}}[]}

*/
module.exports = ({ directives, disableFixes, problems, reportUnusedDisableDirectives = "off" }) => {
module.exports = ({ directives, disableFixes, problems, configuredRules, ruleFilter, reportUnusedDisableDirectives = "off" }) => {
const blockDirectives = directives

@@ -450,2 +457,21 @@ .filter(directive => directive.type === "disable" || directive.type === "enable")

// This determines a list of rules that are not being run by the given ruleFilter, if present.
const rulesToIgnore = configuredRules && ruleFilter
? new Set(Object.keys(configuredRules).filter(ruleId => {
const severity = ConfigOps.getRuleSeverity(configuredRules[ruleId]);
// Ignore for disabled rules.
if (severity === 0) {
return false;
}
return !ruleFilter({ severity, ruleId });
}))
: new Set();
// If no ruleId is supplied that means this directive is applied to all rules, so we can't determine if it's unused if any rules are filtered out.
if (rulesToIgnore.size > 0) {
rulesToIgnore.add(null);
}
const blockDirectivesResult = applyDirectives({

@@ -455,3 +481,4 @@ problems,

disableFixes,
reportUnusedDisableDirectives
reportUnusedDisableDirectives,
rulesToIgnore
});

@@ -462,3 +489,4 @@ const lineDirectivesResult = applyDirectives({

disableFixes,
reportUnusedDisableDirectives
reportUnusedDisableDirectives,
rulesToIgnore
});

@@ -465,0 +493,0 @@

@@ -43,3 +43,3 @@ /**

* Parses a list of "name:string_value" or/and "name" options divided by comma or
* whitespace. Used for "global" and "exported" comments.
* whitespace. Used for "global" comments.
* @param {string} string The string to parse.

@@ -46,0 +46,0 @@ * @param {Comment} comment The comment node which has the string.

@@ -16,14 +16,6 @@ /**

//------------------------------------------------------------------------------
// Helpers
// Typedefs
//------------------------------------------------------------------------------
/**
* Normalizes a rule module to the new-style API
* @param {(Function|{create: Function})} rule A rule object, which can either be a function
* ("old-style") or an object with a `create` method ("new-style")
* @returns {{create: Function}} A new-style rule.
*/
function normalizeRule(rule) {
return typeof rule === "function" ? Object.assign({ create: rule }, rule) : rule;
}
/** @typedef {import("../shared/types").Rule} Rule */

@@ -45,7 +37,7 @@ //------------------------------------------------------------------------------

* @param {string} ruleId Rule id (file name).
* @param {Function} ruleModule Rule handler.
* @param {Rule} rule Rule object.
* @returns {void}
*/
define(ruleId, ruleModule) {
this._rules[ruleId] = normalizeRule(ruleModule);
define(ruleId, rule) {
this._rules[ruleId] = rule;
}

@@ -56,4 +48,3 @@

* @param {string} ruleId Rule id (file name).
* @returns {{create: Function, schema: JsonSchema[]}}
* A rule. This is normalized to always have the new-style shape with a `create` method.
* @returns {Rule} Rule object.
*/

@@ -60,0 +51,0 @@ get(ruleId) {

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

* @property {boolean} warnIgnored Show warnings when the file list includes ignored files
* @property {boolean} [passOnNoPatterns=false] When set to true, missing patterns cause
* the linting operation to short circuit and not report any failures.
* @property {string[]} _ Positional filenames or patterns

@@ -172,3 +174,3 @@ */

description: usingFlatConfig
? "Use this configuration instead of eslint.config.js"
? "Use this configuration instead of eslint.config.js, eslint.config.mjs, or eslint.config.cjs"
: "Use this configuration, overriding .eslintrc.* config options if present"

@@ -376,2 +378,8 @@ },

{
option: "pass-on-no-patterns",
type: "Boolean",
default: false,
description: "Exit with exit code 0 in case no file patterns are passed"
},
{
option: "debug",

@@ -378,0 +386,0 @@ type: "Boolean",

/**
* @fileoverview Mocha test wrapper
* @fileoverview Mocha/Jest test wrapper
* @author Ilya Volodin

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

/*
* This is a wrapper around mocha to allow for DRY unittests for eslint
* Format:
* RuleTester.run("{ruleName}", {
* valid: [
* "{code}",
* { code: "{code}", options: {options}, globals: {globals}, parser: "{parser}", settings: {settings} }
* ],
* invalid: [
* { code: "{code}", errors: {numErrors} },
* { code: "{code}", errors: ["{errorMessage}"] },
* { code: "{code}", options: {options}, globals: {globals}, parser: "{parser}", settings: {settings}, errors: [{ message: "{errorMessage}", type: "{errorNodeType}"}] }
* ]
* });
*
* Variables:
* {code} - String that represents the code to be tested
* {options} - Arguments that are passed to the configurable rules.
* {globals} - An object representing a list of variables that are
* registered as globals
* {parser} - String representing the parser to use
* {settings} - An object representing global settings for all rules
* {numErrors} - If failing case doesn't need to check error message,
* this integer will specify how many errors should be
* received
* {errorMessage} - Message that is returned by the rule on failure
* {errorNodeType} - AST node type that is returned by they rule as
* a cause of the failure.
*/
//------------------------------------------------------------------------------

@@ -46,17 +16,17 @@ // Requirements

assert = require("assert"),
path = require("path"),
util = require("util"),
merge = require("lodash.merge"),
equal = require("fast-deep-equal"),
Traverser = require("../../lib/shared/traverser"),
{ getRuleOptionsSchema, validate } = require("../shared/config-validator"),
Traverser = require("../shared/traverser"),
{ getRuleOptionsSchema } = require("../config/flat-config-helpers"),
{ Linter, SourceCodeFixer, interpolate } = require("../linter"),
CodePath = require("../linter/code-path-analysis/code-path");
const { FlatConfigArray } = require("../config/flat-config-array");
const { defaultConfig } = require("../config/default-config");
const ajv = require("../shared/ajv")({ strictDefaults: true });
const espreePath = require.resolve("espree");
const parserSymbol = Symbol.for("eslint.RuleTester.parser");
const { SourceCode } = require("../source-code");
const { ConfigArraySymbol } = require("@humanwhocodes/config-array");

@@ -68,2 +38,3 @@ //------------------------------------------------------------------------------

/** @typedef {import("../shared/types").Parser} Parser */
/** @typedef {import("../shared/types").LanguageOptions} LanguageOptions */
/** @typedef {import("../shared/types").Rule} Rule */

@@ -78,8 +49,5 @@

* @property {any[]} [options] Options for the test case.
* @property {LanguageOptions} [languageOptions] The language options to use in the test case.
* @property {{ [name: string]: any }} [settings] Settings for the test case.
* @property {string} [filename] The fake filename for the test case. Useful for rules that make assertion about filenames.
* @property {string} [parser] The absolute path for the parser.
* @property {{ [name: string]: any }} [parserOptions] Options for the parser.
* @property {{ [name: string]: "readonly" | "writable" | "off" }} [globals] The additional global variables.
* @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.

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

* @property {string} [filename] The fake filename for the test case. Useful for rules that make assertion about filenames.
* @property {string} [parser] The absolute path for the parser.
* @property {{ [name: string]: any }} [parserOptions] Options for the parser.
* @property {{ [name: string]: "readonly" | "writable" | "off" }} [globals] The additional global variables.
* @property {{ [name: string]: boolean }} [env] Environments for the test case.
* @property {LanguageOptions} [languageOptions] The language options to use in the test case.
* @property {boolean} [only] Run only this test case or the subset of test cases with this property.

@@ -128,5 +93,10 @@ */

const testerDefaultConfig = { rules: {} };
let defaultConfig = { rules: {} };
/*
* RuleTester uses this config as its default. This can be overwritten via
* setDefaultConfig().
*/
let sharedDefaultConfig = { rules: {} };
/*
* List every parameters possible on a test case that are not related to eslint

@@ -178,33 +148,7 @@ * configuration

/** @type {Map<string,WeakSet>} */
const forbiddenMethodCalls = new Map(forbiddenMethods.map(methodName => ([methodName, new WeakSet()])));
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"
};
/**

@@ -341,72 +285,2 @@ * Clones a given value deeply.

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

@@ -427,17 +301,37 @@ * @param {string} ruleName Name of the rule.

/**
* Emit a deprecation warning if `context.parserServices` is used.
* @param {string} ruleName Name of the rule.
* @returns {void}
* Function to replace forbidden `SourceCode` methods. Allows just one call per method.
* @param {string} methodName The name of the method to forbid.
* @param {Function} prototype The prototype with the original method to call.
* @returns {Function} The function that throws the error.
*/
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"
function throwForbiddenMethodError(methodName, prototype) {
const original = prototype[methodName];
return function(...args) {
const called = forbiddenMethodCalls.get(methodName);
/* eslint-disable no-invalid-this -- needed to operate as a method. */
if (!called.has(this)) {
called.add(this);
return original.apply(this, args);
}
/* eslint-enable no-invalid-this -- not needed past this point */
throw new Error(
`\`SourceCode#${methodName}()\` cannot be called inside a rule.`
);
}
};
}
const metaSchemaDescription = `
\t- If the rule has options, set \`meta.schema\` to an array or non-empty object to enable options validation.
\t- If the rule doesn't have options, omit \`meta.schema\` to enforce that no options can be passed to the rule.
\t- You can also set \`meta.schema\` to \`false\` to opt-out of options validation (not recommended).
\thttps://eslint.org/docs/latest/extend/custom-rules#options-schemas
`;
//------------------------------------------------------------------------------

@@ -491,3 +385,3 @@ // Public Interface

*/
constructor(testerConfig) {
constructor(testerConfig = {}) {

@@ -499,15 +393,9 @@ /**

*/
this.testerConfig = merge(
{},
defaultConfig,
this.testerConfig = [
sharedDefaultConfig,
testerConfig,
{ rules: { "rule-tester/validate-ast": "error" } }
);
];
/**
* Rule definitions to define before tests.
* @type {Object}
*/
this.rules = {};
this.linter = new Linter();
this.linter = new Linter({ configType: "flat" });
}

@@ -525,6 +413,6 @@

}
defaultConfig = config;
sharedDefaultConfig = config;
// Make sure the rules object exists since it is assumed to exist later
defaultConfig.rules = defaultConfig.rules || {};
sharedDefaultConfig.rules = sharedDefaultConfig.rules || {};
}

@@ -537,3 +425,3 @@

static getDefaultConfig() {
return defaultConfig;
return sharedDefaultConfig;
}

@@ -547,3 +435,7 @@

static resetDefaultConfig() {
defaultConfig = merge({}, testerDefaultConfig);
sharedDefaultConfig = {
rules: {
...testerDefaultConfig.rules
}
};
}

@@ -619,14 +511,2 @@

/**
* Define a rule for one particular run of tests.
* @param {string} name The name of the rule to define.
* @param {Function | Rule} rule The rule definition.
* @returns {void}
*/
defineRule(name, rule) {
if (typeof rule === "function") {
emitLegacyRuleAPIWarning(name);
}
this.rules[name] = rule;
}

@@ -636,3 +516,3 @@ /**

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

@@ -642,4 +522,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.
* @throws {TypeError|Error} If `rule` is not an object with a `create` method,
* or if non-object `test`, or if a required scenario of the given type is missing.
* @returns {void}

@@ -652,4 +532,9 @@ */

scenarioErrors = [],
linter = this.linter;
linter = this.linter,
ruleId = `rule-to-test/${ruleName}`;
if (!rule || typeof rule !== "object" || typeof rule.create !== "function") {
throw new TypeError("Rule must be an object with a `create` method");
}
if (!test || typeof test !== "object") {

@@ -671,51 +556,52 @@ throw new TypeError(`Test Scenarios for rule ${ruleName} : Could not find test scenario object`);

if (typeof rule === "function") {
emitLegacyRuleAPIWarning(ruleName);
}
const baseConfig = [
{ files: ["**"] }, // Make sure the default config matches for all files
{
plugins: {
linter.defineRule(ruleName, Object.assign({}, rule, {
// copy root plugin over
"@": {
// Create a wrapper rule that freezes the `context` properties.
create(context) {
freezeDeeply(context.options);
freezeDeeply(context.settings);
freezeDeeply(context.parserOptions);
/*
* Parsers are wrapped to detect more errors, so this needs
* to be a new object for each call to run(), otherwise the
* parsers will be wrapped multiple times.
*/
parsers: {
...defaultConfig[0].plugins["@"].parsers
},
// wrap all deprecated methods
const newContext = Object.create(
context,
Object.fromEntries(Object.keys(DEPRECATED_SOURCECODE_PASSTHROUGHS).map(methodName => [
methodName,
{
value(...args) {
/*
* The rules key on the default plugin is a proxy to lazy-load
* just the rules that are needed. So, don't create a new object
* here, just use the default one to keep that performance
* enhancement.
*/
rules: defaultConfig[0].plugins["@"].rules
},
"rule-to-test": {
rules: {
[ruleName]: Object.assign({}, rule, {
// emit deprecation warning
emitDeprecatedContextMethodWarning(ruleName, methodName);
// Create a wrapper rule that freezes the `context` properties.
create(context) {
freezeDeeply(context.options);
freezeDeeply(context.settings);
freezeDeeply(context.parserOptions);
// call the original method
return context[methodName].call(this, ...args);
},
enumerable: true
// freezeDeeply(context.languageOptions);
return rule.create(context);
}
})
}
]))
);
// emit warning about context.parserServices
const parserServices = context.parserServices;
Object.defineProperty(newContext, "parserServices", {
get() {
emitParserServicesWarning(ruleName);
return parserServices;
}
});
},
languageOptions: {
...defaultConfig[0].languageOptions
}
},
...defaultConfig.slice(1)
];
Object.freeze(newContext);
return (typeof rule === "function" ? rule : rule.create)(newContext);
}
}));
linter.defineRules(this.rules);
/**

@@ -729,5 +615,23 @@ * Run the rule for the given item

function runRuleForItem(item) {
let config = merge({}, testerConfig),
code, filename, output, beforeAST, afterAST;
const configs = new FlatConfigArray(testerConfig, { baseConfig });
/*
* Modify the returned config so that the parser is wrapped to catch
* access of the start/end properties. This method is called just
* once per code snippet being tested, so each test case gets a clean
* parser.
*/
configs[ConfigArraySymbol.finalizeConfig] = function(...args) {
// can't do super here :(
const proto = Object.getPrototypeOf(this);
const calculatedConfig = proto[ConfigArraySymbol.finalizeConfig].apply(this, args);
// wrap the parser to catch start/end property access
calculatedConfig.languageOptions.parser = wrapParser(calculatedConfig.languageOptions.parser);
return calculatedConfig;
};
let code, filename, output, beforeAST, afterAST;
if (typeof item === "string") {

@@ -748,2 +652,13 @@ code = item;

// wrap any parsers
if (itemConfig.languageOptions && itemConfig.languageOptions.parser) {
const parser = itemConfig.languageOptions.parser;
if (parser && typeof parser !== "object") {
throw new Error("Parser must be an object with a parse() or parseForESLint() method.");
}
}
/*

@@ -753,6 +668,3 @@ * Create the config object from the tester config and this item

*/
config = merge(
config,
itemConfig
);
configs.push(itemConfig);
}

@@ -764,19 +676,40 @@

let ruleConfig = 1;
if (hasOwnProperty(item, "options")) {
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);
ruleConfig = [1, ...item.options];
}
configs.push({
rules: {
[ruleId]: ruleConfig
}
config.rules[ruleName] = [1].concat(item.options);
} else {
config.rules[ruleName] = 1;
});
let schema;
try {
schema = getRuleOptionsSchema(rule);
} catch (err) {
err.message += metaSchemaDescription;
throw err;
}
const schema = getRuleOptionsSchema(rule);
/*
* Check and throw an error if the schema is an empty object (`schema:{}`), because such schema
* doesn't validate or enforce anything and is therefore considered a possible error. If the intent
* was to skip options validation, `schema:false` should be set instead (explicit opt-out).
*
* For this purpose, a schema object is considered empty if it doesn't have any own enumerable string-keyed
* properties. While `ajv.compile()` does use enumerable properties from the prototype chain as well,
* it caches compiled schemas by serializing only own enumerable properties, so it's generally not a good idea
* to use inherited properties in schemas because schemas that differ only in inherited properties would end up
* having the same cache entry that would be correct for only one of them.
*
* At this point, `schema` can only be an object or `null`.
*/
if (schema && Object.keys(schema).length === 0) {
throw new Error(`\`schema: {}\` is a no-op${metaSchemaDescription}`);
}

@@ -788,23 +721,23 @@ /*

*/
linter.defineRule("rule-tester/validate-ast", {
create() {
return {
Program(node) {
beforeAST = cloneDeeplyExcludesParent(node);
},
"Program:exit"(node) {
afterAST = node;
configs.push({
plugins: {
"rule-tester": {
rules: {
"validate-ast": {
create() {
return {
Program(node) {
beforeAST = cloneDeeplyExcludesParent(node);
},
"Program:exit"(node) {
afterAST = node;
}
};
}
}
}
};
}
}
});
if (typeof config.parser === "string") {
assert(path.isAbsolute(config.parser), "Parsers provided as strings to RuleTester must be absolute paths");
} else {
config.parser = espreePath;
}
linter.defineParser(config.parser, wrapParser(require(config.parser)));
if (schema) {

@@ -836,6 +769,13 @@ ajv.validateSchema(schema);

validate(config, "rule-tester", id => (id === ruleName ? rule : null));
// check for validation errors
try {
configs.normalizeSync();
configs.getConfig("test.js");
} catch (error) {
error.message = `ESLint configuration in rule-tester is invalid: ${error.message}`;
throw error;
}
// Verify the code.
const { getComments, applyLanguageOptions, applyInlineConfig, finalize } = SourceCode.prototype;
const { applyLanguageOptions, applyInlineConfig, finalize } = SourceCode.prototype;
const originalCurrentSegments = Object.getOwnPropertyDescriptor(CodePath.prototype, "currentSegments");

@@ -845,3 +785,2 @@ let messages;

try {
SourceCode.prototype.getComments = getCommentsDeprecation;
Object.defineProperty(CodePath.prototype, "currentSegments", {

@@ -855,8 +794,7 @@ get() {

forbiddenMethods.forEach(methodName => {
SourceCode.prototype[methodName] = throwForbiddenMethodError(methodName);
SourceCode.prototype[methodName] = throwForbiddenMethodError(methodName, SourceCode.prototype);
});
messages = linter.verify(code, config, filename);
messages = linter.verify(code, configs, filename);
} finally {
SourceCode.prototype.getComments = getComments;
Object.defineProperty(CodePath.prototype, "currentSegments", originalCurrentSegments);

@@ -868,2 +806,3 @@ SourceCode.prototype.applyInlineConfig = applyInlineConfig;

const fatalErrorMessage = messages.find(m => m.fatal);

@@ -876,3 +815,3 @@

output = SourceCodeFixer.applyFixes(code, messages).output;
const errorMessageInFix = linter.verify(output, config, filename).find(m => m.fatal);
const errorMessageInFix = linter.verify(output, configs, filename).find(m => m.fatal);

@@ -893,3 +832,5 @@ assert(!errorMessageInFix, [

beforeAST,
afterAST: cloneDeeplyExcludesParent(afterAST)
afterAST: cloneDeeplyExcludesParent(afterAST),
configs,
filename
};

@@ -983,2 +924,18 @@ }

for (const message of messages) {
if (hasOwnProperty(message, "suggestions")) {
/** @type {Map<string, number>} */
const seenMessageIndices = new Map();
for (let i = 0; i < message.suggestions.length; i += 1) {
const suggestionMessage = message.suggestions[i].desc;
const previous = seenMessageIndices.get(suggestionMessage);
assert.ok(!seenMessageIndices.has(suggestionMessage), `Suggestion message '${suggestionMessage}' reported from suggestion ${i} was previously reported by suggestion ${previous}. Suggestion messages should be unique within an error.`);
seenMessageIndices.set(suggestionMessage, i);
}
}
}
if (typeof item.errors === "number") {

@@ -1006,3 +963,3 @@

const hasMessageOfThisRule = messages.some(m => m.ruleId === ruleName);
const hasMessageOfThisRule = messages.some(m => m.ruleId === ruleId);

@@ -1166,2 +1123,13 @@ for (let i = 0, l = item.errors.length; i < l; i++) {

// Verify if suggestion fix makes a syntax error or not.
const errorMessageInSuggestion =
linter.verify(codeWithAppliedSuggestion, result.configs, result.filename).find(m => m.fatal);
assert(!errorMessageInSuggestion, [
"A fatal parsing error occurred in suggestion fix.",
`Error: ${errorMessageInSuggestion && errorMessageInSuggestion.message}`,
"Suggestion output:",
codeWithAppliedSuggestion
].join("\n"));
assert.strictEqual(codeWithAppliedSuggestion, expectedSuggestion.output, `Expected the applied suggestion fix to match the test suggestion output for suggestion at index: ${index} on error with message: "${message.message}"`);

@@ -1168,0 +1136,0 @@ }

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

"require-await": () => require("./require-await"),
"require-jsdoc": () => require("./require-jsdoc"),
"require-unicode-regexp": () => require("./require-unicode-regexp"),

@@ -300,3 +299,2 @@ "require-yield": () => require("./require-yield"),

"use-isnan": () => require("./use-isnan"),
"valid-jsdoc": () => require("./valid-jsdoc"),
"valid-typeof": () => require("./valid-typeof"),

@@ -303,0 +301,0 @@ "vars-on-top": () => require("./vars-on-top"),

@@ -443,3 +443,3 @@ /**

description: "Disallow expressions where the operation doesn't affect the value",
recommended: false,
recommended: true,
url: "https://eslint.org/docs/latest/rules/no-constant-binary-expression"

@@ -446,0 +446,0 @@ },

@@ -23,3 +23,3 @@ /**

schema: {},
schema: [],

@@ -26,0 +26,0 @@ fixable: null,

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

description: "Disallow empty static blocks",
recommended: false,
recommended: true,
url: "https://eslint.org/docs/latest/rules/no-empty-static-block"

@@ -21,0 +21,0 @@ },

@@ -29,3 +29,3 @@ /**

description: "Disallow unnecessary semicolons",
recommended: true,
recommended: false,
url: "https://eslint.org/docs/latest/rules/no-extra-semi"

@@ -32,0 +32,0 @@ },

@@ -15,3 +15,3 @@ /**

const INDEX_OF_PATTERN = /^(?:i|lastI)ndexOf$/u;
const ALLOWABLE_OPERATORS = ["~", "!!", "+", "*"];
const ALLOWABLE_OPERATORS = ["~", "!!", "+", "- -", "-", "*"];

@@ -304,2 +304,10 @@ /**

}
// -(-foo)
operatorAllowed = options.allow.includes("- -");
if (!operatorAllowed && options.number && node.operator === "-" && node.argument.type === "UnaryExpression" && node.argument.operator === "-" && !isNumeric(node.argument.argument)) {
const recommendation = `Number(${sourceCode.getText(node.argument.argument)})`;
report(node, recommendation, false);
}
},

@@ -322,2 +330,10 @@

// foo - 0
operatorAllowed = options.allow.includes("-");
if (!operatorAllowed && options.number && node.operator === "-" && node.right.type === "Literal" && node.right.value === 0 && !isNumeric(node.left)) {
const recommendation = `Number(${sourceCode.getText(node.left)})`;
report(node, recommendation, true);
}
// "" + foo

@@ -324,0 +340,0 @@ operatorAllowed = options.allow.includes("+");

@@ -52,3 +52,3 @@ /**

description: "Disallow variable or `function` declarations in nested blocks",
recommended: true,
recommended: false,
url: "https://eslint.org/docs/latest/rules/no-inner-declarations"

@@ -55,0 +55,0 @@ },

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

if (temp) {
allowedFlags = new RegExp(`[${temp}]`, "giu");
allowedFlags = new RegExp(`[${temp}]`, "gu");
}

@@ -61,0 +61,0 @@ }

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

description: "Disallow mixed spaces and tabs for indentation",
recommended: true,
recommended: false,
url: "https://eslint.org/docs/latest/rules/no-mixed-spaces-and-tabs"

@@ -24,0 +24,0 @@ },

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

description: "Disallow `new` operators with global non-constructor functions",
recommended: false,
recommended: true,
url: "https://eslint.org/docs/latest/rules/no-new-native-nonconstructor"

@@ -28,0 +28,0 @@ },

/**
* @fileoverview Rule to disallow use of the new operator with the `Symbol` object
* @author Alberto Rodríguez
* @deprecated in ESLint v9.0.0
*/

@@ -19,6 +20,12 @@

description: "Disallow `new` operators with the `Symbol` object",
recommended: true,
recommended: false,
url: "https://eslint.org/docs/latest/rules/no-new-symbol"
},
deprecated: true,
replacedBy: [
"no-new-native-nonconstructor"
],
schema: [],

@@ -25,0 +32,0 @@

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

schema: [{
type: "object",
properties: {

@@ -40,0 +41,0 @@ allowInParentheses: {

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

description: "Disallow unused private class members",
recommended: false,
recommended: true,
url: "https://eslint.org/docs/latest/rules/no-unused-private-class-members"

@@ -22,0 +22,0 @@ },

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

//------------------------------------------------------------------------------
// Typedefs
//------------------------------------------------------------------------------
/** @typedef {import("../shared/types").Rule} Rule */
//------------------------------------------------------------------------------
// Private Members
//------------------------------------------------------------------------------
// JSON schema that disallows passing any options
const noOptionsSchema = Object.freeze({
type: "array",
minItems: 0,
maxItems: 0
});
//------------------------------------------------------------------------------
// Requirements

@@ -53,4 +70,6 @@ //------------------------------------------------------------------------------

* Gets a complete options schema for a rule.
* @param {{create: Function, schema: (Array|null)}} rule A new-style rule object
* @returns {Object} JSON Schema for the rule's options.
* @param {Rule} rule A rule object
* @throws {TypeError} If `meta.schema` is specified but is not an array, object or `false`.
* @returns {Object|null} JSON Schema for the rule's options.
* `null` if rule wasn't passed or its `meta.schema` is `false`.
*/

@@ -62,5 +81,22 @@ function getRuleOptionsSchema(rule) {

const schema = rule.schema || rule.meta && rule.meta.schema;
if (!rule.meta) {
return { ...noOptionsSchema }; // default if `meta.schema` is not specified
}
// Given a tuple of schemas, insert warning level at the beginning
const schema = rule.meta.schema;
if (typeof schema === "undefined") {
return { ...noOptionsSchema }; // default if `meta.schema` is not specified
}
// `schema:false` is an allowed explicit opt-out of options validation for the rule
if (schema === false) {
return null;
}
if (typeof schema !== "object" || schema === null) {
throw new TypeError("Rule's `meta.schema` must be an array or object");
}
// ESLint-specific array form needs to be converted into a valid JSON Schema definition
if (Array.isArray(schema)) {

@@ -75,12 +111,9 @@ if (schema.length) {

}
return {
type: "array",
minItems: 0,
maxItems: 0
};
// `schema:[]` is an explicit way to specify that the rule does not accept any options
return { ...noOptionsSchema };
}
// Given a full schema, leave it alone
return schema || null;
// `schema:<object>` is assumed to be a valid JSON Schema definition
return schema;
}

@@ -87,0 +120,0 @@

@@ -171,3 +171,3 @@ /**

* @property {Record<string, Processor>} [processors] The definition of plugin processors.
* @property {Record<string, Function | Rule>} [rules] The definition of plugin rules.
* @property {Record<string, Rule>} [rules] The definition of plugin rules.
*/

@@ -174,0 +174,0 @@

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

// Cache for comments found using getComments().
this._commentCache = new WeakMap();
// don't allow further modification of this object

@@ -477,77 +474,2 @@ Object.freeze(this);

/**
* Gets all comments for the given node.
* @param {ASTNode} node The AST node to get the comments for.
* @returns {Object} An object containing a leading and trailing array
* of comments indexed by their position.
* @public
* @deprecated replaced by getCommentsBefore(), getCommentsAfter(), and getCommentsInside().
*/
getComments(node) {
if (this._commentCache.has(node)) {
return this._commentCache.get(node);
}
const comments = {
leading: [],
trailing: []
};
/*
* Return all comments as leading comments of the Program node when
* there is no executable code.
*/
if (node.type === "Program") {
if (node.body.length === 0) {
comments.leading = node.comments;
}
} else {
/*
* Return comments as trailing comments of nodes that only contain
* comments (to mimic the comment attachment behavior present in Espree).
*/
if ((node.type === "BlockStatement" || node.type === "ClassBody") && node.body.length === 0 ||
node.type === "ObjectExpression" && node.properties.length === 0 ||
node.type === "ArrayExpression" && node.elements.length === 0 ||
node.type === "SwitchStatement" && node.cases.length === 0
) {
comments.trailing = this.getTokens(node, {
includeComments: true,
filter: isCommentToken
});
}
/*
* Iterate over tokens before and after node and collect comment tokens.
* Do not include comments that exist outside of the parent node
* to avoid duplication.
*/
let currentToken = this.getTokenBefore(node, { includeComments: true });
while (currentToken && isCommentToken(currentToken)) {
if (node.parent && node.parent.type !== "Program" && (currentToken.start < node.parent.start)) {
break;
}
comments.leading.push(currentToken);
currentToken = this.getTokenBefore(currentToken, { includeComments: true });
}
comments.leading.reverse();
currentToken = this.getTokenAfter(node, { includeComments: true });
while (currentToken && isCommentToken(currentToken)) {
if (node.parent && node.parent.type !== "Program" && (currentToken.end > node.parent.end)) {
break;
}
comments.trailing.push(currentToken);
currentToken = this.getTokenAfter(currentToken, { includeComments: true });
}
}
this._commentCache.set(node, comments);
return comments;
}
/**
* Retrieves the JSDoc comment for a given node.

@@ -968,3 +890,3 @@ * @param {ASTNode} node The AST node to get the comment for.

case "exported":
Object.assign(exportedVariables, commentParser.parseStringConfig(directiveValue, comment));
Object.assign(exportedVariables, commentParser.parseListConfig(directiveValue, comment));
break;

@@ -971,0 +893,0 @@

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

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

@@ -28,5 +27,4 @@ //-----------------------------------------------------------------------------

shouldUseFlatConfig,
FlatRuleTester,
FileEnumerator,
LegacyESLint: ESLint
LegacyESLint
};
{
"name": "eslint",
"version": "8.56.0",
"version": "9.0.0-alpha.0",
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",

@@ -20,2 +20,3 @@ "description": "An AST-based pattern checker for JavaScript.",

"build:readme": "node tools/update-readme.js",
"build:rules-index": "node Makefile.js generateRuleIndexPage",
"lint": "node Makefile.js lint",

@@ -68,8 +69,7 @@ "lint:docs:js": "node Makefile.js lintDocsJS",

"@eslint-community/regexpp": "^4.6.1",
"@eslint/eslintrc": "^2.1.4",
"@eslint/js": "8.56.0",
"@eslint/eslintrc": "^3.0.0",
"@eslint/js": "9.0.0-alpha.0",
"@humanwhocodes/config-array": "^0.11.13",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
"@ungap/structured-clone": "^1.2.0",
"ajv": "^6.12.4",

@@ -79,3 +79,2 @@ "chalk": "^4.0.0",

"debug": "^4.3.2",
"doctrine": "^3.0.0",
"escape-string-regexp": "^4.0.0",

@@ -97,3 +96,2 @@ "eslint-scope": "^7.2.2",

"is-path-inside": "^3.0.3",
"js-yaml": "^4.1.0",
"json-stable-stringify-without-jsonify": "^1.0.1",

@@ -128,4 +126,4 @@ "levn": "^0.4.1",

"eslint-plugin-internal-rules": "file:tools/internal-rules",
"eslint-plugin-jsdoc": "^46.2.5",
"eslint-plugin-n": "^16.4.0",
"eslint-plugin-jsdoc": "^46.9.0",
"eslint-plugin-n": "^16.6.0",
"eslint-plugin-unicorn": "^49.0.0",

@@ -140,2 +138,3 @@ "eslint-release": "^3.2.0",

"gray-matter": "^4.0.3",
"js-yaml": "^4.1.0",
"lint-staged": "^11.0.0",

@@ -146,3 +145,3 @@ "load-perf": "^0.2.0",

"markdownlint": "^0.32.0",
"markdownlint-cli": "^0.37.0",
"markdownlint-cli": "^0.38.0",
"marked": "^4.0.8",

@@ -157,3 +156,2 @@ "memfs": "^3.0.1",

"mocha": "^8.3.2",
"mocha-junit-reporter": "^2.0.0",
"node-polyfill-webpack-plugin": "^1.0.3",

@@ -185,4 +183,4 @@ "npm-license": "^0.3.3",

"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
}

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

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.)
Prerequisites: [Node.js](https://nodejs.org/) (`^18.18.0`, `^20.9.0`, or `>=21.1.0`) built with SSL support. (If you are using an official Node.js distribution, SSL is always built in.)

@@ -297,3 +297,3 @@ You can install and configure ESLint using this command:

<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://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="64"></a> <a href="https://www.workleap.com"><img src="https://avatars.githubusercontent.com/u/53535748?u=d1e55d7661d724bf2281c1bfd33cb8f99fe2465f&v=4" alt="Workleap" height="64"></a></p><h3>Bronze Sponsors</h3>
<p><a href="https://www.jetbrains.com/"><img src="https://images.opencollective.com/jetbrains/eb04ddc/logo.png" alt="JetBrains" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="64"></a> <a href="https://www.workleap.com"><img src="https://avatars.githubusercontent.com/u/53535748?u=d1e55d7661d724bf2281c1bfd33cb8f99fe2465f&v=4" alt="Workleap" 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://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://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104?v=4" alt="Nx" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a></p>

@@ -300,0 +300,0 @@ <!--sponsorsend-->

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

SocketSocket SOC 2 Logo

Product

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

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc