Socket
Socket
Sign inDemoInstall

eslint

Package Overview
Dependencies
88
Maintainers
4
Versions
356
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 9.0.0-beta.1 to 9.0.0-beta.2

lib/rules/utils/char-source.js

17

lib/config/rule-validator.js

@@ -170,5 +170,18 @@ /**

if (validateRule.errors) {
throw new Error(`Key "rules": Key "${ruleId}": ${
throw new Error(`Key "rules": Key "${ruleId}":\n${
validateRule.errors.map(
error => `\tValue ${JSON.stringify(error.data)} ${error.message}.\n`
error => {
if (
error.keyword === "additionalProperties" &&
error.schema === false &&
typeof error.parentSchema?.properties === "object" &&
typeof error.params?.additionalProperty === "string"
) {
const expectedProperties = Object.keys(error.parentSchema.properties).map(property => `"${property}"`);
return `\tValue ${JSON.stringify(error.data)} ${error.message}.\n\t\tUnexpected property "${error.params.additionalProperty}". Expected properties: ${expectedProperties.join(", ")}.\n`;
}
return `\tValue ${JSON.stringify(error.data)} ${error.message}.\n`;
}
).join("")

@@ -175,0 +188,0 @@ }`);

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

});
const controller = new AbortController();

@@ -910,5 +911,8 @@ debug(`${filePaths.length} files found in: ${Date.now() - startTime}ms`);

return fs.readFile(filePath, "utf8")
return fs.readFile(filePath, { encoding: "utf8", signal: controller.signal })
.then(text => {
// fail immediately if an error occurred in another file
controller.signal.throwIfAborted();
// do the linting

@@ -937,2 +941,5 @@ const result = verifyText({

return result;
}).catch(error => {
controller.abort(error);
throw error;
});

@@ -939,0 +946,0 @@

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

DoWhileStatement: increaseComplexity,
AssignmentPattern: increaseComplexity,

@@ -124,2 +125,14 @@ // Avoid `default`

MemberExpression(node) {
if (node.optional === true) {
increaseComplexity();
}
},
CallExpression(node) {
if (node.optional === true) {
increaseComplexity();
}
},
onCodePathEnd(codePath, node) {

@@ -126,0 +139,0 @@ const complexity = complexities.pop();

173

lib/rules/no-misleading-character-class.js

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

const { CALL, CONSTRUCT, ReferenceTracker, getStringIfConstant } = require("@eslint-community/eslint-utils");
const {
CALL,
CONSTRUCT,
ReferenceTracker,
getStaticValue,
getStringIfConstant
} = require("@eslint-community/eslint-utils");
const { RegExpParser, visitRegExpAST } = require("@eslint-community/regexpp");

@@ -12,2 +18,3 @@ const { isCombiningCharacter, isEmojiModifier, isRegionalIndicatorSymbol, isSurrogatePair } = require("./utils/unicode");

const { isValidWithUnicodeFlag } = require("./utils/regular-expressions");
const { parseStringLiteral, parseTemplateToken } = require("./utils/char-source");

@@ -198,2 +205,29 @@ //------------------------------------------------------------------------------

/**
* Gets the value of the given node if it's a static value other than a regular expression object,
* or the node's `regex` property.
* The purpose of this method is to provide a replacement for `getStaticValue` in environments where certain regular expressions cannot be evaluated.
* A known example is Node.js 18 which does not support the `v` flag.
* Calling `getStaticValue` on a regular expression node with the `v` flag on Node.js 18 always returns `null`.
* A limitation of this method is that it can only detect a regular expression if the specified node is itself a regular expression literal node.
* @param {ASTNode | undefined} node The node to be inspected.
* @param {Scope} initialScope Scope to start finding variables. This function tries to resolve identifier references which are in the given scope.
* @returns {{ value: any } | { regex: { pattern: string, flags: string } } | null} The static value of the node, or `null`.
*/
function getStaticValueOrRegex(node, initialScope) {
if (!node) {
return null;
}
if (node.type === "Literal" && node.regex) {
return { regex: node.regex };
}
const staticValue = getStaticValue(node, initialScope);
if (staticValue?.value instanceof RegExp) {
return null;
}
return staticValue;
}
//------------------------------------------------------------------------------

