eslint-plugin-react
Advanced tools
Comparing version 7.34.4 to 7.35.0
'use strict'; | ||
const fromEntries = require('object.fromentries'); | ||
const entries = require('object.entries'); | ||
const plugin = require('..'); | ||
const allRules = require('../lib/rules'); | ||
const legacyConfig = plugin.configs.all; | ||
function filterRules(rules, predicate) { | ||
return fromEntries(entries(rules).filter((entry) => predicate(entry[1]))); | ||
} | ||
/** | ||
* @param {object} rules - rules object mapping rule name to rule module | ||
* @returns {Record<string, 2>} | ||
*/ | ||
function configureAsError(rules) { | ||
return fromEntries(Object.keys(rules).map((key) => [`react/${key}`, 2])); | ||
} | ||
const activeRules = filterRules(allRules, (rule) => !rule.meta.deprecated); | ||
const activeRulesConfig = configureAsError(activeRules); | ||
const deprecatedRules = filterRules(allRules, (rule) => rule.meta.deprecated); | ||
module.exports = { | ||
plugins: { | ||
/** | ||
* @type {{ | ||
* deprecatedRules: Record<string, import('eslint').Rule.RuleModule>, | ||
* rules: Record<string, import('eslint').Rule.RuleModule>, | ||
* }} | ||
*/ | ||
react: { | ||
deprecatedRules, | ||
rules: allRules, | ||
}, | ||
}, | ||
rules: activeRulesConfig, | ||
languageOptions: { | ||
parserOptions: { | ||
ecmaFeatures: { | ||
jsx: true, | ||
}, | ||
}, | ||
}, | ||
plugins: { react: plugin }, | ||
rules: legacyConfig.rules, | ||
languageOptions: { parserOptions: legacyConfig.parserOptions }, | ||
}; | ||
// this is so the `languageOptions` property won't be warned in the new config system | ||
Object.defineProperty(module.exports, 'languageOptions', { enumerable: false }); |
'use strict'; | ||
const all = require('./all'); | ||
const plugin = require('..'); | ||
module.exports = Object.assign({}, all, { | ||
languageOptions: Object.assign({}, all.languageOptions, { | ||
parserOptions: Object.assign({}, all.languageOptions.parserOptions, { | ||
jsxPragma: null, // for @typescript/eslint-parser | ||
}), | ||
}), | ||
rules: { | ||
'react/react-in-jsx-scope': 0, | ||
'react/jsx-uses-react': 0, | ||
}, | ||
}); | ||
const legacyConfig = plugin.configs['jsx-runtime']; | ||
// this is so the `languageOptions` property won't be warned in the new config system | ||
module.exports = { | ||
plugins: { react: plugin }, | ||
rules: legacyConfig.rules, | ||
languageOptions: { parserOptions: legacyConfig.parserOptions }, | ||
}; | ||
Object.defineProperty(module.exports, 'languageOptions', { enumerable: false }); |
'use strict'; | ||
const all = require('./all'); | ||
const plugin = require('..'); | ||
module.exports = Object.assign({}, all, { | ||
languageOptions: all.languageOptions, | ||
rules: { | ||
'react/display-name': 2, | ||
'react/jsx-key': 2, | ||
'react/jsx-no-comment-textnodes': 2, | ||
'react/jsx-no-duplicate-props': 2, | ||
'react/jsx-no-target-blank': 2, | ||
'react/jsx-no-undef': 2, | ||
'react/jsx-uses-react': 2, | ||
'react/jsx-uses-vars': 2, | ||
'react/no-children-prop': 2, | ||
'react/no-danger-with-children': 2, | ||
'react/no-deprecated': 2, | ||
'react/no-direct-mutation-state': 2, | ||
'react/no-find-dom-node': 2, | ||
'react/no-is-mounted': 2, | ||
'react/no-render-return-value': 2, | ||
'react/no-string-refs': 2, | ||
'react/no-unescaped-entities': 2, | ||
'react/no-unknown-property': 2, | ||
'react/no-unsafe': 0, | ||
'react/prop-types': 2, | ||
'react/react-in-jsx-scope': 2, | ||
'react/require-render-return': 2, | ||
}, | ||
}); | ||
const legacyConfig = plugin.configs.recommended; | ||
// this is so the `languageOptions` property won't be warned in the new config system | ||
module.exports = { | ||
plugins: { react: plugin }, | ||
rules: legacyConfig.rules, | ||
languageOptions: { parserOptions: legacyConfig.parserOptions }, | ||
}; | ||
Object.defineProperty(module.exports, 'languageOptions', { enumerable: false }); |
106
index.js
'use strict'; | ||
const configAll = require('./configs/all'); | ||
const configRecommended = require('./configs/recommended'); | ||
const configRuntime = require('./configs/jsx-runtime'); | ||
const fromEntries = require('object.fromentries'); | ||
const entries = require('object.entries'); | ||
const allRules = require('./lib/rules'); | ||
function filterRules(rules, predicate) { | ||
return fromEntries(entries(rules).filter((entry) => predicate(entry[1]))); | ||
} | ||
/** | ||
* @param {object} rules - rules object mapping rule name to rule module | ||
* @returns {Record<string, 2>} | ||
*/ | ||
function configureAsError(rules) { | ||
return fromEntries(Object.keys(rules).map((key) => [`react/${key}`, 2])); | ||
} | ||
const activeRules = filterRules(allRules, (rule) => !rule.meta.deprecated); | ||
const activeRulesConfig = configureAsError(activeRules); | ||
const deprecatedRules = filterRules(allRules, (rule) => rule.meta.deprecated); | ||
// for legacy config system | ||
@@ -14,19 +30,81 @@ const plugins = [ | ||
module.exports = { | ||
deprecatedRules: configAll.plugins.react.deprecatedRules, | ||
const plugin = { | ||
deprecatedRules, | ||
rules: allRules, | ||
configs: { | ||
recommended: Object.assign({}, configRecommended, { | ||
parserOptions: configRecommended.languageOptions.parserOptions, | ||
recommended: { | ||
plugins, | ||
}), | ||
all: Object.assign({}, configAll, { | ||
parserOptions: configAll.languageOptions.parserOptions, | ||
parserOptions: { | ||
ecmaFeatures: { | ||
jsx: true, | ||
}, | ||
}, | ||
rules: { | ||
'react/display-name': 2, | ||
'react/jsx-key': 2, | ||
'react/jsx-no-comment-textnodes': 2, | ||
'react/jsx-no-duplicate-props': 2, | ||
'react/jsx-no-target-blank': 2, | ||
'react/jsx-no-undef': 2, | ||
'react/jsx-uses-react': 2, | ||
'react/jsx-uses-vars': 2, | ||
'react/no-children-prop': 2, | ||
'react/no-danger-with-children': 2, | ||
'react/no-deprecated': 2, | ||
'react/no-direct-mutation-state': 2, | ||
'react/no-find-dom-node': 2, | ||
'react/no-is-mounted': 2, | ||
'react/no-render-return-value': 2, | ||
'react/no-string-refs': 2, | ||
'react/no-unescaped-entities': 2, | ||
'react/no-unknown-property': 2, | ||
'react/no-unsafe': 0, | ||
'react/prop-types': 2, | ||
'react/react-in-jsx-scope': 2, | ||
'react/require-render-return': 2, | ||
}, | ||
}, | ||
all: { | ||
plugins, | ||
}), | ||
'jsx-runtime': Object.assign({}, configRuntime, { | ||
parserOptions: configRuntime.languageOptions.parserOptions, | ||
parserOptions: { | ||
ecmaFeatures: { | ||
jsx: true, | ||
}, | ||
}, | ||
rules: activeRulesConfig, | ||
}, | ||
'jsx-runtime': { | ||
plugins, | ||
}), | ||
parserOptions: { | ||
ecmaFeatures: { | ||
jsx: true, | ||
}, | ||
jsxPragma: null, // for @typescript/eslint-parser | ||
}, | ||
rules: { | ||
'react/react-in-jsx-scope': 0, | ||
'react/jsx-uses-react': 0, | ||
}, | ||
}, | ||
}, | ||
}; | ||
plugin.configs.flat = { | ||
recommended: { | ||
plugins: { react: plugin }, | ||
rules: plugin.configs.recommended.rules, | ||
languageOptions: { parserOptions: plugin.configs.recommended.parserOptions }, | ||
}, | ||
all: { | ||
plugins: { react: plugin }, | ||
rules: plugin.configs.all.rules, | ||
languageOptions: { parserOptions: plugin.configs.all.parserOptions }, | ||
}, | ||
'jsx-runtime': { | ||
plugins: { react: plugin }, | ||
rules: plugin.configs['jsx-runtime'].rules, | ||
languageOptions: { parserOptions: plugin.configs['jsx-runtime'].parserOptions }, | ||
}, | ||
}; | ||
module.exports = plugin; |
@@ -8,2 +8,3 @@ /** | ||
const minimatch = require('minimatch'); | ||
const docsUrl = require('../util/docsUrl'); | ||
@@ -74,2 +75,31 @@ const report = require('../util/report'); | ||
}, | ||
{ | ||
type: 'object', | ||
properties: { | ||
propNamePattern: { type: 'string' }, | ||
allowedFor: { | ||
type: 'array', | ||
uniqueItems: true, | ||
items: { type: 'string' }, | ||
}, | ||
message: { type: 'string' }, | ||
}, | ||
additionalProperties: false, | ||
}, | ||
{ | ||
type: 'object', | ||
properties: { | ||
propNamePattern: { type: 'string' }, | ||
disallowedFor: { | ||
type: 'array', | ||
uniqueItems: true, | ||
minItems: 1, | ||
items: { type: 'string' }, | ||
}, | ||
message: { type: 'string' }, | ||
}, | ||
required: ['disallowedFor'], | ||
additionalProperties: false, | ||
}, | ||
], | ||
@@ -86,2 +116,4 @@ }, | ||
const propName = typeof value === 'string' ? value : value.propName; | ||
const propPattern = value.propNamePattern; | ||
const prop = propName || propPattern; | ||
const options = { | ||
@@ -91,8 +123,21 @@ allowList: typeof value === 'string' ? [] : (value.allowedFor || []), | ||
message: typeof value === 'string' ? null : value.message, | ||
isPattern: !!value.propNamePattern, | ||
}; | ||
return [propName, options]; | ||
return [prop, options]; | ||
})); | ||
function getPropOptions(prop) { | ||
// Get config options having pattern | ||
const propNamePatternArray = Array.from(forbid.entries()).filter((propEntry) => propEntry[1].isPattern); | ||
// Match current prop with pattern options, return if matched | ||
const propNamePattern = propNamePatternArray.find((propPatternVal) => minimatch(prop, propPatternVal[0])); | ||
// Get options for matched propNamePattern | ||
const propNamePatternOptions = propNamePattern && propNamePattern[1]; | ||
const options = forbid.get(prop) || propNamePatternOptions; | ||
return options; | ||
} | ||
function isForbidden(prop, tagName) { | ||
const options = forbid.get(prop); | ||
const options = getPropOptions(prop); | ||
if (!options) { | ||
@@ -128,3 +173,3 @@ return false; | ||
const customMessage = forbid.get(prop).message; | ||
const customMessage = getPropOptions(prop).message; | ||
@@ -131,0 +176,0 @@ report(context, customMessage || messages.propIsForbidden, !customMessage && 'propIsForbidden', { |
@@ -5,3 +5,3 @@ 'use strict'; | ||
/** @type {Record<string, import('eslint').Rule.RuleModule>} */ | ||
/** @satisfies {Record<string, import('eslint').Rule.RuleModule>} */ | ||
module.exports = { | ||
@@ -54,2 +54,3 @@ 'boolean-prop-naming': require('./boolean-prop-naming'), | ||
'jsx-props-no-spreading': require('./jsx-props-no-spreading'), | ||
'jsx-props-no-spread-multi': require('./jsx-props-no-spread-multi'), | ||
'jsx-sort-default-props': require('./jsx-sort-default-props'), | ||
@@ -56,0 +57,0 @@ 'jsx-sort-props': require('./jsx-sort-props'), |
@@ -9,5 +9,7 @@ /** | ||
const repeat = require('string.prototype.repeat'); | ||
const has = require('hasown'); | ||
const astUtil = require('../util/ast'); | ||
const docsUrl = require('../util/docsUrl'); | ||
const getSourceCode = require('../util/eslint').getSourceCode; | ||
const report = require('../util/report'); | ||
@@ -22,4 +24,12 @@ | ||
matchIndent: 'Expected closing tag to match indentation of opening.', | ||
alignWithOpening: 'Expected closing tag to be aligned with the line containing the opening tag', | ||
}; | ||
const defaultOption = 'tag-aligned'; | ||
const optionMessageMap = { | ||
'tag-aligned': 'matchIndent', | ||
'line-aligned': 'alignWithOpening', | ||
}; | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
@@ -36,5 +46,37 @@ module.exports = { | ||
messages, | ||
schema: [{ | ||
anyOf: [ | ||
{ | ||
enum: ['tag-aligned', 'line-aligned'], | ||
}, | ||
{ | ||
type: 'object', | ||
properties: { | ||
location: { | ||
enum: ['tag-aligned', 'line-aligned'], | ||
}, | ||
}, | ||
additionalProperties: false, | ||
}, | ||
], | ||
}], | ||
}, | ||
create(context) { | ||
const config = context.options[0]; | ||
let option = defaultOption; | ||
if (typeof config === 'string') { | ||
option = config; | ||
} else if (typeof config === 'object') { | ||
if (has(config, 'location')) { | ||
option = config.location; | ||
} | ||
} | ||
function getIndentation(openingStartOfLine, opening) { | ||
if (option === 'line-aligned') return openingStartOfLine.column; | ||
if (option === 'tag-aligned') return opening.loc.start.column; | ||
} | ||
function handleClosingElement(node) { | ||
@@ -44,4 +86,13 @@ if (!node.parent) { | ||
} | ||
const sourceCode = getSourceCode(context); | ||
const opening = node.parent.openingElement || node.parent.openingFragment; | ||
const openingLoc = sourceCode.getFirstToken(opening).loc.start; | ||
const openingLine = sourceCode.lines[openingLoc.line - 1]; | ||
const openingStartOfLine = { | ||
column: /^\s*/.exec(openingLine)[0].length, | ||
line: openingLoc.line, | ||
}; | ||
if (opening.loc.start.line === node.loc.start.line) { | ||
@@ -51,9 +102,20 @@ return; | ||
if (opening.loc.start.column === node.loc.start.column) { | ||
if ( | ||
opening.loc.start.column === node.loc.start.column | ||
&& option === 'tag-aligned' | ||
) { | ||
return; | ||
} | ||
if ( | ||
openingStartOfLine.column === node.loc.start.column | ||
&& option === 'line-aligned' | ||
) { | ||
return; | ||
} | ||
const messageId = astUtil.isNodeFirstInLine(context, node) | ||
? 'matchIndent' | ||
? optionMessageMap[option] | ||
: 'onOwnLine'; | ||
report(context, messages[messageId], messageId, { | ||
@@ -63,3 +125,7 @@ node, | ||
fix(fixer) { | ||
const indent = repeat(' ', opening.loc.start.column); | ||
const indent = repeat( | ||
' ', | ||
getIndentation(openingStartOfLine, opening) | ||
); | ||
if (astUtil.isNodeFirstInLine(context, node)) { | ||
@@ -66,0 +132,0 @@ return fixer.replaceTextRange( |
@@ -8,2 +8,3 @@ /** | ||
const minimatch = require('minimatch'); | ||
const docsUrl = require('../util/docsUrl'); | ||
@@ -43,2 +44,7 @@ const getText = require('../util/eslint').getText; | ||
checkInlineFunction: { type: 'boolean' }, | ||
ignoreComponentNames: { | ||
type: 'array', | ||
uniqueItems: true, | ||
items: { type: 'string' }, | ||
}, | ||
}, | ||
@@ -56,2 +62,7 @@ additionalProperties: false, | ||
checkInlineFunction: { type: 'boolean' }, | ||
ignoreComponentNames: { | ||
type: 'array', | ||
uniqueItems: true, | ||
items: { type: 'string' }, | ||
}, | ||
}, | ||
@@ -69,2 +80,7 @@ additionalProperties: false, | ||
checkInlineFunction: { type: 'boolean' }, | ||
ignoreComponentNames: { | ||
type: 'array', | ||
uniqueItems: true, | ||
items: { type: 'string' }, | ||
}, | ||
}, | ||
@@ -85,2 +101,12 @@ additionalProperties: false, | ||
}, | ||
{ | ||
type: 'object', | ||
properties: { | ||
ignoreComponentNames: { | ||
type: 'array', | ||
uniqueItems: true, | ||
items: { type: 'string' }, | ||
}, | ||
}, | ||
}, | ||
], | ||
@@ -119,4 +145,13 @@ }], | ||
const ignoreComponentNames = configuration.ignoreComponentNames || []; | ||
return { | ||
JSXAttribute(node) { | ||
const componentName = node.parent.name.name; | ||
const isComponentNameIgnored = ignoreComponentNames.some((ignoredComponentNamePattern) => { | ||
const isIgnored = minimatch(componentName, ignoredComponentNamePattern); | ||
return isIgnored; | ||
}); | ||
if ( | ||
@@ -133,2 +168,3 @@ !node.value | ||
) | ||
|| isComponentNameIgnored | ||
) { | ||
@@ -135,0 +171,0 @@ return; |
@@ -91,3 +91,3 @@ /** | ||
function findJSXElementOrFragment(variables, name, previousReferences) { | ||
function findJSXElementOrFragment(startNode, name, previousReferences) { | ||
function find(refs, prevRefs) { | ||
@@ -101,3 +101,3 @@ for (let i = refs.length - 1; i >= 0; i--) { | ||
|| ((writeExpr && writeExpr.type === 'Identifier') | ||
&& findJSXElementOrFragment(variables, writeExpr.name, prevRefs)); | ||
&& findJSXElementOrFragment(startNode, writeExpr.name, prevRefs)); | ||
} | ||
@@ -109,3 +109,3 @@ } | ||
const variable = variableUtil.getVariable(variables, name); | ||
const variable = variableUtil.getVariableFromContext(context, startNode, name); | ||
if (variable && variable.references) { | ||
@@ -156,4 +156,3 @@ const containDuplicates = previousReferences.some((ref) => includes(variable.references, ref)); | ||
const variables = variableUtil.variablesInScope(context, node); | ||
const element = findJSXElementOrFragment(variables, node.expression.name, []); | ||
const element = findJSXElementOrFragment(node, node.expression.name, []); | ||
@@ -160,0 +159,0 @@ if (element) { |
@@ -164,4 +164,3 @@ /** | ||
} | ||
const variables = variableUtil.variablesInScope(context, node); | ||
const leftSideVar = variableUtil.getVariable(variables, leftSide.name); | ||
const leftSideVar = variableUtil.getVariableFromContext(context, node, leftSide.name); | ||
if (leftSideVar) { | ||
@@ -168,0 +167,0 @@ const leftSideValue = leftSideVar.defs |
@@ -100,4 +100,3 @@ /** | ||
const variable = variableUtil | ||
.variablesInScope(context, node) | ||
.find((item) => item.name === name); | ||
.getVariableFromContext(context, node, name); | ||
@@ -104,0 +103,0 @@ if (!variable || !variable.defs[0] || !variable.defs[0].node) { |
@@ -35,4 +35,3 @@ /** | ||
function findSpreadVariable(node, name) { | ||
return variableUtil.variablesInScope(context, node) | ||
.find((item) => item.name === name); | ||
return variableUtil.getVariableFromContext(context, node, name); | ||
} | ||
@@ -132,4 +131,3 @@ /** | ||
if (props.type === 'Identifier') { | ||
const variable = variableUtil.variablesInScope(context, node) | ||
.find((item) => item.name === props.name); | ||
const variable = variableUtil.getVariableFromContext(context, node, props.name); | ||
if (variable && variable.defs.length && variable.defs[0].node.init) { | ||
@@ -136,0 +134,0 @@ props = variable.defs[0].node.init; |
@@ -10,2 +10,3 @@ /** | ||
const fromEntries = require('object.fromentries/polyfill')(); | ||
const minimatch = require('minimatch'); | ||
@@ -59,9 +60,28 @@ const docsUrl = require('../util/docsUrl'); | ||
schema: [], | ||
schema: [{ | ||
type: 'object', | ||
properties: { | ||
customComponentNames: { | ||
items: { | ||
type: 'string', | ||
}, | ||
minItems: 0, | ||
type: 'array', | ||
uniqueItems: true, | ||
}, | ||
}, | ||
}], | ||
}, | ||
create(context) { | ||
const configuration = context.options[0] || {}; | ||
const customComponentNames = configuration.customComponentNames || []; | ||
return { | ||
JSXAttribute(node) { | ||
if (jsxUtil.isDOMComponent(node.parent) && isDangerous(node.name.name)) { | ||
const functionName = node.parent.name.name; | ||
const enableCheckingCustomComponent = customComponentNames.some((name) => minimatch(functionName, name)); | ||
if ((enableCheckingCustomComponent || jsxUtil.isDOMComponent(node.parent)) && isDangerous(node.name.name)) { | ||
report(context, messages.dangerousProp, 'dangerousProp', { | ||
@@ -68,0 +88,0 @@ node, |
@@ -11,3 +11,2 @@ /** | ||
const report = require('../util/report'); | ||
const getMessageData = require('../util/message'); | ||
@@ -228,2 +227,3 @@ // ------------------------------------------------------------------------------ | ||
/* eslint-disable eslint-plugin/no-unused-message-ids -- false positives, these messageIds are used */ | ||
const messages = { | ||
@@ -269,11 +269,7 @@ emptyIsMeaningless: 'An empty “{{attributeName}}” attribute is meaningless.', | ||
data, | ||
suggest: [ | ||
Object.assign( | ||
getMessageData('suggestRemoveNonString', messages.suggestRemoveNonString), | ||
{ | ||
data, | ||
fix(fixer) { return fixer.remove(parentNode); }, | ||
} | ||
), | ||
], | ||
suggest: [{ | ||
messageId: 'suggestRemoveNonString', | ||
data, | ||
fix(fixer) { return fixer.remove(parentNode); }, | ||
}], | ||
}); | ||
@@ -289,11 +285,7 @@ return; | ||
data, | ||
suggest: [ | ||
Object.assign( | ||
getMessageData('suggestRemoveEmpty', messages.suggestRemoveEmpty), | ||
{ | ||
data, | ||
fix(fixer) { return fixer.remove(node.parent); }, | ||
} | ||
), | ||
], | ||
suggest: [{ | ||
messageId: 'suggestRemoveEmpty', | ||
data, | ||
fix(fixer) { return fixer.remove(node.parent); }, | ||
}], | ||
}); | ||
@@ -314,14 +306,12 @@ return; | ||
const suggest = [{ | ||
messageId: 'suggestRemoveInvalid', | ||
data, | ||
fix(fixer) { return fixer.removeRange(singlePart.range); }, | ||
}]; | ||
report(context, messages.neverValid, 'neverValid', { | ||
node, | ||
data, | ||
suggest: [ | ||
Object.assign( | ||
getMessageData('suggestRemoveInvalid', messages.suggestRemoveInvalid), | ||
{ | ||
data, | ||
fix(fixer) { return fixer.removeRange(singlePart.range); }, | ||
} | ||
), | ||
], | ||
suggest, | ||
}); | ||
@@ -335,14 +325,12 @@ } else if (!allowedTags.has(parentNodeName)) { | ||
const suggest = [{ | ||
messageId: 'suggestRemoveInvalid', | ||
data, | ||
fix(fixer) { return fixer.removeRange(singlePart.range); }, | ||
}]; | ||
report(context, messages.notValidFor, 'notValidFor', { | ||
node, | ||
data, | ||
suggest: [ | ||
Object.assign( | ||
getMessageData('suggestRemoveInvalid', messages.suggestRemoveInvalid), | ||
{ | ||
data, | ||
fix(fixer) { return fixer.removeRange(singlePart.range); }, | ||
} | ||
), | ||
], | ||
suggest, | ||
}); | ||
@@ -384,15 +372,13 @@ } | ||
for (const whitespacePart of whitespaceParts) { | ||
const data = { attributeName }; | ||
if (whitespacePart.range[0] === (node.range[0] + 1) || whitespacePart.range[1] === (node.range[1] - 1)) { | ||
report(context, messages.spaceDelimited, 'spaceDelimited', { | ||
node, | ||
data: { attributeName }, | ||
suggest: [ | ||
Object.assign( | ||
getMessageData('suggestRemoveWhitespaces', messages.suggestRemoveWhitespaces), | ||
{ | ||
data: { attributeName }, | ||
fix(fixer) { return fixer.removeRange(whitespacePart.range); }, | ||
} | ||
), | ||
], | ||
data, | ||
suggest: [{ | ||
messageId: 'suggestRemoveWhitespaces', | ||
data, | ||
fix(fixer) { return fixer.removeRange(whitespacePart.range); }, | ||
}], | ||
}); | ||
@@ -402,12 +388,8 @@ } else if (whitespacePart.value !== '\u0020') { | ||
node, | ||
data: { attributeName }, | ||
suggest: [ | ||
Object.assign( | ||
getMessageData('suggestRemoveWhitespaces', messages.suggestRemoveWhitespaces), | ||
{ | ||
data: { attributeName }, | ||
fix(fixer) { return fixer.replaceTextRange(whitespacePart.range, '\u0020'); }, | ||
} | ||
), | ||
], | ||
data, | ||
suggest: [{ | ||
messageId: 'suggestRemoveWhitespaces', | ||
data, | ||
fix(fixer) { return fixer.replaceTextRange(whitespacePart.range, '\u0020'); }, | ||
}], | ||
}); | ||
@@ -437,11 +419,7 @@ } | ||
data, | ||
suggest: [ | ||
Object.assign( | ||
getMessageData('suggestRemoveDefault', messages.suggestRemoveDefault), | ||
{ | ||
data, | ||
fix(fixer) { return fixer.remove(node); }, | ||
} | ||
), | ||
], | ||
suggest: [{ | ||
messageId: 'suggestRemoveDefault', | ||
data, | ||
fix(fixer) { return fixer.remove(node); }, | ||
}], | ||
}); | ||
@@ -459,8 +437,7 @@ return; | ||
data, | ||
suggest: [ | ||
Object.assign( | ||
getMessageData('suggestRemoveEmpty', messages.suggestRemoveEmpty), | ||
{ data, fix } | ||
), | ||
], | ||
suggest: [{ | ||
messageId: 'suggestRemoveEmpty', | ||
data, | ||
fix, | ||
}], | ||
}); | ||
@@ -488,8 +465,7 @@ return; | ||
data, | ||
suggest: [ | ||
Object.assign( | ||
getMessageData('suggestRemoveDefault', messages.suggestRemoveDefault), | ||
{ data, fix } | ||
), | ||
], | ||
suggest: [{ | ||
messageId: 'suggestRemoveDefault', | ||
data, | ||
fix, | ||
}], | ||
}); | ||
@@ -502,8 +478,7 @@ } else if (node.value.expression.type === 'Identifier' && node.value.expression.name === 'undefined') { | ||
data, | ||
suggest: [ | ||
Object.assign( | ||
getMessageData('suggestRemoveDefault', messages.suggestRemoveDefault), | ||
{ data, fix } | ||
), | ||
], | ||
suggest: [{ | ||
messageId: 'suggestRemoveDefault', | ||
data, | ||
fix, | ||
}], | ||
}); | ||
@@ -538,11 +513,7 @@ } | ||
data, | ||
suggest: [ | ||
Object.assign( | ||
getMessageData('suggestRemoveInvalid', messages.suggestRemoveInvalid), | ||
{ | ||
data, | ||
fix(fixer) { return fixer.replaceText(value, value.raw.replace(value.value, '')); }, | ||
} | ||
), | ||
], | ||
suggest: [{ | ||
messageId: 'suggestRemoveInvalid', | ||
data, | ||
fix(fixer) { return fixer.replaceText(value, value.raw.replace(value.value, '')); }, | ||
}], | ||
}); | ||
@@ -549,0 +520,0 @@ } else if (!validTagSet.has(node.arguments[0].value)) { |
@@ -40,4 +40,3 @@ /** | ||
function checkIfReactIsInScope(node) { | ||
const variables = variableUtil.variablesInScope(context, node); | ||
if (variableUtil.findVariable(variables, pragma)) { | ||
if (variableUtil.getVariableFromContext(context, node, pragma)) { | ||
return; | ||
@@ -44,0 +43,0 @@ } |
@@ -27,2 +27,9 @@ /** | ||
function isPropWithNoDefaulVal(prop) { | ||
if (prop.type === 'RestElement' || prop.type === 'ExperimentalRestProperty') { | ||
return false; | ||
} | ||
return prop.value.type !== 'AssignmentPattern'; | ||
} | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
@@ -138,12 +145,20 @@ module.exports = { | ||
} else if (props.type === 'ObjectPattern') { | ||
// Filter required props with default value and report error | ||
props.properties.filter((prop) => { | ||
if (prop.type === 'RestElement' || prop.type === 'ExperimentalRestProperty') { | ||
return false; | ||
} | ||
const propType = propTypes[prop.key.name]; | ||
if (!propType || propType.isRequired) { | ||
return false; | ||
} | ||
return prop.value.type !== 'AssignmentPattern'; | ||
const propName = prop && prop.key && prop.key.name; | ||
const isPropRequired = propTypes[propName] && propTypes[propName].isRequired; | ||
return propTypes[propName] && isPropRequired && !isPropWithNoDefaulVal(prop); | ||
}).forEach((prop) => { | ||
report(context, messages.noDefaultWithRequired, 'noDefaultWithRequired', { | ||
node: prop, | ||
data: { name: prop.key.name }, | ||
}); | ||
}); | ||
// Filter non required props with no default value and report error | ||
props.properties.filter((prop) => { | ||
const propName = prop && prop.key && prop.key.name; | ||
const isPropRequired = propTypes[propName] && propTypes[propName].isRequired; | ||
return propTypes[propName] && !isPropRequired && isPropWithNoDefaulVal(prop); | ||
}).forEach((prop) => { | ||
report(context, messages.shouldAssignObjectDefault, 'shouldAssignObjectDefault', { | ||
@@ -150,0 +165,0 @@ node: prop, |
@@ -94,4 +94,3 @@ /** | ||
function findVariableByName(node, name) { | ||
const variable = variableUtil.variablesInScope(context, node) | ||
.find((item) => item.name === name); | ||
const variable = variableUtil.getVariableFromContext(context, node, name); | ||
@@ -98,0 +97,0 @@ if (!variable || !variable.defs[0] || !variable.defs[0].node) { |
@@ -132,3 +132,4 @@ /** | ||
noSortAlphabetically, | ||
sortShapeProp | ||
sortShapeProp, | ||
checkTypes | ||
); | ||
@@ -135,0 +136,0 @@ } |
@@ -64,4 +64,3 @@ /** | ||
function checkIdentifiers(node) { | ||
const variable = variableUtil.variablesInScope(context, node) | ||
.find((item) => item.name === node.name); | ||
const variable = variableUtil.getVariableFromContext(context, node, node.name); | ||
@@ -68,0 +67,0 @@ if (!variable || !variable.defs[0] || !variable.defs[0].node.init) { |
@@ -14,5 +14,6 @@ import eslint from 'eslint'; | ||
type JSXFragment = ASTNode; | ||
type JSXOpeningElement = ASTNode; | ||
type JSXSpreadAttribute = ASTNode; | ||
type Context = eslint.Rule.RuleContext | ||
type Context = eslint.Rule.RuleContext; | ||
@@ -19,0 +20,0 @@ type TypeDeclarationBuilder = (annotation: ASTNode, parentName: string, seen: Set<typeof annotation>) => object; |
@@ -672,10 +672,3 @@ /** | ||
} | ||
let variableInScope; | ||
const variables = variableUtil.variablesInScope(context, node); | ||
for (i = 0, j = variables.length; i < j; i++) { | ||
if (variables[i].name === variableName) { | ||
variableInScope = variables[i]; | ||
break; | ||
} | ||
} | ||
const variableInScope = variableUtil.getVariableFromContext(context, node, variableName); | ||
if (!variableInScope) { | ||
@@ -682,0 +675,0 @@ return null; |
@@ -16,4 +16,3 @@ 'use strict'; | ||
const pragma = pragmaUtil.getFromContext(context); | ||
const variables = variableUtil.variablesInScope(context, node); | ||
const variableInScope = variableUtil.getVariable(variables, variable); | ||
const variableInScope = variableUtil.getVariableFromContext(context, node, variable); | ||
if (variableInScope) { | ||
@@ -20,0 +19,0 @@ const latestDef = variableUtil.getLatestVariableDefinition(variableInScope); |
@@ -968,4 +968,3 @@ /** | ||
case 'Identifier': { | ||
const firstMatchingVariable = variableUtil.variablesInScope(context, node) | ||
.find((variableInScope) => variableInScope.name === propTypes.name); | ||
const firstMatchingVariable = variableUtil.getVariableFromContext(context, node, propTypes.name); | ||
if (firstMatchingVariable) { | ||
@@ -972,0 +971,0 @@ const defInScope = firstMatchingVariable.defs[firstMatchingVariable.defs.length - 1]; |
@@ -129,2 +129,3 @@ /** | ||
* @param {Boolean=} sortShapeProp whether or not to sort propTypes defined in PropTypes.shape. | ||
* @param {Boolean=} checkTypes whether or not sorting of prop type definitions are checked. | ||
* @returns {Object|*|{range, text}} the sort order of the two elements. | ||
@@ -140,3 +141,4 @@ */ | ||
noSortAlphabetically, | ||
sortShapeProp | ||
sortShapeProp, | ||
checkTypes | ||
) { | ||
@@ -188,2 +190,3 @@ function sortInSource(allNodes, source) { | ||
const sourceCodeText = getText(context); | ||
let separator = ''; | ||
source = nodes.reduceRight((acc, attr, index) => { | ||
@@ -193,2 +196,6 @@ const sortedAttr = sortedAttributes[index]; | ||
let sortedAttrText = sourceCodeText.slice(commentNode.start, commentNode.end); | ||
const sortedAttrTextLastChar = sortedAttrText[sortedAttrText.length - 1]; | ||
if (!separator && [';', ','].some((allowedSep) => sortedAttrTextLastChar === allowedSep)) { | ||
separator = sortedAttrTextLastChar; | ||
} | ||
if (sortShapeProp && isShapeProp(sortedAttr.value)) { | ||
@@ -204,3 +211,4 @@ const shape = getShapeProperties(sortedAttr.value); | ||
} | ||
return `${acc.slice(0, commentnodeMap.get(attr).start)}${sortedAttrText}${acc.slice(commentnodeMap.get(attr).end)}`; | ||
const sortedAttrTextVal = checkTypes && !sortedAttrText.endsWith(separator) ? `${sortedAttrText}${separator}` : sortedAttrText; | ||
return `${acc.slice(0, commentnodeMap.get(attr).start)}${sortedAttrTextVal}${acc.slice(commentnodeMap.get(attr).end)}`; | ||
}, source); | ||
@@ -207,0 +215,0 @@ }); |
@@ -8,3 +8,2 @@ /** | ||
const toReversed = require('array.prototype.toreversed'); | ||
const getScope = require('./eslint').getScope; | ||
@@ -33,26 +32,29 @@ | ||
/** | ||
* List all variable in a given scope | ||
* Searches for a variable in the given scope. | ||
* | ||
* Contain a patch for babel-eslint to avoid https://github.com/babel/babel-eslint/issues/21 | ||
* | ||
* @param {Object} context The current rule context. | ||
* @param {ASTNode} node The node to start looking from. | ||
* @returns {Array} The variables list | ||
* @param {string} name The name of the variable to search. | ||
* @returns {Object | undefined} Variable if the variable was found, undefined if not. | ||
*/ | ||
function variablesInScope(context, node) { | ||
function getVariableFromContext(context, node, name) { | ||
let scope = getScope(context, node); | ||
let variables = scope.variables; | ||
while (scope.type !== 'global') { | ||
while (scope) { | ||
let variable = getVariable(scope.variables, name); | ||
if (!variable && scope.childScopes.length) { | ||
variable = getVariable(scope.childScopes[0].variables, name); | ||
if (!variable && scope.childScopes[0].childScopes.length) { | ||
variable = getVariable(scope.childScopes[0].childScopes[0].variables, name); | ||
} | ||
} | ||
if (variable) { | ||
return variable; | ||
} | ||
scope = scope.upper; | ||
variables = scope.variables.concat(variables); | ||
} | ||
if (scope.childScopes.length) { | ||
variables = scope.childScopes[0].variables.concat(variables); | ||
if (scope.childScopes[0].childScopes.length) { | ||
variables = scope.childScopes[0].childScopes[0].variables.concat(variables); | ||
} | ||
} | ||
return toReversed(variables); | ||
return undefined; | ||
} | ||
@@ -68,3 +70,3 @@ | ||
function findVariableByName(context, node, name) { | ||
const variable = getVariable(variablesInScope(context, node), name); | ||
const variable = getVariableFromContext(context, node, name); | ||
@@ -99,4 +101,4 @@ if (!variable || !variable.defs[0] || !variable.defs[0].node) { | ||
getVariable, | ||
variablesInScope, | ||
getVariableFromContext, | ||
getLatestVariableDefinition, | ||
}; |
@@ -15,2 +15,4 @@ /** | ||
const ULTIMATE_LATEST_SEMVER = '999.999.999'; | ||
let warnedForMissingVersion = false; | ||
@@ -48,2 +50,33 @@ | ||
function convertConfVerToSemver(confVer) { | ||
const fullSemverString = /^[0-9]+\.[0-9]+$/.test(confVer) ? `${confVer}.0` : confVer; | ||
return semver.coerce(fullSemverString.split('.').map((part) => Number(part)).join('.')); | ||
} | ||
let defaultVersion = ULTIMATE_LATEST_SEMVER; | ||
function resetDefaultVersion() { | ||
defaultVersion = ULTIMATE_LATEST_SEMVER; | ||
} | ||
function readDefaultReactVersionFromContext(context) { | ||
// .eslintrc shared settings (https://eslint.org/docs/user-guide/configuring#adding-shared-settings) | ||
if (context.settings && context.settings.react && context.settings.react.defaultVersion) { | ||
let settingsDefaultVersion = context.settings.react.defaultVersion; | ||
if (typeof settingsDefaultVersion !== 'string') { | ||
error('Warning: default React version specified in eslint-pluigin-react-settings must be a string; ' | ||
+ `got "${typeof settingsDefaultVersion}"`); | ||
} | ||
settingsDefaultVersion = String(settingsDefaultVersion); | ||
const result = convertConfVerToSemver(settingsDefaultVersion); | ||
if (result) { | ||
defaultVersion = result.version; | ||
} else { | ||
error(`Warning: React version specified in eslint-plugin-react-settings must be a valid semver version, or "detect"; got “${settingsDefaultVersion}”. Falling back to latest version as default.`); | ||
} | ||
} else { | ||
defaultVersion = ULTIMATE_LATEST_SEMVER; | ||
} | ||
} | ||
// TODO, semver-major: remove context fallback | ||
@@ -65,7 +98,10 @@ function detectReactVersion(context) { | ||
if (!warnedForMissingVersion) { | ||
error('Warning: React version was set to "detect" in eslint-plugin-react settings, ' | ||
+ 'but the "react" package is not installed. Assuming latest React version for linting.'); | ||
let sentence2 = 'Assuming latest React version for linting.'; | ||
if (defaultVersion !== ULTIMATE_LATEST_SEMVER) { | ||
sentence2 = `Assuming default React version for linting: "${defaultVersion}".`; | ||
} | ||
error(`Warning: React version was set to "detect" in eslint-plugin-react settings, but the "react" package is not installed. ${sentence2}`); | ||
warnedForMissingVersion = true; | ||
} | ||
cachedDetectedReactVersion = '999.999.999'; | ||
cachedDetectedReactVersion = defaultVersion; | ||
return cachedDetectedReactVersion; | ||
@@ -77,5 +113,4 @@ } | ||
const defaultVersion = '999.999.999'; | ||
function getReactVersionFromContext(context) { | ||
readDefaultReactVersionFromContext(context); | ||
let confVer = defaultVersion; | ||
@@ -98,4 +133,4 @@ // .eslintrc shared settings (https://eslint.org/docs/user-guide/configuring#adding-shared-settings) | ||
} | ||
confVer = /^[0-9]+\.[0-9]+$/.test(confVer) ? `${confVer}.0` : confVer; | ||
const result = semver.coerce(confVer.split('.').map((part) => Number(part)).join('.')); | ||
const result = convertConfVerToSemver(confVer); | ||
if (!result) { | ||
@@ -119,3 +154,3 @@ error(`Warning: React version specified in eslint-plugin-react-settings must be a valid semver version, or "detect"; got “${confVer}”`); | ||
+ 'but the "flow-bin" package is not installed. Assuming latest Flow version for linting.'); | ||
return '999.999.999'; | ||
return ULTIMATE_LATEST_SEMVER; | ||
} | ||
@@ -142,4 +177,4 @@ throw e; | ||
} | ||
confVer = /^[0-9]+\.[0-9]+$/.test(confVer) ? `${confVer}.0` : confVer; | ||
const result = semver.coerce(confVer.split('.').map((part) => Number(part)).join('.')); | ||
const result = convertConfVerToSemver(confVer); | ||
if (!result) { | ||
@@ -168,2 +203,3 @@ error(`Warning: Flow version specified in eslint-plugin-react-settings must be a valid semver version, or "detect"; got “${confVer}”`); | ||
resetDetectedVersion, | ||
resetDefaultVersion, | ||
}; |
{ | ||
"name": "eslint-plugin-react", | ||
"version": "7.34.4", | ||
"version": "7.35.0", | ||
"author": "Yannick Croissant <yannick.croissant+npm@gmail.com>", | ||
@@ -18,3 +18,3 @@ "description": "React specific linting rules for ESLint", | ||
"type-check": "tsc", | ||
"unit-test": "istanbul cover node_modules/mocha/bin/_mocha tests/lib/**/*.js tests/util/**/*.js tests/index.js", | ||
"unit-test": "istanbul cover node_modules/mocha/bin/_mocha tests/lib/**/*.js tests/util/**/*.js tests/index.js tests/flat-config.js", | ||
"update:eslint-docs": "eslint-doc-generator" | ||
@@ -32,3 +32,2 @@ }, | ||
"array.prototype.flatmap": "^1.3.2", | ||
"array.prototype.toreversed": "^1.1.2", | ||
"array.prototype.tosorted": "^1.1.4", | ||
@@ -51,4 +50,4 @@ "doctrine": "^2.1.0", | ||
"devDependencies": { | ||
"@babel/core": "^7.24.7", | ||
"@babel/eslint-parser": "^7.24.7", | ||
"@babel/core": "^7.24.9", | ||
"@babel/eslint-parser": "^7.24.8", | ||
"@babel/plugin-syntax-decorators": "^7.24.7", | ||
@@ -64,3 +63,3 @@ "@babel/plugin-syntax-do-expressions": "^7.24.7", | ||
"babel-eslint": "^8 || ^9 || ^10.1.0", | ||
"eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8", | ||
"eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7", | ||
"eslint-config-airbnb-base": "^15.0.0", | ||
@@ -86,3 +85,3 @@ "eslint-doc-generator": "^1.7.1", | ||
"peerDependencies": { | ||
"eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" | ||
"eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" | ||
}, | ||
@@ -89,0 +88,0 @@ "engines": { |
@@ -45,3 +45,5 @@ # `eslint-plugin-react` <sup>[![Version Badge][npm-version-svg]][package-url]</sup> | ||
// You can also use `16.0`, `16.3`, etc, if you want to override the detected value. | ||
// It will default to "latest" and warn if missing, and to "detect" in the future | ||
// Defaults to the "defaultVersion" setting and warns if missing, and to "detect" in the future | ||
"defaultVersion": "", // Default React version to use when the version you have installed cannot be detected. | ||
// If not provided, defaults to the latest React version. | ||
"flowVersion": "0.53" // Flow version | ||
@@ -207,23 +209,18 @@ }, | ||
<!-- markdownlint-disable-next-line no-duplicate-heading --> | ||
### Shareable configs | ||
### Flat Configs | ||
There're also 3 shareable configs. | ||
This plugin exports 3 flat configs: | ||
- `eslint-plugin-react/configs/all` | ||
- `eslint-plugin-react/configs/recommended` | ||
- `eslint-plugin-react/configs/jsx-runtime` | ||
- `flat.all` | ||
- `flat.recommended` | ||
- `flat['jsx-runtime']` | ||
If your eslint.config.js is ESM, include the `.js` extension (e.g. `eslint-plugin-react/recommended.js`). Note that the next semver-major will require omitting the extension for these imports. | ||
The flat configs are available via the root plugin import. They will configure the plugin under the `react/` namespace and enable JSX in [`languageOptions.parserOptions`](https://eslint.org/docs/latest/use/configure/language-options#specifying-parser-options). | ||
**Note**: These configurations will import `eslint-plugin-react` and enable JSX in [`languageOptions.parserOptions`](https://eslint.org/docs/latest/user-guide/configuring/configuration-files-new#configuration-objects). | ||
In the new config system, `plugin:` protocol(e.g. `plugin:react/recommended`) is no longer valid. | ||
As eslint does not automatically import the preset config (shareable config), you explicitly do it by yourself. | ||
```js | ||
const reactRecommended = require('eslint-plugin-react/configs/recommended'); | ||
const reactPlugin = require('eslint-plugin-react'); | ||
module.exports = [ | ||
… | ||
reactRecommended, // This is not a plugin object, but a shareable config object | ||
reactPlugin.configs.flat.recommended, // This is not a plugin object, but a shareable config object | ||
… | ||
@@ -239,3 +236,3 @@ ]; | ||
```js | ||
const reactRecommended = require('eslint-plugin-react/configs/recommended'); | ||
const reactPlugin = require('eslint-plugin-react'); | ||
const globals = require('globals'); | ||
@@ -247,5 +244,5 @@ | ||
files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'], | ||
...reactRecommended, | ||
...reactPlugin.configs.flat.recommended, | ||
languageOptions: { | ||
...reactRecommended.languageOptions, | ||
...reactPlugin.configs.flat.recommended.languageOptions, | ||
globals: { | ||
@@ -264,3 +261,3 @@ ...globals.serviceworker, | ||
```js | ||
const reactRecommended = require('eslint-plugin-react/configs/recommended'); | ||
const reactPlugin = require('eslint-plugin-react'); | ||
const globals = require('globals'); | ||
@@ -272,3 +269,3 @@ | ||
files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'], | ||
...reactRecommended, | ||
...reactPlugin.configs.flat.recommended, | ||
}, | ||
@@ -347,2 +344,3 @@ { | ||
| [jsx-props-no-multi-spaces](docs/rules/jsx-props-no-multi-spaces.md) | Disallow multiple spaces between inline JSX props | | | 🔧 | | | | ||
| [jsx-props-no-spread-multi](docs/rules/jsx-props-no-spread-multi.md) | Disallow JSX prop spreading the same identifier multiple times | | | | | | | ||
| [jsx-props-no-spreading](docs/rules/jsx-props-no-spreading.md) | Disallow JSX prop spreading | | | | | | | ||
@@ -349,0 +347,0 @@ | [jsx-sort-default-props](docs/rules/jsx-sort-default-props.md) | Enforce defaultProps declarations alphabetical sorting | | | | | ❌ | |
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
829902
19
139
22314
422
+ Added@eslint/config-array@0.18.0(transitive)
+ Added@eslint/eslintrc@3.1.0(transitive)
+ Added@eslint/js@9.9.1(transitive)
+ Added@eslint/object-schema@2.1.4(transitive)
+ Added@humanwhocodes/retry@0.3.0(transitive)
+ Addeddebug@4.3.6(transitive)
+ Addedeslint@9.9.1(transitive)
+ Addedeslint-scope@8.0.2(transitive)
+ Addedeslint-visitor-keys@4.0.0(transitive)
+ Addedespree@10.1.0(transitive)
+ Addedfile-entry-cache@8.0.0(transitive)
+ Addedflat-cache@4.0.1(transitive)
+ Addedglobals@14.0.0(transitive)
+ Addedms@2.1.2(transitive)
- Removedarray.prototype.toreversed@^1.1.2
- Removed@eslint/eslintrc@2.1.4(transitive)
- Removed@eslint/js@8.57.0(transitive)
- Removed@humanwhocodes/config-array@0.11.14(transitive)
- Removed@humanwhocodes/object-schema@2.0.3(transitive)
- Removed@ungap/structured-clone@1.2.0(transitive)
- Removedarray.prototype.toreversed@1.1.2(transitive)
- Removeddebug@4.3.7(transitive)
- Removeddoctrine@3.0.0(transitive)
- Removedeslint@8.57.0(transitive)
- Removedeslint-scope@7.2.2(transitive)
- Removedespree@9.6.1(transitive)
- Removedfile-entry-cache@6.0.1(transitive)
- Removedflat-cache@3.2.0(transitive)
- Removedfs.realpath@1.0.0(transitive)
- Removedglob@7.2.3(transitive)
- Removedglobals@13.24.0(transitive)
- Removedgraphemer@1.4.0(transitive)
- Removedinflight@1.0.6(transitive)
- Removedinherits@2.0.4(transitive)
- Removedms@2.1.3(transitive)
- Removedonce@1.4.0(transitive)
- Removedpath-is-absolute@1.0.1(transitive)
- Removedrimraf@3.0.2(transitive)
- Removedtype-fest@0.20.2(transitive)
- Removedwrappy@1.0.2(transitive)