eslint-plugin-regexp
Advanced tools
Comparing version
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const utils_1 = require("../utils"); | ||
const ast_utils_1 = require("../utils/ast-utils"); | ||
const type_tracker_1 = require("../utils/type-tracker"); | ||
const replacements_utils_1 = require("../utils/replacements-utils"); | ||
const regexp_ast_analysis_1 = require("regexp-ast-analysis"); | ||
const regexpp_1 = require("regexpp"); | ||
class CapturingData { | ||
constructor(regexpContext) { | ||
this.unused = new Set(); | ||
this.unusedNames = new Map(); | ||
this.indexToCapturingGroup = new Map(); | ||
this.countOfCapturingGroup = 0; | ||
this.state = { | ||
used: false, | ||
track: true, | ||
}; | ||
this.regexpContext = regexpContext; | ||
} | ||
getUnused() { | ||
const unusedCapturingGroups = new Set(this.unused); | ||
const unusedNames = new Set(); | ||
for (const cgNodes of this.unusedNames.values()) { | ||
for (const cgNode of cgNodes) { | ||
if (!unusedCapturingGroups.has(cgNode)) { | ||
unusedNames.add(cgNode); | ||
} | ||
} | ||
} | ||
return { | ||
unusedCapturingGroups, | ||
unusedNames, | ||
}; | ||
} | ||
usedIndex(ref) { | ||
const cgNode = this.indexToCapturingGroup.get(ref); | ||
if (cgNode) { | ||
this.unused.delete(cgNode); | ||
} | ||
} | ||
usedName(ref) { | ||
const cgNodes = this.unusedNames.get(ref); | ||
if (cgNodes) { | ||
this.unusedNames.delete(ref); | ||
for (const cgNode of cgNodes) { | ||
this.unused.delete(cgNode); | ||
} | ||
} | ||
} | ||
usedAllNames() { | ||
for (const cgNodes of this.unusedNames.values()) { | ||
for (const cgNode of cgNodes) { | ||
this.unused.delete(cgNode); | ||
} | ||
} | ||
this.unusedNames.clear(); | ||
} | ||
usedAllUnnamed() { | ||
this.unused.clear(); | ||
} | ||
isAllUsed() { | ||
return !this.unused.size && !this.unusedNames.size; | ||
} | ||
markAsUsed() { | ||
this.state.used = true; | ||
} | ||
markAsCannotTrack() { | ||
this.state.track = false; | ||
} | ||
isNeedReport() { | ||
return this.state.used && this.state.track && !this.isAllUsed(); | ||
} | ||
visitor() { | ||
return { | ||
onCapturingGroupEnter: (cgNode) => { | ||
this.countOfCapturingGroup++; | ||
if (!cgNode.references.length) { | ||
this.unused.add(cgNode); | ||
this.indexToCapturingGroup.set(this.countOfCapturingGroup, cgNode); | ||
} | ||
else if (cgNode.references.some((ref) => typeof ref.ref === "string")) { | ||
return; | ||
} | ||
if (cgNode.name) { | ||
const array = this.unusedNames.get(cgNode.name); | ||
if (array) { | ||
array.push(cgNode); | ||
} | ||
else { | ||
this.unusedNames.set(cgNode.name, [cgNode]); | ||
} | ||
} | ||
}, | ||
}; | ||
} | ||
} | ||
function getCapturingGroupIdentifier(group) { | ||
@@ -104,12 +11,2 @@ if (group.name) { | ||
} | ||
function getAllCapturingGroups(pattern) { | ||
const groups = []; | ||
regexpp_1.visitRegExpAST(pattern, { | ||
onCapturingGroupEnter(group) { | ||
groups.push(group); | ||
}, | ||
}); | ||
groups.sort((a, b) => a.start - b.start); | ||
return groups; | ||
} | ||
exports.default = utils_1.createRule("no-unused-capturing-group", { | ||
@@ -134,5 +31,3 @@ meta: { | ||
unusedCapturingGroup: "Capturing group {{identifier}} is defined but never used.", | ||
unusedName: "Capturing group {{identifier}} has a name, but its name is never used.", | ||
makeNonCapturing: "Making this a non-capturing group.", | ||
removeName: "Remove the unused name.", | ||
}, | ||
@@ -145,10 +40,7 @@ type: "suggestion", | ||
const fixable = (_b = (_a = context.options[0]) === null || _a === void 0 ? void 0 : _a.fixable) !== null && _b !== void 0 ? _b : false; | ||
const typeTracer = type_tracker_1.createTypeTracker(context); | ||
const capturingDataMap = new Map(); | ||
function reportUnused(capturingData) { | ||
const { node, getRegexpLocation, fixReplaceNode, patternAst, } = capturingData.regexpContext; | ||
const { unusedCapturingGroups, unusedNames, } = capturingData.getUnused(); | ||
function reportUnused(unused, regexpContext) { | ||
const { node, getRegexpLocation, fixReplaceNode, getAllCapturingGroups, } = regexpContext; | ||
const fixableGroups = new Set(); | ||
for (const group of getAllCapturingGroups(patternAst).reverse()) { | ||
if (unusedCapturingGroups.has(group)) { | ||
for (const group of [...getAllCapturingGroups()].reverse()) { | ||
if (unused.has(group)) { | ||
fixableGroups.add(group); | ||
@@ -160,3 +52,3 @@ } | ||
} | ||
for (const cgNode of unusedCapturingGroups) { | ||
for (const cgNode of unused) { | ||
const fix = fixableGroups.has(cgNode) | ||
@@ -176,255 +68,80 @@ ? fixReplaceNode(cgNode, cgNode.raw.replace(/^\((?:\?<[^<>]+>)?/, "(?:")) | ||
} | ||
for (const cgNode of unusedNames) { | ||
const fix = fixReplaceNode(cgNode, cgNode.raw.replace(/^\(\?<[^<>]+>/, "(")); | ||
context.report({ | ||
node, | ||
loc: getRegexpLocation(cgNode), | ||
messageId: "unusedName", | ||
data: { identifier: getCapturingGroupIdentifier(cgNode) }, | ||
fix: fixable ? fix : null, | ||
suggest: [{ messageId: "removeName", fix }], | ||
}); | ||
} | ||
} | ||
function verifyForMatch(node) { | ||
const capturingData = capturingDataMap.get(node.arguments[0]); | ||
if (capturingData == null || capturingData.isAllUsed()) { | ||
return; | ||
function getCapturingGroupReferences(regexpContext) { | ||
const capturingGroupReferences = regexpContext.getCapturingGroupReferences(); | ||
if (!capturingGroupReferences.length) { | ||
return null; | ||
} | ||
if (!typeTracer.isString(node.callee.object)) { | ||
capturingData.markAsCannotTrack(); | ||
return; | ||
} | ||
if (capturingData.regexpContext.flags.global) { | ||
capturingData.markAsUsed(); | ||
} | ||
else { | ||
capturingData.markAsUsed(); | ||
verifyForExecResult(node, capturingData); | ||
} | ||
} | ||
function verifyForSearch(node) { | ||
const capturingData = capturingDataMap.get(node.arguments[0]); | ||
if (capturingData == null || capturingData.isAllUsed()) { | ||
return; | ||
} | ||
if (!typeTracer.isString(node.callee.object)) { | ||
capturingData.markAsCannotTrack(); | ||
return; | ||
} | ||
capturingData.markAsUsed(); | ||
} | ||
function verifyForTest(node) { | ||
const capturingData = capturingDataMap.get(node.callee.object); | ||
if (capturingData == null || capturingData.isAllUsed()) { | ||
return; | ||
} | ||
capturingData.markAsUsed(); | ||
} | ||
function verifyForReplace(node) { | ||
const capturingData = capturingDataMap.get(node.arguments[0]); | ||
if (capturingData == null || capturingData.isAllUsed()) { | ||
return; | ||
} | ||
if (!typeTracer.isString(node.callee.object)) { | ||
capturingData.markAsCannotTrack(); | ||
return; | ||
} | ||
const replacementNode = node.arguments[1]; | ||
if (replacementNode.type === "FunctionExpression" || | ||
replacementNode.type === "ArrowFunctionExpression") { | ||
capturingData.markAsUsed(); | ||
verifyForReplaceFunction(replacementNode, capturingData); | ||
} | ||
else { | ||
const evaluated = ast_utils_1.getStaticValue(context, node.arguments[1]); | ||
if (!evaluated || typeof evaluated.value !== "string") { | ||
capturingData.markAsCannotTrack(); | ||
return; | ||
const indexRefs = []; | ||
const namedRefs = []; | ||
let hasUnknownName = false; | ||
let hasSplit = false; | ||
for (const ref of capturingGroupReferences) { | ||
if (ref.type === "UnknownUsage" || ref.type === "UnknownRef") { | ||
return null; | ||
} | ||
capturingData.markAsUsed(); | ||
verifyForReplacePattern(evaluated.value, capturingData); | ||
} | ||
} | ||
function verifyForReplacePattern(replacementPattern, capturingData) { | ||
for (const replacement of replacements_utils_1.parseReplacementsForString(replacementPattern)) { | ||
if (replacement.type === "ReferenceElement") { | ||
if (typeof replacement.ref === "number") { | ||
capturingData.usedIndex(replacement.ref); | ||
if (ref.type === "ArrayRef" || | ||
ref.type === "ReplacementRef" || | ||
ref.type === "ReplacerFunctionRef") { | ||
if (ref.kind === "index") { | ||
if (ref.ref) { | ||
indexRefs.push(ref.ref); | ||
} | ||
else { | ||
return null; | ||
} | ||
} | ||
else { | ||
capturingData.usedName(replacement.ref); | ||
} | ||
} | ||
} | ||
} | ||
function verifyForReplaceFunction(replacementNode, capturingData) { | ||
for (let index = 0; index < replacementNode.params.length; index++) { | ||
const arg = replacementNode.params[index]; | ||
if (arg.type === "RestElement") { | ||
capturingData.markAsCannotTrack(); | ||
return; | ||
} | ||
if (index === 0) { | ||
continue; | ||
} | ||
else if (index <= capturingData.countOfCapturingGroup) { | ||
capturingData.usedIndex(index); | ||
} | ||
else if (capturingData.countOfCapturingGroup + 3 <= index) { | ||
capturingData.usedAllNames(); | ||
} | ||
} | ||
} | ||
function verifyForExec(node) { | ||
const capturingData = capturingDataMap.get(node.callee.object); | ||
if (capturingData == null || capturingData.isAllUsed()) { | ||
return; | ||
} | ||
capturingData.markAsUsed(); | ||
verifyForExecResult(node, capturingData); | ||
} | ||
function verifyForExecResult(node, capturingData) { | ||
for (const ref of ast_utils_1.extractPropertyReferences(node, context)) { | ||
if (hasNameRef(ref)) { | ||
if (ref.name === "groups") { | ||
for (const namedRef of ref.extractPropertyReferences()) { | ||
if (hasNameRef(namedRef)) { | ||
capturingData.usedName(namedRef.name); | ||
} | ||
else { | ||
capturingData.usedAllNames(); | ||
} | ||
if (ref.ref) { | ||
namedRefs.push(ref.ref); | ||
} | ||
else { | ||
hasUnknownName = true; | ||
} | ||
} | ||
else { | ||
capturingData.usedIndex(Number(ref.name)); | ||
} | ||
} | ||
else { | ||
capturingData.markAsCannotTrack(); | ||
return; | ||
else if (ref.type === "Split") { | ||
hasSplit = true; | ||
} | ||
} | ||
} | ||
function verifyForMatchAll(node) { | ||
const capturingData = capturingDataMap.get(node.arguments[0]); | ||
if (capturingData == null || capturingData.isAllUsed()) { | ||
return; | ||
} | ||
if (!typeTracer.isString(node.callee.object)) { | ||
capturingData.markAsCannotTrack(); | ||
return; | ||
} | ||
capturingData.markAsUsed(); | ||
for (const iterationRef of ast_utils_1.extractPropertyReferences(node, context)) { | ||
if (!iterationRef.extractPropertyReferences) { | ||
capturingData.markAsCannotTrack(); | ||
return; | ||
} | ||
if (hasNameRef(iterationRef)) { | ||
if (Number.isNaN(Number(iterationRef.name))) { | ||
continue; | ||
return { | ||
unusedIndexRef(index) { | ||
if (hasSplit) { | ||
return false; | ||
} | ||
} | ||
for (const ref of iterationRef.extractPropertyReferences()) { | ||
if (hasNameRef(ref)) { | ||
if (ref.name === "groups") { | ||
for (const namedRef of ref.extractPropertyReferences()) { | ||
if (hasNameRef(namedRef)) { | ||
capturingData.usedName(namedRef.name); | ||
} | ||
else { | ||
capturingData.usedAllNames(); | ||
} | ||
} | ||
} | ||
else { | ||
capturingData.usedIndex(Number(ref.name)); | ||
} | ||
return !indexRefs.includes(index); | ||
}, | ||
unusedNamedRef(name) { | ||
if (hasUnknownName) { | ||
return false; | ||
} | ||
else { | ||
capturingData.markAsCannotTrack(); | ||
return; | ||
} | ||
} | ||
} | ||
return !namedRefs.includes(name); | ||
}, | ||
}; | ||
} | ||
function verifyForSplit(node) { | ||
const capturingData = capturingDataMap.get(node.arguments[0]); | ||
if (capturingData == null || capturingData.isAllUsed()) { | ||
return; | ||
function createVisitor(regexpContext) { | ||
const references = getCapturingGroupReferences(regexpContext); | ||
if (!references) { | ||
return {}; | ||
} | ||
if (!typeTracer.isString(node.callee.object)) { | ||
capturingData.markAsCannotTrack(); | ||
return; | ||
} | ||
capturingData.markAsUsed(); | ||
capturingData.usedAllUnnamed(); | ||
} | ||
function createVisitor(regexpContext) { | ||
const { regexpNode } = regexpContext; | ||
const capturingData = new CapturingData(regexpContext); | ||
capturingDataMap.set(regexpNode, capturingData); | ||
for (const ref of ast_utils_1.extractExpressionReferences(regexpNode, context)) { | ||
if (ref.type === "argument" || ref.type === "member") { | ||
capturingDataMap.set(ref.node, capturingData); | ||
const unused = new Set(); | ||
const allCapturingGroups = regexpContext.getAllCapturingGroups(); | ||
for (let index = 0; index < allCapturingGroups.length; index++) { | ||
const cgNode = allCapturingGroups[index]; | ||
if (cgNode.references.length || | ||
!references.unusedIndexRef(index + 1)) { | ||
continue; | ||
} | ||
else { | ||
capturingData.markAsCannotTrack(); | ||
if (cgNode.name && !references.unusedNamedRef(cgNode.name)) { | ||
continue; | ||
} | ||
unused.add(cgNode); | ||
} | ||
return capturingData.visitor(); | ||
reportUnused(unused, regexpContext); | ||
return {}; | ||
} | ||
return utils_1.compositingVisitors(utils_1.defineRegexpVisitor(context, { | ||
return utils_1.defineRegexpVisitor(context, { | ||
createVisitor, | ||
}), { | ||
"Program:exit"() { | ||
for (const capturingData of new Set(capturingDataMap.values())) { | ||
if (capturingData.isNeedReport()) { | ||
reportUnused(capturingData); | ||
} | ||
} | ||
}, | ||
"CallExpression:exit"(node) { | ||
if (!ast_utils_1.isKnownMethodCall(node, { | ||
match: 1, | ||
test: 1, | ||
search: 1, | ||
replace: 2, | ||
replaceAll: 2, | ||
matchAll: 1, | ||
exec: 1, | ||
split: 1, | ||
})) { | ||
return; | ||
} | ||
if (node.callee.property.name === "match") { | ||
verifyForMatch(node); | ||
} | ||
else if (node.callee.property.name === "test") { | ||
verifyForTest(node); | ||
} | ||
else if (node.callee.property.name === "search") { | ||
verifyForSearch(node); | ||
} | ||
else if (node.callee.property.name === "replace" || | ||
node.callee.property.name === "replaceAll") { | ||
verifyForReplace(node); | ||
} | ||
else if (node.callee.property.name === "exec") { | ||
verifyForExec(node); | ||
} | ||
else if (node.callee.property.name === "matchAll") { | ||
verifyForMatchAll(node); | ||
} | ||
else if (node.callee.property.name === "split") { | ||
verifyForSplit(node); | ||
} | ||
}, | ||
}); | ||
}, | ||
}); | ||
function hasNameRef(ref) { | ||
return ref.type === "destructuring" || ref.type === "member"; | ||
} |
@@ -6,3 +6,3 @@ "use strict"; | ||
const type_tracker_1 = require("../utils/type-tracker"); | ||
const regexp_ast_analysis_1 = require("regexp-ast-analysis"); | ||
const case_variation_1 = require("../utils/regexp-ast/case-variation"); | ||
class RegExpReference { | ||
@@ -99,35 +99,5 @@ constructor(regExpContext) { | ||
} | ||
const flagsNoI = Object.assign(Object.assign({}, flags), { ignoreCase: false }); | ||
let unnecessary = true; | ||
return { | ||
onAssertionEnter(aNode) { | ||
if (unnecessary) { | ||
if (aNode.kind === "word" && flags.unicode) { | ||
unnecessary = false; | ||
} | ||
} | ||
}, | ||
onCharacterEnter(cNode) { | ||
if (unnecessary) { | ||
if (regexp_ast_analysis_1.toCharSet(cNode, flags).size > 1) { | ||
unnecessary = false; | ||
} | ||
} | ||
}, | ||
onCharacterSetEnter(cNode) { | ||
if (unnecessary) { | ||
if (cNode.kind === "word" && flags.unicode) { | ||
unnecessary = false; | ||
} | ||
if (cNode.kind === "property") { | ||
const caseInsensitive = regexp_ast_analysis_1.toCharSet(cNode, flags); | ||
const caseSensitive = regexp_ast_analysis_1.toCharSet(cNode, flagsNoI); | ||
if (!caseInsensitive.equals(caseSensitive)) { | ||
unnecessary = false; | ||
} | ||
} | ||
} | ||
}, | ||
onPatternLeave() { | ||
if (unnecessary) { | ||
onPatternLeave(pattern) { | ||
if (!case_variation_1.isCaseVariant(pattern, flags, false)) { | ||
context.report({ | ||
@@ -134,0 +104,0 @@ node: regexpNode, |
@@ -6,2 +6,10 @@ "use strict"; | ||
const mention_1 = require("../utils/mention"); | ||
function isDigits(element) { | ||
return ((element.type === "CharacterSet" && | ||
element.kind === "digit" && | ||
!element.negate) || | ||
(element.type === "CharacterClassRange" && | ||
element.min.value === utils_1.CP_DIGIT_ZERO && | ||
element.max.value === utils_1.CP_DIGIT_NINE)); | ||
} | ||
exports.default = utils_1.createRule("prefer-d", { | ||
@@ -15,3 +23,14 @@ meta: { | ||
fixable: "code", | ||
schema: [], | ||
schema: [ | ||
{ | ||
type: "object", | ||
properties: { | ||
insideCharacterClass: { | ||
type: "string", | ||
enum: ["ignore", "range", "d"], | ||
}, | ||
}, | ||
additionalProperties: false, | ||
}, | ||
], | ||
messages: { | ||
@@ -23,4 +42,5 @@ unexpected: "Unexpected {{type}} {{expr}}. Use '{{instead}}' instead.", | ||
create(context) { | ||
var _a, _b; | ||
const insideCharacterClass = (_b = (_a = context.options[0]) === null || _a === void 0 ? void 0 : _a.insideCharacterClass) !== null && _b !== void 0 ? _b : "d"; | ||
function createVisitor({ node, flags, getRegexpLocation, fixReplaceNode, }) { | ||
let reportedCharacterClass = false; | ||
return { | ||
@@ -37,3 +57,2 @@ onCharacterClassEnter(ccNode) { | ||
if (predefined) { | ||
reportedCharacterClass = true; | ||
context.report({ | ||
@@ -50,25 +69,24 @@ node, | ||
}); | ||
return; | ||
} | ||
}, | ||
onCharacterClassLeave() { | ||
reportedCharacterClass = false; | ||
}, | ||
onCharacterClassRangeEnter(ccrNode) { | ||
if (reportedCharacterClass) { | ||
if (insideCharacterClass === "ignore") { | ||
return; | ||
} | ||
if (ccrNode.min.value === utils_1.CP_DIGIT_ZERO && | ||
ccrNode.max.value === utils_1.CP_DIGIT_NINE) { | ||
const instead = "\\d"; | ||
context.report({ | ||
node, | ||
loc: getRegexpLocation(ccrNode), | ||
messageId: "unexpected", | ||
data: { | ||
type: "character class range", | ||
expr: mention_1.mention(ccrNode), | ||
instead, | ||
}, | ||
fix: fixReplaceNode(ccrNode, instead), | ||
}); | ||
const expected = insideCharacterClass === "d" ? "\\d" : "0-9"; | ||
for (const e of ccNode.elements) { | ||
if (isDigits(e) && e.raw !== expected) { | ||
context.report({ | ||
node, | ||
loc: getRegexpLocation(e), | ||
messageId: "unexpected", | ||
data: { | ||
type: e.type === "CharacterSet" | ||
? "character set" | ||
: "character class range", | ||
expr: mention_1.mention(e), | ||
instead: expected, | ||
}, | ||
fix: fixReplaceNode(e, expected), | ||
}); | ||
} | ||
} | ||
@@ -75,0 +93,0 @@ }, |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.extractPropertyReferences = void 0; | ||
exports.extractPropertyReferencesForPattern = exports.extractPropertyReferences = void 0; | ||
const utils_1 = require("./utils"); | ||
@@ -26,3 +26,3 @@ const extract_expression_references_1 = require("./extract-expression-references"); | ||
} | ||
yield { type: "unknown" }; | ||
yield { type: "unknown", node: ref.node }; | ||
} | ||
@@ -32,2 +32,6 @@ } | ||
exports.extractPropertyReferences = extractPropertyReferences; | ||
function* extractPropertyReferencesForPattern(node, context) { | ||
yield* iteratePropertyReferencesForPattern(node, context); | ||
} | ||
exports.extractPropertyReferencesForPattern = extractPropertyReferencesForPattern; | ||
function isShallowCopy(node) { | ||
@@ -49,2 +53,3 @@ const parent = utils_1.getParent(node); | ||
type: "unknown", | ||
node, | ||
*extractPropertyReferences() { | ||
@@ -59,2 +64,3 @@ yield* extractPropertyReferences(node, context); | ||
name: property, | ||
node, | ||
*extractPropertyReferences() { | ||
@@ -75,2 +81,3 @@ yield* extractPropertyReferences(node, context); | ||
type: "unknown", | ||
node, | ||
*extractPropertyReferences() { | ||
@@ -85,2 +92,3 @@ yield* iteratePropertyReferencesForPattern(prop.value, context); | ||
name: property, | ||
node: prop, | ||
*extractPropertyReferences() { | ||
@@ -109,2 +117,3 @@ yield* iteratePropertyReferencesForPattern(prop.value, context); | ||
name: String(index), | ||
node: element, | ||
*extractPropertyReferences() { | ||
@@ -122,2 +131,3 @@ yield* iteratePropertyReferencesForPattern(element, context); | ||
type: "unknown", | ||
node: element, | ||
*extractPropertyReferences() { | ||
@@ -132,2 +142,3 @@ yield* iteratePropertyReferencesForPattern(element, context); | ||
type: "iteration", | ||
node, | ||
*extractPropertyReferences() { | ||
@@ -159,3 +170,3 @@ let left = node.left; | ||
else { | ||
yield { type: "unknown" }; | ||
yield { type: "unknown", node: target }; | ||
} | ||
@@ -182,2 +193,3 @@ } | ||
type: "unknown", | ||
node: ref.node, | ||
extractPropertyReferences: ref.extractPropertyReferences, | ||
@@ -184,0 +196,0 @@ }; |
@@ -22,2 +22,4 @@ "use strict"; | ||
const pattern_source_1 = require("./ast-utils/pattern-source"); | ||
const extract_capturing_group_references_1 = require("./extract-capturing-group-references"); | ||
const type_tracker_1 = require("./type-tracker"); | ||
__exportStar(require("./unicode"), exports); | ||
@@ -272,2 +274,4 @@ const regexpRules = new WeakMap(); | ||
let cacheUsageOfPattern = null; | ||
const cacheCapturingGroupReferenceMap = new Map(); | ||
let cacheAllCapturingGroups = null; | ||
return { | ||
@@ -295,2 +299,18 @@ getRegexpLocation: (range, offsets) => { | ||
getUsageOfPattern: () => (cacheUsageOfPattern !== null && cacheUsageOfPattern !== void 0 ? cacheUsageOfPattern : (cacheUsageOfPattern = get_usage_of_pattern_1.getUsageOfPattern(regexpNode, context))), | ||
getCapturingGroupReferences: (options) => { | ||
var _a; | ||
const strictTypes = Boolean((_a = options === null || options === void 0 ? void 0 : options.strictTypes) !== null && _a !== void 0 ? _a : true); | ||
const cacheCapturingGroupReference = cacheCapturingGroupReferenceMap.get(strictTypes); | ||
if (cacheCapturingGroupReference) { | ||
return cacheCapturingGroupReference; | ||
} | ||
const countOfCapturingGroup = getAllCapturingGroupsWithCache() | ||
.length; | ||
const capturingGroupReferences = [ | ||
...extract_capturing_group_references_1.extractCapturingGroupReferences(regexpNode, flags, type_tracker_1.createTypeTracker(context), countOfCapturingGroup, context, { strictTypes }), | ||
]; | ||
cacheCapturingGroupReferenceMap.set(strictTypes, capturingGroupReferences); | ||
return capturingGroupReferences; | ||
}, | ||
getAllCapturingGroups: getAllCapturingGroupsWithCache, | ||
pattern: parsedPattern.raw, | ||
@@ -301,2 +321,5 @@ patternAst: parsedPattern, | ||
}; | ||
function getAllCapturingGroupsWithCache() { | ||
return (cacheAllCapturingGroups !== null && cacheAllCapturingGroups !== void 0 ? cacheAllCapturingGroups : (cacheAllCapturingGroups = getAllCapturingGroups(parsedPattern))); | ||
} | ||
} | ||
@@ -433,16 +456,2 @@ function buildUnparsableRegExpContextBase({ patternSource, patternNode, regexpNode, context, flags: originalFlags, flagsString, flagsNode, ownsFlags, }) { | ||
return (fixer) => { | ||
let patternFix = null; | ||
if (includePattern) { | ||
if (!patternSource) { | ||
return null; | ||
} | ||
const patternRange = patternSource.getReplaceRange({ | ||
start: 0, | ||
end: patternSource.value.length, | ||
}); | ||
if (patternRange == null) { | ||
return null; | ||
} | ||
patternFix = patternRange.replace(fixer, patternSource.value); | ||
} | ||
let newFlags; | ||
@@ -461,7 +470,10 @@ if (typeof replacement === "string") { | ||
} | ||
if (utils_1.isRegexpLiteral(regexpNode)) { | ||
if (includePattern && utils_1.isRegexpLiteral(regexpNode)) { | ||
return fixer.replaceText(regexpNode, `/${regexpNode.regex.pattern}/${newFlags}`); | ||
} | ||
let flagsFix; | ||
if (flagsNode) { | ||
if (utils_1.isRegexpLiteral(regexpNode)) { | ||
flagsFix = fixer.replaceTextRange(getFlagsRange(regexpNode), newFlags); | ||
} | ||
else if (flagsNode) { | ||
const range = getFlagsRange(flagsNode); | ||
@@ -480,5 +492,16 @@ if (range == null) { | ||
} | ||
if (!patternFix) { | ||
if (!includePattern) { | ||
return flagsFix; | ||
} | ||
if (!patternSource) { | ||
return null; | ||
} | ||
const patternRange = patternSource.getReplaceRange({ | ||
start: 0, | ||
end: patternSource.value.length, | ||
}); | ||
if (patternRange == null) { | ||
return null; | ||
} | ||
const patternFix = patternRange.replace(fixer, patternSource.value); | ||
return [patternFix, flagsFix]; | ||
@@ -660,1 +683,11 @@ }; | ||
exports.isEscapeSequence = isEscapeSequence; | ||
function getAllCapturingGroups(pattern) { | ||
const groups = []; | ||
regexpp_1.visitRegExpAST(pattern, { | ||
onCapturingGroupEnter(group) { | ||
groups.push(group); | ||
}, | ||
}); | ||
groups.sort((a, b) => a.start - b.start); | ||
return groups; | ||
} |
@@ -63,2 +63,3 @@ "use strict"; | ||
const prefer_named_capture_group_1 = __importDefault(require("../rules/prefer-named-capture-group")); | ||
const prefer_named_replacement_1 = __importDefault(require("../rules/prefer-named-replacement")); | ||
const prefer_plus_quantifier_1 = __importDefault(require("../rules/prefer-plus-quantifier")); | ||
@@ -71,2 +72,3 @@ const prefer_predefined_assertion_1 = __importDefault(require("../rules/prefer-predefined-assertion")); | ||
const prefer_regexp_test_1 = __importDefault(require("../rules/prefer-regexp-test")); | ||
const prefer_result_array_groups_1 = __importDefault(require("../rules/prefer-result-array-groups")); | ||
const prefer_star_quantifier_1 = __importDefault(require("../rules/prefer-star-quantifier")); | ||
@@ -82,2 +84,3 @@ const prefer_t_1 = __importDefault(require("../rules/prefer-t")); | ||
const unicode_escape_1 = __importDefault(require("../rules/unicode-escape")); | ||
const use_ignore_case_1 = __importDefault(require("../rules/use-ignore-case")); | ||
exports.rules = [ | ||
@@ -140,2 +143,3 @@ confusing_quantifier_1.default, | ||
prefer_named_capture_group_1.default, | ||
prefer_named_replacement_1.default, | ||
prefer_plus_quantifier_1.default, | ||
@@ -148,2 +152,3 @@ prefer_predefined_assertion_1.default, | ||
prefer_regexp_test_1.default, | ||
prefer_result_array_groups_1.default, | ||
prefer_star_quantifier_1.default, | ||
@@ -159,2 +164,3 @@ prefer_t_1.default, | ||
unicode_escape_1.default, | ||
use_ignore_case_1.default, | ||
]; |
@@ -9,13 +9,4 @@ "use strict"; | ||
const ast_utils_1 = require("../ast-utils"); | ||
const ts = (() => { | ||
try { | ||
return require("typescript"); | ||
} | ||
catch (e) { | ||
if (e.code === "MODULE_NOT_FOUND") { | ||
return undefined; | ||
} | ||
throw e; | ||
} | ||
})(); | ||
const ts_utils_1 = require("../ts-utils"); | ||
const ts = ts_utils_1.getTypeScript(); | ||
const cacheTypeTracker = new WeakMap(); | ||
@@ -28,6 +19,3 @@ function createTypeTracker(context) { | ||
} | ||
const tsNodeMap = context.parserServices.esTreeNodeToTSNodeMap; | ||
const checker = context.parserServices.program && | ||
context.parserServices.program.getTypeChecker(); | ||
const availableTS = Boolean(ts && tsNodeMap && checker); | ||
const { tsNodeMap, checker, usedTS } = ts_utils_1.getTypeScriptTools(context); | ||
const cacheTypeInfo = new WeakMap(); | ||
@@ -49,3 +37,3 @@ const tracker = { | ||
} | ||
if (availableTS) { | ||
if (usedTS) { | ||
return false; | ||
@@ -108,3 +96,3 @@ } | ||
} | ||
if (availableTS) { | ||
if (usedTS) { | ||
return getTypeByTs(node); | ||
@@ -400,3 +388,3 @@ } | ||
} | ||
return availableTS ? getTypeByTs(node) : null; | ||
return usedTS ? getTypeByTs(node) : null; | ||
} | ||
@@ -410,25 +398,24 @@ function getTypeByTs(node) { | ||
var _a, _b; | ||
if ((tsType.flags & ts.TypeFlags.StringLike) !== 0) { | ||
if (ts_utils_1.isStringLine(tsType)) { | ||
return type_data_1.STRING; | ||
} | ||
if ((tsType.flags & ts.TypeFlags.NumberLike) !== 0) { | ||
if (ts_utils_1.isNumberLike(tsType)) { | ||
return type_data_1.NUMBER; | ||
} | ||
if ((tsType.flags & ts.TypeFlags.BooleanLike) !== 0) { | ||
if (ts_utils_1.isBooleanLike(tsType)) { | ||
return type_data_1.BOOLEAN; | ||
} | ||
if ((tsType.flags & ts.TypeFlags.BigIntLike) !== 0) { | ||
if (ts_utils_1.isBigIntLike(tsType)) { | ||
return type_data_1.BIGINT; | ||
} | ||
if ((tsType.flags & ts.TypeFlags.Any) !== 0 || | ||
(tsType.flags & ts.TypeFlags.Unknown) !== 0) { | ||
if (ts_utils_1.isAny(tsType) || ts_utils_1.isUnknown(tsType)) { | ||
return null; | ||
} | ||
if (isArrayLikeObject(tsType)) { | ||
if (ts_utils_1.isArrayLikeObject(tsType)) { | ||
return type_data_1.UNKNOWN_ARRAY; | ||
} | ||
if (isReferenceObject(tsType) && tsType.target !== tsType) { | ||
if (ts_utils_1.isReferenceObject(tsType) && tsType.target !== tsType) { | ||
return getTypeFromTsType(tsType.target); | ||
} | ||
if ((tsType.flags & ts.TypeFlags.TypeParameter) !== 0) { | ||
if (ts_utils_1.isTypeParameter(tsType)) { | ||
const constraintType = getConstraintType(tsType); | ||
@@ -440,3 +427,3 @@ if (constraintType) { | ||
} | ||
if (isUnionOrIntersection(tsType)) { | ||
if (ts_utils_1.isUnionOrIntersection(tsType)) { | ||
return type_data_1.TypeUnionOrIntersection.buildType(function* () { | ||
@@ -451,3 +438,3 @@ for (const t of tsType.types) { | ||
} | ||
if (isClassOrInterface(tsType)) { | ||
if (ts_utils_1.isClassOrInterface(tsType)) { | ||
const name = tsType.symbol.escapedName; | ||
@@ -457,3 +444,3 @@ const typeName = (_b = (_a = /^Readonly(.*)/.exec(name)) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : name; | ||
} | ||
if (isObject(tsType)) { | ||
if (ts_utils_1.isObject(tsType)) { | ||
return type_data_1.UNKNOWN_OBJECT; | ||
@@ -475,24 +462,2 @@ } | ||
exports.createTypeTracker = createTypeTracker; | ||
function isArrayLikeObject(tsType) { | ||
return (isObject(tsType) && | ||
(tsType.objectFlags & | ||
(ts.ObjectFlags.ArrayLiteral | | ||
ts.ObjectFlags.EvolvingArray | | ||
ts.ObjectFlags.Tuple)) !== | ||
0); | ||
} | ||
function isClassOrInterface(tsType) { | ||
return (isObject(tsType) && | ||
(tsType.objectFlags & ts.ObjectFlags.ClassOrInterface) !== 0); | ||
} | ||
function isObject(tsType) { | ||
return (tsType.flags & ts.TypeFlags.Object) !== 0; | ||
} | ||
function isReferenceObject(tsType) { | ||
return (isObject(tsType) && | ||
(tsType.objectFlags & ts.ObjectFlags.Reference) !== 0); | ||
} | ||
function isUnionOrIntersection(tsType) { | ||
return (tsType.flags & ts.TypeFlags.UnionOrIntersection) !== 0; | ||
} | ||
function typeTextToTypeInfo(typeText) { | ||
@@ -499,0 +464,0 @@ if (typeText == null) { |
{ | ||
"name": "eslint-plugin-regexp", | ||
"version": "1.3.1", | ||
"version": "1.4.0", | ||
"description": "ESLint plugin for finding RegExp mistakes and RegExp style guide violations.", | ||
@@ -5,0 +5,0 @@ "engines": { |
@@ -162,2 +162,3 @@ # Introduction | ||
| [regexp/sort-alternatives](https://ota-meshi.github.io/eslint-plugin-regexp/rules/sort-alternatives.html) | sort alternatives if order doesn't matter | :wrench: | | ||
| [regexp/use-ignore-case](https://ota-meshi.github.io/eslint-plugin-regexp/rules/use-ignore-case.html) | use the `i` flag if it simplifies the pattern | :wrench: | | ||
@@ -178,4 +179,6 @@ ### Stylistic Issues | ||
| [regexp/prefer-named-capture-group](https://ota-meshi.github.io/eslint-plugin-regexp/rules/prefer-named-capture-group.html) | enforce using named capture groups | | | ||
| [regexp/prefer-named-replacement](https://ota-meshi.github.io/eslint-plugin-regexp/rules/prefer-named-replacement.html) | enforce using named replacement | :wrench: | | ||
| [regexp/prefer-plus-quantifier](https://ota-meshi.github.io/eslint-plugin-regexp/rules/prefer-plus-quantifier.html) | enforce using `+` quantifier | :star::wrench: | | ||
| [regexp/prefer-question-quantifier](https://ota-meshi.github.io/eslint-plugin-regexp/rules/prefer-question-quantifier.html) | enforce using `?` quantifier | :star::wrench: | | ||
| [regexp/prefer-result-array-groups](https://ota-meshi.github.io/eslint-plugin-regexp/rules/prefer-result-array-groups.html) | enforce using result array `groups` | :wrench: | | ||
| [regexp/prefer-star-quantifier](https://ota-meshi.github.io/eslint-plugin-regexp/rules/prefer-star-quantifier.html) | enforce using `*` quantifier | :star::wrench: | | ||
@@ -182,0 +185,0 @@ | [regexp/prefer-unicode-codepoint-escapes](https://ota-meshi.github.io/eslint-plugin-regexp/rules/prefer-unicode-codepoint-escapes.html) | enforce use of unicode codepoint escapes | :star::wrench: | |
608412
3.85%127
4.96%15186
4.31%235
1.29%