@@ -231,60 +265,5 @@ // Rule Definition

const parser = new RegExpParser();
const checkedPatternNodes = new Set();
/**
* Generates a granular loc for context.report, if directly calculable.
* @param {Character[]} chars Individual characters being reported on.
* @param {Node} node Parent string node to report within.
* @returns {Object | null} Granular loc for context.report, if directly calculable.
* @see https://github.com/eslint/eslint/pull/17515
*/
function generateReportLocation(chars, node) {
// Limit to to literals and expression-less templates with raw values === their value.
switch (node.type) {
case "TemplateLiteral":
if (node.expressions.length || sourceCode.getText(node).slice(1, -1) !== node.quasis[0].value.cooked) {
return null;
}
break;
case "Literal":
if (typeof node.value === "string" && node.value !== node.raw.slice(1, -1)) {
return null;
}
break;
default:
return null;
}
return {
start: sourceCode.getLocFromIndex(node.range[0] + 1 + chars[0].start),
end: sourceCode.getLocFromIndex(node.range[0] + 1 + chars.at(-1).end)
};
}
/**
* Finds the report loc(s) for a range of matches.
* @param {Character[][]} matches Characters that should trigger a report.
* @param {Node} node The node to report.
* @returns {Object | null} Node loc(s) for context.report.
*/
function getNodeReportLocations(matches, node) {
const locs = [];
for (const chars of matches) {
const loc = generateReportLocation(chars, node);
// If a report can't match to a range, don't report any others
if (!loc) {
return [node.loc];
}
locs.push(loc);
}
return locs;
}
/**
* Verify a given regular expression.

@@ -327,3 +306,2 @@ * @param {Node} node The node to report.

}
}

@@ -334,2 +312,49 @@ }

let codeUnits = null;
/**
* Finds the report loc(s) for a range of matches.
* Only literals and expression-less templates generate granular errors.
* @param {Character[][]} matches Lists of individual characters being reported on.
* @returns {Location[]} locs for context.report.
* @see https://github.com/eslint/eslint/pull/17515
*/
function getNodeReportLocations(matches) {
if (!astUtils.isStaticTemplateLiteral(node) && node.type !== "Literal") {
return matches.length ? [node.loc] : [];
}
return matches.map(chars => {
const firstIndex = chars[0].start;
const lastIndex = chars.at(-1).end - 1;
let start;
let end;
if (node.type === "TemplateLiteral") {
const source = sourceCode.getText(node);
const offset = node.range[0];
codeUnits ??= parseTemplateToken(source);
start = offset + codeUnits[firstIndex].start;
end = offset + codeUnits[lastIndex].end;
} else if (typeof node.value === "string") { // String Literal
const source = node.raw;
const offset = node.range[0];
codeUnits ??= parseStringLiteral(source);
start = offset + codeUnits[firstIndex].start;
end = offset + codeUnits[lastIndex].end;
} else { // RegExp Literal
const offset = node.range[0] + 1; // Add 1 to skip the leading slash.
start = offset + firstIndex;
end = offset + lastIndex + 1;
}
return {
start: sourceCode.getLocFromIndex(start),
end: sourceCode.getLocFromIndex(end)
};
});
}
for (const [kind, matches] of foundKindMatches) {

@@ -345,3 +370,3 @@ let suggest;

const locs = getNodeReportLocations(matches, node);
const locs = getNodeReportLocations(matches);

@@ -361,2 +386,5 @@ for (const loc of locs) {

"Literal[regex]"(node) {
if (checkedPatternNodes.has(node)) {
return;
}
verify(node, node.regex.pattern, node.regex.flags, fixer => {

@@ -382,9 +410,28 @@ if (!isValidWithUnicodeFlag(context.languageOptions.ecmaVersion, node.regex.pattern)) {

})) {
let pattern, flags;
const [patternNode, flagsNode] = refNode.arguments;
const pattern = getStringIfConstant(patternNode, scope);
const flags = getStringIfConstant(flagsNode, scope);
const evaluatedPattern = getStaticValueOrRegex(patternNode, scope);
if (typeof pattern === "string") {
verify(patternNode, pattern, flags || "", fixer => {
if (!evaluatedPattern) {
continue;
}
if (flagsNode) {
if (evaluatedPattern.regex) {
pattern = evaluatedPattern.regex.pattern;
checkedPatternNodes.add(patternNode);
} else {
pattern = String(evaluatedPattern.value);
}
flags = getStringIfConstant(flagsNode, scope);
} else {
if (evaluatedPattern.regex) {
continue;
}
pattern = String(evaluatedPattern.value);
flags = "";
}
if (typeof flags === "string") {
verify(patternNode, pattern, flags, fixer => {
if (!isValidWithUnicodeFlag(context.languageOptions.ecmaVersion, pattern)) {

@@ -391,0 +438,0 @@ return null;

@@ -37,6 +37,13 @@ /**

}
},
allowImportNames: {
type: "array",
items: {
type: "string"
}
}
},
additionalProperties: false,
required: ["name"]
required: ["name"],
not: { required: ["importNames", "allowImportNames"] }
}

@@ -70,2 +77,10 @@ ]

},
allowImportNames: {
type: "array",
items: {
type: "string"
},
minItems: 1,
uniqueItems: true
},
group: {

@@ -82,2 +97,5 @@ type: "array",

},
allowImportNamePattern: {
type: "string"
},
message: {

@@ -92,3 +110,12 @@ type: "string",

additionalProperties: false,
required: ["group"]
required: ["group"],
not: {
anyOf: [
{ required: ["importNames", "allowImportNames"] },
{ required: ["importNamePattern", "allowImportNamePattern"] },
{ required: ["importNames", "allowImportNamePattern"] },
{ required: ["importNamePattern", "allowImportNames"] },
{ required: ["allowImportNames", "allowImportNamePattern"] }
]
}
},

@@ -138,3 +165,19 @@ uniqueItems: true

// eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
importNameWithCustomMessage: "'{{importName}}' import from '{{importSource}}' is restricted. {{customMessage}}"
importNameWithCustomMessage: "'{{importName}}' import from '{{importSource}}' is restricted. {{customMessage}}",
allowedImportName: "'{{importName}}' import from '{{importSource}}' is restricted because only '{{allowedImportNames}}' import(s) is/are allowed.",
// eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
allowedImportNameWithCustomMessage: "'{{importName}}' import from '{{importSource}}' is restricted because only '{{allowedImportNames}}' import(s) is/are allowed. {{customMessage}}",
everythingWithAllowImportNames: "* import is invalid because only '{{allowedImportNames}}' from '{{importSource}}' is/are allowed.",
// eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
everythingWithAllowImportNamesAndCustomMessage: "* import is invalid because only '{{allowedImportNames}}' from '{{importSource}}' is/are allowed. {{customMessage}}",
allowedImportNamePattern: "'{{importName}}' import from '{{importSource}}' is restricted because only imports that match the pattern '{{allowedImportNamePattern}}' are allowed from '{{importSource}}'.",
// eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
allowedImportNamePatternWithCustomMessage: "'{{importName}}' import from '{{importSource}}' is restricted because only imports that match the pattern '{{allowedImportNamePattern}}' are allowed from '{{importSource}}'. {{customMessage}}",
everythingWithAllowedImportNamePattern: "* import is invalid because only imports that match the pattern '{{allowedImportNamePattern}}' from '{{importSource}}' are allowed.",
// eslint-disable-next-line eslint-plugin/report-message-format -- Custom message might not end in a period
everythingWithAllowedImportNamePatternWithCustomMessage: "* import is invalid because only imports that match the pattern '{{allowedImportNamePattern}}' from '{{importSource}}' are allowed. {{customMessage}}"
},

@@ -183,3 +226,4 @@

message: importSource.message,
importNames: importSource.importNames
importNames: importSource.importNames,
allowImportNames: importSource.allowImportNames
});

@@ -199,8 +243,14 @@ }

// relative paths are supported for this rule
const restrictedPatternGroups = restrictedPatterns.map(({ group, message, caseSensitive, importNames, importNamePattern }) => ({
matcher: ignore({ allowRelativePaths: true, ignorecase: !caseSensitive }).add(group),
customMessage: message,
importNames,
importNamePattern
}));
const restrictedPatternGroups = restrictedPatterns.map(
({ group, message, caseSensitive, importNames, importNamePattern, allowImportNames, allowImportNamePattern }) => (
{
matcher: ignore({ allowRelativePaths: true, ignorecase: !caseSensitive }).add(group),
customMessage: message,
importNames,
importNamePattern,
allowImportNames,
allowImportNamePattern
}
)
);

@@ -228,38 +278,5 @@ // if no imports are restricted we don't need to check

const restrictedImportNames = restrictedPathEntry.importNames;
const allowedImportNames = restrictedPathEntry.allowImportNames;
if (restrictedImportNames) {
if (importNames.has("*")) {
const specifierData = importNames.get("*")[0];
context.report({
node,
messageId: customMessage ? "everythingWithCustomMessage" : "everything",
loc: specifierData.loc,
data: {
importSource,
importNames: restrictedImportNames,
customMessage
}
});
}
restrictedImportNames.forEach(importName => {
if (importNames.has(importName)) {
const specifiers = importNames.get(importName);
specifiers.forEach(specifier => {
context.report({
node,
messageId: customMessage ? "importNameWithCustomMessage" : "importName",
loc: specifier.loc,
data: {
importSource,
customMessage,
importName
}
});
});
}
});
} else {
if (!restrictedImportNames && !allowedImportNames) {
context.report({

@@ -273,3 +290,68 @@ node,

});
return;
}
importNames.forEach((specifiers, importName) => {
if (importName === "*") {
const [specifier] = specifiers;
if (restrictedImportNames) {
context.report({
node,
messageId: customMessage ? "everythingWithCustomMessage" : "everything",
loc: specifier.loc,
data: {
importSource,
importNames: restrictedImportNames,
customMessage
}
});
} else if (allowedImportNames) {
context.report({
node,
messageId: customMessage ? "everythingWithAllowImportNamesAndCustomMessage" : "everythingWithAllowImportNames",
loc: specifier.loc,
data: {
importSource,
allowedImportNames,
customMessage
}
});
}
return;
}
if (restrictedImportNames && restrictedImportNames.includes(importName)) {
specifiers.forEach(specifier => {
context.report({
node,
messageId: customMessage ? "importNameWithCustomMessage" : "importName",
loc: specifier.loc,
data: {
importSource,
customMessage,
importName
}
});
});
}
if (allowedImportNames && !allowedImportNames.includes(importName)) {
specifiers.forEach(specifier => {
context.report({
node,
loc: specifier.loc,
messageId: customMessage ? "allowedImportNameWithCustomMessage" : "allowedImportName",
data: {
importSource,
customMessage,
importName,
allowedImportNames
}
});
});
}
});
});

@@ -293,8 +375,10 @@ }

const restrictedImportNamePattern = group.importNamePattern ? new RegExp(group.importNamePattern, "u") : null;
const allowedImportNames = group.allowImportNames;
const allowedImportNamePattern = group.allowImportNamePattern ? new RegExp(group.allowImportNamePattern, "u") : null;
/*
/**
* If we are not restricting to any specific import names and just the pattern itself,
* report the error and move on
*/
if (!restrictedImportNames && !restrictedImportNamePattern) {
if (!restrictedImportNames && !allowedImportNames && !restrictedImportNamePattern && !allowedImportNamePattern) {
context.report({

@@ -326,2 +410,24 @@ node,

});
} else if (allowedImportNames) {
context.report({
node,
messageId: customMessage ? "everythingWithAllowImportNamesAndCustomMessage" : "everythingWithAllowImportNames",
loc: specifier.loc,
data: {
importSource,
allowedImportNames,
customMessage
}
});
} else if (allowedImportNamePattern) {
context.report({
node,
messageId: customMessage ? "everythingWithAllowedImportNamePatternWithCustomMessage" : "everythingWithAllowedImportNamePattern",
loc: specifier.loc,
data: {
importSource,
allowedImportNamePattern,
customMessage
}
});
} else {

@@ -360,2 +466,32 @@ context.report({

}
if (allowedImportNames && !allowedImportNames.includes(importName)) {
specifiers.forEach(specifier => {
context.report({
node,
messageId: customMessage ? "allowedImportNameWithCustomMessage" : "allowedImportName",
loc: specifier.loc,
data: {
importSource,
customMessage,
importName,
allowedImportNames
}
});
});
} else if (allowedImportNamePattern && !allowedImportNamePattern.test(importName)) {
specifiers.forEach(specifier => {
context.report({
node,
messageId: customMessage ? "allowedImportNamePatternWithCustomMessage" : "allowedImportNamePattern",
loc: specifier.loc,
data: {
importSource,
customMessage,
importName,
allowedImportNamePattern
}
});
});
}
});

@@ -362,0 +498,0 @@ }

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

type: "string"
},
ignoreClassWithStaticInitBlock: {
type: "boolean"
}

@@ -96,3 +99,4 @@ },

ignoreRestSiblings: false,
caughtErrors: "all"
caughtErrors: "all",
ignoreClassWithStaticInitBlock: false
};

@@ -110,2 +114,3 @@

config.caughtErrors = firstOption.caughtErrors || config.caughtErrors;
config.ignoreClassWithStaticInitBlock = firstOption.ignoreClassWithStaticInitBlock || config.ignoreClassWithStaticInitBlock;

@@ -619,2 +624,10 @@ if (firstOption.varsIgnorePattern) {

if (type === "ClassName") {
const hasStaticBlock = def.node.body.body.some(node => node.type === "StaticBlock");
if (config.ignoreClassWithStaticInitBlock && hasStaticBlock) {
continue;
}
}
// skip catch variables

@@ -621,0 +634,0 @@ if (type === "CatchClause") {

{
"name": "eslint",
"version": "9.0.0-beta.1",
"version": "9.0.0-beta.2",
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",

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

"@eslint/eslintrc": "^3.0.2",
"@eslint/js": "9.0.0-beta.1",
"@eslint/js": "9.0.0-beta.2",
"@humanwhocodes/config-array": "^0.11.14",

@@ -72,0 +72,0 @@ "@humanwhocodes/module-importer": "^1.0.1",

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

### Which Node.js versions does ESLint support?
ESLint updates the supported Node.js versions with each major release of ESLint. At that time, ESLint's supported Node.js versions are updated to be:
1. The most recent maintenance release of Node.js
1. The lowest minor version of the Node.js LTS release that includes the features the ESLint team wants to use.
1. The Node.js Current release
ESLint is also expected to work with Node.js versions released after the Node.js Current release.
Refer to the [Quick Start Guide](https://eslint.org/docs/latest/use/getting-started#prerequisites) for the officially supported Node.js versions for a given ESLint release.
### Where to ask for help?

@@ -217,2 +229,7 @@

</td><td align="center" valign="top" width="11%">
<a href="https://github.com/fasttime">
<img src="https://github.com/fasttime.png?s=75" width="75" height="75" alt="Francesco Trotta's Avatar"><br />
Francesco Trotta
</a>
</td><td align="center" valign="top" width="11%">
<a href="https://github.com/mdjermanovic">

@@ -255,7 +272,2 @@ <img src="https://github.com/mdjermanovic.png?s=75" width="75" height="75" alt="Milos Djermanovic's Avatar"><br />

</td><td align="center" valign="top" width="11%">
<a href="https://github.com/fasttime">
<img src="https://github.com/fasttime.png?s=75" width="75" height="75" alt="Francesco Trotta's Avatar"><br />
Francesco Trotta
</a>
</td><td align="center" valign="top" width="11%">
<a href="https://github.com/Tanujkanti4441">

@@ -262,0 +274,0 @@ <img src="https://github.com/Tanujkanti4441.png?s=75" width="75" height="75" alt="Tanuj Kanti's Avatar"><br />

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

SocketSocket SOC 2 Logo

Product

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

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc