Update checked-in dependencies

This commit is contained in:
github-actions[bot] 2023-07-13 09:09:17 +00:00
parent 4fad06f438
commit 40a500c743
4168 changed files with 298222 additions and 374905 deletions

38
node_modules/eslint/README.md generated vendored
View file

@ -10,14 +10,14 @@
# ESLint
[Website](https://eslint.org) |
[Configuring](https://eslint.org/docs/latest/use/configure) |
[Configure ESLint](https://eslint.org/docs/latest/use/configure) |
[Rules](https://eslint.org/docs/rules/) |
[Contributing](https://eslint.org/docs/latest/contribute) |
[Reporting Bugs](https://eslint.org/docs/latest/contribute/report-bugs) |
[Contribute to ESLint](https://eslint.org/docs/latest/contribute) |
[Report Bugs](https://eslint.org/docs/latest/contribute/report-bugs) |
[Code of Conduct](https://eslint.org/conduct) |
[Twitter](https://twitter.com/geteslint) |
[Mailing List](https://groups.google.com/group/eslint) |
[Chat Room](https://eslint.org/chat)
[Discord](https://eslint.org/chat) |
[Mastodon](https://fosstodon.org/@eslint)
ESLint is a tool for identifying and reporting on patterns found in ECMAScript/JavaScript code. In many ways, it is similar to JSLint and JSHint with a few exceptions:
@ -59,7 +59,7 @@ After that, you can run ESLint on any file or directory like this:
## Configuration
After running `npm init @eslint/config`, you'll have a `.eslintrc` file in your directory. In it, you'll see some rules configured like this:
After running `npm init @eslint/config`, you'll have an `.eslintrc` file in your directory. In it, you'll see some rules configured like this:
```json
{
@ -117,7 +117,7 @@ Yes, ESLint natively supports parsing JSX syntax (this must be enabled in [confi
### What ECMAScript versions does ESLint support?
ESLint has full support for ECMAScript 3, 5 (default), 2015, 2016, 2017, 2018, 2019, 2020, 2021 and 2022. You can set your desired ECMAScript syntax (and other settings, like global variables or your target environments) through [configuration](https://eslint.org/docs/latest/use/configure).
ESLint has full support for ECMAScript 3, 5 (default), 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, and 2023. You can set your desired ECMAScript syntax (and other settings, like global variables or your target environments) through [configuration](https://eslint.org/docs/latest/use/configure).
### What about experimental features?
@ -129,7 +129,7 @@ Once a language feature has been adopted into the ECMAScript standard (stage 4 a
### Where to ask for help?
Join our [Mailing List](https://groups.google.com/group/eslint) or [Chatroom](https://eslint.org/chat).
Open a [discussion](https://github.com/eslint/eslint/discussions) or stop by our [Discord server](https://eslint.org/chat).
### Why doesn't ESLint lock dependency versions?
@ -213,11 +213,6 @@ The people who manage releases, review feature requests, and meet regularly to e
Nicholas C. Zakas
</a>
</td><td align="center" valign="top" width="11%">
<a href="https://github.com/btmills">
<img src="https://github.com/btmills.png?s=75" width="75" height="75"><br />
Brandon Mills
</a>
</td><td align="center" valign="top" width="11%">
<a href="https://github.com/mdjermanovic">
<img src="https://github.com/mdjermanovic.png?s=75" width="75" height="75"><br />
Milos Djermanovic
@ -250,14 +245,9 @@ The people who review and fix bugs and help triage issues.
Bryan Mishkin
</a>
</td><td align="center" valign="top" width="11%">
<a href="https://github.com/SaraSoueidan">
<img src="https://github.com/SaraSoueidan.png?s=75" width="75" height="75"><br />
Sara Soueidan
</a>
</td><td align="center" valign="top" width="11%">
<a href="https://github.com/yeonjuan">
<img src="https://github.com/yeonjuan.png?s=75" width="75" height="75"><br />
YeonJuan
<a href="https://github.com/fasttime">
<img src="https://github.com/fasttime.png?s=75" width="75" height="75"><br />
Francesco Trotta
</a>
</td></tr></tbody></table>
@ -292,9 +282,9 @@ The following companies, organizations, and individuals support ESLint's ongoing
<!--sponsorsstart-->
<h3>Platinum Sponsors</h3>
<p><a href="#"><img src="https://images.opencollective.com/2021-frameworks-fund/logo.png" alt="Chrome Frameworks Fund" height="undefined"></a> <a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="undefined"></a></p><h3>Gold Sponsors</h3>
<p><a href="https://ridicorp.com/career/"><img src="https://images.opencollective.com/ridi-corporation/175dcf3/logo.png" alt="RIDI" height="96"></a> <a href="https://engineering.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a></p><h3>Silver Sponsors</h3>
<p><a href="https://sentry.io"><img src="https://avatars.githubusercontent.com/u/1396951?v=4" alt="Sentry" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a></p><h3>Bronze Sponsors</h3>
<p><a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8: free icons, photos, illustrations, and music" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://www.ignitionapp.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Ignition" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a> <a href="https://quickbookstoolhub.com"><img src="https://avatars.githubusercontent.com/u/95090305?u=e5bc398ef775c9ed19f955c675cdc1fb6abf01df&v=4" alt="QuickBooks Tool hub" height="32"></a></p>
<p><a href="https://engineering.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a></p><h3>Silver Sponsors</h3>
<p><a href="https://sentry.io"><img src="https://avatars.githubusercontent.com/u/1396951?v=4" alt="Sentry" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="64"></a></p><h3>Bronze Sponsors</h3>
<p><a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8: free icons, photos, illustrations, and music" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://transloadit.com/"><img src="https://avatars.githubusercontent.com/u/125754?v=4" alt="Transloadit" height="32"></a> <a href="https://www.ignitionapp.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Ignition" height="32"></a> <a href="https://opensource.mercedes-benz.com/"><img src="https://avatars.githubusercontent.com/u/34240465?v=4" alt="Mercedes-Benz Group" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a> <a href="https://quickbookstoolhub.com"><img src="https://avatars.githubusercontent.com/u/95090305?u=e5bc398ef775c9ed19f955c675cdc1fb6abf01df&v=4" alt="QuickBooks Tool hub" height="32"></a></p>
<!--sponsorsend-->
## Technology Sponsors

View file

@ -1,31 +0,0 @@
/**
* @fileoverview Config to enable all rules.
* @author Robert Fletcher
*/
"use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const builtInRules = require("../lib/rules");
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
const allRules = {};
for (const [ruleId, rule] of builtInRules) {
if (!rule.meta.deprecated) {
allRules[ruleId] = "error";
}
}
//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------
/** @type {import("../lib/shared/types").ConfigData} */
module.exports = { rules: allRules };

View file

@ -1,76 +0,0 @@
/**
* @fileoverview Configuration applied when a user configuration extends from
* eslint:recommended.
* @author Nicholas C. Zakas
*/
"use strict";
/* eslint sort-keys: ["error", "asc"] -- Long, so make more readable */
/** @type {import("../lib/shared/types").ConfigData} */
module.exports = {
rules: {
"constructor-super": "error",
"for-direction": "error",
"getter-return": "error",
"no-async-promise-executor": "error",
"no-case-declarations": "error",
"no-class-assign": "error",
"no-compare-neg-zero": "error",
"no-cond-assign": "error",
"no-const-assign": "error",
"no-constant-condition": "error",
"no-control-regex": "error",
"no-debugger": "error",
"no-delete-var": "error",
"no-dupe-args": "error",
"no-dupe-class-members": "error",
"no-dupe-else-if": "error",
"no-dupe-keys": "error",
"no-duplicate-case": "error",
"no-empty": "error",
"no-empty-character-class": "error",
"no-empty-pattern": "error",
"no-ex-assign": "error",
"no-extra-boolean-cast": "error",
"no-extra-semi": "error",
"no-fallthrough": "error",
"no-func-assign": "error",
"no-global-assign": "error",
"no-import-assign": "error",
"no-inner-declarations": "error",
"no-invalid-regexp": "error",
"no-irregular-whitespace": "error",
"no-loss-of-precision": "error",
"no-misleading-character-class": "error",
"no-mixed-spaces-and-tabs": "error",
"no-new-symbol": "error",
"no-nonoctal-decimal-escape": "error",
"no-obj-calls": "error",
"no-octal": "error",
"no-prototype-builtins": "error",
"no-redeclare": "error",
"no-regex-spaces": "error",
"no-self-assign": "error",
"no-setter-return": "error",
"no-shadow-restricted-names": "error",
"no-sparse-arrays": "error",
"no-this-before-super": "error",
"no-undef": "error",
"no-unexpected-multiline": "error",
"no-unreachable": "error",
"no-unsafe-finally": "error",
"no-unsafe-negation": "error",
"no-unsafe-optional-chaining": "error",
"no-unused-labels": "error",
"no-unused-vars": "error",
"no-useless-backreference": "error",
"no-useless-catch": "error",
"no-useless-escape": "error",
"no-with": "error",
"require-yield": "error",
"use-isnan": "error",
"valid-typeof": "error"
}
};

View file

@ -128,6 +128,10 @@ const es2023 = {
...es2022
};
const es2024 = {
...es2023
};
//-----------------------------------------------------------------------------
// Exports
@ -145,5 +149,6 @@ module.exports = {
es2020,
es2021,
es2022,
es2023
es2023,
es2024
};

View file

@ -6,12 +6,12 @@
],
"deprecated": {
"name": "Deprecated",
"description": "These rules have been deprecated in accordance with the <a href=\"/docs/use/rule-deprecation\">deprecation policy</a>, and replaced by newer rules:",
"description": "These rules have been deprecated in accordance with the <a href=\"{{ '/use/rule-deprecation' | url }}\">deprecation policy</a>, and replaced by newer rules:",
"rules": []
},
"removed": {
"name": "Removed",
"description": "These rules from older versions of ESLint (before the <a href=\"/docs/use/rule-deprecation\">deprecation policy</a> existed) have been replaced by newer rules:",
"description": "These rules from older versions of ESLint (before the <a href=\"{{ '/use/rule-deprecation' | url }}\">deprecation policy</a> existed) have been replaced by newer rules:",
"rules": [
{ "removed": "generator-star", "replacedBy": ["generator-star-spacing"] },
{ "removed": "global-strict", "replacedBy": ["strict"] },

View file

@ -158,7 +158,17 @@ function validateFixTypes(fixTypes) {
* @private
*/
function calculateStatsPerFile(messages) {
return messages.reduce((stat, message) => {
const stat = {
errorCount: 0,
fatalErrorCount: 0,
warningCount: 0,
fixableErrorCount: 0,
fixableWarningCount: 0
};
for (let i = 0; i < messages.length; i++) {
const message = messages[i];
if (message.fatal || message.severity === 2) {
stat.errorCount++;
if (message.fatal) {
@ -173,14 +183,8 @@ function calculateStatsPerFile(messages) {
stat.fixableWarningCount++;
}
}
return stat;
}, {
errorCount: 0,
fatalErrorCount: 0,
warningCount: 0,
fixableErrorCount: 0,
fixableWarningCount: 0
});
}
return stat;
}
/**
@ -190,20 +194,25 @@ function calculateStatsPerFile(messages) {
* @private
*/
function calculateStatsPerRun(results) {
return results.reduce((stat, result) => {
stat.errorCount += result.errorCount;
stat.fatalErrorCount += result.fatalErrorCount;
stat.warningCount += result.warningCount;
stat.fixableErrorCount += result.fixableErrorCount;
stat.fixableWarningCount += result.fixableWarningCount;
return stat;
}, {
const stat = {
errorCount: 0,
fatalErrorCount: 0,
warningCount: 0,
fixableErrorCount: 0,
fixableWarningCount: 0
});
};
for (let i = 0; i < results.length; i++) {
const result = results[i];
stat.errorCount += result.errorCount;
stat.fatalErrorCount += result.fatalErrorCount;
stat.warningCount += result.warningCount;
stat.fixableErrorCount += result.fixableErrorCount;
stat.fixableWarningCount += result.fixableWarningCount;
}
return stat;
}
/**
@ -308,9 +317,11 @@ function createIgnoreResult(filePath, baseDir) {
filePath: path.resolve(filePath),
messages: [
{
ruleId: null,
fatal: false,
severity: 1,
message
message,
nodeType: null
}
],
suppressedMessages: [],
@ -615,8 +626,8 @@ class CLIEngine {
useEslintrc: options.useEslintrc,
builtInRules,
loadRules,
getEslintRecommendedConfig: () => require("../../conf/eslint-recommended.js"),
getEslintAllConfig: () => require("../../conf/eslint-all.js")
getEslintRecommendedConfig: () => require("@eslint/js").configs.recommended,
getEslintAllConfig: () => require("@eslint/js").configs.all
});
const fileEnumerator = new FileEnumerator({
configArrayFactory,

View file

@ -217,8 +217,8 @@ class FileEnumerator {
cwd = process.cwd(),
configArrayFactory = new CascadingConfigArrayFactory({
cwd,
getEslintRecommendedConfig: () => require("../../conf/eslint-recommended.js"),
getEslintAllConfig: () => require("../../conf/eslint-all.js")
getEslintRecommendedConfig: () => require("@eslint/js").configs.recommended,
getEslintAllConfig: () => require("@eslint/js").configs.all
}),
extensions = null,
globInputPaths = true,
@ -349,7 +349,7 @@ class FileEnumerator {
return this._iterateFilesWithFile(absolutePath);
}
if (globInputPaths && isGlobPattern(pattern)) {
return this._iterateFilesWithGlob(absolutePath, isDot);
return this._iterateFilesWithGlob(pattern, isDot);
}
return [];
@ -398,15 +398,17 @@ class FileEnumerator {
_iterateFilesWithGlob(pattern, dotfiles) {
debug(`Glob: ${pattern}`);
const directoryPath = path.resolve(getGlobParent(pattern));
const globPart = pattern.slice(directoryPath.length + 1);
const { cwd } = internalSlotsMap.get(this);
const directoryPath = path.resolve(cwd, getGlobParent(pattern));
const absolutePath = path.resolve(cwd, pattern);
const globPart = absolutePath.slice(directoryPath.length + 1);
/*
* recursive if there are `**` or path separators in the glob part.
* Otherwise, patterns such as `src/*.js`, it doesn't need recursive.
*/
const recursive = /\*\*|\/|\\/u.test(globPart);
const selector = new Minimatch(pattern, minimatchOpts);
const selector = new Minimatch(absolutePath, minimatchOpts);
debug(`recursive? ${recursive}`);

30
node_modules/eslint/lib/cli.js generated vendored
View file

@ -19,12 +19,11 @@ const fs = require("fs"),
path = require("path"),
{ promisify } = require("util"),
{ ESLint } = require("./eslint"),
{ FlatESLint } = require("./eslint/flat-eslint"),
{ FlatESLint, shouldUseFlatConfig } = require("./eslint/flat-eslint"),
createCLIOptions = require("./options"),
log = require("./shared/logging"),
RuntimeInfo = require("./shared/runtime-info");
const { Legacy: { naming } } = require("@eslint/eslintrc");
const { findFlatConfigFile } = require("./eslint/flat-eslint");
const { ModuleImporter } = require("@humanwhocodes/module-importer");
const debug = require("debug")("eslint:cli");
@ -275,31 +274,6 @@ async function printResults(engine, results, format, outputFile, resultsMeta) {
return true;
}
/**
* Returns whether flat config should be used.
* @param {boolean} [allowFlatConfig] Whether or not to allow flat config.
* @returns {Promise<boolean>} Where flat config should be used.
*/
async function shouldUseFlatConfig(allowFlatConfig) {
if (!allowFlatConfig) {
return false;
}
switch (process.env.ESLINT_USE_FLAT_CONFIG) {
case "true":
return true;
case "false":
return false;
default:
/*
* If neither explicitly enabled nor disabled, then use the presence
* of a flat config file to determine enablement.
*/
return !!(await findFlatConfigFile(process.cwd()));
}
}
//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------
@ -329,7 +303,7 @@ const cli = {
* switch to flat config we can remove this logic.
*/
const usingFlatConfig = await shouldUseFlatConfig(allowFlatConfig);
const usingFlatConfig = allowFlatConfig && await shouldUseFlatConfig();
debug("Using flat config?", usingFlatConfig);

View file

@ -19,9 +19,6 @@ exports.defaultConfig = [
{
plugins: {
"@": {
parsers: {
espree: require("espree")
},
/*
* Because we try to delay loading rules until absolutely
@ -43,7 +40,7 @@ exports.defaultConfig = [
languageOptions: {
sourceType: "module",
ecmaVersion: "latest",
parser: "@/espree",
parser: require("espree"),
parserOptions: {}
}
},
@ -51,7 +48,7 @@ exports.defaultConfig = [
// default ignores are listed here
{
ignores: [
"**/node_modules/*",
"**/node_modules/",
".git/"
]
},

View file

@ -13,7 +13,7 @@ const { ConfigArray, ConfigArraySymbol } = require("@humanwhocodes/config-array"
const { flatConfigSchema } = require("./flat-config-schema");
const { RuleValidator } = require("./rule-validator");
const { defaultConfig } = require("./default-config");
const recommendedConfig = require("../../conf/eslint-recommended");
const jsPlugin = require("@eslint/js");
//-----------------------------------------------------------------------------
// Helpers
@ -36,6 +36,45 @@ function splitPluginIdentifier(identifier) {
};
}
/**
* Returns the name of an object in the config by reading its `meta` key.
* @param {Object} object The object to check.
* @returns {string?} The name of the object if found or `null` if there
* is no name.
*/
function getObjectId(object) {
// first check old-style name
let name = object.name;
if (!name) {
if (!object.meta) {
return null;
}
name = object.meta.name;
if (!name) {
return null;
}
}
// now check for old-style version
let version = object.version;
if (!version) {
version = object.meta && object.meta.version;
}
// if there's a version then append that
if (version) {
return `${name}@${version}`;
}
return name;
}
const originalBaseConfig = Symbol("originalBaseConfig");
//-----------------------------------------------------------------------------
@ -96,17 +135,23 @@ class FlatConfigArray extends ConfigArray {
*/
[ConfigArraySymbol.preprocessConfig](config) {
if (config === "eslint:recommended") {
return recommendedConfig;
// 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") {
/*
* Load `eslint-all.js` here instead of at the top level to avoid loading all rule modules
* when it isn't necessary. `eslint-all.js` reads `meta` of rule objects to filter out deprecated ones,
* so requiring `eslint-all.js` module loads all rule modules as a consequence.
*/
return require("../../conf/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;
}
/*
@ -145,16 +190,15 @@ class FlatConfigArray extends ConfigArray {
// Check parser value
if (languageOptions && languageOptions.parser) {
if (typeof languageOptions.parser === "string") {
const { pluginName, objectName: localParserName } = splitPluginIdentifier(languageOptions.parser);
const { parser } = languageOptions;
parserName = languageOptions.parser;
if (typeof parser === "object") {
parserName = getObjectId(parser);
if (!plugins || !plugins[pluginName] || !plugins[pluginName].parsers || !plugins[pluginName].parsers[localParserName]) {
throw new TypeError(`Key "parser": Could not find "${localParserName}" in plugin "${pluginName}".`);
if (!parserName) {
invalidParser = true;
}
languageOptions.parser = plugins[pluginName].parsers[localParserName];
} else {
invalidParser = true;
}
@ -172,6 +216,13 @@ class FlatConfigArray extends ConfigArray {
}
config.processor = plugins[pluginName].processors[localProcessorName];
} else if (typeof processor === "object") {
processorName = getObjectId(processor);
if (!processorName) {
invalidProcessor = true;
}
} else {
invalidProcessor = true;
}
@ -185,16 +236,25 @@ class FlatConfigArray extends ConfigArray {
value: function() {
if (invalidParser) {
throw new Error("Caching is not supported when parser is an object.");
throw new Error("Could not serialize parser object (missing 'meta' object).");
}
if (invalidProcessor) {
throw new Error("Caching is not supported when processor is an object.");
throw new Error("Could not serialize processor object (missing 'meta' object).");
}
return {
...this,
plugins: Object.keys(plugins),
plugins: Object.entries(plugins).map(([namespace, plugin]) => {
const pluginId = getObjectId(plugin);
if (!pluginId) {
return namespace;
}
return `${namespace}:${pluginId}`;
}),
languageOptions: {
...languageOptions,
parser: parserName

View file

@ -126,32 +126,65 @@ function normalizeRuleOptions(ruleOptions) {
// Assertions
//-----------------------------------------------------------------------------
/**
* The error type when a rule's options are configured with an invalid type.
*/
class InvalidRuleOptionsError extends Error {
/**
* @param {string} ruleId Rule name being configured.
* @param {any} value The invalid value.
*/
constructor(ruleId, value) {
super(`Key "${ruleId}": Expected severity of "off", 0, "warn", 1, "error", or 2.`);
this.messageTemplate = "invalid-rule-options";
this.messageData = { ruleId, value };
}
}
/**
* Validates that a value is a valid rule options entry.
* @param {string} ruleId Rule name being configured.
* @param {any} value The value to check.
* @returns {void}
* @throws {TypeError} If the value isn't a valid rule options.
* @throws {InvalidRuleOptionsError} If the value isn't a valid rule options.
*/
function assertIsRuleOptions(value) {
function assertIsRuleOptions(ruleId, value) {
if (typeof value !== "string" && typeof value !== "number" && !Array.isArray(value)) {
throw new TypeError("Expected a string, number, or array.");
throw new InvalidRuleOptionsError(ruleId, value);
}
}
/**
* The error type when a rule's severity is invalid.
*/
class InvalidRuleSeverityError extends Error {
/**
* @param {string} ruleId Rule name being configured.
* @param {any} value The invalid value.
*/
constructor(ruleId, value) {
super(`Key "${ruleId}": Expected severity of "off", 0, "warn", 1, "error", or 2.`);
this.messageTemplate = "invalid-rule-severity";
this.messageData = { ruleId, value };
}
}
/**
* Validates that a value is valid rule severity.
* @param {string} ruleId Rule name being configured.
* @param {any} value The value to check.
* @returns {void}
* @throws {TypeError} If the value isn't a valid rule severity.
* @throws {InvalidRuleSeverityError} If the value isn't a valid rule severity.
*/
function assertIsRuleSeverity(value) {
function assertIsRuleSeverity(ruleId, value) {
const severity = typeof value === "string"
? ruleSeverities.get(value.toLowerCase())
: ruleSeverities.get(value);
if (typeof severity === "undefined") {
throw new TypeError("Expected severity of \"off\", 0, \"warn\", 1, \"error\", or 2.");
throw new InvalidRuleSeverityError(ruleId, value);
}
}
@ -179,18 +212,6 @@ function assertIsObject(value) {
}
}
/**
* Validates that a value is an object or a string.
* @param {any} value The value to check.
* @returns {void}
* @throws {TypeError} If the value isn't an object or a string.
*/
function assertIsObjectOrString(value) {
if ((!value || typeof value !== "object") && typeof value !== "string") {
throw new TypeError("Expected an object or string.");
}
}
//-----------------------------------------------------------------------------
// Low-Level Schemas
//-----------------------------------------------------------------------------
@ -242,15 +263,13 @@ const globalsSchema = {
const parserSchema = {
merge: "replace",
validate(value) {
assertIsObjectOrString(value);
if (typeof value === "object" && typeof value.parse !== "function" && typeof value.parseForESLint !== "function") {
throw new TypeError("Expected object to have a parse() or parseForESLint() method.");
if (!value || typeof value !== "object" ||
(typeof value.parse !== "function" && typeof value.parseForESLint !== "function")
) {
throw new TypeError("Expected object with parse() or parseForESLint() method.");
}
if (typeof value === "string") {
assertIsPluginMemberName(value);
}
}
};
@ -371,39 +390,28 @@ const rulesSchema = {
validate(value) {
assertIsObject(value);
let lastRuleId;
/*
* We are not checking the rule schema here because there is no
* guarantee that the rule definition is present at this point. Instead
* we wait and check the rule schema during the finalization step
* of calculating a config.
*/
for (const ruleId of Object.keys(value)) {
// Performance: One try-catch has less overhead than one per loop iteration
try {
/*
* We are not checking the rule schema here because there is no
* guarantee that the rule definition is present at this point. Instead
* we wait and check the rule schema during the finalization step
* of calculating a config.
*/
for (const ruleId of Object.keys(value)) {
// avoid hairy edge case
if (ruleId === "__proto__") {
continue;
}
lastRuleId = ruleId;
const ruleOptions = value[ruleId];
assertIsRuleOptions(ruleOptions);
if (Array.isArray(ruleOptions)) {
assertIsRuleSeverity(ruleOptions[0]);
} else {
assertIsRuleSeverity(ruleOptions);
}
// avoid hairy edge case
if (ruleId === "__proto__") {
continue;
}
const ruleOptions = value[ruleId];
assertIsRuleOptions(ruleId, ruleOptions);
if (Array.isArray(ruleOptions)) {
assertIsRuleSeverity(ruleId, ruleOptions[0]);
} else {
assertIsRuleSeverity(ruleId, ruleOptions);
}
} catch (error) {
error.message = `Key "${lastRuleId}": ${error.message}`;
throw error;
}
}
};

View file

@ -223,7 +223,7 @@ function globMatch({ basePath, pattern }) {
* should be thrown when a pattern is unmatched.
* @returns {Promise<Array<string>>} An array of matching file paths
* or an empty array if there are no matches.
* @throws {UnmatchedSearchPatternsErrror} If there is a pattern that doesn't
* @throws {UnmatchedSearchPatternsError} If there is a pattern that doesn't
* match any files.
*/
async function globSearch({
@ -526,9 +526,9 @@ async function findFiles({
}
// save patterns for later use based on whether globs are enabled
if (globInputPaths && isGlobPattern(filePath)) {
if (globInputPaths && isGlobPattern(pattern)) {
const basePath = globParent(filePath);
const basePath = path.resolve(cwd, globParent(pattern));
// group in cwd if possible and split out others
if (isPathInside(basePath, cwd)) {
@ -568,41 +568,6 @@ async function findFiles({
];
}
/**
* Checks whether a file exists at the given location
* @param {string} resolvedPath A path from the CWD
* @throws {Error} As thrown by `fs.statSync` or `fs.isFile`.
* @returns {boolean} `true` if a file exists
*/
function fileExists(resolvedPath) {
try {
return fs.statSync(resolvedPath).isFile();
} catch (error) {
if (error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
return false;
}
throw error;
}
}
/**
* Checks whether a directory exists at the given location
* @param {string} resolvedPath A path from the CWD
* @throws {Error} As thrown by `fs.statSync` or `fs.isDirectory`.
* @returns {boolean} `true` if a directory exists
*/
function directoryExists(resolvedPath) {
try {
return fs.statSync(resolvedPath).isDirectory();
} catch (error) {
if (error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
return false;
}
throw error;
}
}
//-----------------------------------------------------------------------------
// Results-related Helpers
//-----------------------------------------------------------------------------
@ -626,14 +591,10 @@ function isErrorMessage(message) {
*/
function createIgnoreResult(filePath, baseDir) {
let message;
const isHidden = filePath.split(path.sep)
.find(segment => /^\./u.test(segment));
const isInNodeModules = baseDir && path.relative(baseDir, filePath).startsWith("node_modules");
const isInNodeModules = baseDir && path.dirname(path.relative(baseDir, filePath)).split(path.sep).includes("node_modules");
if (isHidden) {
message = "File ignored by default. Use a negated ignore pattern (like \"--ignore-pattern '!<relative/path/to/filename>'\") to override.";
} else if (isInNodeModules) {
message = "File ignored by default. Use \"--ignore-pattern '!node_modules/*'\" to override.";
if (isInNodeModules) {
message = "File ignored by default because it is located under the node_modules directory. Use ignore pattern \"!**/node_modules/\" to override.";
} else {
message = "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to override.";
}
@ -642,9 +603,11 @@ function createIgnoreResult(filePath, baseDir) {
filePath: path.resolve(filePath),
messages: [
{
ruleId: null,
fatal: false,
severity: 1,
message
message,
nodeType: null
}
],
suppressedMessages: [],
@ -793,6 +756,9 @@ function processOptions({
if (typeof ignore !== "boolean") {
errors.push("'ignore' must be a boolean.");
}
if (!isArrayOfNonEmptyString(ignorePatterns) && ignorePatterns !== null) {
errors.push("'ignorePatterns' must be an array of non-empty strings or null.");
}
if (typeof overrideConfig !== "object") {
errors.push("'overrideConfig' must be an object or null.");
}
@ -829,7 +795,7 @@ function processOptions({
// when overrideConfigFile is true that means don't do config file lookup
configFile: overrideConfigFile === true ? false : overrideConfigFile,
overrideConfig,
cwd,
cwd: path.normalize(cwd),
errorOnUnmatchedPattern,
fix,
fixTypes,
@ -924,8 +890,6 @@ function getCacheFile(cacheFile, cwd) {
module.exports = {
isGlobPattern,
directoryExists,
fileExists,
findFiles,
isNonEmptyString,

View file

@ -289,7 +289,7 @@ function processOptions({
cacheLocation,
cacheStrategy,
configFile: overrideConfigFile,
cwd,
cwd: path.normalize(cwd),
errorOnUnmatchedPattern,
extensions,
fix,

View file

@ -55,6 +55,7 @@ const LintResultCache = require("../cli-engine/lint-result-cache");
/** @typedef {import("../shared/types").ConfigData} ConfigData */
/** @typedef {import("../shared/types").DeprecatedRuleInfo} DeprecatedRuleInfo */
/** @typedef {import("../shared/types").LintMessage} LintMessage */
/** @typedef {import("../shared/types").LintResult} LintResult */
/** @typedef {import("../shared/types").ParserOptions} ParserOptions */
/** @typedef {import("../shared/types").Plugin} Plugin */
/** @typedef {import("../shared/types").ResultsMeta} ResultsMeta */
@ -76,7 +77,7 @@ const LintResultCache = require("../cli-engine/lint-result-cache");
* @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 all ignore patterns except for the default ones.
* @property {string[]} [ignorePatterns] Ignore file patterns to use in addition to config ignores.
* @property {string[]} [ignorePatterns] Ignore file patterns to use in addition to config ignores. These patterns are relative to `cwd`.
* @property {ConfigData} [overrideConfig] Override config object, overrides all configs used with this instance
* @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
@ -102,7 +103,17 @@ const importedConfigFileModificationTime = new Map();
* @private
*/
function calculateStatsPerFile(messages) {
return messages.reduce((stat, message) => {
const stat = {
errorCount: 0,
fatalErrorCount: 0,
warningCount: 0,
fixableErrorCount: 0,
fixableWarningCount: 0
};
for (let i = 0; i < messages.length; i++) {
const message = messages[i];
if (message.fatal || message.severity === 2) {
stat.errorCount++;
if (message.fatal) {
@ -117,37 +128,8 @@ function calculateStatsPerFile(messages) {
stat.fixableWarningCount++;
}
}
return stat;
}, {
errorCount: 0,
fatalErrorCount: 0,
warningCount: 0,
fixableErrorCount: 0,
fixableWarningCount: 0
});
}
/**
* It will calculate the error and warning count for collection of results from all files
* @param {LintResult[]} results Collection of messages from all the files
* @returns {Object} Contains the stats
* @private
*/
function calculateStatsPerRun(results) {
return results.reduce((stat, result) => {
stat.errorCount += result.errorCount;
stat.fatalErrorCount += result.fatalErrorCount;
stat.warningCount += result.warningCount;
stat.fixableErrorCount += result.fixableErrorCount;
stat.fixableWarningCount += result.fixableWarningCount;
return stat;
}, {
errorCount: 0,
fatalErrorCount: 0,
warningCount: 0,
fixableErrorCount: 0,
fixableWarningCount: 0
});
}
return stat;
}
/**
@ -261,7 +243,7 @@ function compareResultsByFilePath(a, b) {
* 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|null>} The filename if found or `null` if not.
* @returns {Promise<string|undefined>} The filename if found or `undefined` if not.
*/
function findFlatConfigFile(cwd) {
return findUp(
@ -320,6 +302,45 @@ async function loadFlatConfigFile(filePath) {
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 {FlatESLint} eslint The instance to create the config array for.
@ -342,25 +363,13 @@ async function calculateConfigArray(eslint, {
return slots.configs;
}
// determine where to load config file from
let configFilePath;
let basePath = cwd;
const { configFilePath, basePath, error } = await locateConfigFileToUse({ configFile, cwd });
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) {
throw new Error("Could not find config file.");
}
basePath = path.resolve(path.dirname(configFilePath));
// config file is required to calculate config
if (error) {
throw error;
}
const configs = new FlatConfigArray(baseConfig || [], { basePath, shouldIgnore });
// load config file
@ -377,45 +386,39 @@ async function calculateConfigArray(eslint, {
// add in any configured defaults
configs.push(...slots.defaultConfigs);
let allIgnorePatterns = [];
// append command line ignore patterns
if (ignorePatterns) {
if (typeof ignorePatterns === "string") {
allIgnorePatterns.push(ignorePatterns);
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 {
allIgnorePatterns.push(...ignorePatterns);
}
}
/*
* If the config file basePath is different than the cwd, then
* the ignore patterns won't work correctly. Here, we adjust the
* ignore pattern to include the correct relative path. Patterns
* loaded from ignore files are always relative to the cwd, whereas
* the config file basePath can be an ancestor of the cwd.
*/
if (basePath !== cwd && allIgnorePatterns.length) {
const relativeIgnorePath = path.relative(basePath, cwd);
const relativeIgnorePath = path.relative(basePath, cwd);
relativeIgnorePatterns = ignorePatterns.map(pattern => {
const negated = pattern.startsWith("!");
const basePattern = negated ? pattern.slice(1) : pattern;
allIgnorePatterns = allIgnorePatterns.map(pattern => {
const negated = pattern.startsWith("!");
const basePattern = negated ? pattern.slice(1) : pattern;
return (negated ? "!" : "") +
return (negated ? "!" : "") +
path.posix.join(relativeIgnorePath, basePattern);
});
}
if (allIgnorePatterns.length) {
});
}
/*
* Ignore patterns are added to the end of the config array
* so they can override default ignores.
*/
configs.push({
ignores: allIgnorePatterns
ignores: relativeIgnorePatterns
});
}
@ -528,43 +531,6 @@ function shouldMessageBeFixed(message, config, fixTypes) {
return Boolean(rule && rule.meta && fixTypes.has(rule.meta.type));
}
/**
* Collect used deprecated rules.
* @param {Array<FlatConfig>} configs The configs to evaluate.
* @returns {IterableIterator<DeprecatedRuleInfo>} Used deprecated rules.
*/
function *iterateRuleDeprecationWarnings(configs) {
const processedRuleIds = new Set();
for (const config of configs) {
for (const [ruleId, ruleConfig] of Object.entries(config.rules)) {
// Skip if it was processed.
if (processedRuleIds.has(ruleId)) {
continue;
}
processedRuleIds.add(ruleId);
// Skip if it's not used.
if (!getRuleSeverity(ruleConfig)) {
continue;
}
const rule = getRuleFromConfig(ruleId, config);
// Skip if it's not deprecated.
if (!(rule && rule.meta && rule.meta.deprecated)) {
continue;
}
// This rule was used and deprecated.
yield {
ruleId,
replacedBy: rule.meta.replacedBy || []
};
}
}
}
/**
* Creates an error to be thrown when an array of results passed to `getRulesMetaForResults` was not created by the current engine.
* @returns {TypeError} An error object.
@ -789,7 +755,6 @@ class FlatESLint {
errorOnUnmatchedPattern
} = eslintOptions;
const startTime = Date.now();
const usedConfigs = [];
const fixTypesSet = fixTypes ? new Set(fixTypes) : null;
// Delete cache file; should this be done here?
@ -847,15 +812,6 @@ class FlatESLint {
return void 0;
}
/*
* Store used configs for:
* - this method uses to collect used deprecated rules.
* - `--fix-type` option uses to get the loaded rule's meta data.
*/
if (!usedConfigs.includes(config)) {
usedConfigs.push(config);
}
// Skip if there is cached result.
if (lintResultCache) {
const cachedResult =
@ -924,22 +880,10 @@ class FlatESLint {
lintResultCache.reconcile();
}
let usedDeprecatedRules;
const finalResults = results.filter(result => !!result);
return processLintReport(this, {
results: finalResults,
...calculateStatsPerRun(finalResults),
// Initialize it lazily because CLI and `ESLint` API don't use it.
get usedDeprecatedRules() {
if (!usedDeprecatedRules) {
usedDeprecatedRules = Array.from(
iterateRuleDeprecationWarnings(usedConfigs)
);
}
return usedDeprecatedRules;
}
results: finalResults
});
}
@ -1001,7 +945,6 @@ class FlatESLint {
const results = [];
const startTime = Date.now();
const resolvedFilename = path.resolve(cwd, filePath || "__placeholder__.js");
let config;
// Clear the last used config arrays.
if (resolvedFilename && await this.isPathIgnored(resolvedFilename)) {
@ -1010,9 +953,6 @@ class FlatESLint {
}
} else {
// TODO: Needed?
config = configs.getConfig(resolvedFilename);
// Do lint.
results.push(verifyText({
text: code,
@ -1027,21 +967,9 @@ class FlatESLint {
}
debug(`Linting complete in: ${Date.now() - startTime}ms`);
let usedDeprecatedRules;
return processLintReport(this, {
results,
...calculateStatsPerRun(results),
// Initialize it lazily because CLI and `ESLint` API don't use it.
get usedDeprecatedRules() {
if (!usedDeprecatedRules) {
usedDeprecatedRules = Array.from(
iterateRuleDeprecationWarnings(config)
);
}
return usedDeprecatedRules;
}
results
});
}
@ -1160,6 +1088,19 @@ class FlatESLint {
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.
* @param {string} filePath The path of the file to check.
@ -1172,11 +1113,31 @@ class FlatESLint {
}
}
/**
* Returns whether flat config should be used.
* @returns {Promise<boolean>} Whether flat config should be used.
*/
async function shouldUseFlatConfig() {
switch (process.env.ESLINT_USE_FLAT_CONFIG) {
case "true":
return true;
case "false":
return false;
default:
/*
* If neither explicitly enabled nor disabled, then use the presence
* of a flat config file to determine enablement.
*/
return !!(await findFlatConfigFile(process.cwd()));
}
}
//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------
module.exports = {
FlatESLint,
findFlatConfigFile
shouldUseFlatConfig
};

View file

@ -5,6 +5,16 @@
"use strict";
//------------------------------------------------------------------------------
// Typedefs
//------------------------------------------------------------------------------
/** @typedef {import("../shared/types").LintMessage} LintMessage */
//------------------------------------------------------------------------------
// Module Definition
//------------------------------------------------------------------------------
const escapeRegExp = require("escape-string-regexp");
/**
@ -196,7 +206,7 @@ function processUnusedDisableDirectives(allDirectives) {
* @param {Object} options options for applying directives. This is the same as the options
* for the exported function, except that `reportUnusedDisableDirectives` is not supported
* (this function always reports unused disable directives).
* @returns {{problems: Problem[], unusedDisableDirectives: Problem[]}} An object with a list
* @returns {{problems: LintMessage[], unusedDisableDirectives: LintMessage[]}} An object with a list
* of problems (including suppressed ones) and unused eslint-disable directives
*/
function applyDirectives(options) {

View file

@ -109,7 +109,7 @@ module.exports = {
text += "final[label=\"\",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];\n";
}
if (codePath.thrownSegments.length > 0) {
text += "thrown[label=\"✘\",shape=circle,width=0.3,height=0.3,fixedsize];\n";
text += "thrown[label=\"✘\",shape=circle,width=0.3,height=0.3,fixedsize=true];\n";
}
const traceMap = Object.create(null);

View file

@ -19,6 +19,12 @@ const levn = require("levn"),
const debug = require("debug")("eslint:config-comment-parser");
//------------------------------------------------------------------------------
// Typedefs
//------------------------------------------------------------------------------
/** @typedef {import("../shared/types").LintMessage} LintMessage */
//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------
@ -61,7 +67,7 @@ module.exports = class ConfigCommentParser {
* Parses a JSON-like config.
* @param {string} string The string to parse.
* @param {Object} location Start line and column of comments for potential error message.
* @returns {({success: true, config: Object}|{success: false, error: Problem})} Result map object
* @returns {({success: true, config: Object}|{success: false, error: LintMessage})} Result map object
*/
parseJsonConfig(string, location) {
debug("Parsing JSON config");
@ -109,7 +115,8 @@ module.exports = class ConfigCommentParser {
severity: 2,
message: `Failed to parse JSON from '${normalizedString}': ${ex.message}`,
line: location.start.line,
column: location.start.column + 1
column: location.start.column + 1,
nodeType: null
}
};

View file

@ -364,7 +364,7 @@ function extractDirectiveComment(value) {
* @param {ASTNode} ast The top node of the AST.
* @param {function(string): {create: Function}} ruleMapper A map from rule IDs to defined rules
* @param {string|null} warnInlineConfig If a string then it should warn directive comments as disabled. The string value is the config name what the setting came from.
* @returns {{configuredRules: Object, enabledGlobals: {value:string,comment:Token}[], exportedVariables: Object, problems: Problem[], disableDirectives: DisableDirective[]}}
* @returns {{configuredRules: Object, enabledGlobals: {value:string,comment:Token}[], exportedVariables: Object, problems: LintMessage[], disableDirectives: DisableDirective[]}}
* A collection of the directive comments that were found, along with any problems that occurred when parsing
*/
function getDirectiveComments(ast, ruleMapper, warnInlineConfig) {
@ -592,7 +592,7 @@ function findEslintEnv(text) {
* Convert "/path/to/<text>" to "<text>".
* `CLIEngine#executeOnText()` method gives "/path/to/<text>" if the filename
* was omitted because `configArray.extractConfig()` requires an absolute path.
* But the linter should pass `<text>` to `RuleContext#getFilename()` in that
* But the linter should pass `<text>` to `RuleContext#filename` in that
* case.
* Also, code blocks can have their virtual filename. If the parent filename was
* `<text>`, the virtual filename is `<text>/0_foo.js` or something like (i.e.,
@ -775,7 +775,7 @@ function analyzeScope(ast, languageOptions, visitorKeys) {
* @param {string} text The text to parse.
* @param {LanguageOptions} languageOptions Options to pass to the parser
* @param {string} filePath The path to the file being parsed.
* @returns {{success: false, error: Problem}|{success: true, sourceCode: SourceCode}}
* @returns {{success: false, error: LintMessage}|{success: true, sourceCode: SourceCode}}
* An object containing the AST and parser services if parsing was successful, or the error if parsing failed
* @private
*/
@ -851,69 +851,13 @@ function parse(text, languageOptions, filePath) {
severity: 2,
message,
line: ex.lineNumber,
column: ex.column
column: ex.column,
nodeType: null
}
};
}
}
/**
* Gets the scope for the current node
* @param {ScopeManager} scopeManager The scope manager for this AST
* @param {ASTNode} currentNode The node to get the scope of
* @returns {eslint-scope.Scope} The scope information for this node
*/
function getScope(scopeManager, currentNode) {
// On Program node, get the outermost scope to avoid return Node.js special function scope or ES modules scope.
const inner = currentNode.type !== "Program";
for (let node = currentNode; node; node = node.parent) {
const scope = scopeManager.acquire(node, inner);
if (scope) {
if (scope.type === "function-expression-name") {
return scope.childScopes[0];
}
return scope;
}
}
return scopeManager.scopes[0];
}
/**
* Marks a variable as used in the current scope
* @param {ScopeManager} scopeManager The scope manager for this AST. The scope may be mutated by this function.
* @param {ASTNode} currentNode The node currently being traversed
* @param {LanguageOptions} languageOptions The options used to parse this text
* @param {string} name The name of the variable that should be marked as used.
* @returns {boolean} True if the variable was found and marked as used, false if not.
*/
function markVariableAsUsed(scopeManager, currentNode, languageOptions, name) {
const parserOptions = languageOptions.parserOptions;
const sourceType = languageOptions.sourceType;
const hasGlobalReturn =
(parserOptions.ecmaFeatures && parserOptions.ecmaFeatures.globalReturn) ||
sourceType === "commonjs";
const specialScope = hasGlobalReturn || sourceType === "module";
const currentScope = getScope(scopeManager, currentNode);
// Special Node.js scope means we need to start one level deeper
const initialScope = currentScope.type === "global" && specialScope ? currentScope.childScopes[0] : currentScope;
for (let scope = initialScope; scope; scope = scope.upper) {
const variable = scope.variables.find(scopeVar => scopeVar.name === name);
if (variable) {
variable.eslintUsed = true;
return true;
}
}
return false;
}
/**
* Runs a rule, and gets its listeners
* @param {Rule} rule A normalized rule with a `create` method
@ -930,22 +874,6 @@ function createRuleListeners(rule, ruleContext) {
}
}
/**
* Gets all the ancestors of a given node
* @param {ASTNode} node The node
* @returns {ASTNode[]} All the ancestor nodes in the AST, not including the provided node, starting
* from the root node and going inwards to the parent node.
*/
function getAncestors(node) {
const ancestorsStartingAtParent = [];
for (let ancestor = node.parent; ancestor; ancestor = ancestor.parent) {
ancestorsStartingAtParent.push(ancestor);
}
return ancestorsStartingAtParent.reverse();
}
// methods that exist on SourceCode object
const DEPRECATED_SOURCECODE_PASSTHROUGHS = {
getSource: "getText",
@ -975,7 +903,7 @@ const BASE_TRAVERSAL_CONTEXT = Object.freeze(
(contextInfo, methodName) =>
Object.assign(contextInfo, {
[methodName](...args) {
return this.getSourceCode()[DEPRECATED_SOURCECODE_PASSTHROUGHS[methodName]](...args);
return this.sourceCode[DEPRECATED_SOURCECODE_PASSTHROUGHS[methodName]](...args);
}
}),
{}
@ -994,7 +922,7 @@ const BASE_TRAVERSAL_CONTEXT = Object.freeze(
* @param {boolean} disableFixes If true, it doesn't make `fix` properties.
* @param {string | undefined} cwd cwd of the cli
* @param {string} physicalFilename The full path of the file on disk without any code block information
* @returns {Problem[]} An array of reported problems
* @returns {LintMessage[]} An array of reported problems
*/
function runRules(sourceCode, configuredRules, ruleMapper, parserName, languageOptions, settings, filename, disableFixes, cwd, physicalFilename) {
const emitter = createEmitter();
@ -1021,14 +949,18 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserName, languageO
Object.assign(
Object.create(BASE_TRAVERSAL_CONTEXT),
{
getAncestors: () => getAncestors(currentNode),
getDeclaredVariables: sourceCode.scopeManager.getDeclaredVariables.bind(sourceCode.scopeManager),
getAncestors: () => sourceCode.getAncestors(currentNode),
getDeclaredVariables: node => sourceCode.getDeclaredVariables(node),
getCwd: () => cwd,
cwd,
getFilename: () => filename,
filename,
getPhysicalFilename: () => physicalFilename || filename,
getScope: () => getScope(sourceCode.scopeManager, currentNode),
physicalFilename: physicalFilename || filename,
getScope: () => sourceCode.getScope(currentNode),
getSourceCode: () => sourceCode,
markVariableAsUsed: name => markVariableAsUsed(sourceCode.scopeManager, currentNode, languageOptions, name),
sourceCode,
markVariableAsUsed: name => sourceCode.markVariableAsUsed(name, currentNode),
parserOptions: {
...languageOptions.parserOptions
},
@ -1322,7 +1254,8 @@ class Linter {
severity: 2,
message: `Configured parser '${config.parser}' was not found.`,
line: 0,
column: 0
column: 0,
nodeType: null
}];
}
parserName = config.parser;
@ -1533,7 +1466,8 @@ class Linter {
severity: 2,
message,
line: ex.lineNumber,
column: ex.column
column: ex.column,
nodeType: null
}
];
}
@ -1798,7 +1732,8 @@ class Linter {
severity: 1,
message: `No matching configuration found for ${filename}.`,
line: 0,
column: 0
column: 0,
nodeType: null
}
];
}
@ -1863,7 +1798,8 @@ class Linter {
severity: 2,
message,
line: ex.lineNumber,
column: ex.column
column: ex.column,
nodeType: null
}
];
}
@ -1909,7 +1845,7 @@ class Linter {
/**
* Given a list of reported problems, distinguish problems between normal messages and suppressed messages.
* The normal messages will be returned and the suppressed messages will be stored as lastSuppressedMessages.
* @param {Problem[]} problems A list of reported problems.
* @param {Array<LintMessage|SuppressedLintMessage>} problems A list of reported problems.
* @returns {LintMessage[]} A list of LintMessage.
*/
_distinguishSuppressedMessages(problems) {

View file

@ -17,6 +17,8 @@ const interpolate = require("./interpolate");
// Typedefs
//------------------------------------------------------------------------------
/** @typedef {import("../shared/types").LintMessage} LintMessage */
/**
* An error message description
* @typedef {Object} MessageDescriptor
@ -29,23 +31,6 @@ const interpolate = require("./interpolate");
* @property {Array<{desc?: string, messageId?: string, fix: Function}>} suggest Suggestion descriptions and functions to create a the associated fixes.
*/
/**
* Information about the report
* @typedef {Object} ReportInfo
* @property {string} ruleId The rule ID
* @property {(0|1|2)} severity Severity of the error
* @property {(string|undefined)} message The message
* @property {(string|undefined)} [messageId] The message ID
* @property {number} line The line number
* @property {number} column The column number
* @property {(number|undefined)} [endLine] The ending line number
* @property {(number|undefined)} [endColumn] The ending column number
* @property {(string|null)} nodeType Type of node
* @property {string} source Source text
* @property {({text: string, range: (number[]|null)}|null)} [fix] The fix object
* @property {Array<{text: string, range: (number[]|null)}|null>} [suggestions] Suggestion info
*/
//------------------------------------------------------------------------------
// Module Definition
//------------------------------------------------------------------------------
@ -239,7 +224,7 @@ function mapSuggestions(descriptor, sourceCode, messages) {
* @param {{start: SourceLocation, end: (SourceLocation|null)}} options.loc Start and end location
* @param {{text: string, range: (number[]|null)}} options.fix The fix object
* @param {Array<{text: string, range: (number[]|null)}>} options.suggestions The array of suggestions objects
* @returns {function(...args): ReportInfo} Function that returns information about the report
* @returns {LintMessage} Information about the report
*/
function createProblem(options) {
const problem = {
@ -314,7 +299,7 @@ function validateSuggestions(suggest, messages) {
* problem for the Node.js API.
* @param {{ruleId: string, severity: number, sourceCode: SourceCode, messageIds: Object, disableFixes: boolean}} metadata Metadata for the reported problem
* @param {SourceCode} sourceCode The `SourceCode` instance for the text being linted
* @returns {function(...args): ReportInfo} Function that returns information about the report
* @returns {function(...args): LintMessage} Function that returns information about the report
*/
module.exports = function createReportTranslator(metadata) {

View file

@ -33,7 +33,7 @@ const { ConfigArraySymbol } = require("@humanwhocodes/config-array");
/** @typedef {import("../shared/types").Parser} Parser */
/** @typedef {import("../shared/types").LanguageOptions} LanguageOptions */
/* eslint-disable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */
/**
* A test case that is expected to pass lint.
* @typedef {Object} ValidTestCase
@ -72,7 +72,6 @@ const { ConfigArraySymbol } = require("@humanwhocodes/config-array");
* @property {number} [endLine] The 1-based line number of the reported end location.
* @property {number} [endColumn] The 1-based column number of the reported end location.
*/
/* eslint-enable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */
//------------------------------------------------------------------------------
// Private Members
@ -345,7 +344,7 @@ class FlatRuleTester {
* @returns {void}
*/
static setDefaultConfig(config) {
if (typeof config !== "object") {
if (typeof config !== "object" || config === null) {
throw new TypeError("FlatRuleTester.setDefaultConfig: config must be an object");
}
sharedDefaultConfig = config;

View file

@ -63,7 +63,7 @@ const { SourceCode } = require("../source-code");
/** @typedef {import("../shared/types").Parser} Parser */
/* eslint-disable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */
/**
* A test case that is expected to pass lint.
* @typedef {Object} ValidTestCase
@ -108,7 +108,6 @@ const { SourceCode } = require("../source-code");
* @property {number} [endLine] The 1-based line number of the reported end location.
* @property {number} [endColumn] The 1-based column number of the reported end location.
*/
/* eslint-enable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */
//------------------------------------------------------------------------------
// Private Members
@ -412,7 +411,7 @@ class RuleTester {
* @returns {void}
*/
static setDefaultConfig(config) {
if (typeof config !== "object") {
if (typeof config !== "object" || config === null) {
throw new TypeError("RuleTester.setDefaultConfig: config must be an object");
}
defaultConfig = config;

View file

@ -142,7 +142,7 @@ module.exports = {
docs: {
description: "Enforce getter and setter pairs in objects and classes",
recommended: false,
url: "https://eslint.org/docs/rules/accessor-pairs"
url: "https://eslint.org/docs/latest/rules/accessor-pairs"
},
schema: [{
@ -178,7 +178,7 @@ module.exports = {
const checkGetWithoutSet = config.getWithoutSet === true;
const checkSetWithoutGet = config.setWithoutGet !== false;
const enforceForClassMembers = config.enforceForClassMembers !== false;
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
/**
* Reports the given node.
@ -223,43 +223,6 @@ module.exports = {
}
}
/**
* Creates a new `AccessorData` object for the given getter or setter node.
* @param {ASTNode} node A getter or setter node.
* @returns {AccessorData} New `AccessorData` object that contains the given node.
* @private
*/
function createAccessorData(node) {
const name = astUtils.getStaticPropertyName(node);
const key = (name !== null) ? name : sourceCode.getTokens(node.key);
return {
key,
getters: node.kind === "get" ? [node] : [],
setters: node.kind === "set" ? [node] : []
};
}
/**
* Merges the given `AccessorData` object into the given accessors list.
* @param {AccessorData[]} accessors The list to merge into.
* @param {AccessorData} accessorData The object to merge.
* @returns {AccessorData[]} The same instance with the merged object.
* @private
*/
function mergeAccessorData(accessors, accessorData) {
const equalKeyElement = accessors.find(a => areEqualKeys(a.key, accessorData.key));
if (equalKeyElement) {
equalKeyElement.getters.push(...accessorData.getters);
equalKeyElement.setters.push(...accessorData.setters);
} else {
accessors.push(accessorData);
}
return accessors;
}
/**
* Checks accessor pairs in the given list of nodes.
* @param {ASTNode[]} nodes The list to check.
@ -267,10 +230,39 @@ module.exports = {
* @private
*/
function checkList(nodes) {
const accessors = nodes
.filter(isAccessorKind)
.map(createAccessorData)
.reduce(mergeAccessorData, []);
const accessors = [];
let found = false;
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (isAccessorKind(node)) {
// Creates a new `AccessorData` object for the given getter or setter node.
const name = astUtils.getStaticPropertyName(node);
const key = (name !== null) ? name : sourceCode.getTokens(node.key);
// Merges the given `AccessorData` object into the given accessors list.
for (let j = 0; j < accessors.length; j++) {
const accessor = accessors[j];
if (areEqualKeys(accessor.key, key)) {
accessor.getters.push(...node.kind === "get" ? [node] : []);
accessor.setters.push(...node.kind === "set" ? [node] : []);
found = true;
break;
}
}
if (!found) {
accessors.push({
key,
getters: node.kind === "get" ? [node] : [],
setters: node.kind === "set" ? [node] : []
});
}
found = false;
}
}
for (const { getters, setters } of accessors) {
if (checkSetWithoutGet && setters.length && !getters.length) {

View file

@ -19,7 +19,7 @@ module.exports = {
docs: {
description: "Enforce linebreaks after opening and before closing array brackets",
recommended: false,
url: "https://eslint.org/docs/rules/array-bracket-newline"
url: "https://eslint.org/docs/latest/rules/array-bracket-newline"
},
fixable: "whitespace",
@ -56,7 +56,7 @@ module.exports = {
},
create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
//----------------------------------------------------------------------

View file

@ -18,7 +18,7 @@ module.exports = {
docs: {
description: "Enforce consistent spacing inside array brackets",
recommended: false,
url: "https://eslint.org/docs/rules/array-bracket-spacing"
url: "https://eslint.org/docs/latest/rules/array-bracket-spacing"
},
fixable: "whitespace",
@ -53,7 +53,7 @@ module.exports = {
},
create(context) {
const spaced = context.options[0] === "always",
sourceCode = context.getSourceCode();
sourceCode = context.sourceCode;
/**
* Determines whether an option is set, relative to the spacing option.

View file

@ -16,7 +16,7 @@ const astUtils = require("./utils/ast-utils");
//------------------------------------------------------------------------------
const TARGET_NODE_TYPE = /^(?:Arrow)?FunctionExpression$/u;
const TARGET_METHODS = /^(?:every|filter|find(?:Last)?(?:Index)?|flatMap|forEach|map|reduce(?:Right)?|some|sort)$/u;
const TARGET_METHODS = /^(?:every|filter|find(?:Last)?(?:Index)?|flatMap|forEach|map|reduce(?:Right)?|some|sort|toSorted)$/u;
/**
* Checks a given code path segment is reachable.
@ -141,7 +141,7 @@ module.exports = {
docs: {
description: "Enforce `return` statements in callbacks of array methods",
recommended: false,
url: "https://eslint.org/docs/rules/array-callback-return"
url: "https://eslint.org/docs/latest/rules/array-callback-return"
},
schema: [
@ -172,7 +172,7 @@ module.exports = {
create(context) {
const options = context.options[0] || { allowImplicit: false, checkForEach: false };
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
let funcInfo = {
arrayMethodName: null,

View file

@ -19,7 +19,7 @@ module.exports = {
docs: {
description: "Enforce line breaks after each array element",
recommended: false,
url: "https://eslint.org/docs/rules/array-element-newline"
url: "https://eslint.org/docs/latest/rules/array-element-newline"
},
fixable: "whitespace",
@ -47,6 +47,7 @@ module.exports = {
]
}
},
type: "array",
items: [
{
oneOf: [
@ -78,7 +79,7 @@ module.exports = {
},
create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
//----------------------------------------------------------------------
// Helpers
@ -239,19 +240,25 @@ module.exports = {
.some(element => element.loc.start.line !== element.loc.end.line);
}
const linebreaksCount = node.elements.map((element, i) => {
let linebreaksCount = 0;
for (let i = 0; i < node.elements.length; i++) {
const element = node.elements[i];
const previousElement = elements[i - 1];
if (i === 0 || element === null || previousElement === null) {
return false;
continue;
}
const commaToken = sourceCode.getFirstTokenBetween(previousElement, element, astUtils.isCommaToken);
const lastTokenOfPreviousElement = sourceCode.getTokenBefore(commaToken);
const firstTokenOfCurrentElement = sourceCode.getTokenAfter(commaToken);
return !astUtils.isTokenOnSameLine(lastTokenOfPreviousElement, firstTokenOfCurrentElement);
}).filter(isBreak => isBreak === true).length;
if (!astUtils.isTokenOnSameLine(lastTokenOfPreviousElement, firstTokenOfCurrentElement)) {
linebreaksCount++;
}
}
const needsLinebreaks = (
elements.length >= options.minItems ||

View file

@ -22,7 +22,7 @@ module.exports = {
docs: {
description: "Require braces around arrow function bodies",
recommended: false,
url: "https://eslint.org/docs/rules/arrow-body-style"
url: "https://eslint.org/docs/latest/rules/arrow-body-style"
},
schema: {
@ -74,7 +74,7 @@ module.exports = {
const asNeeded = !options[0] || options[0] === "as-needed";
const never = options[0] === "never";
const requireReturnForObjectLiteral = options[1] && options[1].requireReturnForObjectLiteral;
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
let funcInfo = null;
/**

View file

@ -35,7 +35,7 @@ module.exports = {
docs: {
description: "Require parentheses around arrow function arguments",
recommended: false,
url: "https://eslint.org/docs/rules/arrow-parens"
url: "https://eslint.org/docs/latest/rules/arrow-parens"
},
fixable: "code",
@ -69,7 +69,7 @@ module.exports = {
const asNeeded = context.options[0] === "as-needed";
const requireForBlockBody = asNeeded && context.options[1] && context.options[1].requireForBlockBody === true;
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
/**
* Finds opening paren of parameters for the given arrow function, if it exists.

View file

@ -22,7 +22,7 @@ module.exports = {
docs: {
description: "Enforce consistent spacing before and after the arrow in arrow functions",
recommended: false,
url: "https://eslint.org/docs/rules/arrow-spacing"
url: "https://eslint.org/docs/latest/rules/arrow-spacing"
},
fixable: "whitespace",
@ -61,7 +61,7 @@ module.exports = {
rule.before = rule.before !== false;
rule.after = rule.after !== false;
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
/**
* Get tokens of arrow(`=>`) and before/after arrow.

View file

@ -16,18 +16,19 @@ module.exports = {
docs: {
description: "Enforce the use of variables within the scope they are defined",
recommended: false,
url: "https://eslint.org/docs/rules/block-scoped-var"
url: "https://eslint.org/docs/latest/rules/block-scoped-var"
},
schema: [],
messages: {
outOfScope: "'{{name}}' used outside of binding context."
outOfScope: "'{{name}}' declared on line {{definitionLine}} column {{definitionColumn}} is used outside of binding context."
}
},
create(context) {
let stack = [];
const sourceCode = context.sourceCode;
/**
* Makes a block scope.
@ -49,12 +50,22 @@ module.exports = {
/**
* Reports a given reference.
* @param {eslint-scope.Reference} reference A reference to report.
* @param {eslint-scope.Definition} definition A definition for which to report reference.
* @returns {void}
*/
function report(reference) {
function report(reference, definition) {
const identifier = reference.identifier;
const definitionPosition = definition.name.loc.start;
context.report({ node: identifier, messageId: "outOfScope", data: { name: identifier.name } });
context.report({
node: identifier,
messageId: "outOfScope",
data: {
name: identifier.name,
definitionLine: definitionPosition.line,
definitionColumn: definitionPosition.column + 1
}
});
}
/**
@ -83,7 +94,7 @@ module.exports = {
}
// Gets declared variables, and checks its references.
const variables = context.getDeclaredVariables(node);
const variables = sourceCode.getDeclaredVariables(node);
for (let i = 0; i < variables.length; ++i) {
@ -91,7 +102,7 @@ module.exports = {
variables[i]
.references
.filter(isOutsideOfScope)
.forEach(report);
.forEach(ref => report(ref, variables[i].defs.find(def => def.parent === node)));
}
}

View file

@ -19,7 +19,7 @@ module.exports = {
docs: {
description: "Disallow or enforce spaces inside of blocks after opening block and before closing block",
recommended: false,
url: "https://eslint.org/docs/rules/block-spacing"
url: "https://eslint.org/docs/latest/rules/block-spacing"
},
fixable: "whitespace",
@ -37,7 +37,7 @@ module.exports = {
create(context) {
const always = (context.options[0] !== "never"),
messageId = always ? "missing" : "extra",
sourceCode = context.getSourceCode();
sourceCode = context.sourceCode;
/**
* Gets the open brace token from a given node.

View file

@ -19,7 +19,7 @@ module.exports = {
docs: {
description: "Enforce consistent brace style for blocks",
recommended: false,
url: "https://eslint.org/docs/rules/brace-style"
url: "https://eslint.org/docs/latest/rules/brace-style"
},
schema: [
@ -53,7 +53,7 @@ module.exports = {
create(context) {
const style = context.options[0] || "1tbs",
params = context.options[1] || {},
sourceCode = context.getSourceCode();
sourceCode = context.sourceCode;
//--------------------------------------------------------------------------
// Helpers

View file

@ -21,7 +21,7 @@ module.exports = {
docs: {
description: "Require `return` statements after callbacks",
recommended: false,
url: "https://eslint.org/docs/rules/callback-return"
url: "https://eslint.org/docs/latest/rules/callback-return"
},
schema: [{
@ -37,7 +37,7 @@ module.exports = {
create(context) {
const callbacks = context.options[0] || ["callback", "cb", "next"],
sourceCode = context.getSourceCode();
sourceCode = context.sourceCode;
//--------------------------------------------------------------------------
// Helpers

View file

@ -23,7 +23,7 @@ module.exports = {
docs: {
description: "Enforce camelcase naming convention",
recommended: false,
url: "https://eslint.org/docs/rules/camelcase"
url: "https://eslint.org/docs/latest/rules/camelcase"
},
schema: [
@ -73,6 +73,7 @@ module.exports = {
const ignoreImports = options.ignoreImports;
const ignoreGlobals = options.ignoreGlobals;
const allow = options.allow || [];
const sourceCode = context.sourceCode;
//--------------------------------------------------------------------------
// Helpers
@ -245,8 +246,8 @@ module.exports = {
return {
// Report camelcase of global variable references ------------------
Program() {
const scope = context.getScope();
Program(node) {
const scope = sourceCode.getScope(node);
if (!ignoreGlobals) {
@ -295,7 +296,7 @@ module.exports = {
"ClassExpression",
"CatchClause"
]](node) {
for (const variable of context.getDeclaredVariables(node)) {
for (const variable of sourceCode.getDeclaredVariables(node)) {
if (isGoodName(variable.name)) {
continue;
}
@ -345,7 +346,7 @@ module.exports = {
// Report camelcase in import --------------------------------------
ImportDeclaration(node) {
for (const variable of context.getDeclaredVariables(node)) {
for (const variable of sourceCode.getDeclaredVariables(node)) {
if (isGoodName(variable.name)) {
continue;
}

View file

@ -107,7 +107,7 @@ module.exports = {
docs: {
description: "Enforce or disallow capitalization of the first letter of a comment",
recommended: false,
url: "https://eslint.org/docs/rules/capitalized-comments"
url: "https://eslint.org/docs/latest/rules/capitalized-comments"
},
fixable: "code",
@ -139,7 +139,7 @@ module.exports = {
const capitalize = context.options[0] || "always",
normalizedOptions = getAllNormalizedOptions(context.options[1]),
sourceCode = context.getSourceCode();
sourceCode = context.sourceCode;
createRegExpForIgnorePatterns(normalizedOptions);

View file

@ -23,7 +23,7 @@ module.exports = {
docs: {
description: "Enforce that class methods utilize `this`",
recommended: false,
url: "https://eslint.org/docs/rules/class-methods-use-this"
url: "https://eslint.org/docs/latest/rules/class-methods-use-this"
},
schema: [{
@ -133,7 +133,7 @@ module.exports = {
if (isIncludedInstanceMethod(node.parent) && !methodUsesThis) {
context.report({
node,
loc: astUtils.getFunctionHeadLoc(node, context.getSourceCode()),
loc: astUtils.getFunctionHeadLoc(node, context.sourceCode),
messageId: "missingThis",
data: {
name: astUtils.getFunctionNameWithKind(node)

View file

@ -78,7 +78,7 @@ module.exports = {
docs: {
description: "Require or disallow trailing commas",
recommended: false,
url: "https://eslint.org/docs/rules/comma-dangle"
url: "https://eslint.org/docs/latest/rules/comma-dangle"
},
fixable: "code",
@ -136,7 +136,7 @@ module.exports = {
create(context) {
const options = normalizeOptions(context.options[0], context.languageOptions.ecmaVersion);
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
/**
* Gets the last item of the given node.

View file

@ -18,7 +18,7 @@ module.exports = {
docs: {
description: "Enforce consistent spacing before and after commas",
recommended: false,
url: "https://eslint.org/docs/rules/comma-spacing"
url: "https://eslint.org/docs/latest/rules/comma-spacing"
},
fixable: "whitespace",
@ -48,7 +48,7 @@ module.exports = {
create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const tokensAndComments = sourceCode.tokensAndComments;
const options = {

View file

@ -19,7 +19,7 @@ module.exports = {
docs: {
description: "Enforce consistent comma style",
recommended: false,
url: "https://eslint.org/docs/rules/comma-style"
url: "https://eslint.org/docs/latest/rules/comma-style"
},
fixable: "code",
@ -51,7 +51,7 @@ module.exports = {
create(context) {
const style = context.options[0] || "last",
sourceCode = context.getSourceCode();
sourceCode = context.sourceCode;
const exceptions = {
ArrayPattern: true,
ArrowFunctionExpression: true,

View file

@ -25,7 +25,7 @@ module.exports = {
docs: {
description: "Enforce a maximum cyclomatic complexity allowed in a program",
recommended: false,
url: "https://eslint.org/docs/rules/complexity"
url: "https://eslint.org/docs/latest/rules/complexity"
},
schema: [

View file

@ -18,7 +18,7 @@ module.exports = {
docs: {
description: "Enforce consistent spacing inside computed property brackets",
recommended: false,
url: "https://eslint.org/docs/rules/computed-property-spacing"
url: "https://eslint.org/docs/latest/rules/computed-property-spacing"
},
fixable: "whitespace",
@ -49,7 +49,7 @@ module.exports = {
},
create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const propertyNameMustBeSpaced = context.options[0] === "always"; // default is "never"
const enforceForClassMembers = !context.options[1] || context.options[1].enforceForClassMembers;

View file

@ -48,7 +48,7 @@ module.exports = {
docs: {
description: "Require `return` statements to either always or never specify values",
recommended: false,
url: "https://eslint.org/docs/rules/consistent-return"
url: "https://eslint.org/docs/latest/rules/consistent-return"
},
schema: [{
@ -104,7 +104,7 @@ module.exports = {
} else if (node.type === "ArrowFunctionExpression") {
// `=>` token
loc = context.getSourceCode().getTokenBefore(node.body, astUtils.isArrowToken).loc;
loc = context.sourceCode.getTokenBefore(node.body, astUtils.isArrowToken).loc;
} else if (
node.parent.type === "MethodDefinition" ||
(node.parent.type === "Property" && node.parent.method)
@ -115,7 +115,7 @@ module.exports = {
} else {
// Function name or `function` keyword.
loc = (node.id || context.getSourceCode().getFirstToken(node)).loc;
loc = (node.id || context.sourceCode.getFirstToken(node)).loc;
}
if (!name) {

View file

@ -16,7 +16,7 @@ module.exports = {
docs: {
description: "Enforce consistent naming when capturing the current execution context",
recommended: false,
url: "https://eslint.org/docs/rules/consistent-this"
url: "https://eslint.org/docs/latest/rules/consistent-this"
},
schema: {
@ -36,6 +36,7 @@ module.exports = {
create(context) {
let aliases = [];
const sourceCode = context.sourceCode;
if (context.options.length === 0) {
aliases.push("that");
@ -115,10 +116,11 @@ module.exports = {
/**
* Check each alias to ensure that is was assigned to the correct value.
* @param {ASTNode} node The node that represents the scope to check.
* @returns {void}
*/
function ensureWasAssigned() {
const scope = context.getScope();
function ensureWasAssigned(node) {
const scope = sourceCode.getScope(node);
aliases.forEach(alias => {
checkWasAssigned(alias, scope);

View file

@ -124,7 +124,7 @@ module.exports = {
docs: {
description: "Require `super()` calls in constructors",
recommended: true,
url: "https://eslint.org/docs/rules/constructor-super"
url: "https://eslint.org/docs/latest/rules/constructor-super"
},
schema: [],

View file

@ -22,7 +22,7 @@ module.exports = {
docs: {
description: "Enforce consistent brace style for all control statements",
recommended: false,
url: "https://eslint.org/docs/rules/curly"
url: "https://eslint.org/docs/latest/rules/curly"
},
schema: {
@ -70,7 +70,7 @@ module.exports = {
const multiOrNest = (context.options[0] === "multi-or-nest");
const consistent = (context.options[1] === "consistent");
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
//--------------------------------------------------------------------------
// Helpers

View file

@ -17,7 +17,7 @@ module.exports = {
docs: {
description: "Enforce default clauses in switch statements to be last",
recommended: false,
url: "https://eslint.org/docs/rules/default-case-last"
url: "https://eslint.org/docs/latest/rules/default-case-last"
},
schema: [],

View file

@ -18,7 +18,7 @@ module.exports = {
docs: {
description: "Require `default` cases in `switch` statements",
recommended: false,
url: "https://eslint.org/docs/rules/default-case"
url: "https://eslint.org/docs/latest/rules/default-case"
},
schema: [{
@ -42,7 +42,7 @@ module.exports = {
? new RegExp(options.commentPattern, "u")
: DEFAULT_COMMENT_PATTERN;
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
//--------------------------------------------------------------------------
// Helpers

View file

@ -13,7 +13,7 @@ module.exports = {
docs: {
description: "Enforce default parameters to be last",
recommended: false,
url: "https://eslint.org/docs/rules/default-param-last"
url: "https://eslint.org/docs/latest/rules/default-param-last"
},
schema: [],

View file

@ -19,7 +19,7 @@ module.exports = {
docs: {
description: "Enforce consistent newlines before and after dots",
recommended: false,
url: "https://eslint.org/docs/rules/dot-location"
url: "https://eslint.org/docs/latest/rules/dot-location"
},
schema: [
@ -43,7 +43,7 @@ module.exports = {
// default to onObject if no preference is passed
const onObject = config === "object" || !config;
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
/**
* Reports if the dot between object and property is on the correct location.

View file

@ -28,7 +28,7 @@ module.exports = {
docs: {
description: "Enforce dot notation whenever possible",
recommended: false,
url: "https://eslint.org/docs/rules/dot-notation"
url: "https://eslint.org/docs/latest/rules/dot-notation"
},
schema: [
@ -59,7 +59,7 @@ module.exports = {
create(context) {
const options = context.options[0] || {};
const allowKeywords = options.allowKeywords === void 0 || options.allowKeywords;
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
let allowPattern;
@ -133,8 +133,7 @@ module.exports = {
}
if (
node.computed &&
node.property.type === "TemplateLiteral" &&
node.property.expressions.length === 0
astUtils.isStaticTemplateLiteral(node.property)
) {
checkComputedProperty(node, node.property.quasis[0].value.cooked);
}

View file

@ -16,7 +16,7 @@ module.exports = {
docs: {
description: "Require or disallow newline at the end of files",
recommended: false,
url: "https://eslint.org/docs/rules/eol-last"
url: "https://eslint.org/docs/latest/rules/eol-last"
},
fixable: "whitespace",
@ -40,7 +40,7 @@ module.exports = {
return {
Program: function checkBadEOF(node) {
const sourceCode = context.getSourceCode(),
const sourceCode = context.sourceCode,
src = sourceCode.getText(),
lastLine = sourceCode.lines[sourceCode.lines.length - 1],
location = {

View file

@ -23,7 +23,7 @@ module.exports = {
docs: {
description: "Require the use of `===` and `!==`",
recommended: false,
url: "https://eslint.org/docs/rules/eqeqeq"
url: "https://eslint.org/docs/latest/rules/eqeqeq"
},
schema: {
@ -68,7 +68,7 @@ module.exports = {
create(context) {
const config = context.options[0] || "always";
const options = context.options[1] || {};
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const nullOption = (config === "always")
? options.null || "always"

View file

@ -17,7 +17,7 @@ module.exports = {
docs: {
description: "Enforce \"for\" loop update clause moving the counter in the right direction",
recommended: true,
url: "https://eslint.org/docs/rules/for-direction"
url: "https://eslint.org/docs/latest/rules/for-direction"
},
fixable: null,

View file

@ -23,7 +23,7 @@ module.exports = {
docs: {
description: "Require or disallow spacing between function identifiers and their invocations",
recommended: false,
url: "https://eslint.org/docs/rules/func-call-spacing"
url: "https://eslint.org/docs/latest/rules/func-call-spacing"
},
fixable: "whitespace",
@ -73,7 +73,7 @@ module.exports = {
const never = context.options[0] !== "always";
const allowNewlines = !never && context.options[1] && context.options[1].allowNewlines;
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const text = sourceCode.getText();
/**

View file

@ -76,7 +76,7 @@ module.exports = {
docs: {
description: "Require function names to match the name of the variable or property to which they are assigned",
recommended: false,
url: "https://eslint.org/docs/rules/func-name-matching"
url: "https://eslint.org/docs/latest/rules/func-name-matching"
},
schema: {

View file

@ -32,7 +32,7 @@ module.exports = {
docs: {
description: "Require or disallow named `function` expressions",
recommended: false,
url: "https://eslint.org/docs/rules/func-names"
url: "https://eslint.org/docs/latest/rules/func-names"
},
schema: {
@ -69,7 +69,7 @@ module.exports = {
create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
/**
* Returns the config option for the given node.
@ -159,7 +159,7 @@ module.exports = {
function handleFunction(node) {
// Skip recursive functions.
const nameVar = context.getDeclaredVariables(node)[0];
const nameVar = sourceCode.getDeclaredVariables(node)[0];
if (isFunctionName(nameVar) && nameVar.references.length > 0) {
return;

View file

@ -16,7 +16,7 @@ module.exports = {
docs: {
description: "Enforce the consistent use of either `function` declarations or expressions",
recommended: false,
url: "https://eslint.org/docs/rules/func-style"
url: "https://eslint.org/docs/latest/rules/func-style"
},
schema: [

View file

@ -17,7 +17,7 @@ module.exports = {
docs: {
description: "Enforce line breaks between arguments of a function call",
recommended: false,
url: "https://eslint.org/docs/rules/function-call-argument-newline"
url: "https://eslint.org/docs/latest/rules/function-call-argument-newline"
},
fixable: "whitespace",
@ -35,7 +35,7 @@ module.exports = {
},
create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const checkers = {
unexpected: {

View file

@ -22,7 +22,7 @@ module.exports = {
docs: {
description: "Enforce consistent line breaks inside function parentheses",
recommended: false,
url: "https://eslint.org/docs/rules/function-paren-newline"
url: "https://eslint.org/docs/latest/rules/function-paren-newline"
},
fixable: "whitespace",
@ -57,7 +57,7 @@ module.exports = {
},
create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const rawOption = context.options[0] || "multiline";
const multilineOption = rawOption === "multiline";
const multilineArgumentsOption = rawOption === "multiline-arguments";

View file

@ -33,7 +33,7 @@ module.exports = {
docs: {
description: "Enforce consistent spacing around `*` operators in generator functions",
recommended: false,
url: "https://eslint.org/docs/rules/generator-star-spacing"
url: "https://eslint.org/docs/latest/rules/generator-star-spacing"
},
fixable: "whitespace",
@ -102,7 +102,7 @@ module.exports = {
};
}(context.options[0] || {}));
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
/**
* Checks if the given token is a star token or not.

View file

@ -37,7 +37,7 @@ module.exports = {
docs: {
description: "Enforce `return` statements in getters",
recommended: true,
url: "https://eslint.org/docs/rules/getter-return"
url: "https://eslint.org/docs/latest/rules/getter-return"
},
fixable: null,
@ -64,7 +64,7 @@ module.exports = {
create(context) {
const options = context.options[0] || { allowImplicit: false };
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
let funcInfo = {
upper: null,

View file

@ -61,7 +61,7 @@ module.exports = {
docs: {
description: "Require `require()` calls to be placed at top-level module scope",
recommended: false,
url: "https://eslint.org/docs/rules/global-require"
url: "https://eslint.org/docs/latest/rules/global-require"
},
schema: [],
@ -71,12 +71,14 @@ module.exports = {
},
create(context) {
const sourceCode = context.sourceCode;
return {
CallExpression(node) {
const currentScope = context.getScope();
const currentScope = sourceCode.getScope(node);
if (node.callee.name === "require" && !isShadowed(currentScope, node.callee)) {
const isGoodRequire = context.getAncestors().every(parent => ACCEPTABLE_PARENTS.has(parent.type));
const isGoodRequire = sourceCode.getAncestors(node).every(parent => ACCEPTABLE_PARENTS.has(parent.type));
if (!isGoodRequire) {
context.report({ node, messageId: "unexpected" });

View file

@ -98,7 +98,7 @@ module.exports = {
docs: {
description: "Require grouped accessor pairs in object literals and classes",
recommended: false,
url: "https://eslint.org/docs/rules/grouped-accessor-pairs"
url: "https://eslint.org/docs/latest/rules/grouped-accessor-pairs"
},
schema: [
@ -115,7 +115,7 @@ module.exports = {
create(context) {
const order = context.options[0] || "anyOrder";
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
/**
* Reports the given accessor pair.
@ -137,43 +137,6 @@ module.exports = {
});
}
/**
* Creates a new `AccessorData` object for the given getter or setter node.
* @param {ASTNode} node A getter or setter node.
* @returns {AccessorData} New `AccessorData` object that contains the given node.
* @private
*/
function createAccessorData(node) {
const name = astUtils.getStaticPropertyName(node);
const key = (name !== null) ? name : sourceCode.getTokens(node.key);
return {
key,
getters: node.kind === "get" ? [node] : [],
setters: node.kind === "set" ? [node] : []
};
}
/**
* Merges the given `AccessorData` object into the given accessors list.
* @param {AccessorData[]} accessors The list to merge into.
* @param {AccessorData} accessorData The object to merge.
* @returns {AccessorData[]} The same instance with the merged object.
* @private
*/
function mergeAccessorData(accessors, accessorData) {
const equalKeyElement = accessors.find(a => areEqualKeys(a.key, accessorData.key));
if (equalKeyElement) {
equalKeyElement.getters.push(...accessorData.getters);
equalKeyElement.setters.push(...accessorData.setters);
} else {
accessors.push(accessorData);
}
return accessors;
}
/**
* Checks accessor pairs in the given list of nodes.
* @param {ASTNode[]} nodes The list to check.
@ -182,11 +145,39 @@ module.exports = {
* @private
*/
function checkList(nodes, shouldCheck) {
const accessors = nodes
.filter(shouldCheck)
.filter(isAccessorKind)
.map(createAccessorData)
.reduce(mergeAccessorData, []);
const accessors = [];
let found = false;
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (shouldCheck(node) && isAccessorKind(node)) {
// Creates a new `AccessorData` object for the given getter or setter node.
const name = astUtils.getStaticPropertyName(node);
const key = (name !== null) ? name : sourceCode.getTokens(node.key);
// Merges the given `AccessorData` object into the given accessors list.
for (let j = 0; j < accessors.length; j++) {
const accessor = accessors[j];
if (areEqualKeys(accessor.key, key)) {
accessor.getters.push(...node.kind === "get" ? [node] : []);
accessor.setters.push(...node.kind === "set" ? [node] : []);
found = true;
break;
}
}
if (!found) {
accessors.push({
key,
getters: node.kind === "get" ? [node] : [],
setters: node.kind === "set" ? [node] : []
});
}
found = false;
}
}
for (const { getters, setters } of accessors) {

View file

@ -17,7 +17,7 @@ module.exports = {
docs: {
description: "Require `for-in` loops to include an `if` statement",
recommended: false,
url: "https://eslint.org/docs/rules/guard-for-in"
url: "https://eslint.org/docs/latest/rules/guard-for-in"
},
schema: [],

View file

@ -22,7 +22,7 @@ module.exports = {
docs: {
description: "Require error handling in callbacks",
recommended: false,
url: "https://eslint.org/docs/rules/handle-callback-err"
url: "https://eslint.org/docs/latest/rules/handle-callback-err"
},
schema: [
@ -38,6 +38,7 @@ module.exports = {
create(context) {
const errorArgument = context.options[0] || "err";
const sourceCode = context.sourceCode;
/**
* Checks if the given argument should be interpreted as a regexp pattern.
@ -79,7 +80,7 @@ module.exports = {
* @returns {void}
*/
function checkForError(node) {
const scope = context.getScope(),
const scope = sourceCode.getScope(node),
parameters = getParameters(scope),
firstParameter = parameters[0];

View file

@ -121,7 +121,7 @@ module.exports = {
docs: {
description: "Disallow specified identifiers",
recommended: false,
url: "https://eslint.org/docs/rules/id-blacklist"
url: "https://eslint.org/docs/latest/rules/id-blacklist"
},
schema: {
@ -140,6 +140,7 @@ module.exports = {
const denyList = new Set(context.options);
const reportedNodes = new Set();
const sourceCode = context.sourceCode;
let globalScope;
@ -231,8 +232,8 @@ module.exports = {
return {
Program() {
globalScope = context.getScope();
Program(node) {
globalScope = sourceCode.getScope(node);
},
Identifier(node) {

View file

@ -101,7 +101,7 @@ module.exports = {
docs: {
description: "Disallow specified identifiers",
recommended: false,
url: "https://eslint.org/docs/rules/id-denylist"
url: "https://eslint.org/docs/latest/rules/id-denylist"
},
schema: {
@ -121,6 +121,7 @@ module.exports = {
const denyList = new Set(context.options);
const reportedNodes = new Set();
const sourceCode = context.sourceCode;
let globalScope;
@ -210,8 +211,8 @@ module.exports = {
return {
Program() {
globalScope = context.getScope();
Program(node) {
globalScope = sourceCode.getScope(node);
},
[[

View file

@ -9,41 +9,8 @@
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const GraphemeSplitter = require("grapheme-splitter");
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
/**
* Checks if the string given as argument is ASCII or not.
* @param {string} value A string that you want to know if it is ASCII or not.
* @returns {boolean} `true` if `value` is ASCII string.
*/
function isASCII(value) {
if (typeof value !== "string") {
return false;
}
return /^[\u0020-\u007f]*$/u.test(value);
}
/** @type {GraphemeSplitter | undefined} */
let splitter;
/**
* Gets the length of the string. If the string is not in ASCII, counts graphemes.
* @param {string} value A string that you want to get the length.
* @returns {number} The length of `value`.
*/
function getStringLength(value) {
if (isASCII(value)) {
return value.length;
}
if (!splitter) {
splitter = new GraphemeSplitter();
}
return splitter.countGraphemes(value);
}
const { getGraphemeCount } = require("../shared/string-utils");
//------------------------------------------------------------------------------
// Rule Definition
@ -57,7 +24,7 @@ module.exports = {
docs: {
description: "Enforce minimum and maximum identifier lengths",
recommended: false,
url: "https://eslint.org/docs/rules/id-length"
url: "https://eslint.org/docs/latest/rules/id-length"
},
schema: [
@ -169,7 +136,7 @@ module.exports = {
const name = node.name;
const parent = node.parent;
const nameLength = getStringLength(name);
const nameLength = getGraphemeCount(name);
const isShort = nameLength < minLength;
const isLong = nameLength > maxLength;

View file

@ -17,7 +17,7 @@ module.exports = {
docs: {
description: "Require identifiers to match a specified regular expression",
recommended: false,
url: "https://eslint.org/docs/rules/id-match"
url: "https://eslint.org/docs/latest/rules/id-match"
},
schema: [
@ -67,6 +67,7 @@ module.exports = {
onlyDeclarations = !!options.onlyDeclarations,
ignoreDestructuring = !!options.ignoreDestructuring;
const sourceCode = context.sourceCode;
let globalScope;
//--------------------------------------------------------------------------
@ -170,8 +171,8 @@ module.exports = {
return {
Program() {
globalScope = context.getScope();
Program(node) {
globalScope = sourceCode.getScope(node);
},
Identifier(node) {

View file

@ -17,7 +17,7 @@ module.exports = {
docs: {
description: "Enforce the location of arrow function bodies",
recommended: false,
url: "https://eslint.org/docs/rules/implicit-arrow-linebreak"
url: "https://eslint.org/docs/latest/rules/implicit-arrow-linebreak"
},
fixable: "whitespace",
@ -34,7 +34,7 @@ module.exports = {
},
create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const option = context.options[0] || "beside";
/**

View file

@ -28,7 +28,7 @@ module.exports = {
docs: {
description: "Enforce consistent indentation",
recommended: false,
url: "https://eslint.org/docs/rules/indent-legacy"
url: "https://eslint.org/docs/latest/rules/indent-legacy"
},
deprecated: true,
@ -206,7 +206,7 @@ module.exports = {
ObjectExpression: 1
};
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
if (context.options.length) {
if (context.options[0] === "tab") {

View file

@ -12,8 +12,6 @@
// Requirements
//------------------------------------------------------------------------------
const { OrderedMap } = require("js-sdsl");
const astUtils = require("./utils/ast-utils");
//------------------------------------------------------------------------------
@ -125,43 +123,48 @@ const KNOWN_NODES = new Set([
/**
* A mutable balanced binary search tree that stores (key, value) pairs. The keys are numeric, and must be unique.
* This is intended to be a generic wrapper around a balanced binary search tree library, so that the underlying implementation
* A mutable map that stores (key, value) pairs. The keys are numeric indices, and must be unique.
* This is intended to be a generic wrapper around a map with non-negative integer keys, so that the underlying implementation
* can easily be swapped out.
*/
class BinarySearchTree {
class IndexMap {
/**
* Creates an empty tree
* Creates an empty map
* @param {number} maxKey The maximum key
*/
constructor() {
this._orderedMap = new OrderedMap();
this._orderedMapEnd = this._orderedMap.end();
constructor(maxKey) {
// Initializing the array with the maximum expected size avoids dynamic reallocations that could degrade performance.
this._values = Array(maxKey + 1);
}
/**
* Inserts an entry into the tree.
* Inserts an entry into the map.
* @param {number} key The entry's key
* @param {any} value The entry's value
* @returns {void}
*/
insert(key, value) {
this._orderedMap.setElement(key, value);
this._values[key] = value;
}
/**
* Finds the entry with the largest key less than or equal to the provided key
* Finds the value of the entry with the largest key less than or equal to the provided key
* @param {number} key The provided key
* @returns {{key: number, value: *}|null} The found entry, or null if no such entry exists.
* @returns {*|undefined} The value of the found entry, or undefined if no such entry exists.
*/
findLe(key) {
const iterator = this._orderedMap.reverseLowerBound(key);
findLastNotAfter(key) {
const values = this._values;
if (iterator.equals(this._orderedMapEnd)) {
return {};
for (let index = key; index >= 0; index--) {
const value = values[index];
if (value) {
return value;
}
}
return { key: iterator.pointer[0], value: iterator.pointer[1] };
return void 0;
}
/**
@ -171,26 +174,7 @@ class BinarySearchTree {
* @returns {void}
*/
deleteRange(start, end) {
// Exit without traversing the tree if the range has zero size.
if (start === end) {
return;
}
const iterator = this._orderedMap.lowerBound(start);
if (iterator.equals(this._orderedMapEnd)) {
return;
}
if (end > this._orderedMap.back()[0]) {
while (!iterator.equals(this._orderedMapEnd)) {
this._orderedMap.eraseElementByIterator(iterator);
}
} else {
while (iterator.pointer[0] < end) {
this._orderedMap.eraseElementByIterator(iterator);
}
}
this._values.fill(void 0, start, end);
}
}
@ -204,15 +188,19 @@ class TokenInfo {
*/
constructor(sourceCode) {
this.sourceCode = sourceCode;
this.firstTokensByLineNumber = sourceCode.tokensAndComments.reduce((map, token) => {
if (!map.has(token.loc.start.line)) {
map.set(token.loc.start.line, token);
this.firstTokensByLineNumber = new Map();
const tokens = sourceCode.tokensAndComments;
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
if (!this.firstTokensByLineNumber.has(token.loc.start.line)) {
this.firstTokensByLineNumber.set(token.loc.start.line, token);
}
if (!map.has(token.loc.end.line) && sourceCode.text.slice(token.range[1] - token.loc.end.column, token.range[1]).trim()) {
map.set(token.loc.end.line, token);
if (!this.firstTokensByLineNumber.has(token.loc.end.line) && sourceCode.text.slice(token.range[1] - token.loc.end.column, token.range[1]).trim()) {
this.firstTokensByLineNumber.set(token.loc.end.line, token);
}
return map;
}, new Map());
}
}
/**
@ -252,14 +240,15 @@ class OffsetStorage {
* @param {TokenInfo} tokenInfo a TokenInfo instance
* @param {number} indentSize The desired size of each indentation level
* @param {string} indentType The indentation character
* @param {number} maxIndex The maximum end index of any token
*/
constructor(tokenInfo, indentSize, indentType) {
constructor(tokenInfo, indentSize, indentType, maxIndex) {
this._tokenInfo = tokenInfo;
this._indentSize = indentSize;
this._indentType = indentType;
this._tree = new BinarySearchTree();
this._tree.insert(0, { offset: 0, from: null, force: false });
this._indexMap = new IndexMap(maxIndex);
this._indexMap.insert(0, { offset: 0, from: null, force: false });
this._lockedFirstTokens = new WeakMap();
this._desiredIndentCache = new WeakMap();
@ -267,7 +256,7 @@ class OffsetStorage {
}
_getOffsetDescriptor(token) {
return this._tree.findLe(token.range[0]).value;
return this._indexMap.findLastNotAfter(token.range[0]);
}
/**
@ -388,37 +377,36 @@ class OffsetStorage {
* * key: 820, value: { offset: 1, from: bazToken }
*
* To find the offset descriptor for any given token, one needs to find the node with the largest key
* which is <= token.start. To make this operation fast, the nodes are stored in a balanced binary
* search tree indexed by key.
* which is <= token.start. To make this operation fast, the nodes are stored in a map indexed by key.
*/
const descriptorToInsert = { offset, from: fromToken, force };
const descriptorAfterRange = this._tree.findLe(range[1]).value;
const descriptorAfterRange = this._indexMap.findLastNotAfter(range[1]);
const fromTokenIsInRange = fromToken && fromToken.range[0] >= range[0] && fromToken.range[1] <= range[1];
const fromTokenDescriptor = fromTokenIsInRange && this._getOffsetDescriptor(fromToken);
// First, remove any existing nodes in the range from the tree.
this._tree.deleteRange(range[0] + 1, range[1]);
// First, remove any existing nodes in the range from the map.
this._indexMap.deleteRange(range[0] + 1, range[1]);
// Insert a new node into the tree for this range
this._tree.insert(range[0], descriptorToInsert);
// Insert a new node into the map for this range
this._indexMap.insert(range[0], descriptorToInsert);
/*
* To avoid circular offset dependencies, keep the `fromToken` token mapped to whatever it was mapped to previously,
* even if it's in the current range.
*/
if (fromTokenIsInRange) {
this._tree.insert(fromToken.range[0], fromTokenDescriptor);
this._tree.insert(fromToken.range[1], descriptorToInsert);
this._indexMap.insert(fromToken.range[0], fromTokenDescriptor);
this._indexMap.insert(fromToken.range[1], descriptorToInsert);
}
/*
* To avoid modifying the offset of tokens after the range, insert another node to keep the offset of the following
* tokens the same as it was before.
*/
this._tree.insert(range[1], descriptorAfterRange);
this._indexMap.insert(range[1], descriptorAfterRange);
}
/**
@ -510,7 +498,7 @@ module.exports = {
docs: {
description: "Enforce consistent indentation",
recommended: false,
url: "https://eslint.org/docs/rules/indent"
url: "https://eslint.org/docs/latest/rules/indent"
},
fixable: "whitespace",
@ -703,9 +691,9 @@ module.exports = {
}
}
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const tokenInfo = new TokenInfo(sourceCode);
const offsets = new OffsetStorage(tokenInfo, indentSize, indentType === "space" ? " " : "\t");
const offsets = new OffsetStorage(tokenInfo, indentSize, indentType === "space" ? " " : "\t", sourceCode.text.length);
const parameterParens = new WeakSet();
/**
@ -980,19 +968,19 @@ module.exports = {
const parenStack = [];
const parenPairs = [];
tokens.forEach(nextToken => {
for (let i = 0; i < tokens.length; i++) {
const nextToken = tokens[i];
// Accumulate a list of parenthesis pairs
if (astUtils.isOpeningParenToken(nextToken)) {
parenStack.push(nextToken);
} else if (astUtils.isClosingParenToken(nextToken)) {
parenPairs.unshift({ left: parenStack.pop(), right: nextToken });
parenPairs.push({ left: parenStack.pop(), right: nextToken });
}
});
}
parenPairs.forEach(pair => {
const leftParen = pair.left;
const rightParen = pair.right;
for (let i = parenPairs.length - 1; i >= 0; i--) {
const leftParen = parenPairs[i].left;
const rightParen = parenPairs[i].right;
// We only want to handle parens around expressions, so exclude parentheses that are in function parameters and function call arguments.
if (!parameterParens.has(leftParen) && !parameterParens.has(rightParen)) {
@ -1006,7 +994,7 @@ module.exports = {
}
offsets.setDesiredOffset(rightParen, leftParen, 0);
});
}
}
/**
@ -1727,9 +1715,13 @@ module.exports = {
}
// Invoke the queued offset listeners for the nodes that aren't ignored.
listenerCallQueue
.filter(nodeInfo => !ignoredNodes.has(nodeInfo.node))
.forEach(nodeInfo => nodeInfo.listener(nodeInfo.node));
for (let i = 0; i < listenerCallQueue.length; i++) {
const nodeInfo = listenerCallQueue[i];
if (!ignoredNodes.has(nodeInfo.node)) {
nodeInfo.listener(nodeInfo.node);
}
}
// Update the offsets for ignored nodes to prevent their child tokens from being reported.
ignoredNodes.forEach(ignoreNode);
@ -1740,27 +1732,31 @@ module.exports = {
* Create a Map from (tokenOrComment) => (precedingToken).
* This is necessary because sourceCode.getTokenBefore does not handle a comment as an argument correctly.
*/
const precedingTokens = sourceCode.ast.comments.reduce((commentMap, comment) => {
const precedingTokens = new WeakMap();
for (let i = 0; i < sourceCode.ast.comments.length; i++) {
const comment = sourceCode.ast.comments[i];
const tokenOrCommentBefore = sourceCode.getTokenBefore(comment, { includeComments: true });
const hasToken = precedingTokens.has(tokenOrCommentBefore) ? precedingTokens.get(tokenOrCommentBefore) : tokenOrCommentBefore;
return commentMap.set(comment, commentMap.has(tokenOrCommentBefore) ? commentMap.get(tokenOrCommentBefore) : tokenOrCommentBefore);
}, new WeakMap());
precedingTokens.set(comment, hasToken);
}
sourceCode.lines.forEach((line, lineIndex) => {
const lineNumber = lineIndex + 1;
for (let i = 1; i < sourceCode.lines.length + 1; i++) {
if (!tokenInfo.firstTokensByLineNumber.has(lineNumber)) {
if (!tokenInfo.firstTokensByLineNumber.has(i)) {
// Don't check indentation on blank lines
return;
continue;
}
const firstTokenOfLine = tokenInfo.firstTokensByLineNumber.get(lineNumber);
const firstTokenOfLine = tokenInfo.firstTokensByLineNumber.get(i);
if (firstTokenOfLine.loc.start.line !== lineNumber) {
if (firstTokenOfLine.loc.start.line !== i) {
// Don't check the indentation of multi-line tokens (e.g. template literals or block comments) twice.
return;
continue;
}
if (astUtils.isCommentToken(firstTokenOfLine)) {
@ -1785,18 +1781,18 @@ module.exports = {
mayAlignWithBefore && validateTokenIndent(firstTokenOfLine, offsets.getDesiredIndent(tokenBefore)) ||
mayAlignWithAfter && validateTokenIndent(firstTokenOfLine, offsets.getDesiredIndent(tokenAfter))
) {
return;
continue;
}
}
// If the token matches the expected indentation, don't report it.
if (validateTokenIndent(firstTokenOfLine, offsets.getDesiredIndent(firstTokenOfLine))) {
return;
continue;
}
// Otherwise, report the token/comment.
report(firstTokenOfLine, offsets.getDesiredIndent(firstTokenOfLine));
});
}
}
}
);

View file

@ -50,7 +50,7 @@ module.exports = {
docs: {
description: "Require or disallow initialization in variable declarations",
recommended: false,
url: "https://eslint.org/docs/rules/init-declarations"
url: "https://eslint.org/docs/latest/rules/init-declarations"
},
schema: {

View file

@ -44,7 +44,7 @@ module.exports = {
docs: {
description: "Enforce the consistent use of either double or single quotes in JSX attributes",
recommended: false,
url: "https://eslint.org/docs/rules/jsx-quotes"
url: "https://eslint.org/docs/latest/rules/jsx-quotes"
},
fixable: "whitespace",

View file

@ -9,13 +9,7 @@
//------------------------------------------------------------------------------
const astUtils = require("./utils/ast-utils");
const GraphemeSplitter = require("grapheme-splitter");
const splitter = new GraphemeSplitter();
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
const { getGraphemeCount } = require("../shared/string-utils");
/**
* Checks whether a string contains a line terminator as defined in
@ -144,7 +138,7 @@ module.exports = {
docs: {
description: "Enforce consistent spacing between keys and values in object literal properties",
recommended: false,
url: "https://eslint.org/docs/rules/key-spacing"
url: "https://eslint.org/docs/latest/rules/key-spacing"
},
fixable: "whitespace",
@ -332,7 +326,7 @@ module.exports = {
singleLineOptions = ruleOptions.singleLine,
alignmentOptions = ruleOptions.align || null;
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
/**
* Determines if the given property is key-value property.
@ -523,7 +517,7 @@ module.exports = {
const startToken = sourceCode.getFirstToken(property);
const endToken = getLastTokenBeforeColon(property.key);
return splitter.countGraphemes(sourceCode.getText().slice(startToken.range[0], endToken.range[1]));
return getGraphemeCount(sourceCode.getText().slice(startToken.range[0], endToken.range[1]));
}
/**

View file

@ -69,7 +69,7 @@ module.exports = {
docs: {
description: "Enforce consistent spacing before and after keywords",
recommended: false,
url: "https://eslint.org/docs/rules/keyword-spacing"
url: "https://eslint.org/docs/latest/rules/keyword-spacing"
},
fixable: "whitespace",
@ -108,7 +108,7 @@ module.exports = {
},
create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const tokensToIgnore = new WeakSet();

View file

@ -18,7 +18,7 @@ module.exports = {
docs: {
description: "Enforce position of line comments",
recommended: false,
url: "https://eslint.org/docs/rules/line-comment-position"
url: "https://eslint.org/docs/latest/rules/line-comment-position"
},
schema: [
@ -78,7 +78,7 @@ module.exports = {
const defaultIgnoreRegExp = astUtils.COMMENTS_IGNORE_PATTERN;
const fallThroughRegExp = /^\s*falls?\s?through/u;
const customIgnoreRegExp = new RegExp(ignorePattern, "u");
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
//--------------------------------------------------------------------------
// Public

View file

@ -23,7 +23,7 @@ module.exports = {
docs: {
description: "Enforce consistent linebreak style",
recommended: false,
url: "https://eslint.org/docs/rules/linebreak-style"
url: "https://eslint.org/docs/latest/rules/linebreak-style"
},
fixable: "whitespace",
@ -40,7 +40,7 @@ module.exports = {
},
create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
//--------------------------------------------------------------------------
// Helpers

View file

@ -57,7 +57,7 @@ module.exports = {
docs: {
description: "Require empty lines around comments",
recommended: false,
url: "https://eslint.org/docs/rules/lines-around-comment"
url: "https://eslint.org/docs/latest/rules/lines-around-comment"
},
fixable: "whitespace",
@ -113,6 +113,10 @@ module.exports = {
},
applyDefaultIgnorePatterns: {
type: "boolean"
},
afterHashbangComment: {
type: "boolean",
default: false
}
},
additionalProperties: false
@ -134,7 +138,7 @@ module.exports = {
options.beforeBlockComment = typeof options.beforeBlockComment !== "undefined" ? options.beforeBlockComment : true;
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const lines = sourceCode.lines,
numLines = lines.length + 1,
@ -449,6 +453,13 @@ module.exports = {
before: options.beforeBlockComment
});
}
} else if (token.type === "Shebang") {
if (options.afterHashbangComment) {
checkForEmptyLine(token, {
after: options.afterHashbangComment,
before: false
});
}
}
});
}

View file

@ -20,7 +20,7 @@ module.exports = {
docs: {
description: "Require or disallow newlines around directives",
recommended: false,
url: "https://eslint.org/docs/rules/lines-around-directive"
url: "https://eslint.org/docs/latest/rules/lines-around-directive"
},
schema: [{
@ -54,7 +54,7 @@ module.exports = {
},
create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const config = context.options[0] || "always";
const expectLineBefore = typeof config === "string" ? config : config.before;
const expectLineAfter = typeof config === "string" ? config : config.after;

View file

@ -22,7 +22,7 @@ module.exports = {
docs: {
description: "Require or disallow an empty line between class members",
recommended: false,
url: "https://eslint.org/docs/rules/lines-between-class-members"
url: "https://eslint.org/docs/latest/rules/lines-between-class-members"
},
fixable: "whitespace",
@ -55,7 +55,7 @@ module.exports = {
options[0] = context.options[0] || "always";
options[1] = context.options[1] || { exceptAfterSingleLine: false };
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
/**
* Gets a pair of tokens that should be used to check lines between two class member nodes.

View file

@ -112,7 +112,7 @@ function isBooleanCast(expression, scope) {
/**
* Returns true for:
* truthiness checks: value, Boolean(value), !!value
* falsyness checks: !value, !Boolean(value)
* falsiness checks: !value, !Boolean(value)
* nullish checks: value == null, value === undefined || value === null
* @param {ASTNode} expression Test condition
* @param {import('eslint-scope').Scope} scope Scope of the expression
@ -159,9 +159,9 @@ module.exports = {
type: "suggestion",
docs: {
description: "Require or disallow logical assignment logical operator shorthand",
description: "Require or disallow logical assignment operator shorthand",
recommended: false,
url: "https://eslint.org/docs/rules/logical-assignment-operators"
url: "https://eslint.org/docs/latest/rules/logical-assignment-operators"
},
schema: {
@ -188,7 +188,6 @@ module.exports = {
}]
},
fixable: "code",
// eslint-disable-next-line eslint-plugin/require-meta-has-suggestions -- Does not detect conditional suggestions
hasSuggestions: true,
messages: {
assignment: "Assignment (=) can be replaced with operator assignment ({{operator}}).",
@ -205,8 +204,8 @@ module.exports = {
create(context) {
const mode = context.options[0] === "never" ? "never" : "always";
const checkIf = mode === "always" && context.options.length > 1 && context.options[1].enforceForIfStatements;
const sourceCode = context.getSourceCode();
const isStrict = context.getScope().isStrict;
const sourceCode = context.sourceCode;
const isStrict = sourceCode.getScope(sourceCode.ast).isStrict;
/**
* Returns false if the access could be a getter
@ -371,8 +370,11 @@ module.exports = {
return;
}
const requiresOuterParenthesis = logical.parent.type !== "ExpressionStatement" &&
(astUtils.getPrecedence({ type: "AssignmentExpression" }) < astUtils.getPrecedence(logical.parent));
const parentPrecedence = astUtils.getPrecedence(logical.parent);
const requiresOuterParenthesis = logical.parent.type !== "ExpressionStatement" && (
parentPrecedence === -1 ||
astUtils.getPrecedence({ type: "AssignmentExpression" }) < parentPrecedence
);
if (!astUtils.isParenthesised(sourceCode, logical) && requiresOuterParenthesis) {
yield ruleFixer.insertTextBefore(logical, "(");
@ -409,7 +411,7 @@ module.exports = {
}
const body = hasBody ? ifNode.consequent.body[0] : ifNode.consequent;
const scope = context.getScope();
const scope = sourceCode.getScope(ifNode);
const existence = getExistence(ifNode.test, scope);
if (

View file

@ -21,7 +21,7 @@ module.exports = {
docs: {
description: "Enforce a maximum number of classes per file",
recommended: false,
url: "https://eslint.org/docs/rules/max-classes-per-file"
url: "https://eslint.org/docs/latest/rules/max-classes-per-file"
},
schema: [

View file

@ -17,7 +17,7 @@ module.exports = {
docs: {
description: "Enforce a maximum depth that blocks can be nested",
recommended: false,
url: "https://eslint.org/docs/rules/max-depth"
url: "https://eslint.org/docs/latest/rules/max-depth"
},
schema: [

View file

@ -71,7 +71,7 @@ module.exports = {
docs: {
description: "Enforce a maximum line length",
recommended: false,
url: "https://eslint.org/docs/rules/max-len"
url: "https://eslint.org/docs/latest/rules/max-len"
},
schema: [
@ -97,7 +97,7 @@ module.exports = {
*/
const URL_REGEXP = /[^:/?#]:\/\/[^?#]/u;
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
/**
* Computes the length of a line that may contain tabs. The width of each
@ -252,19 +252,23 @@ module.exports = {
return sourceCode.ast.tokens.filter(token => token.type === "RegularExpression");
}
/**
* A reducer to group an AST node by line number, both start and end.
* @param {Object} acc the accumulator
* @param {ASTNode} node the AST node in question
* @returns {Object} the modified accumulator
* @private
*
* reduce an array of AST nodes by line number, both start and end.
* @param {ASTNode[]} arr array of AST nodes
* @returns {Object} accululated AST nodes
*/
function groupByLineNumber(acc, node) {
for (let i = node.loc.start.line; i <= node.loc.end.line; ++i) {
ensureArrayAndPush(acc, i, node);
function groupArrayByLineNumber(arr) {
const obj = {};
for (let i = 0; i < arr.length; i++) {
const node = arr[i];
for (let j = node.loc.start.line; j <= node.loc.end.line; ++j) {
ensureArrayAndPush(obj, j, node);
}
}
return acc;
return obj;
}
/**
@ -312,13 +316,13 @@ module.exports = {
let commentsIndex = 0;
const strings = getAllStrings();
const stringsByLine = strings.reduce(groupByLineNumber, {});
const stringsByLine = groupArrayByLineNumber(strings);
const templateLiterals = getAllTemplateLiterals();
const templateLiteralsByLine = templateLiterals.reduce(groupByLineNumber, {});
const templateLiteralsByLine = groupArrayByLineNumber(templateLiterals);
const regExpLiterals = getAllRegExpLiterals();
const regExpLiteralsByLine = regExpLiterals.reduce(groupByLineNumber, {});
const regExpLiteralsByLine = groupArrayByLineNumber(regExpLiterals);
lines.forEach((line, i) => {

View file

@ -73,7 +73,7 @@ module.exports = {
docs: {
description: "Enforce a maximum number of lines of code in a function",
recommended: false,
url: "https://eslint.org/docs/rules/max-lines-per-function"
url: "https://eslint.org/docs/latest/rules/max-lines-per-function"
},
schema: [
@ -85,7 +85,7 @@ module.exports = {
},
create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const lines = sourceCode.lines;
const option = context.options[0];

View file

@ -36,7 +36,7 @@ module.exports = {
docs: {
description: "Enforce a maximum number of lines per file",
recommended: false,
url: "https://eslint.org/docs/rules/max-lines"
url: "https://eslint.org/docs/latest/rules/max-lines"
},
schema: [
@ -87,7 +87,7 @@ module.exports = {
const skipComments = option && option.skipComments;
const skipBlankLines = option && option.skipBlankLines;
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
/**
* Returns whether or not a token is a comment node type

View file

@ -17,7 +17,7 @@ module.exports = {
docs: {
description: "Enforce a maximum depth that callbacks can be nested",
recommended: false,
url: "https://eslint.org/docs/rules/max-nested-callbacks"
url: "https://eslint.org/docs/latest/rules/max-nested-callbacks"
},
schema: [

View file

@ -24,7 +24,7 @@ module.exports = {
docs: {
description: "Enforce a maximum number of parameters in function definitions",
recommended: false,
url: "https://eslint.org/docs/rules/max-params"
url: "https://eslint.org/docs/latest/rules/max-params"
},
schema: [
@ -57,7 +57,7 @@ module.exports = {
},
create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const option = context.options[0];
let numParams = 3;

View file

@ -22,7 +22,7 @@ module.exports = {
docs: {
description: "Enforce a maximum number of statements allowed per line",
recommended: false,
url: "https://eslint.org/docs/rules/max-statements-per-line"
url: "https://eslint.org/docs/latest/rules/max-statements-per-line"
},
schema: [
@ -45,7 +45,7 @@ module.exports = {
create(context) {
const sourceCode = context.getSourceCode(),
const sourceCode = context.sourceCode,
options = context.options[0] || {},
maxStatementsPerLine = typeof options.max !== "undefined" ? options.max : 1;

View file

@ -24,7 +24,7 @@ module.exports = {
docs: {
description: "Enforce a maximum number of statements allowed in function blocks",
recommended: false,
url: "https://eslint.org/docs/rules/max-statements"
url: "https://eslint.org/docs/latest/rules/max-statements"
},
schema: [

View file

@ -18,11 +18,41 @@ module.exports = {
docs: {
description: "Enforce a particular style for multiline comments",
recommended: false,
url: "https://eslint.org/docs/rules/multiline-comment-style"
url: "https://eslint.org/docs/latest/rules/multiline-comment-style"
},
fixable: "whitespace",
schema: [{ enum: ["starred-block", "separate-lines", "bare-block"] }],
schema: {
anyOf: [
{
type: "array",
items: [
{
enum: ["starred-block", "bare-block"]
}
],
additionalItems: false
},
{
type: "array",
items: [
{
enum: ["separate-lines"]
},
{
type: "object",
properties: {
checkJSDoc: {
type: "boolean"
}
},
additionalProperties: false
}
],
additionalItems: false
}
]
},
messages: {
expectedBlock: "Expected a block comment instead of consecutive line comments.",
expectedBareBlock: "Expected a block comment without padding stars.",
@ -35,8 +65,10 @@ module.exports = {
},
create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const option = context.options[0] || "starred-block";
const params = context.options[1] || {};
const checkJSDoc = !!params.checkJSDoc;
//----------------------------------------------------------------------
// Helpers
@ -333,11 +365,18 @@ module.exports = {
"separate-lines"(commentGroup) {
const [firstComment] = commentGroup;
if (firstComment.type !== "Block" || isJSDocComment(commentGroup)) {
const isJSDoc = isJSDocComment(commentGroup);
if (firstComment.type !== "Block" || (!checkJSDoc && isJSDoc)) {
return;
}
const commentLines = getCommentLines(commentGroup);
let commentLines = getCommentLines(commentGroup);
if (isJSDoc) {
commentLines = commentLines.slice(1, commentLines.length - 1);
}
const tokenAfter = sourceCode.getTokenAfter(firstComment, { includeComments: true });
if (tokenAfter && firstComment.loc.end.line === tokenAfter.loc.start.line) {

View file

@ -19,7 +19,7 @@ module.exports = {
docs: {
description: "Enforce newlines between operands of ternary expressions",
recommended: false,
url: "https://eslint.org/docs/rules/multiline-ternary"
url: "https://eslint.org/docs/latest/rules/multiline-ternary"
},
schema: [
@ -39,7 +39,7 @@ module.exports = {
},
create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
const option = context.options[0];
const multiline = option !== "never";
const allowSingleLine = option === "always-multiline";

View file

@ -84,7 +84,7 @@ module.exports = {
docs: {
description: "Require constructor names to begin with a capital letter",
recommended: false,
url: "https://eslint.org/docs/rules/new-cap"
url: "https://eslint.org/docs/latest/rules/new-cap"
},
schema: [
@ -147,7 +147,7 @@ module.exports = {
const listeners = {};
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
//--------------------------------------------------------------------------
// Helpers

View file

@ -27,24 +27,15 @@ module.exports = {
docs: {
description: "Enforce or disallow parentheses when invoking a constructor with no arguments",
recommended: false,
url: "https://eslint.org/docs/rules/new-parens"
url: "https://eslint.org/docs/latest/rules/new-parens"
},
fixable: "code",
schema: {
anyOf: [
{
type: "array",
items: [
{
enum: ["always", "never"]
}
],
minItems: 0,
maxItems: 1
}
]
},
schema: [
{
enum: ["always", "never"]
}
],
messages: {
missing: "Missing '()' invoking a constructor.",
unnecessary: "Unnecessary '()' invoking a constructor with no arguments."
@ -55,7 +46,7 @@ module.exports = {
const options = context.options;
const always = options[0] !== "never"; // Default is always
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
return {
NewExpression(node) {

View file

@ -24,7 +24,7 @@ module.exports = {
docs: {
description: "Require or disallow an empty line after variable declarations",
recommended: false,
url: "https://eslint.org/docs/rules/newline-after-var"
url: "https://eslint.org/docs/latest/rules/newline-after-var"
},
schema: [
{
@ -43,7 +43,7 @@ module.exports = {
},
create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
// Default `mode` to "always".
const mode = context.options[0] === "never" ? "never" : "always";
@ -212,7 +212,6 @@ module.exports = {
context.report({
node,
messageId: "unexpected",
data: { identifier: node.name },
fix(fixer) {
const linesBetween = sourceCode.getText().slice(lastToken.range[1], nextToken.range[0]).split(astUtils.LINEBREAK_MATCHER);
@ -231,7 +230,6 @@ module.exports = {
context.report({
node,
messageId: "expected",
data: { identifier: node.name },
fix(fixer) {
if ((noNextLineToken ? getLastCommentLineOfBlock(nextLineNum) : lastToken.loc.end.line) === nextToken.loc.start.line) {
return fixer.insertTextBefore(nextToken, "\n\n");

View file

@ -17,7 +17,7 @@ module.exports = {
docs: {
description: "Require an empty line before `return` statements",
recommended: false,
url: "https://eslint.org/docs/rules/newline-before-return"
url: "https://eslint.org/docs/latest/rules/newline-before-return"
},
fixable: "whitespace",
@ -31,7 +31,7 @@ module.exports = {
},
create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
//--------------------------------------------------------------------------
// Helpers

View file

@ -20,7 +20,7 @@ module.exports = {
docs: {
description: "Require a newline after each call in a method chain",
recommended: false,
url: "https://eslint.org/docs/rules/newline-per-chained-call"
url: "https://eslint.org/docs/latest/rules/newline-per-chained-call"
},
fixable: "whitespace",
@ -47,7 +47,7 @@ module.exports = {
const options = context.options[0] || {},
ignoreChainWithDepth = options.ignoreChainWithDepth || 2;
const sourceCode = context.getSourceCode();
const sourceCode = context.sourceCode;
/**
* Get the prefix of a given MemberExpression node.

View file

@ -90,7 +90,7 @@ module.exports = {
docs: {
description: "Disallow the use of `alert`, `confirm`, and `prompt`",
recommended: false,
url: "https://eslint.org/docs/rules/no-alert"
url: "https://eslint.org/docs/latest/rules/no-alert"
},
schema: [],
@ -101,10 +101,12 @@ module.exports = {
},
create(context) {
const sourceCode = context.sourceCode;
return {
CallExpression(node) {
const callee = skipChainExpression(node.callee),
currentScope = context.getScope();
currentScope = sourceCode.getScope(node);
// without window.
if (callee.type === "Identifier") {

View file

@ -17,7 +17,7 @@ module.exports = {
docs: {
description: "Disallow `Array` constructors",
recommended: false,
url: "https://eslint.org/docs/rules/no-array-constructor"
url: "https://eslint.org/docs/latest/rules/no-array-constructor"
},
schema: [],

Some files were not shown because too many files have changed in this diff Show more