Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

eslint

Package Overview
Dependencies
Maintainers
2
Versions
372
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

eslint - npm Package Compare versions

Comparing version 9.0.0 to 9.1.0

12

bin/eslint.js

@@ -151,14 +151,2 @@ #!/usr/bin/env node

// Call the config inspector if `--inspect-config` is present.
if (process.argv.includes("--inspect-config")) {
console.warn("You can also run this command directly using 'npx @eslint/config-inspector' in the same directory as your configuration file.");
const spawn = require("cross-spawn");
spawn.sync("npx", ["@eslint/config-inspector"], { encoding: "utf8", stdio: "inherit" });
return;
}
// Otherwise, call the CLI.

@@ -165,0 +153,0 @@ const cli = require("../lib/cli");

1

conf/globals.js

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

Int8Array: false,
Intl: false,
Map: false,

@@ -75,0 +76,0 @@ Promise: false,

@@ -22,3 +22,3 @@ /**

{ LegacyESLint } = require("./eslint"),
{ ESLint, shouldUseFlatConfig } = require("./eslint/eslint"),
{ ESLint, shouldUseFlatConfig, locateConfigFileToUse } = require("./eslint/eslint"),
createCLIOptions = require("./options"),

@@ -341,2 +341,23 @@ log = require("./shared/logging"),

/**
* Calculates the command string for the --inspect-config operation.
* @param {string} configFile The path to the config file to inspect.
* @returns {Promise<string>} The command string to execute.
*/
async calculateInspectConfigFlags(configFile) {
// find the config file
const {
configFilePath,
basePath,
error
} = await locateConfigFileToUse({ cwd: process.cwd(), configFile });
if (error) {
throw error;
}
return ["--config", configFilePath, "--basePath", basePath];
},
/**
* Executes the CLI based on an array of arguments that is passed in.

@@ -430,2 +451,20 @@ * @param {string|Array|Object} args The arguments to process.

if (options.inspectConfig) {
log.info("You can also run this command directly using 'npx @eslint/config-inspector' in the same directory as your configuration file.");
try {
const flatOptions = await translateOptions(options, "flat");
const spawn = require("cross-spawn");
const flags = await cli.calculateInspectConfigFlags(flatOptions.overrideConfigFile);
spawn.sync("npx", ["@eslint/config-inspector", ...flags], { encoding: "utf8", stdio: "inherit" });
} catch (error) {
log.error(error);
return 2;
}
return 0;
}
debug(`Running on ${useStdin ? "text" : "files"}`);

@@ -432,0 +471,0 @@

@@ -21,2 +21,7 @@ /**

/**
* Fields that are considered metadata and not part of the config object.
*/
const META_FIELDS = new Set(["name"]);
const ruleValidator = new RuleValidator();

@@ -78,3 +83,49 @@

/**
* Wraps a config error with details about where the error occurred.
* @param {Error} error The original error.
* @param {number} originalLength The original length of the config array.
* @param {number} baseLength The length of the base config.
* @returns {TypeError} The new error with details.
*/
function wrapConfigErrorWithDetails(error, originalLength, baseLength) {
let location = "user-defined";
let configIndex = error.index;
/*
* A config array is set up in this order:
* 1. Base config
* 2. Original configs
* 3. User-defined configs
* 4. CLI-defined configs
*
* So we need to adjust the index to account for the base config.
*
* - If the index is less than the base length, it's in the base config
* (as specified by `baseConfig` argument to `FlatConfigArray` constructor).
* - If the index is greater than the base length but less than the original
* length + base length, it's in the original config. The original config
* is passed to the `FlatConfigArray` constructor as the first argument.
* - Otherwise, it's in the user-defined config, which is loaded from the
* config file and merged with any command-line options.
*/
if (error.index < baseLength) {
location = "base";
} else if (error.index < originalLength + baseLength) {
location = "original";
configIndex = error.index - baseLength;
} else {
configIndex = error.index - originalLength - baseLength;
}
return new TypeError(
`${error.message.slice(0, -1)} at ${location} index ${configIndex}.`,
{ cause: error }
);
}
const originalBaseConfig = Symbol("originalBaseConfig");
const originalLength = Symbol("originalLength");
const baseLength = Symbol("baseLength");

