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.2 to 9.0.0-rc.0

3

bin/eslint.js

@@ -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

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