eslint
Advanced tools
Comparing version 9.0.0-beta.2 to 9.0.0-rc.0
@@ -152,3 +152,4 @@ #!/usr/bin/env node | ||
// Otherwise, call the CLI. | ||
const exitCode = await require("../lib/cli").execute( | ||
const cli = require("../lib/cli"); | ||
const exitCode = await cli.execute( | ||
process.argv, | ||
@@ -155,0 +156,0 @@ process.argv.includes("--stdin") ? await readStdin() : null, |
@@ -40,2 +40,3 @@ /** | ||
/** @typedef {import("./options").ParsedCLIOptions} ParsedCLIOptions */ | ||
/** @typedef {import("./shared/types").Plugin} Plugin */ | ||
/** @typedef {import("./shared/types").ResultsMeta} ResultsMeta */ | ||
@@ -52,2 +53,28 @@ | ||
/** | ||
* Loads plugins with the specified names. | ||
* @param {{ "import": (name: string) => Promise<any> }} importer An object with an `import` method called once for each plugin. | ||
* @param {string[]} pluginNames The names of the plugins to be loaded, with or without the "eslint-plugin-" prefix. | ||
* @returns {Promise<Record<string, Plugin>>} A mapping of plugin short names to implementations. | ||
*/ | ||
async function loadPlugins(importer, pluginNames) { | ||
const plugins = {}; | ||
await Promise.all(pluginNames.map(async pluginName => { | ||
const longName = naming.normalizePackageName(pluginName, "eslint-plugin"); | ||
const module = await importer.import(longName); | ||
if (!("default" in module)) { | ||
throw new Error(`"${longName}" cannot be used with the \`--plugin\` option because its default module does not provide a \`default\` export`); | ||
} | ||
const shortName = naming.getShorthandName(pluginName, "eslint-plugin"); | ||
plugins[shortName] = module.default; | ||
})); | ||
return plugins; | ||
} | ||
/** | ||
* Predicate function for whether or not to apply fixes in quiet mode. | ||
@@ -157,13 +184,3 @@ * If a message is a warning, do not apply a fix. | ||
if (plugin) { | ||
const plugins = {}; | ||
for (const pluginName of plugin) { | ||
const shortName = naming.getShorthandName(pluginName, "eslint-plugin"); | ||
const longName = naming.normalizePackageName(pluginName, "eslint-plugin"); | ||
plugins[shortName] = await importer.import(longName); | ||
} | ||
overrideConfig[0].plugins = plugins; | ||
overrideConfig[0].plugins = await loadPlugins(importer, plugin); | ||
} | ||
@@ -170,0 +187,0 @@ |
@@ -591,4 +591,3 @@ /** | ||
flatConfigSchema, | ||
assertIsRuleSeverity, | ||
assertIsRuleOptions | ||
assertIsRuleSeverity | ||
}; |
@@ -910,3 +910,2 @@ /** | ||
module.exports = { | ||
isGlobPattern, | ||
findFiles, | ||
@@ -913,0 +912,0 @@ |
@@ -225,3 +225,2 @@ /** | ||
debug.dump(`${eventName} ${headSegment.id}`); | ||
CodePathSegment.markUsed(headSegment); | ||
@@ -228,0 +227,0 @@ analyzer.emitter.emit( |
"use strict"; | ||
const { Linter } = require("./linter"); | ||
const { interpolate } = require("./interpolate"); | ||
const SourceCodeFixer = require("./source-code-fixer"); | ||
@@ -11,4 +10,3 @@ | ||
// For testers. | ||
SourceCodeFixer, | ||
interpolate | ||
SourceCodeFixer | ||
}; |
"use strict"; | ||
const RuleTester = require("./rule-tester"); | ||
module.exports = { | ||
RuleTester: require("./rule-tester") | ||
RuleTester | ||
}; |
@@ -122,2 +122,26 @@ /** | ||
/** | ||
* A class to store information about a code path segment. | ||
*/ | ||
class SegmentInfo { | ||
/** | ||
* Indicates if super() is called in all code paths. | ||
* @type {boolean} | ||
*/ | ||
calledInEveryPaths = false; | ||
/** | ||
* Indicates if super() is called in any code paths. | ||
* @type {boolean} | ||
*/ | ||
calledInSomePaths = false; | ||
/** | ||
* The nodes which have been validated and don't need to be reconsidered. | ||
* @type {ASTNode[]} | ||
*/ | ||
validNodes = []; | ||
} | ||
//------------------------------------------------------------------------------ | ||
@@ -163,8 +187,4 @@ // Rule Definition | ||
/* | ||
* {Map<string, {calledInSomePaths: boolean, calledInEveryPaths: boolean}>} | ||
* Information for each code path segment. | ||
* - calledInSomePaths: A flag of be called `super()` in some code paths. | ||
* - calledInEveryPaths: A flag of be called `super()` in all code paths. | ||
* - validNodes: | ||
/** | ||
* @type {Record<string, SegmentInfo>} | ||
*/ | ||
@@ -179,6 +199,15 @@ let segInfoMap = Object.create(null); | ||
function isCalledInSomePath(segment) { | ||
return segment.reachable && segInfoMap[segment.id].calledInSomePaths; | ||
return segment.reachable && segInfoMap[segment.id]?.calledInSomePaths; | ||
} | ||
/** | ||
* Determines if a segment has been seen in the traversal. | ||
* @param {CodePathSegment} segment A code path segment to check. | ||
* @returns {boolean} `true` if the segment has been seen. | ||
*/ | ||
function hasSegmentBeenSeen(segment) { | ||
return !!segInfoMap[segment.id]; | ||
} | ||
/** | ||
* Gets the flag which shows `super()` is called in all paths. | ||
@@ -196,6 +225,6 @@ * @param {CodePathSegment} segment A code path segment to get. | ||
if (segment.nextSegments.length === 1 && | ||
segment.nextSegments[0].isLoopedPrevSegment(segment) | ||
) { | ||
segment.nextSegments[0]?.isLoopedPrevSegment(segment)) { | ||
return true; | ||
} | ||
return segment.reachable && segInfoMap[segment.id].calledInEveryPaths; | ||
@@ -257,5 +286,5 @@ } | ||
// Reports if `super()` lacked. | ||
const segments = codePath.returnedSegments; | ||
const calledInEveryPaths = segments.every(isCalledInEveryPath); | ||
const calledInSomePaths = segments.some(isCalledInSomePath); | ||
const seenSegments = codePath.returnedSegments.filter(hasSegmentBeenSeen); | ||
const calledInEveryPaths = seenSegments.every(isCalledInEveryPath); | ||
const calledInSomePaths = seenSegments.some(isCalledInSomePath); | ||
@@ -286,7 +315,3 @@ if (!calledInEveryPaths) { | ||
// Initialize info. | ||
const info = segInfoMap[segment.id] = { | ||
calledInSomePaths: false, | ||
calledInEveryPaths: false, | ||
validNodes: [] | ||
}; | ||
const info = segInfoMap[segment.id] = new SegmentInfo(); | ||
@@ -297,4 +322,6 @@ // When there are previous segments, aggregates these. | ||
if (prevSegments.length > 0) { | ||
info.calledInSomePaths = prevSegments.some(isCalledInSomePath); | ||
info.calledInEveryPaths = prevSegments.every(isCalledInEveryPath); | ||
const seenPrevSegments = prevSegments.filter(hasSegmentBeenSeen); | ||
info.calledInSomePaths = seenPrevSegments.some(isCalledInSomePath); | ||
info.calledInEveryPaths = seenPrevSegments.every(isCalledInEveryPath); | ||
} | ||
@@ -336,8 +363,8 @@ }, | ||
segment => { | ||
const info = segInfoMap[segment.id]; | ||
const prevSegments = segment.prevSegments; | ||
const info = segInfoMap[segment.id] ?? new SegmentInfo(); | ||
const seenPrevSegments = segment.prevSegments.filter(hasSegmentBeenSeen); | ||
// Updates flags. | ||
info.calledInSomePaths = prevSegments.some(isCalledInSomePath); | ||
info.calledInEveryPaths = prevSegments.every(isCalledInEveryPath); | ||
info.calledInSomePaths = seenPrevSegments.some(isCalledInSomePath); | ||
info.calledInEveryPaths = seenPrevSegments.every(isCalledInEveryPath); | ||
@@ -359,2 +386,5 @@ // If flags become true anew, reports the valid nodes. | ||
} | ||
// save just in case we created a new SegmentInfo object | ||
segInfoMap[segment.id] = info; | ||
} | ||
@@ -361,0 +391,0 @@ ); |
@@ -51,5 +51,5 @@ /** | ||
* @param {RegExp} fallthroughCommentPattern A pattern to match comment to. | ||
* @returns {boolean} `true` if the case has a valid fallthrough comment. | ||
* @returns {null | object} the comment if the case has a valid fallthrough comment, otherwise null | ||
*/ | ||
function hasFallthroughComment(caseWhichFallsThrough, subsequentCase, context, fallthroughCommentPattern) { | ||
function getFallthroughComment(caseWhichFallsThrough, subsequentCase, context, fallthroughCommentPattern) { | ||
const sourceCode = context.sourceCode; | ||
@@ -62,3 +62,3 @@ | ||
if (commentInBlock && isFallThroughComment(commentInBlock.value, fallthroughCommentPattern)) { | ||
return true; | ||
return commentInBlock; | ||
} | ||
@@ -69,3 +69,7 @@ } | ||
return Boolean(comment && isFallThroughComment(comment.value, fallthroughCommentPattern)); | ||
if (comment && isFallThroughComment(comment.value, fallthroughCommentPattern)) { | ||
return comment; | ||
} | ||
return null; | ||
} | ||
@@ -109,2 +113,6 @@ | ||
default: false | ||
}, | ||
reportUnusedFallthroughComment: { | ||
type: "boolean", | ||
default: false | ||
} | ||
@@ -116,2 +124,3 @@ }, | ||
messages: { | ||
unusedFallthroughComment: "Found a comment that would permit fallthrough, but case cannot fall through.", | ||
case: "Expected a 'break' statement before 'case'.", | ||
@@ -128,2 +137,3 @@ default: "Expected a 'break' statement before 'default'." | ||
const allowEmptyCase = options.allowEmptyCase || false; | ||
const reportUnusedFallthroughComment = options.reportUnusedFallthroughComment || false; | ||
@@ -134,3 +144,3 @@ /* | ||
*/ | ||
let fallthroughCase = null; | ||
let previousCase = null; | ||
let fallthroughCommentPattern = null; | ||
@@ -178,9 +188,19 @@ | ||
if (fallthroughCase && (!hasFallthroughComment(fallthroughCase, node, context, fallthroughCommentPattern))) { | ||
context.report({ | ||
messageId: node.test ? "case" : "default", | ||
node | ||
}); | ||
if (previousCase && previousCase.node.parent === node.parent) { | ||
const previousCaseFallthroughComment = getFallthroughComment(previousCase.node, node, context, fallthroughCommentPattern); | ||
if (previousCase.isFallthrough && !(previousCaseFallthroughComment)) { | ||
context.report({ | ||
messageId: node.test ? "case" : "default", | ||
node | ||
}); | ||
} else if (reportUnusedFallthroughComment && !previousCase.isSwitchExitReachable && previousCaseFallthroughComment) { | ||
context.report({ | ||
messageId: "unusedFallthroughComment", | ||
node: previousCaseFallthroughComment | ||
}); | ||
} | ||
} | ||
fallthroughCase = null; | ||
previousCase = null; | ||
}, | ||
@@ -196,7 +216,12 @@ | ||
*/ | ||
if (isAnySegmentReachable(currentCodePathSegments) && | ||
(node.consequent.length > 0 || (!allowEmptyCase && hasBlankLinesBetween(node, nextToken))) && | ||
node.parent.cases.at(-1) !== node) { | ||
fallthroughCase = node; | ||
} | ||
const isSwitchExitReachable = isAnySegmentReachable(currentCodePathSegments); | ||
const isFallthrough = isSwitchExitReachable && (node.consequent.length > 0 || (!allowEmptyCase && hasBlankLinesBetween(node, nextToken))) && | ||
node.parent.cases.at(-1) !== node; | ||
previousCase = { | ||
node, | ||
isSwitchExitReachable, | ||
isFallthrough | ||
}; | ||
} | ||
@@ -203,0 +228,0 @@ }; |
@@ -33,2 +33,25 @@ /** | ||
/* | ||
* Information for each code path segment. | ||
* - superCalled: The flag which shows `super()` called in all code paths. | ||
* - invalidNodes: The array of invalid ThisExpression and Super nodes. | ||
*/ | ||
/** | ||
* | ||
*/ | ||
class SegmentInfo { | ||
/** | ||
* Indicates whether `super()` is called in all code paths. | ||
* @type {boolean} | ||
*/ | ||
superCalled = false; | ||
/** | ||
* The array of invalid ThisExpression and Super nodes. | ||
* @type {ASTNode[]} | ||
*/ | ||
invalidNodes = []; | ||
} | ||
//------------------------------------------------------------------------------ | ||
@@ -68,9 +91,3 @@ // Rule Definition | ||
/* | ||
* Information for each code path segment. | ||
* Each key is the id of a code path segment. | ||
* Each value is an object: | ||
* - superCalled: The flag which shows `super()` called in all code paths. | ||
* - invalidNodes: The array of invalid ThisExpression and Super nodes. | ||
*/ | ||
/** @type {Record<string, SegmentInfo>} */ | ||
let segInfoMap = Object.create(null); | ||
@@ -84,3 +101,3 @@ | ||
function isCalled(segment) { | ||
return !segment.reachable || segInfoMap[segment.id].superCalled; | ||
return !segment.reachable || segInfoMap[segment.id]?.superCalled; | ||
} | ||
@@ -291,3 +308,3 @@ | ||
(segment, controller) => { | ||
const info = segInfoMap[segment.id]; | ||
const info = segInfoMap[segment.id] ?? new SegmentInfo(); | ||
@@ -302,2 +319,4 @@ if (info.superCalled) { | ||
} | ||
segInfoMap[segment.id] = info; | ||
} | ||
@@ -304,0 +323,0 @@ ); |
@@ -149,3 +149,5 @@ /** | ||
uselessReturns.push(...segmentInfoMap.get(segment).uselessReturns); | ||
if (segmentInfoMap.has(segment)) { | ||
uselessReturns.push(...segmentInfoMap.get(segment).uselessReturns); | ||
} | ||
} | ||
@@ -186,2 +188,6 @@ | ||
if (!info) { | ||
return; | ||
} | ||
info.uselessReturns = info.uselessReturns.filter(node => { | ||
@@ -280,3 +286,2 @@ if (scopeInfo.traversedTryBlockStatements && scopeInfo.traversedTryBlockStatements.length > 0) { | ||
onCodePathSegmentStart(segment) { | ||
scopeInfo.currentSegments.add(segment); | ||
@@ -283,0 +288,0 @@ |
@@ -9,3 +9,3 @@ /** | ||
/** @typedef {import("./types").Rule} Rule */ | ||
/** @typedef {import("../../shared/types").Rule} Rule */ | ||
@@ -12,0 +12,0 @@ /** |
@@ -6,7 +6,12 @@ /** | ||
const isCombiningCharacter = require("./is-combining-character"); | ||
const isEmojiModifier = require("./is-emoji-modifier"); | ||
const isRegionalIndicatorSymbol = require("./is-regional-indicator-symbol"); | ||
const isSurrogatePair = require("./is-surrogate-pair"); | ||
module.exports = { | ||
isCombiningCharacter: require("./is-combining-character"), | ||
isEmojiModifier: require("./is-emoji-modifier"), | ||
isRegionalIndicatorSymbol: require("./is-regional-indicator-symbol"), | ||
isSurrogatePair: require("./is-surrogate-pair") | ||
isCombiningCharacter, | ||
isEmojiModifier, | ||
isRegionalIndicatorSymbol, | ||
isSurrogatePair | ||
}; |
@@ -165,4 +165,5 @@ /** | ||
module.exports = { | ||
__esModule: true, // Indicate intent for imports, remove ambiguity for Knip (see: https://github.com/eslint/eslint/pull/18005#discussion_r1484422616) | ||
environment, | ||
version | ||
}; |
"use strict"; | ||
const SourceCode = require("./source-code"); | ||
module.exports = { | ||
SourceCode: require("./source-code") | ||
SourceCode | ||
}; |
@@ -21,4 +21,8 @@ /** | ||
/* eslint-disable-next-line n/no-restricted-require -- Too messy to figure out right now. */ | ||
/* eslint-disable n/no-restricted-require -- Should eventually be moved into SourceCode. */ | ||
CodePathAnalyzer = require("../linter/code-path-analysis/code-path-analyzer"), | ||
createEmitter = require("../linter/safe-emitter"), | ||
ConfigCommentParser = require("../linter/config-comment-parser"), | ||
/* eslint-enable n/no-restricted-require -- Should eventually be moved into SourceCode. */ | ||
eslintScope = require("eslint-scope"); | ||
@@ -38,2 +42,12 @@ | ||
const CODE_PATH_EVENTS = [ | ||
"onCodePathStart", | ||
"onCodePathEnd", | ||
"onCodePathSegmentStart", | ||
"onCodePathSegmentEnd", | ||
"onCodePathSegmentLoop", | ||
"onUnreachableCodePathSegmentStart", | ||
"onUnreachableCodePathSegmentEnd" | ||
]; | ||
/** | ||
@@ -305,2 +319,61 @@ * Validates that the given AST has the required information. | ||
const STEP_KIND = { | ||
visit: 1, | ||
call: 2 | ||
}; | ||
/** | ||
* A class to represent a step in the traversal process. | ||
*/ | ||
class TraversalStep { | ||
/** | ||
* The type of the step. | ||
* @type {string} | ||
*/ | ||
type; | ||
/** | ||
* The kind of the step. Represents the same data as the `type` property | ||
* but it's a number for performance. | ||
* @type {number} | ||
*/ | ||
kind; | ||
/** | ||
* The target of the step. | ||
* @type {ASTNode|string} | ||
*/ | ||
target; | ||
/** | ||
* The phase of the step. | ||
* @type {number|undefined} | ||
*/ | ||
phase; | ||
/** | ||
* The arguments of the step. | ||
* @type {Array<any>} | ||
*/ | ||
args; | ||
/** | ||
* Creates a new instance. | ||
* @param {Object} options The options for the step. | ||
* @param {string} options.type The type of the step. | ||
* @param {ASTNode|string} options.target The target of the step. | ||
* @param {number|undefined} [options.phase] The phase of the step. | ||
* @param {Array<any>} options.args The arguments of the step. | ||
* @returns {void} | ||
*/ | ||
constructor({ type, target, phase, args }) { | ||
this.type = type; | ||
this.kind = STEP_KIND[type]; | ||
this.target = target; | ||
this.phase = phase; | ||
this.args = args; | ||
} | ||
} | ||
//------------------------------------------------------------------------------ | ||
@@ -318,2 +391,8 @@ // Public Interface | ||
/** | ||
* The cache of steps that were taken while traversing the source code. | ||
* @type {Array<TraversalStep>} | ||
*/ | ||
#steps; | ||
/** | ||
* @param {string|Object} textOrConfig The source code text or config object. | ||
@@ -979,4 +1058,89 @@ * @param {string} textOrConfig.text The source code text. | ||
/** | ||
* Traverse the source code and return the steps that were taken. | ||
* @returns {Array<TraversalStep>} The steps that were taken while traversing the source code. | ||
*/ | ||
traverse() { | ||
// Because the AST doesn't mutate, we can cache the steps | ||
if (this.#steps) { | ||
return this.#steps; | ||
} | ||
const steps = this.#steps = []; | ||
/* | ||
* This logic works for any AST, not just ESTree. Because ESLint has allowed | ||
* custom parsers to return any AST, we need to ensure that the traversal | ||
* logic works for any AST. | ||
*/ | ||
const emitter = createEmitter(); | ||
let analyzer = { | ||
enterNode(node) { | ||
steps.push(new TraversalStep({ | ||
type: "visit", | ||
target: node, | ||
phase: 1, | ||
args: [node, node.parent] | ||
})); | ||
}, | ||
leaveNode(node) { | ||
steps.push(new TraversalStep({ | ||
type: "visit", | ||
target: node, | ||
phase: 2, | ||
args: [node, node.parent] | ||
})); | ||
}, | ||
emitter | ||
}; | ||
/* | ||
* We do code path analysis for ESTree only. Code path analysis is not | ||
* necessary for other ASTs, and it's also not possible to do for other | ||
* ASTs because the necessary information is not available. | ||
* | ||
* Generally speaking, we can tell that the AST is an ESTree if it has a | ||
* Program node at the top level. This is not a perfect heuristic, but it | ||
* is good enough for now. | ||
*/ | ||
const isESTree = this.ast.type === "Program"; | ||
if (isESTree) { | ||
analyzer = new CodePathAnalyzer(analyzer); | ||
CODE_PATH_EVENTS.forEach(eventName => { | ||
emitter.on(eventName, (...args) => { | ||
steps.push(new TraversalStep({ | ||
type: "call", | ||
target: eventName, | ||
args | ||
})); | ||
}); | ||
}); | ||
} | ||
/* | ||
* The actual AST traversal is done by the `Traverser` class. This class | ||
* is responsible for walking the AST and calling the appropriate methods | ||
* on the `analyzer` object, which is appropriate for the given AST. | ||
*/ | ||
Traverser.traverse(this.ast, { | ||
enter(node, parent) { | ||
// save the parent node on a property for backwards compatibility | ||
node.parent = parent; | ||
analyzer.enterNode(node); | ||
}, | ||
leave(node) { | ||
analyzer.leaveNode(node); | ||
}, | ||
visitorKeys: this.visitorKeys | ||
}); | ||
return steps; | ||
} | ||
} | ||
module.exports = SourceCode; |
{ | ||
"name": "eslint", | ||
"version": "9.0.0-beta.2", | ||
"version": "9.0.0-rc.0", | ||
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>", | ||
@@ -26,2 +26,3 @@ "description": "An AST-based pattern checker for JavaScript.", | ||
"lint:fix:docs:js": "node Makefile.js lintDocsJS -- fix", | ||
"lint:unused": "knip", | ||
"release:generate:alpha": "node Makefile.js generatePrerelease -- alpha", | ||
@@ -52,3 +53,3 @@ "release:generate:beta": "node Makefile.js generatePrerelease -- beta", | ||
], | ||
"docs/**/*.svg": "npx svgo -r --multipass" | ||
"docs/**/*.svg": "npx -y svgo -r --multipass" | ||
}, | ||
@@ -71,3 +72,3 @@ "files": [ | ||
"@eslint/eslintrc": "^3.0.2", | ||
"@eslint/js": "9.0.0-beta.2", | ||
"@eslint/js": "9.0.0-rc.0", | ||
"@humanwhocodes/config-array": "^0.11.14", | ||
@@ -81,3 +82,3 @@ "@humanwhocodes/module-importer": "^1.0.1", | ||
"escape-string-regexp": "^4.0.0", | ||
"eslint-scope": "^8.0.0", | ||
"eslint-scope": "^8.0.1", | ||
"eslint-visitor-keys": "^4.0.0", | ||
@@ -108,2 +109,4 @@ "espree": "^10.0.1", | ||
"@babel/preset-env": "^7.4.3", | ||
"@types/estree": "^1.0.5", | ||
"@types/node": "^20.11.5", | ||
"@wdio/browser-runner": "^8.14.6", | ||
@@ -139,2 +142,3 @@ "@wdio/cli": "^8.14.6", | ||
"js-yaml": "^4.1.0", | ||
"knip": "^5.0.1", | ||
"lint-staged": "^11.0.0", | ||
@@ -147,3 +151,2 @@ "load-perf": "^0.2.0", | ||
"marked": "^4.0.8", | ||
"memfs": "^3.0.1", | ||
"metascraper": "^5.25.7", | ||
@@ -167,4 +170,4 @@ "metascraper-description": "^5.25.7", | ||
"sinon": "^11.0.0", | ||
"typescript": "^5.3.3", | ||
"vite-plugin-commonjs": "^0.10.0", | ||
"webdriverio": "^8.14.6", | ||
"webpack": "^5.23.0", | ||
@@ -171,0 +174,0 @@ "webpack-cli": "^4.5.0", |
@@ -306,4 +306,4 @@ [![npm version](https://img.shields.io/npm/v/eslint.svg)](https://www.npmjs.com/package/eslint) | ||
<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://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://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://bitwarden.com"><img src="https://avatars.githubusercontent.com/u/15990069?v=4" alt="Bitwarden" 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://www.jetbrains.com/"><img src="https://images.opencollective.com/jetbrains/eb04ddc/logo.png" alt="JetBrains" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="64"></a> <a href="https://www.workleap.com"><img src="https://avatars.githubusercontent.com/u/53535748?u=d1e55d7661d724bf2281c1bfd33cb8f99fe2465f&v=4" alt="Workleap" height="64"></a></p><h3>Bronze Sponsors</h3> | ||
@@ -310,0 +310,0 @@ <p><a href="https://www.notion.so"><img src="https://images.opencollective.com/notion/bf3b117/logo.png" alt="notion" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://transloadit.com/"><img src="https://avatars.githubusercontent.com/u/125754?v=4" alt="Transloadit" height="32"></a> <a href="https://www.ignitionapp.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Ignition" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104?v=4" alt="Nx" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a> <a href="https://usenextbase.com"><img src="https://avatars.githubusercontent.com/u/145838380?v=4" alt="Nextbase Starter Kit" height="32"></a></p> |
Sorry, the diff of this file is too big to display
3011375
70827
2
65
397
+ Added@eslint/js@9.0.0-rc.0(transitive)
+ Addedoptionator@0.9.4(transitive)
+ Addedword-wrap@1.2.5(transitive)
- Removed@aashutoshrathi/word-wrap@1.2.6(transitive)
- Removed@eslint/js@9.0.0-beta.2(transitive)
- Removedoptionator@0.9.3(transitive)
Updated@eslint/js@9.0.0-rc.0
Updatedeslint-scope@^8.0.1