@@ -106,2 +157,8 @@ //-----------------------------------------------------------------------------

/**
* The original length of the array before any modifications.
* @type {number}
*/
this[originalLength] = this.length;
if (baseConfig[Symbol.iterator]) {

@@ -114,2 +171,8 @@ this.unshift(...baseConfig);

/**
* The length of the array after applying the base config.
* @type {number}
*/
this[baseLength] = this.length - this[originalLength];
/**
* The base config used to build the config array.

@@ -131,2 +194,45 @@ * @type {Array<FlatConfig>}

/**
* Normalizes the array by calling the superclass method and catching/rethrowing
* any ConfigError exceptions with additional details.
* @param {any} [context] The context to use to normalize the array.
* @returns {Promise<FlatConfigArray>} A promise that resolves when the array is normalized.
*/
normalize(context) {
return super.normalize(context)
.catch(error => {
if (error.name === "ConfigError") {
throw wrapConfigErrorWithDetails(error, this[originalLength], this[baseLength]);
}
throw error;
});
}
/**
* Normalizes the array by calling the superclass method and catching/rethrowing
* any ConfigError exceptions with additional details.
* @param {any} [context] The context to use to normalize the array.
* @returns {FlatConfigArray} The current instance.
* @throws {TypeError} If the config is invalid.
*/
normalizeSync(context) {
try {
return super.normalizeSync(context);
} catch (error) {
if (error.name === "ConfigError") {
throw wrapConfigErrorWithDetails(error, this[originalLength], this[baseLength]);
}
throw error;
}
}
/* eslint-disable class-methods-use-this -- Desired as instance method */

@@ -143,5 +249,5 @@ /**

/*
* If `shouldIgnore` is false, we remove any ignore patterns specified
* in the config so long as it's not a default config and it doesn't
* have a `files` entry.
* If a config object has `ignores` and no other non-meta fields, then it's an object
* for global ignores. If `shouldIgnore` is false, that object shouldn't apply,
* so we'll remove its `ignores`.
*/

@@ -152,3 +258,3 @@ if (

config.ignores &&
!config.files
Object.keys(config).filter(key => !META_FIELDS.has(key)).length === 1
) {

@@ -155,0 +261,0 @@ /* eslint-disable-next-line no-unused-vars -- need to strip off other keys */

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

const minimatch = require("minimatch");
const util = require("util");
const fswalk = require("@nodelib/fs.walk");

@@ -28,3 +27,2 @@ const globParent = require("glob-parent");

const doFsWalk = util.promisify(fswalk.walk);
const Minimatch = minimatch.Minimatch;

@@ -285,52 +283,88 @@ const MINIMATCH_OPTIONS = { dot: true };

const filePaths = (await doFsWalk(basePath, {
const filePaths = (await new Promise((resolve, reject) => {
deepFilter(entry) {
const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
const matchesPattern = matchers.some(matcher => matcher.match(relativePath, true));
let promiseRejected = false;
return matchesPattern && !configs.isDirectoryIgnored(entry.path);
},
entryFilter(entry) {
const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
/**
* Wraps a boolean-returning filter function. The wrapped function will reject the promise if an error occurs.
* @param {Function} filter A filter function to wrap.
* @returns {Function} A function similar to the wrapped filter that rejects the promise if an error occurs.
*/
function wrapFilter(filter) {
return (...args) => {
// entries may be directories or files so filter out directories
if (entry.dirent.isDirectory()) {
// No need to run the filter if an error has been thrown.
if (!promiseRejected) {
try {
return filter(...args);
} catch (error) {
promiseRejected = true;
reject(error);
}
}
return false;
}
};
}
/*
* Optimization: We need to track when patterns are left unmatched
* and so we use `unmatchedPatterns` to do that. There is a bit of
* complexity here because the same file can be matched by more than
* one pattern. So, when we start, we actually need to test every
* pattern against every file. Once we know there are no remaining
* unmatched patterns, then we can switch to just looking for the
* first matching pattern for improved speed.
*/
const matchesPattern = unmatchedPatterns.size > 0
? matchers.reduce((previousValue, matcher) => {
const pathMatches = matcher.match(relativePath);
fswalk.walk(
basePath,
{
deepFilter: wrapFilter(entry => {
const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
const matchesPattern = matchers.some(matcher => matcher.match(relativePath, true));
return matchesPattern && !configs.isDirectoryIgnored(entry.path);
}),
entryFilter: wrapFilter(entry => {
const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
// entries may be directories or files so filter out directories
if (entry.dirent.isDirectory()) {
return false;
}
/*
* We updated the unmatched patterns set only if the path
* matches and the file isn't ignored. If the file is
* ignored, that means there wasn't a match for the
* pattern so it should not be removed.
*
* Performance note: isFileIgnored() aggressively caches
* results so there is no performance penalty for calling
* it twice with the same argument.
* Optimization: We need to track when patterns are left unmatched
* and so we use `unmatchedPatterns` to do that. There is a bit of
* complexity here because the same file can be matched by more than
* one pattern. So, when we start, we actually need to test every
* pattern against every file. Once we know there are no remaining
* unmatched patterns, then we can switch to just looking for the
* first matching pattern for improved speed.
*/
if (pathMatches && !configs.isFileIgnored(entry.path)) {
unmatchedPatterns.delete(matcher.pattern);
}
const matchesPattern = unmatchedPatterns.size > 0
? matchers.reduce((previousValue, matcher) => {
const pathMatches = matcher.match(relativePath);
return pathMatches || previousValue;
}, false)
: matchers.some(matcher => matcher.match(relativePath));
/*
* We updated the unmatched patterns set only if the path
* matches and the file isn't ignored. If the file is
* ignored, that means there wasn't a match for the
* pattern so it should not be removed.
*
* Performance note: isFileIgnored() aggressively caches
* results so there is no performance penalty for calling
* it twice with the same argument.
*/
if (pathMatches && !configs.isFileIgnored(entry.path)) {
unmatchedPatterns.delete(matcher.pattern);
}
return matchesPattern && !configs.isFileIgnored(entry.path);
}
return pathMatches || previousValue;
}, false)
: matchers.some(matcher => matcher.match(relativePath));
return matchesPattern && !configs.isFileIgnored(entry.path);
})
},
(error, entries) => {
// If the promise is already rejected, calling `resolve` or `reject` will do nothing.
if (error) {
reject(error);
} else {
resolve(entries);
}
}
);
})).map(entry => entry.path);

@@ -337,0 +371,0 @@

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

const LintResultCache = require("../cli-engine/lint-result-cache");
const { Retrier } = require("@humanwhocodes/retry");

@@ -855,2 +856,4 @@ /*

const controller = new AbortController();
const retryCodes = new Set(["ENFILE", "EMFILE"]);
const retrier = new Retrier(error => retryCodes.has(error.code));

@@ -924,3 +927,3 @@ debug(`${filePaths.length} files found in: ${Date.now() - startTime}ms`);

return fs.readFile(filePath, { encoding: "utf8", signal: controller.signal })
return retrier.retry(() => fs.readFile(filePath, { encoding: "utf8", signal: controller.signal })
.then(text => {

@@ -955,7 +958,7 @@

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

@@ -1221,3 +1224,4 @@ );

ESLint,
shouldUseFlatConfig
shouldUseFlatConfig,
locateConfigFileToUse
};

@@ -41,12 +41,12 @@ /**

*/
function groupByParentComment(directives) {
function groupByParentDirective(directives) {
const groups = new Map();
for (const directive of directives) {
const { unprocessedDirective: { parentComment } } = directive;
const { unprocessedDirective: { parentDirective } } = directive;
if (groups.has(parentComment)) {
groups.get(parentComment).push(directive);
if (groups.has(parentDirective)) {
groups.get(parentDirective).push(directive);
} else {
groups.set(parentComment, [directive]);
groups.set(parentDirective, [directive]);
}

@@ -61,15 +61,15 @@ }

* @param {Directive[]} directives Unused directives to be removed.
* @param {Token} commentToken The backing Comment token.
* @param {Token} node The backing Comment token.
* @returns {{ description, fix, unprocessedDirective }[]} Details for later creation of output Problems.
*/
function createIndividualDirectivesRemoval(directives, commentToken) {
function createIndividualDirectivesRemoval(directives, node) {
/*
* `commentToken.value` starts right after `//` or `/*`.
* `node.value` starts right after `//` or `/*`.
* All calculated offsets will be relative to this index.
*/
const commentValueStart = commentToken.range[0] + "//".length;
const commentValueStart = node.range[0] + "//".length;
// Find where the list of rules starts. `\S+` matches with the directive name (e.g. `eslint-disable-line`)
const listStartOffset = /^\s*\S+\s+/u.exec(commentToken.value)[0].length;
const listStartOffset = /^\s*\S+\s+/u.exec(node.value)[0].length;

@@ -83,3 +83,3 @@ /*

*/
const listText = commentToken.value
const listText = node.value
.slice(listStartOffset) // remove directive name and all whitespace before the list

@@ -165,9 +165,9 @@ .split(/\s-{2,}\s/u)[0] // remove `-- comment`, if it exists

/**
* Creates a description of deleting an entire unused disable comment.
* Creates a description of deleting an entire unused disable directive.
* @param {Directive[]} directives Unused directives to be removed.
* @param {Token} commentToken The backing Comment token.
* @returns {{ description, fix, unprocessedDirective }} Details for later creation of an output Problem.
* @param {Token} node The backing Comment token.
* @returns {{ description, fix, unprocessedDirective }} Details for later creation of an output problem.
*/
function createCommentRemoval(directives, commentToken) {
const { range } = commentToken;
function createDirectiveRemoval(directives, node) {
const { range } = node;
const ruleIds = directives.filter(directive => directive.ruleId).map(directive => `'${directive.ruleId}'`);

@@ -193,8 +193,8 @@

function processUnusedDirectives(allDirectives) {
const directiveGroups = groupByParentComment(allDirectives);
const directiveGroups = groupByParentDirective(allDirectives);
return directiveGroups.flatMap(
directives => {
const { parentComment } = directives[0].unprocessedDirective;
const remainingRuleIds = new Set(parentComment.ruleIds);
const { parentDirective } = directives[0].unprocessedDirective;
const remainingRuleIds = new Set(parentDirective.ruleIds);

@@ -206,4 +206,4 @@ for (const directive of directives) {

return remainingRuleIds.size
? createIndividualDirectivesRemoval(directives, parentComment.commentToken)
: [createCommentRemoval(directives, parentComment.commentToken)];
? createIndividualDirectivesRemoval(directives, parentDirective.node)
: [createDirectiveRemoval(directives, parentDirective.node)];
}

@@ -381,3 +381,3 @@ );

.map(({ description, fix, unprocessedDirective }) => {
const { parentComment, type, line, column } = unprocessedDirective;
const { parentDirective, type, line, column } = unprocessedDirective;

@@ -398,4 +398,4 @@ let message;

message,
line: type === "disable-next-line" ? parentComment.commentToken.loc.start.line : line,
column: type === "disable-next-line" ? parentComment.commentToken.loc.start.column + 1 : column,
line: type === "disable-next-line" ? parentDirective.node.loc.start.line : line,
column: type === "disable-next-line" ? parentDirective.node.loc.start.column + 1 : column,
severity: options.reportUnusedDisableDirectives === "warn" ? 1 : 2,

@@ -402,0 +402,0 @@ nodeType: null,

@@ -41,3 +41,3 @@ /**

* @property {string} [ignorePath] Specify path of ignore file
* @property {string[]} [ignorePattern] Pattern of files to ignore (in addition to those in .eslintignore)
* @property {string[]} [ignorePattern] Patterns of files to ignore. In eslintrc mode, these are in addition to `.eslintignore`
* @property {boolean} init Run config initialization wizard

@@ -265,3 +265,3 @@ * @property {boolean} inlineConfig Prevent comments from changing config or rules

type: "[String]",
description: "Pattern of files to ignore (in addition to those in .eslintignore)",
description: `Patterns of files to ignore${usingFlatConfig ? "" : " (in addition to those in .eslintignore)"}`,
concatRepeatedArrays: [true, {

@@ -268,0 +268,0 @@ oneValuePerFlag: true

@@ -34,4 +34,3 @@ /**

checkLoops: {
type: "boolean",
default: true
enum: ["all", "allExceptWhileTrue", "none", true, false]
}

@@ -49,7 +48,13 @@ },

create(context) {
const options = context.options[0] || {},
checkLoops = options.checkLoops !== false,
loopSetStack = [];
const options = context.options[0] || {};
let checkLoops = options.checkLoops ?? "allExceptWhileTrue";
const loopSetStack = [];
const sourceCode = context.sourceCode;
if (options.checkLoops === true) {
checkLoops = "all";
} else if (options.checkLoops === false) {
checkLoops = "none";
}
let loopsInCurrentScope = new Set();

@@ -125,3 +130,3 @@

function checkLoop(node) {
if (checkLoops) {
if (checkLoops === "all" || checkLoops === "allExceptWhileTrue") {
trackConstantConditionLoop(node);

@@ -138,3 +143,9 @@ }

IfStatement: reportIfConstant,
WhileStatement: checkLoop,
WhileStatement(node) {
if (node.test.type === "Literal" && node.test.value === true && checkLoops === "allExceptWhileTrue") {
return;
}
checkLoop(node);
},
"WhileStatement:exit": checkConstantConditionLoopInSet,

@@ -141,0 +152,0 @@ DoWhileStatement: checkLoop,

@@ -9,8 +9,2 @@ /**

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const Graphemer = require("graphemer").default;
//------------------------------------------------------------------------------
// Helpers

@@ -22,4 +16,4 @@ //------------------------------------------------------------------------------

/** @type {Graphemer | undefined} */
let splitter;
/** @type {Intl.Segmenter | undefined} */
let segmenter;

@@ -52,7 +46,11 @@ //------------------------------------------------------------------------------

if (!splitter) {
splitter = new Graphemer();
segmenter ??= new Intl.Segmenter("en-US"); // en-US locale should be supported everywhere
let graphemeCount = 0;
// eslint-disable-next-line no-unused-vars -- for-of needs a variable
for (const unused of segmenter.segment(value)) {
graphemeCount++;
}
return splitter.countGraphemes(value);
return graphemeCount;
}

@@ -59,0 +57,0 @@

@@ -376,2 +376,52 @@ /**

/**
* A class to represent a directive comment.
*/
class Directive {
/**
* The type of directive.
* @type {"disable"|"enable"|"disable-next-line"|"disable-line"}
* @readonly
*/
type;
/**
* The node representing the directive.
* @type {ASTNode|Comment}
* @readonly
*/
node;
/**
* Everything after the "eslint-disable" portion of the directive,
* but before the "--" that indicates the justification.
* @type {string}
* @readonly
*/
value;
/**
* The justification for the directive.
* @type {string}
* @readonly
*/
justification;
/**
* Creates a new instance.
* @param {Object} options The options for the directive.
* @param {"disable"|"enable"|"disable-next-line"|"disable-line"} options.type The type of directive.
* @param {ASTNode|Comment} options.node The node representing the directive.
* @param {string} options.value The value of the directive.
* @param {string} options.justification The justification for the directive.
*/
constructor({ type, node, value, justification }) {
this.type = type;
this.node = node;
this.value = value;
this.justification = justification;
}
}
//------------------------------------------------------------------------------

@@ -926,2 +976,80 @@ // Public Interface

/**
* Returns an all directive nodes that enable or disable rules along with any problems
* encountered while parsing the directives.
* @returns {{problems:Array<Problem>,directives:Array<Directive>}} Information
* that ESLint needs to further process the directives.
*/
getDisableDirectives() {
// check the cache first
const cachedDirectives = this[caches].get("disableDirectives");
if (cachedDirectives) {
return cachedDirectives;
}
const problems = [];
const directives = [];
this.getInlineConfigNodes().forEach(comment => {
const { directivePart, justificationPart } = commentParser.extractDirectiveComment(comment.value);
// Step 1: Extract the directive text
const match = directivesPattern.exec(directivePart);
if (!match) {
return;
}
const directiveText = match[1];
// Step 2: Extract the directive value
const lineCommentSupported = /^eslint-disable-(next-)?line$/u.test(directiveText);
if (comment.type === "Line" && !lineCommentSupported) {
return;
}
// Step 3: Validate the directive does not span multiple lines
if (directiveText === "eslint-disable-line" && comment.loc.start.line !== comment.loc.end.line) {
const message = `${directiveText} comment should not span multiple lines.`;
problems.push({
ruleId: null,
message,
loc: comment.loc
});
return;
}
// Step 4: Extract the directive value and create the Directive object
const directiveValue = directivePart.slice(match.index + directiveText.length);
switch (directiveText) {
case "eslint-disable":
case "eslint-enable":
case "eslint-disable-next-line":
case "eslint-disable-line": {
const directiveType = directiveText.slice("eslint-".length);
directives.push(new Directive({
type: directiveType,
node: comment,
value: directiveValue,
justification: justificationPart
}));
}
// no default
}
});
const result = { problems, directives };
this[caches].set("disableDirectives", result);
return result;
}
/**
* Applies language options sent in from the core.

@@ -928,0 +1056,0 @@ * @param {Object} languageOptions The language options for this run.

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

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

"test:fuzz": "node Makefile.js fuzz",
"test:performance": "node Makefile.js perf"
"test:performance": "node Makefile.js perf",
"test:emfile": "node tools/check-emfile-handling.js"
},

@@ -72,5 +73,6 @@ "gitHooks": {

"@eslint/eslintrc": "^3.0.2",
"@eslint/js": "9.0.0",
"@humanwhocodes/config-array": "^0.12.3",
"@eslint/js": "9.1.1",
"@humanwhocodes/config-array": "^0.13.0",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.2.3",
"@nodelib/fs.walk": "^1.2.8",

@@ -91,3 +93,2 @@ "ajv": "^6.12.4",

"glob-parent": "^6.0.2",
"graphemer": "^1.4.0",
"ignore": "^5.2.0",

@@ -126,3 +127,3 @@ "imurmurhash": "^0.1.4",

"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-eslint-plugin": "^5.2.1",
"eslint-plugin-eslint-plugin": "^6.0.0",
"eslint-plugin-internal-rules": "file:tools/internal-rules",

@@ -138,3 +139,3 @@ "eslint-plugin-jsdoc": "^46.9.0",

"glob": "^10.0.0",
"globals": "^14.0.0",
"globals": "^15.0.0",
"got": "^11.8.3",

@@ -141,0 +142,0 @@ "gray-matter": "^4.0.3",

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

After running `npm init @eslint/config`, you'll have an `eslint.config.js` or `eslint.config.mjs` file in your directory. In it, you'll see some rules configured like this:
After running `npm init @eslint/config`, you'll have an `eslint.config.js` (or `eslint.config.mjs`) file in your directory. In it, you'll see some rules configured like this:

@@ -304,3 +304,3 @@ ```js

<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://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>

@@ -307,0 +307,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://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
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc