Comparing version 9.0.0-beta.1 to 9.0.0-beta.2
@@ -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(); |
@@ -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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
3006517
399
70685
317
+ Added@eslint/js@9.0.0-beta.2(transitive)
- Removed@eslint/js@9.0.0-beta.1(transitive)
Updated@eslint/js@9.0.0-beta.2