eslint-plugin-unicorn
Advanced tools
Comparing version 48.0.1 to 49.0.0
{ | ||
"name": "eslint-plugin-unicorn", | ||
"version": "48.0.1", | ||
"version": "49.0.0", | ||
"description": "More than 100 powerful ESLint rules", | ||
@@ -29,2 +29,3 @@ "license": "MIT", | ||
"run-rules-on-codebase": "node ./test/run-rules-on-codebase/lint.mjs", | ||
"bundle-lodash": "echo \"export {defaultsDeep, camelCase, kebabCase, snakeCase, upperFirst, lowerFirst} from 'lodash-es';\" | npx esbuild --bundle --outfile=rules/utils/lodash.js --format=cjs", | ||
"smoke": "eslint-remote-tester --config ./test/smoke/eslint-remote-tester.config.js", | ||
@@ -50,3 +51,3 @@ "test": "npm-run-all --continue-on-error lint test:*", | ||
"dependencies": { | ||
"@babel/helper-validator-identifier": "^7.22.5", | ||
"@babel/helper-validator-identifier": "^7.22.20", | ||
"@eslint-community/eslint-utils": "^4.4.0", | ||
@@ -59,3 +60,2 @@ "ci-info": "^3.8.0", | ||
"jsesc": "^3.0.2", | ||
"lodash": "^4.17.21", | ||
"pluralize": "^8.0.0", | ||
@@ -69,33 +69,33 @@ "read-pkg-up": "^7.0.1", | ||
"devDependencies": { | ||
"@babel/code-frame": "^7.22.5", | ||
"@babel/core": "^7.22.8", | ||
"@babel/eslint-parser": "^7.22.7", | ||
"@babel/code-frame": "^7.22.13", | ||
"@babel/core": "^7.23.2", | ||
"@babel/eslint-parser": "^7.22.15", | ||
"@lubien/fixture-beta-package": "^1.0.0-beta.1", | ||
"@typescript-eslint/parser": "^6.2.0", | ||
"@typescript-eslint/parser": "^6.9.0", | ||
"ava": "^3.15.0", | ||
"c8": "^8.0.0", | ||
"c8": "^8.0.1", | ||
"chalk": "^5.3.0", | ||
"enquirer": "^2.3.6", | ||
"eslint": "^8.44.0", | ||
"enquirer": "^2.4.1", | ||
"eslint": "^8.52.0", | ||
"eslint-ava-rule-tester": "^4.0.0", | ||
"eslint-doc-generator": "^1.4.3", | ||
"eslint-plugin-eslint-plugin": "^5.1.0", | ||
"eslint-doc-generator": "^1.5.2", | ||
"eslint-plugin-eslint-plugin": "^5.1.1", | ||
"eslint-plugin-internal-rules": "file:./scripts/internal-rules/", | ||
"eslint-remote-tester": "^3.0.0", | ||
"eslint-remote-tester": "^3.0.1", | ||
"eslint-remote-tester-repositories": "^1.0.1", | ||
"execa": "^7.1.1", | ||
"execa": "^8.0.1", | ||
"listr": "^0.14.3", | ||
"lodash-es": "^4.17.21", | ||
"markdownlint-cli": "^0.35.0", | ||
"markdownlint-cli": "^0.37.0", | ||
"mem": "^9.0.2", | ||
"npm-package-json-lint": "^7.0.0", | ||
"npm-run-all": "^4.1.5", | ||
"npm-run-all2": "^6.1.1", | ||
"outdent": "^0.8.0", | ||
"typescript": "^5.1.6", | ||
"vue-eslint-parser": "^9.3.1", | ||
"xo": "^0.54.2", | ||
"yaml": "^2.3.1" | ||
"typescript": "^5.2.2", | ||
"vue-eslint-parser": "^9.3.2", | ||
"xo": "^0.56.0", | ||
"yaml": "^2.3.3" | ||
}, | ||
"peerDependencies": { | ||
"eslint": ">=8.44.0" | ||
"eslint": ">=8.52.0" | ||
}, | ||
@@ -121,2 +121,3 @@ "ava": { | ||
"eslint-remote-tester-results", | ||
"rules/utils/lodash.js", | ||
"test/integration/{fixtures,fixtures-local}/**" | ||
@@ -123,0 +124,0 @@ ], |
'use strict'; | ||
const {pick} = require('lodash'); | ||
const isMemberExpression = require('./is-member-expression.js'); | ||
@@ -49,7 +48,12 @@ const {isCallExpression} = require('./call-or-new-expression.js'); | ||
isCallExpression(node, { | ||
...pick(options, ['argumentsLength', 'minimumArguments', 'maximumArguments', 'allowSpreadElement']), | ||
argumentsLength: options.argumentsLength, | ||
minimumArguments: options.minimumArguments, | ||
maximumArguments: options.maximumArguments, | ||
allowSpreadElement: options.allowSpreadElement, | ||
optional: optionalCall, | ||
}) | ||
&& isMemberExpression(node.callee, { | ||
...pick(options, ['object', 'objects', 'computed']), | ||
object: options.object, | ||
objects: options.objects, | ||
computed: options.computed, | ||
property: method, | ||
@@ -56,0 +60,0 @@ properties: methods, |
@@ -118,2 +118,12 @@ 'use strict'; | ||
// `type Foo = { [Identifier: string]: string }` | ||
case 'TSIndexSignature': { | ||
return parent.parameters.includes(node); | ||
} | ||
// `type Identifier = Foo` | ||
case 'TSTypeAliasDeclaration': { | ||
return parent.id === node; | ||
} | ||
case 'TSPropertySignature': { | ||
@@ -120,0 +130,0 @@ return parent.key === node; |
@@ -31,6 +31,5 @@ 'use strict'; | ||
const {raw: original, regex} = node; | ||
// Regular Expressions with `u` flag are not well handled by `regexp-tree` | ||
// Regular Expressions with `u` and `v` flag are not well handled by `regexp-tree` | ||
// https://github.com/DmitrySoshnikov/regexp-tree/issues/162 | ||
if (regex.flags.includes('u')) { | ||
if (regex.flags.includes('u') || regex.flags.includes('v')) { | ||
return; | ||
@@ -37,0 +36,0 @@ } |
@@ -65,3 +65,3 @@ 'use strict'; | ||
isNameAllowed(originalName) | ||
|| isNameAllowed(originalName.replace(/_+$/g, '')) | ||
|| isNameAllowed(originalName.replaceAll(/_+$/g, '')) | ||
) { | ||
@@ -68,0 +68,0 @@ return; |
@@ -92,3 +92,3 @@ 'use strict'; | ||
); | ||
const lastProperty = objectPattern.properties[objectPattern.properties.length - 1]; | ||
const lastProperty = objectPattern.properties.at(-1); | ||
@@ -138,3 +138,3 @@ const hasRest = lastProperty && lastProperty.type === 'RestElement'; | ||
const {properties} = objectPattern; | ||
const lastProperty = properties[properties.length - 1]; | ||
const lastProperty = properties.at(-1); | ||
@@ -141,0 +141,0 @@ yield fixer.replaceText(node, newMember); |
'use strict'; | ||
const {upperFirst} = require('lodash'); | ||
const {upperFirst} = require('./utils/lodash.js'); | ||
@@ -4,0 +4,0 @@ const MESSAGE_ID_INVALID_EXPORT = 'invalidExport'; |
'use strict'; | ||
const path = require('node:path'); | ||
const readPkgUp = require('read-pkg-up'); | ||
@@ -50,90 +51,67 @@ const semver = require('semver'); | ||
// We don't need to normalize the package.json data, because we are only using 2 properties and those 2 properties | ||
// aren't validated by the normalization. But when this plugin is used in a monorepo, the name field in the | ||
// package.json is invalid and would make this plugin throw an error. See also #1871 | ||
const packageResult = readPkgUp.sync({normalize: false}); | ||
const hasPackage = Boolean(packageResult); | ||
const packageJson = hasPackage ? packageResult.packageJson : {}; | ||
const packageDependencies = { | ||
...packageJson.dependencies, | ||
...packageJson.devDependencies, | ||
}; | ||
const DEPENDENCY_INCLUSION_RE = /^[+-]\s*@?\S+\/?\S+/; | ||
const VERSION_COMPARISON_RE = /^(?<name>@?\S\/?\S+)@(?<condition>>|>=)(?<version>\d+(?:\.\d+){0,2}(?:-[\da-z-]+(?:\.[\da-z-]+)*)?(?:\+[\da-z-]+(?:\.[\da-z-]+)*)?)/i; | ||
const PKG_VERSION_RE = /^(?<condition>>|>=)(?<version>\d+(?:\.\d+){0,2}(?:-[\da-z-]+(?:\.[\da-z-]+)*)?(?:\+[\da-z-]+(?:\.[\da-z-]+)*)?)\s*$/; | ||
const ISO8601_DATE = /\d{4}-\d{2}-\d{2}/; | ||
function parseTodoWithArguments(string, {terms}) { | ||
const lowerCaseString = string.toLowerCase(); | ||
const lowerCaseTerms = terms.map(term => term.toLowerCase()); | ||
const hasTerm = lowerCaseTerms.some(term => lowerCaseString.includes(term)); | ||
if (!hasTerm) { | ||
return false; | ||
/** @param {string} dirname */ | ||
function getPackageHelpers(dirname) { | ||
// We don't need to normalize the package.json data, because we are only using 2 properties and those 2 properties | ||
// aren't validated by the normalization. But when this plugin is used in a monorepo, the name field in the | ||
// package.json can be invalid and would make this plugin throw an error. See also #1871 | ||
/** @type {readPkgUp.ReadResult | undefined} */ | ||
let packageResult; | ||
try { | ||
packageResult = readPkgUp.sync({normalize: false, cwd: dirname}); | ||
} catch { | ||
// This can happen if package.json files have comments in them etc. | ||
packageResult = undefined; | ||
} | ||
const TODO_ARGUMENT_RE = /\[(?<rawArguments>[^}]+)]/i; | ||
const result = TODO_ARGUMENT_RE.exec(string); | ||
const hasPackage = Boolean(packageResult); | ||
const packageJson = packageResult ? packageResult.packageJson : {}; | ||
if (!result) { | ||
return false; | ||
} | ||
const packageDependencies = { | ||
...packageJson.dependencies, | ||
...packageJson.devDependencies, | ||
}; | ||
const {rawArguments} = result.groups; | ||
function parseTodoWithArguments(string, {terms}) { | ||
const lowerCaseString = string.toLowerCase(); | ||
const lowerCaseTerms = terms.map(term => term.toLowerCase()); | ||
const hasTerm = lowerCaseTerms.some(term => lowerCaseString.includes(term)); | ||
const parsedArguments = rawArguments | ||
.split(',') | ||
.map(argument => parseArgument(argument.trim())); | ||
if (!hasTerm) { | ||
return false; | ||
} | ||
return createArgumentGroup(parsedArguments); | ||
} | ||
const TODO_ARGUMENT_RE = /\[(?<rawArguments>[^}]+)]/i; | ||
const result = TODO_ARGUMENT_RE.exec(string); | ||
function createArgumentGroup(arguments_) { | ||
const groups = {}; | ||
for (const {value, type} of arguments_) { | ||
groups[type] = groups[type] || []; | ||
groups[type].push(value); | ||
} | ||
if (!result) { | ||
return false; | ||
} | ||
return groups; | ||
} | ||
const {rawArguments} = result.groups; | ||
function parseArgument(argumentString) { | ||
if (ISO8601_DATE.test(argumentString)) { | ||
return { | ||
type: 'dates', | ||
value: argumentString, | ||
}; | ||
} | ||
const parsedArguments = rawArguments | ||
.split(',') | ||
.map(argument => parseArgument(argument.trim())); | ||
if (hasPackage && DEPENDENCY_INCLUSION_RE.test(argumentString)) { | ||
const condition = argumentString[0] === '+' ? 'in' : 'out'; | ||
const name = argumentString.slice(1).trim(); | ||
return { | ||
type: 'dependencies', | ||
value: { | ||
name, | ||
condition, | ||
}, | ||
}; | ||
return createArgumentGroup(parsedArguments); | ||
} | ||
if (hasPackage && VERSION_COMPARISON_RE.test(argumentString)) { | ||
const {groups} = VERSION_COMPARISON_RE.exec(argumentString); | ||
const name = groups.name.trim(); | ||
const condition = groups.condition.trim(); | ||
const version = groups.version.trim(); | ||
function parseArgument(argumentString, dirname) { | ||
const {hasPackage} = getPackageHelpers(dirname); | ||
if (ISO8601_DATE.test(argumentString)) { | ||
return { | ||
type: 'dates', | ||
value: argumentString, | ||
}; | ||
} | ||
const hasEngineKeyword = name.indexOf('engine:') === 0; | ||
const isNodeEngine = hasEngineKeyword && name === 'engine:node'; | ||
if (hasPackage && DEPENDENCY_INCLUSION_RE.test(argumentString)) { | ||
const condition = argumentString[0] === '+' ? 'in' : 'out'; | ||
const name = argumentString.slice(1).trim(); | ||
if (hasEngineKeyword && isNodeEngine) { | ||
return { | ||
type: 'engines', | ||
type: 'dependencies', | ||
value: { | ||
name, | ||
condition, | ||
version, | ||
}, | ||
@@ -143,52 +121,89 @@ }; | ||
if (!hasEngineKeyword) { | ||
if (hasPackage && VERSION_COMPARISON_RE.test(argumentString)) { | ||
const {groups} = VERSION_COMPARISON_RE.exec(argumentString); | ||
const name = groups.name.trim(); | ||
const condition = groups.condition.trim(); | ||
const version = groups.version.trim(); | ||
const hasEngineKeyword = name.indexOf('engine:') === 0; | ||
const isNodeEngine = hasEngineKeyword && name === 'engine:node'; | ||
if (hasEngineKeyword && isNodeEngine) { | ||
return { | ||
type: 'engines', | ||
value: { | ||
condition, | ||
version, | ||
}, | ||
}; | ||
} | ||
if (!hasEngineKeyword) { | ||
return { | ||
type: 'dependencies', | ||
value: { | ||
name, | ||
condition, | ||
version, | ||
}, | ||
}; | ||
} | ||
} | ||
if (hasPackage && PKG_VERSION_RE.test(argumentString)) { | ||
const result = PKG_VERSION_RE.exec(argumentString); | ||
const {condition, version} = result.groups; | ||
return { | ||
type: 'dependencies', | ||
type: 'packageVersions', | ||
value: { | ||
name, | ||
condition, | ||
version, | ||
condition: condition.trim(), | ||
version: version.trim(), | ||
}, | ||
}; | ||
} | ||
} | ||
if (hasPackage && PKG_VERSION_RE.test(argumentString)) { | ||
const result = PKG_VERSION_RE.exec(argumentString); | ||
const {condition, version} = result.groups; | ||
// Currently being ignored as integration tests pointed | ||
// some TODO comments have `[random data like this]` | ||
return { | ||
type: 'packageVersions', | ||
value: { | ||
condition: condition.trim(), | ||
version: version.trim(), | ||
}, | ||
type: 'unknowns', | ||
value: argumentString, | ||
}; | ||
} | ||
// Currently being ignored as integration tests pointed | ||
// some TODO comments have `[random data like this]` | ||
return { | ||
type: 'unknowns', | ||
value: argumentString, | ||
}; | ||
} | ||
function parseTodoMessage(todoString) { | ||
// @example "TODO [...]: message here" | ||
// @example "TODO [...] message here" | ||
const argumentsEnd = todoString.indexOf(']'); | ||
function parseTodoMessage(todoString) { | ||
// @example "TODO [...]: message here" | ||
// @example "TODO [...] message here" | ||
const argumentsEnd = todoString.indexOf(']'); | ||
const afterArguments = todoString.slice(argumentsEnd + 1).trim(); | ||
const afterArguments = todoString.slice(argumentsEnd + 1).trim(); | ||
// Check if have to skip colon | ||
// @example "TODO [...]: message here" | ||
const dropColon = afterArguments[0] === ':'; | ||
if (dropColon) { | ||
return afterArguments.slice(1).trim(); | ||
} | ||
// Check if have to skip colon | ||
// @example "TODO [...]: message here" | ||
const dropColon = afterArguments[0] === ':'; | ||
if (dropColon) { | ||
return afterArguments.slice(1).trim(); | ||
return afterArguments; | ||
} | ||
return afterArguments; | ||
return {packageResult, hasPackage, packageJson, packageDependencies, parseArgument, parseTodoMessage, parseTodoWithArguments}; | ||
} | ||
const DEPENDENCY_INCLUSION_RE = /^[+-]\s*@?\S+\/?\S+/; | ||
const VERSION_COMPARISON_RE = /^(?<name>@?\S\/?\S+)@(?<condition>>|>=)(?<version>\d+(?:\.\d+){0,2}(?:-[\da-z-]+(?:\.[\da-z-]+)*)?(?:\+[\da-z-]+(?:\.[\da-z-]+)*)?)/i; | ||
const PKG_VERSION_RE = /^(?<condition>>|>=)(?<version>\d+(?:\.\d+){0,2}(?:-[\da-z-]+(?:\.[\da-z-]+)*)?(?:\+[\da-z-]+(?:\.[\da-z-]+)*)?)\s*$/; | ||
const ISO8601_DATE = /\d{4}-\d{2}-\d{2}/; | ||
function createArgumentGroup(arguments_) { | ||
const groups = {}; | ||
for (const {value, type} of arguments_) { | ||
groups[type] = groups[type] || []; | ||
groups[type].push(value); | ||
} | ||
return groups; | ||
} | ||
function reachedDate(past, now) { | ||
@@ -268,2 +283,5 @@ return Date.parse(past) < Date.parse(now); | ||
const dirname = path.dirname(context.filename); | ||
const {packageJson, packageDependencies, parseArgument, parseTodoMessage, parseTodoWithArguments} = getPackageHelpers(dirname); | ||
const {sourceCode} = context; | ||
@@ -290,9 +308,14 @@ const comments = sourceCode.getAllComments(); | ||
// Since we have priority, we leave only the comments that we didn't use. | ||
const fakeContext = { | ||
...context, | ||
sourceCode: { | ||
...sourceCode, | ||
getAllComments: () => options.allowWarningComments ? [] : unusedComments, | ||
const fakeContext = new Proxy(context, { | ||
get(target, property, receiver) { | ||
if (property === 'sourceCode') { | ||
return { | ||
...sourceCode, | ||
getAllComments: () => options.allowWarningComments ? [] : unusedComments, | ||
}; | ||
} | ||
return Reflect.get(target, property, receiver); | ||
}, | ||
}; | ||
}); | ||
const rules = baseRule.create(fakeContext); | ||
@@ -492,3 +515,3 @@ | ||
const withoutWhitespace = unknown.replace(/ /g, ''); | ||
const withoutWhitespace = unknown.replaceAll(' ', ''); | ||
@@ -495,0 +518,0 @@ if (parseArgument(withoutWhitespace).type !== 'unknowns') { |
'use strict'; | ||
const path = require('node:path'); | ||
const {camelCase, kebabCase, snakeCase, upperFirst} = require('lodash'); | ||
const {camelCase, kebabCase, snakeCase, upperFirst} = require('./utils/lodash.js'); | ||
const cartesianProductSamples = require('./utils/cartesian-product-samples.js'); | ||
@@ -5,0 +5,0 @@ |
@@ -10,3 +10,3 @@ 'use strict'; | ||
const firstToken = parentheses[0] || node; | ||
const lastToken = parentheses[parentheses.length - 1] || node; | ||
const lastToken = parentheses.at(-1) || node; | ||
@@ -13,0 +13,0 @@ let [start] = firstToken.range; |
'use strict'; | ||
const {defaultsDeep} = require('lodash'); | ||
const {defaultsDeep} = require('./utils/lodash.js'); | ||
const {getStringIfConstant} = require('@eslint-community/eslint-utils'); | ||
@@ -4,0 +4,0 @@ const {isCallExpression} = require('./ast/index.js'); |
@@ -403,3 +403,3 @@ 'use strict'; | ||
context.on('ReturnStatement', node => { | ||
const currentFunction = functionStack[functionStack.length - 1]; | ||
const currentFunction = functionStack.at(-1); | ||
if (!currentFunction) { | ||
@@ -406,0 +406,0 @@ return; |
@@ -86,3 +86,3 @@ 'use strict'; | ||
const callbackLastToken = isParenthesized | ||
? callbackParentheses[callbackParentheses.length - 1] | ||
? callbackParentheses.at(-1) | ||
: callback; | ||
@@ -89,0 +89,0 @@ if ( |
@@ -14,3 +14,3 @@ 'use strict'; | ||
// Find exactly one trailing space, allow exactly one space | ||
const hasTrailingSpace = value => value.length > 1 && value.charAt(value.length - 1) === ' ' && value.charAt(value.length - 2) !== ' '; | ||
const hasTrailingSpace = value => value.length > 1 && value.at(-1) === ' ' && value.at(-2) !== ' '; | ||
@@ -17,0 +17,0 @@ /** @param {import('eslint').Rule.RuleContext} context */ |
@@ -11,3 +11,3 @@ 'use strict'; | ||
function checkEscape(context, node, value) { | ||
const fixedValue = value.replace(/(?<=(?:^|[^\\])(?:\\\\)*\\)x/g, 'u00'); | ||
const fixedValue = value.replaceAll(/(?<=(?:^|[^\\])(?:\\\\)*\\)x/g, 'u00'); | ||
@@ -14,0 +14,0 @@ if (value !== fixedValue) { |
@@ -33,3 +33,3 @@ 'use strict'; | ||
case 'SequenceExpression': { | ||
return notPromise(node.expressions[node.expressions.length - 1]); | ||
return notPromise(node.expressions.at(-1)); | ||
} | ||
@@ -36,0 +36,0 @@ |
@@ -28,3 +28,3 @@ 'use strict'; | ||
// We only check cases where the last case is the `default` case | ||
if (defaultCase !== cases[cases.length - 1]) { | ||
if (defaultCase !== cases.at(-1)) { | ||
return; | ||
@@ -31,0 +31,0 @@ } |
@@ -239,3 +239,3 @@ 'use strict'; | ||
const firstUndefined = undefinedArguments[0]; | ||
const lastUndefined = undefinedArguments[undefinedArguments.length - 1]; | ||
const lastUndefined = undefinedArguments.at(-1); | ||
@@ -242,0 +242,0 @@ return { |
@@ -31,3 +31,3 @@ 'use strict'; | ||
const {before, dotAndFractions, after} = match.groups; | ||
const fixedDotAndFractions = dotAndFractions.replace(/[.0_]+$/g, ''); | ||
const fixedDotAndFractions = dotAndFractions.replaceAll(/[.0_]+$/g, ''); | ||
const formatted = ((before + fixedDotAndFractions) || '0') + after; | ||
@@ -34,0 +34,0 @@ |
@@ -114,3 +114,3 @@ 'use strict'; | ||
const strippedNumber = number.replace(/_/g, ''); | ||
const strippedNumber = number.replaceAll('_', ''); | ||
const {prefix, data} = numeric.getPrefix(strippedNumber); | ||
@@ -117,0 +117,0 @@ |
@@ -75,3 +75,3 @@ 'use strict'; | ||
const isLastParameter = (parameters, parameter) => { | ||
const lastParameter = parameters[parameters.length - 1]; | ||
const lastParameter = parameters.at(-1); | ||
@@ -126,3 +126,3 @@ // See 'default-param-last' rule | ||
const checkExpression = (node, left, right, assignment) => { | ||
const currentFunction = functionStack[functionStack.length - 1]; | ||
const currentFunction = functionStack.at(-1); | ||
@@ -129,0 +129,0 @@ if (!currentFunction || !isDefaultExpression(left, right)) { |
@@ -15,3 +15,3 @@ 'use strict'; | ||
const dashToCamelCase = string => string.replace(/-[a-z]/g, s => s[1].toUpperCase()); | ||
const dashToCamelCase = string => string.replaceAll(/-[a-z]/g, s => s[1].toUpperCase()); | ||
@@ -18,0 +18,0 @@ function getFix(callExpression, context) { |
'use strict'; | ||
const {findVariable} = require('@eslint-community/eslint-utils'); | ||
const {getAncestor} = require('./utils/index.js'); | ||
const {isStaticRequire, isStringLiteral, isMemberExpression} = require('./ast/index.js'); | ||
@@ -8,4 +11,71 @@ const MESSAGE_ID = 'prefer-event-target'; | ||
const packagesShouldBeIgnored = new Set([ | ||
'@angular/core', | ||
'eventemitter3', | ||
]); | ||
const isConstVariableDeclarationId = node => | ||
node.parent.type === 'VariableDeclarator' | ||
&& node.parent.id === node | ||
&& node.parent.parent.type === 'VariableDeclaration' | ||
&& node.parent.parent.kind === 'const' | ||
&& node.parent.parent.declarations.includes(node.parent); | ||
function isAwaitImportOrRequireFromIgnoredPackages(node) { | ||
if (!node) { | ||
return false; | ||
} | ||
let source; | ||
if (isStaticRequire(node)) { | ||
[source] = node.arguments; | ||
} else if (node.type === 'AwaitExpression' && node.argument.type === 'ImportExpression') { | ||
({source} = node.argument); | ||
} | ||
if (isStringLiteral(source) && packagesShouldBeIgnored.has(source.value)) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
function isFromIgnoredPackage(node) { | ||
if (!node) { | ||
return false; | ||
} | ||
const importDeclaration = getAncestor(node, 'ImportDeclaration'); | ||
if (packagesShouldBeIgnored.has(importDeclaration?.source.value)) { | ||
return true; | ||
} | ||
// `const {EventEmitter} = ...` | ||
if ( | ||
node.parent.type === 'Property' | ||
&& node.parent.value === node | ||
&& node.parent.key.type === 'Identifier' | ||
&& node.parent.key.name === 'EventEmitter' | ||
&& node.parent.parent.type === 'ObjectPattern' | ||
&& node.parent.parent.properties.includes(node.parent) | ||
&& isConstVariableDeclarationId(node.parent.parent) | ||
&& isAwaitImportOrRequireFromIgnoredPackages(node.parent.parent.parent.init) | ||
) { | ||
return true; | ||
} | ||
// `const EventEmitter = (...).EventEmitter` | ||
if ( | ||
isConstVariableDeclarationId(node) | ||
&& isMemberExpression(node.parent.init, {property: 'EventEmitter', optional: false, computed: false}) | ||
&& isAwaitImportOrRequireFromIgnoredPackages(node.parent.init.object) | ||
) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
/** @param {import('eslint').Rule.RuleContext} context */ | ||
const create = () => ({ | ||
const create = context => ({ | ||
Identifier(node) { | ||
@@ -25,2 +95,8 @@ if (!( | ||
const scope = context.sourceCode.getScope(node); | ||
const variableNode = findVariable(scope, node)?.defs[0]?.name; | ||
if (isFromIgnoredPackage(variableNode)) { | ||
return; | ||
} | ||
return { | ||
@@ -27,0 +103,0 @@ node, |
@@ -151,3 +151,3 @@ 'use strict'; | ||
if (exportDeclaration) { | ||
const lastSpecifier = exportDeclaration.specifiers[exportDeclaration.specifiers.length - 1]; | ||
const lastSpecifier = exportDeclaration.specifiers.at(-1); | ||
@@ -154,0 +154,0 @@ // `export {} from 'foo';` |
@@ -102,3 +102,3 @@ 'use strict'; | ||
const firstToken = parentheses[0] || initObject; | ||
const lastToken = parentheses[parentheses.length - 1] || initObject; | ||
const lastToken = parentheses.at(-1) || initObject; | ||
const startToken = sourceCode.getTokenBefore(firstToken); | ||
@@ -105,0 +105,0 @@ const [start] = startToken.range; |
@@ -21,3 +21,3 @@ 'use strict'; | ||
const leftQuote = node.raw.charAt(0); | ||
const rightQuote = node.raw.charAt(node.raw.length - 1); | ||
const rightQuote = node.raw.at(-1); | ||
return `${leftQuote}${value}${rightQuote}`; | ||
@@ -24,0 +24,0 @@ }; |
@@ -80,2 +80,6 @@ 'use strict'; | ||
const isRegExpWithoutGlobalFlag = (node, scope) => { | ||
if (isRegexLiteral(node)) { | ||
return !node.regex.flags.includes('g'); | ||
} | ||
const staticResult = getStaticValue(node, scope); | ||
@@ -82,0 +86,0 @@ |
@@ -77,3 +77,3 @@ 'use strict'; | ||
.filter(({node, isArrayLiteral}) => (!isArrayLiteral || node.elements.length > 0)); | ||
const lastArgument = nonEmptyArguments[nonEmptyArguments.length - 1]; | ||
const lastArgument = nonEmptyArguments.at(-1); | ||
@@ -80,0 +80,0 @@ let text = nonEmptyArguments |
@@ -20,3 +20,3 @@ 'use strict'; | ||
const {pattern, flags} = node.regex; | ||
if (flags.replace('u', '') !== 'g') { | ||
if (flags.replace('u', '').replace('v', '') !== 'g') { | ||
return; | ||
@@ -29,3 +29,4 @@ } | ||
tree = parseRegExp(pattern, flags, { | ||
unicodePropertyEscape: true, | ||
unicodePropertyEscape: flags.includes('u'), | ||
unicodeSet: flags.includes('v'), | ||
namedGroups: true, | ||
@@ -32,0 +33,0 @@ lookbehind: true, |
@@ -194,3 +194,3 @@ 'use strict'; | ||
const lastStatement = ifStatements[ifStatements.length - 1].statement; | ||
const lastStatement = ifStatements.at(-1).statement; | ||
if (lastStatement.alternate) { | ||
@@ -197,0 +197,0 @@ const {alternate} = lastStatement; |
'use strict'; | ||
const path = require('node:path'); | ||
const {defaultsDeep, upperFirst, lowerFirst} = require('lodash'); | ||
const {defaultsDeep, upperFirst, lowerFirst} = require('./utils/lodash.js'); | ||
const avoidCapture = require('./utils/avoid-capture.js'); | ||
@@ -5,0 +5,0 @@ const cartesianProductSamples = require('./utils/cartesian-product-samples.js'); |
@@ -82,3 +82,3 @@ 'use strict'; | ||
const dedented = stripIndent(joined); | ||
const trimmed = dedented.replace(new RegExp(`^${eol}|${eol}[ \t]*$`, 'g'), ''); | ||
const trimmed = dedented.replaceAll(new RegExp(`^${eol}|${eol}[ \t]*$`, 'g'), ''); | ||
@@ -85,0 +85,0 @@ const fixed |
'use strict'; | ||
module.exports = string => string.replace( | ||
module.exports = string => string.replaceAll( | ||
/(?<=(?:^|[^\\])(?:\\\\)*)(?<symbol>(?:`|\$(?={)))/g, | ||
'\\$<symbol>', | ||
); |
@@ -51,3 +51,4 @@ 'use strict'; | ||
toLocation: require('./to-location.js'), | ||
getAncestor: require('./get-ancestor.js'), | ||
}; | ||
@@ -212,3 +212,3 @@ 'use strict'; | ||
case 'SequenceExpression': { | ||
if (isNumber(node.expressions[node.expressions.length - 1], scope)) { | ||
if (isNumber(node.expressions.at(-1), scope)) { | ||
return true; | ||
@@ -215,0 +215,0 @@ } |
@@ -28,3 +28,3 @@ 'use strict'; | ||
return ( | ||
reference?.resolved | ||
Boolean(reference?.resolved) | ||
&& reference.resolved.defs.length > 0 | ||
@@ -31,0 +31,0 @@ ); |
@@ -51,3 +51,3 @@ 'use strict'; | ||
const [start] = (parentheses[0] || node).range; | ||
const [, end] = (parentheses[parentheses.length - 1] || node).range; | ||
const [, end] = (parentheses.at(-1) || node).range; | ||
return [start, end]; | ||
@@ -54,0 +54,0 @@ } |
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
647989
15
210
20575
- Removedlodash@^4.17.21
- Removedlodash@4.17.21(transitive)