From 88ec49542069ebfc02b32856e4ec4e8b71d80d11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Fri, 27 Sep 2024 11:03:38 -0400 Subject: [PATCH 1/5] add eslint-plugin-regexp --- eslint.config.mjs | 2 ++ package.json | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index b57e472..a593d21 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,11 +1,13 @@ import globals from "globals"; import js from "@eslint/js"; +import * as regexpPlugin from "eslint-plugin-regexp" export default [ { files: ["**/*.js", "**/*.mjs"], }, js.configs.recommended, + regexpPlugin.configs["flat/recommended"], { languageOptions: { globals: { diff --git a/package.json b/package.json index d6ed850..6b37d6e 100644 --- a/package.json +++ b/package.json @@ -28,11 +28,12 @@ "jsesc": "~3.0.2" }, "devDependencies": { + "@unicode/unicode-16.0.0": "^1.6.0", "eslint": "^9.10.0", + "eslint-plugin-regexp": "^2.6.0", "globals": "^15.9.0", "npm-run-all": "^4.1.5", "regenerate": "~1.0.1", - "typescript": "^4.5.2", - "@unicode/unicode-16.0.0": "^1.6.0" + "typescript": "^4.5.2" } } From 2c37af0eba3d3f11652ba4b3fb1d95b4920b4725 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Fri, 27 Sep 2024 11:07:12 -0400 Subject: [PATCH 2/5] fix regexp/strict lint errors Avoid using annexB regexp syntax --- parser.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parser.js b/parser.js index e1736c6..1768b5b 100644 --- a/parser.js +++ b/parser.js @@ -588,7 +588,7 @@ // If no unicode flag, then try to parse ExtendedAtom -> ExtendedPatternCharacter. // ExtendedPatternCharacter var res; - if (!isUnicodeMode && (res = matchReg(/^{/))) { + if (!isUnicodeMode && (res = matchReg(/^\{/))) { atom = createCharacter(res); } else { bail('Expected atom'); @@ -762,7 +762,7 @@ // PatternCharacter return createCharacter(res); } - else if (!isUnicodeMode && (res = matchReg(/^(?:]|})/))) { + else if (!isUnicodeMode && (res = matchReg(/^(?:\]|\})/))) { // ExtendedPatternCharacter, first part. See parseTerm. return createCharacter(res); } From 4615c04a3a619004812060d235315093f25af9f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Fri, 27 Sep 2024 11:08:58 -0400 Subject: [PATCH 3/5] regexp/prefer-d \d is shorter than [0-9] --- parser.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/parser.js b/parser.js index 1768b5b..cc77060 100644 --- a/parser.js +++ b/parser.js @@ -704,15 +704,15 @@ else if (match('?')) { quantifier = createQuantifier(0, 1, undefined, undefined, "?"); } - else if (res = matchReg(/^\{([0-9]+)\}/)) { + else if (res = matchReg(/^\{(\d+)\}/)) { min = parseInt(res[1], 10); quantifier = createQuantifier(min, min, res.range[0], res.range[1]); } - else if (res = matchReg(/^\{([0-9]+),\}/)) { + else if (res = matchReg(/^\{(\d+),\}/)) { min = parseInt(res[1], 10); quantifier = createQuantifier(min, undefined, res.range[0], res.range[1]); } - else if (res = matchReg(/^\{([0-9]+),([0-9]+)\}/)) { + else if (res = matchReg(/^\{(\d+),(\d+)\}/)) { min = parseInt(res[1], 10); max = parseInt(res[2], 10); if (min > max) { @@ -906,7 +906,7 @@ return createEscaped('singleEscape', 0x0008, '\\b'); } else if (match('B')) { bail('\\B not possible inside of CharacterClass', '', from); - } else if (!isUnicodeMode && (res = matchReg(/^c([0-9])/))) { + } else if (!isUnicodeMode && (res = matchReg(/^c(\d)/))) { // B.1.4 // c ClassControlLetter, ClassControlLetter = DecimalDigit return createEscaped('controlLetter', res[1] + 16, res[1], 2); From bd8a924f718b9551743dab4bb8096ed6fbd0fdb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Fri, 27 Sep 2024 11:12:19 -0400 Subject: [PATCH 4/5] fix regexp/no-useless-escape --- parser.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parser.js b/parser.js index cc77060..83e68d5 100644 --- a/parser.js +++ b/parser.js @@ -1010,7 +1010,7 @@ var res; if (res = matchReg(/^[dDsSwW]/)) { return createCharacterClassEscape(res[0]); - } else if (features.unicodePropertyEscape && isUnicodeMode && (res = matchReg(/^([pP])\{([^\}]+)\}/))) { + } else if (features.unicodePropertyEscape && isUnicodeMode && (res = matchReg(/^([pP])\{([^}]+)\}/))) { // https://github.com/jviereck/regjsparser/issues/77 return addRaw({ type: 'unicodePropertyEscape', @@ -1184,7 +1184,7 @@ var tmp; var l = lookahead(); if ( - (isUnicodeMode && /[\^\$\.\*\+\?\(\)\\\[\]\{\}\|\/]/.test(l)) || + (isUnicodeMode && /[\^$.*+?()\\[\]{}|/]/.test(l)) || (!isUnicodeMode && l !== "c") ) { if (l === "k" && features.lookbehind) { From b0201ffe1f5d993f39039c8a7f38ae93ae27166f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Fri, 27 Sep 2024 11:18:25 -0400 Subject: [PATCH 5/5] supress regexp/use-ignore-case errors This rule recommends to simplify the following usage /^[dDsSwW]/ to /^[dsw]/i This regex is for matching the CharacterClassEscape // CharacterClassEscape :: one of d D s S w W If we simplify to the /i flag and suppose CharacterClassEscape now allows a new production `x` but not `X`, it is prone to errors that we accidentally allows `X` because of the `i` flag. For this reason I suggest to disable this rule. --- eslint.config.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/eslint.config.mjs b/eslint.config.mjs index a593d21..d32ce6d 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -36,6 +36,7 @@ export default [ "no-useless-escape": ["off"], "no-empty": ["off"], "no-unused-vars": ["error", { "caughtErrors": "none" }], + "regexp/use-ignore-case": ["off"], }, }, ];