├── .eslintrc-any.json
├── .eslintrc.json
├── .gitignore
├── .gitlab-ci.yml
├── .readme
├── developing.md
├── mixed-rules.svg
└── rules.svg
├── CHANGELOG.md
├── LICENSE.txt
├── README.md
├── _config.yml
├── dev_resources
├── mixed-rules.xml
└── rules.xml
├── docs
└── rules
│ ├── invalid-regex-rule.md
│ └── required-regex-rule.md
├── lib
├── index.js
├── rules
│ ├── common-fields-definitions.js
│ ├── invalid-regex-rule.js
│ └── required-regex-rule.js
└── utils
│ ├── check-utils.js
│ ├── create-utils.js
│ ├── options-utils.js
│ └── report-utils.js
├── package-lock.json
├── package.json
└── tests
├── lib
├── index.test.js
└── rules
│ ├── .eslintrc.json
│ ├── case-001-minified.js
│ ├── case-002-minified.js
│ ├── case-003-stack.txt
│ ├── case-004-stack.txt
│ ├── case-005-stack.txt
│ ├── e2e-tests.js
│ ├── invalid-regex-detailed-rule.e2e-test.js
│ ├── invalid-regex-rule.e2e-test.js
│ ├── required-regex-detailed-rule.e2e-test.js
│ └── required-regex-rule.e2e-test.js
└── tests.js
/.eslintrc-any.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [ "plugin:base-style-config/common-rules" ],
3 | "plugins": [ "base-style-config" ],
4 | "parser": "any-eslint-parser"
5 | }
6 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "plugin:base-style-config/js-rules",
4 | "plugin:base-style-config/import-rules"
5 | ],
6 | "env": {
7 | "node": true
8 | },
9 | "parserOptions": {
10 | "ecmaVersion": 6
11 | },
12 | "root": true,
13 | "rules": {
14 | "global-require": "error",
15 | "init-declarations": "off",
16 | "max-params": "off",
17 | "no-console": "error",
18 | "no-invalid-this": "error",
19 | "no-mixed-requires": "error",
20 | "no-new-require": "error",
21 | "no-param-reassign": "off",
22 | "object-curly-newline": ["error", {
23 | "consistent": true
24 | }
25 | ],
26 | "quotes": [
27 | "error",
28 | "single"
29 | ],
30 | "semi": [
31 | "error",
32 | "never"
33 | ],
34 | "valid-jsdoc": "off"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Folders
2 | .gradle/
3 | build/
4 | node_modules/
5 | # Files
6 | *.tgz
7 | *.log
8 | # IDEs
9 | .vscode
10 | .vscode/
11 | .project
12 | .classpath
13 | .settings/
14 | *.sublime-project
15 | *.sublime-workspace
16 | .idea/
17 |
--------------------------------------------------------------------------------
/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | image: node:latest
2 |
3 | cache:
4 | key: "${CI_COMMIT_SHA}"
5 | untracked: true
6 | paths:
7 | - node_modules/
8 |
9 | stages:
10 | - inception
11 | - assess
12 | - test
13 | - assemble
14 |
15 | create-cache:
16 | stage: inception
17 | script:
18 | - npm install
19 | cache:
20 | key: "${CI_COMMIT_SHA}"
21 | policy: push
22 | untracked: true
23 | paths:
24 | - node_modules/
25 |
26 | assess:
27 | stage: assess
28 | script:
29 | - npm run lint
30 | cache:
31 | key: "${CI_COMMIT_SHA}"
32 | policy: pull
33 | untracked: true
34 | paths:
35 | - node_modules/
36 |
37 | test:
38 | stage: test
39 | script:
40 | - npm test
41 | cache:
42 | key: "${CI_COMMIT_SHA}"
43 | policy: pull
44 | untracked: true
45 | paths:
46 | - node_modules/
47 | coverage: '/Branches.*?(\d+(?:\.\d+)?)%/'
48 | artifacts:
49 | when: always
50 | paths:
51 | - build/coverage/lcov-report/
52 | reports:
53 | coverage_report:
54 | coverage_format: cobertura
55 | path: build/coverage/cobertura-coverage.xml
56 |
57 | pages:
58 | stage: assemble
59 | dependencies:
60 | - test
61 | script:
62 | - mkdir -p to_public
63 | - touch to_public/index.html
64 | - mv build/coverage/lcov-report/ to_public/coverage
65 | - mv to_public public
66 | artifacts:
67 | paths:
68 | - public
69 | only:
70 | - master
71 |
--------------------------------------------------------------------------------
/.readme/developing.md:
--------------------------------------------------------------------------------
1 | # Extending/Developing
2 |
3 | ## Prerequisites
4 |
5 | * [NodeJS](https://nodejs.org/en/download)/npm [1].
6 | * [Git](https://git-scm.com/downloads) (if you are going to clone the project).
7 |
8 | > [1] [Downloading NodeJS](https://nodejs.org/en/download) will also download and provide [`npm`](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm#using-a-node-installer-to-install-nodejs-and-npm).
9 |
10 | ## Getting it
11 |
12 | [Clone](https://help.github.com/articles/cloning-a-repository/) the project in the desired folder by executing:
13 |
14 | ```sh
15 | git clone https://github.com/gmullerb/eslint-plugin-regex
16 | ```
17 |
18 | or
19 |
20 | ```sh
21 | git clone https://gitlab.com/gmullerb/eslint-plugin-regex
22 | ```
23 |
24 | (you can also fork)
25 |
26 | ## Set up
27 |
28 | Run:
29 |
30 | ```sh
31 | npm install
32 | ```
33 |
34 | It will install project dependencies, as [eslint](https://www.npmjs.com/package/eslint), [any-eslint-parser](https://www.npmjs.com/package/any-eslint-parser), [eslint-plugin-base-style-config](https://www.npmjs.com/package/eslint-plugin-base-style-config), etc.
35 |
36 | > Recommendation: Immediately after installation, run `npm run check` to be sure that initial code is "ok".
37 |
38 | ### Npm scripts
39 |
40 | [`package.json`](../package.json):
41 |
42 | * `lint.common`: checks common style of "all" files defined in [`.eslintrc-any.json`](../.eslintrc-any.json) using [any-eslint-parser](https://www.npmjs.com/package/any-eslint-parser).
43 | * `lint.source`: checks eslint style of `js` files.
44 | * `lint`: runs both lints (`lint.common` and `lint.source`).
45 | * `test.only`: runs test.
46 | * `test`: runs test with coverage report.
47 |
48 | Additionally:
49 |
50 | * `npm run check`: will execute all tasks (`lint`, `test`, etc.).
51 | * `npm run`: will list all available script/task for the project.
52 |
53 | ## Folders structure
54 |
55 | ```
56 | /lib
57 | /rules
58 | /utils
59 | /tests
60 | /lib
61 | /rules
62 | ```
63 |
64 | - `lib/rules`: Rules files.
65 | - `lib/utils`: Utilities files.
66 | - `tests/lib/rules`: Test files.
67 |
68 | ## Main documentation
69 |
70 | [Back](../README.md)
71 |
--------------------------------------------------------------------------------
/.readme/mixed-rules.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.readme/rules.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # ESLint Plugin Regex Change Log
2 |
3 | ## 1.10.0 - August 2022
4 |
5 | * Closes #19, Allows to add additional RegEx flags.
6 | * Fixes bug `RangeError: Maximum call stack size exceeded`.
7 | * Replaces the recursive approach to a loop one.
8 | * Improves code.
9 | * Clones `$`.
10 | * Simplifies code.
11 | * Improves code performance.
12 | * Improves code coverage.
13 | * Adds JSDoc to improve Learnability and Maintainability.
14 | * Improves project configuration.
15 | * Renames `readme` folder to `.readme` in order to prevent it to be packed.
16 | * Removes `.npmignore` (It doesn't work when using `files`: https://github.com/npm/npm/issues/4479)
17 | * Updates CI configuration.
18 | * Updates Documentation.
19 |
20 | ## 1.9.1 - July 2022
21 |
22 | * Fixes duplicate entries bug that happens when adding custom regex rule with same name.
23 | * Message changed from `Error: "SomeRuleName" can not be used as eslint-plugin-regex rule name` to `Error: "SomeRuleName" already defined as eslint-plugin-regex rule name`.
24 | * Updates documentation.
25 | * Changes background color to white for images.
26 |
27 | ## 1.9.0 - April 2022
28 |
29 | * Closes #15, Allowing mixing different error level.
30 | * This bug required that the same rule can be defined with different error levels, that is not possible at the moment on eslint. Error level is not available at plugin level (as indicate by ESLint official documentation: "Keep in mind that the error level is not part of context.options, as the error level cannot be known or modified from inside a rule"). So I came up a different approach, which not only **allows to use "the same" rule with different error levels**, but also **allows Mixing custom set of regex rules**, this is useful for creating package/libraries/plugins with your own set of rules and sharing it with others.
31 | * Fixes #16, Unable to use both "ignore" and "inspect" inside "files" property.
32 | * Fixes an issue with remaining source calculation and minified files.
33 | * Removes `replacement.function === 'string'` validation, it's not necessary, eslint already validates json.
34 | * Improves project configuration.
35 | * Now uses [`any-eslint-parser`](https://www.npmjs.com/package/any-eslint-parser)
36 | * Improves documentation.
37 | * Mixing
38 | * Fixes spelling in tests.
39 |
40 | ## 1.8.0 - August 2021
41 |
42 | * Closes #11, Improves the specification of the number of the line of the found error for the final report, Thanks to Maxim-Mazurok for His collaboration.
43 |
44 | ## 1.7.0 - February 2021
45 |
46 | * Closes #6, Removes the requirement of `return` presence for replacement functions for invalid patterns.
47 |
48 | ## 1.6.0 - February 2021
49 |
50 | * Adds additional parameter `$` to replacement function for invalid patterns in order to allow smaller definitions.
51 |
52 | ## 1.5.0 - February 2021
53 |
54 | * Adds capturing groups to replacement functions for invalid patterns.
55 |
56 | ## 1.4.0 - February 2021
57 |
58 | * Adds replacements with functions for invalid patterns.
59 |
60 | ## 1.3.0 - January 2021
61 |
62 | * Adds replacements for invalid patterns.
63 | * Improves code coverage.
64 | * Improves documentation.
65 | * Improves project configuration.
66 |
67 | ## 1.2.1 - August 2020
68 |
69 | * Fixes #4 peerDependencies.
70 | * Updates README file.
71 |
72 | ## 1.2.0 - August 2020
73 |
74 | * Adds the specific line to the report for multiline regex (Big debt, Found some time, Done, Yes!).
75 | * Updates README file.
76 |
77 | ## 1.1.0 - April 2020
78 |
79 | * Adds handling for detailed patterns.
80 | * Updates README file.
81 | * Improves project configuration.
82 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Gonzalo Müller Bravo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ESLint rules using Regular Expressions
7 |
8 | [](https://eslint-plugin-regex.github.io/)
9 | [](https://www.npmjs.com/package/eslint-plugin-regex)
10 | [](https://www.npmjs.com/package/eslint-plugin-regex)
11 | [](https://www.npmjs.com/package/eslint-plugin-regex)
12 | [](LICENSE.txt)
13 | [](https://gmullerb.gitlab.io/eslint-plugin-regex/coverage/index.html)
14 | [](https://github.com/gmullerb/eslint-plugin-regex)
15 | [](https://gitlab.com/gmullerb/eslint-plugin-regex)
16 |
17 | __________________
18 |
19 | ## Quick Start
20 |
21 | 1 . Add dependencies:
22 |
23 | `package.json`:
24 |
25 | ```json
26 | "engines" : {
27 | "node" : ">=6.0.0"
28 | },
29 | "devDependencies": {
30 | "eslint": ">=4.0.0",
31 | "eslint-plugin-regex": "1.10.0",
32 | ```
33 |
34 | 2 . Configure eslint:
35 |
36 | Short configuration:
37 |
38 | `.eslintrc.json`:
39 |
40 | ```json
41 | {
42 | "plugins": [
43 | "regex"
44 | ],
45 | "rules": {
46 | "regex/invalid": [
47 | "error", [
48 | "invalidRegex1",
49 | "invalidRegexN"
50 | ]
51 | ],
52 | "regex/required": [
53 | "error", [
54 | "requiredRegex1",
55 | "requiredRegexN"
56 | ],
57 | "ignoreFilesRegex"
58 | ]
59 | }
60 | }
61 | ```
62 |
63 | Files will be checked for the absence of `invalidRegex1` and `invalidRegexN`, and for the presence of `requiredRegex1` and `requiredRegexN`, and files with name matching `ignoreFilesRegex` will not be checked.
64 |
65 | Detailed configuration:
66 |
67 | `.eslintrc.json`:
68 |
69 | ```json
70 | {
71 | "plugins": [
72 | "regex"
73 | ],
74 | "rules": {
75 | "regex/invalid": [
76 | "error", [{
77 | "regex": "invalidRegex1",
78 | "replacement": "newValue"
79 | }, {
80 | "id": "regexIdN",
81 | "message": "errorMessageN",
82 | "regex": "invalidRegexN",
83 | "files": {
84 | "ignore": "ignoreFilesRegexN"
85 | }
86 | }
87 | ]
88 | ],
89 | "regex/required": [
90 | "error", [{
91 | "id": "regexId1",
92 | "regex": "requiredRegex1",
93 | "message": "errorMessage1",
94 | "files": {
95 | "inspect": "inspectFilesRegex1"
96 | }
97 | }, {
98 | "regex": "requiredRegexN",
99 | "files": {
100 | "ignore": "ignoreFilesRegexA",
101 | "inspect": "inspectFilesRegexZ"
102 | }
103 | }
104 | ]
105 | ]
106 | }
107 | }
108 | ```
109 |
110 | Files will be checked for:
111 |
112 | * The absence of `invalidRegex1` but if found it will be replaced with `newValue`.
113 | * The absence of `invalidRegexN` only in files with name not matching `ignoreFilesRegexN`, but if found `errorMessageN` will be shown.
114 | * The presence of `requiredRegex1` only in files with name matching `inspectFilesRegex1`, but if not found `errorMessage1` will be shown.
115 | * The presence of `requiredRegexN` only in files with name matching `inspectFilesRegexA` and not matching `ignoreFilesRegexZ`.
116 |
117 | __________________
118 |
119 | ## Goals
120 |
121 | The idea is to allow the creation of different eslint rules based on Regular Expressions in order to have some "freedom" to create quick ESLint custom rules.
122 |
123 | ## Rules
124 |
125 | Name | Fixable | Description
126 | ----------------------------------------------------- | ------- | -----------
127 | [`regex/invalid`](docs/rules/invalid-regex-rule.md) | Yes | checks that specified patterns are not found
128 | [`regex/required`](docs/rules/required-regex-rule.md) | No | checks that specified patterns are found
129 |
130 | Each rule defines a set of patterns:
131 |
132 | 
133 |
134 | ### 📏 `regex/invalid`
135 |
136 | This rule checks that specified patterns are not found in files, i.e. **Invalid patterns**.
137 |
138 | ✏ Example of **incorrect** code for this rule:
139 |
140 | ```javascript
141 | /* eslint regex/invalid: ['error', ['"']] */
142 |
143 | const text = 'Hello "My Friend"'
144 | ```
145 |
146 | The error message will reflect the exact location, e.g.:
147 |
148 | ```bash
149 | /path/to/some.js
150 | 34:25 error Invalid regular expression /"/gm found regex/invalid
151 | ```
152 |
153 | ✏ Example of **correct** code for this rule:
154 |
155 | ```javascript
156 | /* eslint regex/invalid: ['error', ['"']] */
157 |
158 | const text = 'Hello \'My Friend\''
159 | ```
160 |
161 | ### 📏 `regex/required`
162 |
163 | This rule looks for specific patterns that must be present in each file, i.e. **Required patterns**.
164 |
165 | ✏ Example of **incorrect** code for this rule:
166 |
167 | ```javascript
168 | /* eslint regex/required: ["error", ["^// Copyright My Friend"]] */
169 |
170 | const text = 'Hello "My Friend"'
171 | ```
172 |
173 | The error message will point to the beginning of the file, e.g.:
174 |
175 | ```sh
176 | /path/to/some.js
177 | 1:1 error Required regular expression /^\/\/ Copyright My Friend/gm not found in file regex/required
178 | ```
179 |
180 | ✏ Example of **correct** code for this rule:
181 |
182 | ```javascript
183 | /* eslint regex/required: ["error", ["^// Copyright My Friend"]] */
184 |
185 | // Copyright My Friend
186 | const text = 'Hello "My Friend"'
187 | ```
188 |
189 | ### Options
190 |
191 | Both rule has two options:
192 |
193 | * **array** of patterns definitions to analyze. [REQUIRED]
194 | * Each pattern definition can be 'Short' or 'Detailed'.
195 | * a **string** representing the regular expression for ignoring files for all patterns. [OPTIONAL]
196 |
197 | ```json
198 | [
199 | "error",
200 | [
201 | "regex1",
202 | "regexN"
203 | ],
204 | "ignoreFilesRegex"
205 | ]
206 | ```
207 |
208 | #### The *string* representing the regular expression
209 |
210 | Remember, Slashes (`/`) are not required in the string that defines the regex,
211 |
212 | e.g. To get the following regex `/^(test|spec)$/`, define:
213 |
214 | * **`"^(test|spec)$"`**, when using `.eslintrc.js` or `.eslintrc.json`.
215 |
216 | e.g. To get the following regex `/\bhttp:/`, define:
217 |
218 | * **`"\bhttp:"`**, when using `.eslintrc.js`, or
219 | * **`"\\bhttp:"`**, when using `.eslintrc.json`. (backslash needs to be double in a json file)
220 |
221 | e.g. To get the following regex `/.*test\.js/`, define:
222 |
223 | * **`".*test\.js"`**, when using `.eslintrc.js`, or
224 | * **`".*test\\.js"`**, when using `.eslintrc.json`. (backslash needs to be double in a json file)
225 |
226 | #### Short pattern definition
227 |
228 | Each pattern is specified by just a **`string`** representing the regular expression, i.e. `"regex"`
229 |
230 | ```json
231 | {
232 | "regex/invalid": [
233 | "error",
234 | [
235 | "invalidRegex1",
236 | "invalidRegexN"
237 | ]
238 | ],
239 | "regex/required": [
240 | "error",
241 | [
242 | "requiredRegex1",
243 | "requiredRegexN"
244 | ]
245 | ]
246 | }
247 | ```
248 |
249 | #### Detailed pattern definition
250 |
251 | It is specified by an `object`, with the following fields:
252 |
253 | * `regex`: A **required** `string` for `regex/required` and `regex/invalid` representing the **Regular expression to look for**. [REQUIRED]
254 | * `flags`: A combination of flags, `i`, `s` and/or `u`, to be used by the Regular Expression. [OPTIONAL]
255 | * `replacement` for `regex/invalid` [1]: [OPTIONAL]
256 | * An optional `string` used to replace the **invalid** found pattern, or
257 | * An optional `object` that establish how the **invalid** found pattern will be replaced:
258 | * `function`: used to replace the **invalid** found pattern.
259 | * It will receive 3 parameters: `text`, `captured` and `$`, that can be used as desired.
260 | * It must return a `string` value, if not, return value will be ignored.
261 | * Its definition must be only the body of the function.
262 | * One must be defined, either the `string` or `function`.
263 | * `id`: An optional `string` representing the **Pattern Id**. [OPTIONAL]
264 | * `message`: An optional `string` specifying the **Message to be shown when an error happens** (invalid `regex` is found or required `regex` is not found). [OPTIONAL]
265 | * `files`: An optional `object` specifying which files to analyze: [OPTIONAL]
266 | * `ignore`: A `string` representing **Regular expression of the files to be ignored** when validating this specific pattern.
267 | * `inspect`: A `string` representing **Regular expression of the files to be inspected** when validating this specific pattern.
268 |
269 | ```json
270 | {
271 | "id": "regexId",
272 | "regex": "regex",
273 | "flags": "isu",
274 | "replacement": "replacementString",
275 | "message": "errorMessage",
276 | "files": {
277 | "ignore": "ignoreFilesRegex",
278 | "inspect": "inspectFilesRegex"
279 | }
280 | }
281 | ```
282 |
283 | > * `regex` is the **only** Required field.
284 | > * When `ignore` and `inspect` are present, `ignore` takes precedence.
285 | > * Global ignore file pattern, takes precedence over `files` patterns.
286 | >
287 | > [1] In order to fix issues `eslint` must be run with `--fix` option.
288 |
289 | Using `message` is pretty useful since it will give a better understanding to the developer when an error happens:
290 |
291 | e.g. Given the following definition:
292 |
293 | ```json
294 | {
295 | "regex": "someRegex",
296 | "message": "The Useful Error MessagE"
297 | }
298 | ```
299 |
300 | then shown error will be similar to:
301 |
302 | ```sh
303 | /path/to/some.js
304 | 1:1 error The Useful Error MessagE regex/required
305 | ```
306 |
307 | or
308 |
309 | ```bash
310 | /path/to/some.js
311 | 34:25 error The Useful Error MessagE regex/invalid
312 | ```
313 |
314 | instead of
315 |
316 | ```sh
317 | /path/to/some.js
318 | 1:1 error Required regular expression /someRegex/gm not found in file regex/required
319 | ```
320 |
321 | or
322 |
323 | ```bash
324 | /path/to/some.js
325 | 34:25 error Invalid regular expression /someRegex/gm found regex/invalid
326 | ```
327 |
328 | ##### Definition of the Function used to replace the *invalid* found pattern
329 |
330 | Definition of the function must be done as a `string` in 1 line, and the following rules apply:
331 |
332 | * It must return a `string` value, if not, return value will be ignored, i.e. it will silently fail.
333 | * Its definition must be **only the body of the function**.
334 | * For "simple" functions where the `return` is found at the beginning of the body of the function and the **exact** word *return* is not present, `return` can be omitted.
335 | * If the function has invalid Javascript code, the function will be ignored, i.e. it will silently fail.
336 |
337 | Function will receive 3 parameters, to be used as desired:
338 |
339 | * `text`: a `string` with the value of the invalid text found.
340 | * `captured`: an `array` of strings with the values of the captured groups for the regex.
341 | * `$`: an `array` of strings, with the value of the invalid text found plus the values of the captured groups for the regex, i.e.
342 | * `$[0]` = `text`: a `string` with the value of the invalid text found.
343 | * `$[1..]` = `captured`: an `array` of strings with the values of the captured groups for the regex.
344 | * `$[1]` = `captured[0]` and so on.
345 | * It allows smaller definitions.
346 |
347 | **Using parameter `text`**
348 |
349 | e.g.
350 |
351 | ```javascript
352 | function(text, captured, $) {
353 | return text.trim()
354 | }
355 | ```
356 |
357 | `"return text.trim()"` => only the body of the function + returns a `string` value based on `text`
358 |
359 | Having the following rule in `.eslintrc.json`:
360 |
361 | ```json
362 | {
363 | "id": "regexIdN",
364 | "regex": "\\serror\\w*\\s",
365 | "replacement": {
366 | "function": "return text.trim()"
367 | }
368 | }
369 | ```
370 |
371 | or using `$`:
372 |
373 | ```json
374 | {
375 | "id": "regexIdN",
376 | "regex": "\\serror\\w*\\s",
377 | "replacement": {
378 | "function": "return $[0].trim()"
379 | }
380 | }
381 | ```
382 |
383 | then, given:
384 |
385 | `example.js`
386 |
387 | ```javascript
388 | const exception = " error19 "
389 | ```
390 |
391 | when linting with fix, the result will be:
392 |
393 | ```javascript
394 | const exception = "error19"
395 | ```
396 |
397 | As the body of the function is "simple", i.e. the `return` is found at the beginning of the body of the function, and besides, the word *return* is not present, then the definition could be done as:
398 |
399 | ```json
400 | {
401 | "id": "regexIdN",
402 | "regex": "\\serror\\w*\\s",
403 | "replacement": {
404 | "function": "text.trim()"
405 | }
406 | }
407 | ```
408 |
409 | or
410 |
411 | ```json
412 | {
413 | "id": "regexIdN",
414 | "regex": "\\serror\\w*\\s",
415 | "replacement": {
416 | "function": "$[0].trim()"
417 | }
418 | }
419 | ```
420 |
421 | **Using parameter `captured`**
422 |
423 | e.g.
424 |
425 | `"return captured[0]"` => only the body of the function + returns a `string` value based on `captured`
426 |
427 | Having the following rule in `.eslintrc.json`:
428 |
429 | ```json
430 | {
431 | "id": "regexIdN",
432 | "regex": "\\serror(\\w*)\\s",
433 | "replacement": {
434 | "function": "return captured[0]"
435 | }
436 | }
437 | ```
438 |
439 | or using `$`:
440 |
441 | ```json
442 | {
443 | "id": "regexIdN",
444 | "regex": "\\serror(\\w*)\\s",
445 | "replacement": {
446 | "function": "return $[1]"
447 | }
448 | }
449 | ```
450 |
451 | then, given:
452 |
453 | `example.js`
454 |
455 | ```javascript
456 | const exception = " error19 "
457 | ```
458 |
459 | when linting with fix, the result will be:
460 |
461 | ```javascript
462 | const exception = "19"
463 | ```
464 |
465 | As the body of the function is "simple", i.e. the `return` is found at the beginning of the body of the function, and besides, the word *return* is not present, then the definition could be done as:
466 |
467 | ```json
468 | {
469 | "id": "regexIdN",
470 | "regex": "\\serror(\\w*)\\s",
471 | "replacement": {
472 | "function": "captured[0]"
473 | }
474 | }
475 | ```
476 |
477 | or
478 |
479 | ```json
480 | {
481 | "id": "regexIdN",
482 | "regex": "\\serror(\\w*)\\s",
483 | "replacement": {
484 | "function": "$[1]"
485 | }
486 | }
487 | ```
488 |
489 | **Using parameters `text` and `captured`**
490 |
491 | e.g.
492 |
493 | `"return text + ' = ' + captured[0] + ' + ' + captured[1] + ' = ' + (parseInt(captured[0]) + parseInt(captured[1]))"` => only the body of the function + returns a `string` value based on `text` and `captured`
494 |
495 | Having the following rule in `.eslintrc.json`:
496 |
497 | ```json
498 | {
499 | "id": "regexIdN",
500 | "regex": "(\\d+)\\+(\\d+)",
501 | "replacement": {
502 | "function": "return text + ' = ' + captured[0] + ' + ' + captured[1] + ' = ' + (parseInt(captured[0]) + parseInt(captured[1]))"
503 | }
504 | }
505 | ```
506 |
507 | or using `$`:
508 |
509 | ```json
510 | {
511 | "id": "regexIdN",
512 | "regex": "(\\d+)\\+(\\d+)",
513 | "replacement": {
514 | "function": "return $[0] + ' = ' + $[1] + ' + ' + $[2] + ' = ' + (parseInt($[1]) + parseInt($[2]))"
515 | }
516 | }
517 | ```
518 |
519 | or :
520 |
521 | ```json
522 | {
523 | "id": "regexIdN",
524 | "regex": "(\\d+)\\+(\\d+)",
525 | "replacement": {
526 | "function": "return text + ' = ' + $[1] + ' + ' + $[2] + ' = ' + (parseInt($[1]) + parseInt($[2]))"
527 | }
528 | }
529 | ```
530 |
531 | or :
532 |
533 | ```json
534 | {
535 | "id": "regexIdN",
536 | "regex": "(\\d+)\\+(\\d+)",
537 | "replacement": {
538 | "function": "return `${text} = ${captured[0]} + ${captured[1]} = ${parseInt($[1]) + parseInt($[2])}`"
539 | }
540 | }
541 | ```
542 |
543 | then, given:
544 |
545 | `example.js`
546 |
547 | ```javascript
548 | const sum = "4+5"
549 | ```
550 |
551 | when linting with fix, the result will be:
552 |
553 | ```javascript
554 | const sum = "4+5 = 4 + 5 = 9"
555 | ```
556 |
557 | As the body of the function is "simple", i.e. the `return` is found at the beginning of the body of the function, and besides, the word *return* is not present, then the definition could be done as:
558 |
559 | ```json
560 | {
561 | "id": "regexIdN",
562 | "regex": "(\\d+)\\+(\\d+)",
563 | "replacement": {
564 | "function": "text + ' = ' + $[1] + ' + ' + $[2] + ' = ' + (parseInt($[1]) + parseInt($[2]))"
565 | }
566 | }
567 | ```
568 |
569 | or :
570 |
571 | ```json
572 | {
573 | "id": "regexIdN",
574 | "regex": "(\\d+)\\+(\\d+)",
575 | "replacement": {
576 | "function": "`${text} = ${captured[0]} + ${captured[1]} = ${parseInt($[1]) + parseInt($[2])}`"
577 | }
578 | }
579 | ```
580 |
581 | **When `return` keyword is required**
582 |
583 | e.g.
584 |
585 | e.g. `const result = text === 'superb' ? 'Superb' : text; return result` => only the body of the function + returns a `string` value based on `text`.
586 |
587 | Since the `return` is not found at the beginning of the body of the function, `return` cannot be omitted, then rule definition will be as usual:
588 |
589 | ```json
590 | {
591 | "id": "regexIdN",
592 | "regex": "\\w+",
593 | "replacement": {
594 | "function": "const result = text === 'superb' ? 'Superb' : text; return result"
595 | }
596 | }
597 | ```
598 |
599 | > Some cases may use Comma operator, e.g. `"function": "result = text === 'superb' ? 'Superb' : text, result"`
600 |
601 | e.g. `return text === 'return' ? 'Return' : text` => only the body of the function + returns a `string` value based on `text`.
602 |
603 | Since the *exact* word *return* is present, this will **required** `return`, then rule definition will be as usual:
604 |
605 | ```json
606 | {
607 | "id": "regexIdN",
608 | "regex": "\\w+",
609 | "replacement": {
610 | "function": "return text === 'return' ? 'Return' : text"
611 | }
612 | }
613 | ```
614 |
615 | Following case does not required `return`:
616 |
617 | e.g. `return text === 'Return' ? 'RETURN' : text` => only the body of the function + returns a `string` value based on `text`.
618 |
619 | Since the **exact** word *return* is not present, this will allow the following rule definition to be:
620 |
621 | ```json
622 | {
623 | "id": "regexIdN",
624 | "regex": "\\w+",
625 | "replacement": {
626 | "function": "text === 'Return' ? 'RETURN' : text"
627 | }
628 | }
629 | ```
630 |
631 | ###### Debugging of the Replacement Function for *invalid* found pattern
632 |
633 | * It is possible to add `console` statements to print some information in the Replacement Function.
634 |
635 | ```json
636 | {
637 | "regex": "\\serror(\\w*)\\s",
638 | "replacement": {
639 | "function": "const extract = captured[0]; console.log(extract); return extract"
640 | }
641 | }
642 | ```
643 |
644 | ##### RegExp Flags
645 |
646 | The following flags can be add to the regex:
647 |
648 | * `i`: For case insensitive search.
649 | * `s`: To allow `.` to match newline characters.
650 | * `u`: To treat the regex as a sequence of unicode code points.
651 |
652 | To define the flags to be used, employ the field `flags` in the detailed pattern:
653 |
654 | * A combination of flags can be used, e.g. `"is"`.
655 | * Order of flags is irrelevant, e.g. `"si"`.
656 | * It's case insensitive, e.g. `"iS"`, `"Is"` and `"IS"` are the same.
657 | * Invalid flags will be reported as an error by eslint.
658 |
659 | > By default, `"gm"` is always added by the engine (since It's required).
660 |
661 | e.g.
662 |
663 | Having the following detailed pattern:
664 |
665 | ```json
666 | {
667 | "regex": "invalid",
668 | "flags": "i"
669 | }
670 | ```
671 |
672 | `Invalid`, `inValid`, `INvalid` or `INVALID` will match.
673 |
674 | ### String to Regular expression conversion
675 |
676 | Internally, each string from the array will be converted into a Regular Expression with `global` and `multiline` options, e.g.:
677 |
678 | `"someRegex"` will be transformed into `/someRegex/gm`
679 |
680 | > Remember that backslash needs to be double in strings of a json file, e.g. To get the following regex `/\bhttp:/` define the following string `"\\bhttp:"`.
681 |
682 | ### Empty Meta characters
683 |
684 | For some special cases when using meta characters that may result in an empty match, e.g. `^`, eslint-plugin-regex will report only the first case found, and after that case is fixed, the following will be report, if present.
685 |
686 | e.g.
687 |
688 | ```json
689 | {
690 | "regex": "^(?!(?:(feature|fix|docs|config|refactor|revert|test).*[\\.:]$)|(\\*\\s\\w.*\\.$)|$)"
691 | }
692 | ```
693 |
694 | `/path/to/some.js`:
695 |
696 | ```text
697 | config(ALL):
698 |
699 | * Use eslint-plugin-regex for commit message linting
700 | * Use eslint-plugin-regex for commit message linting
701 | ```
702 |
703 | When linting, `eslint-plugin-regex` will only report the first case:
704 |
705 | ```bash
706 | /path/to/some.js
707 | 3:1 error Invalid regular expression /^(?!(?:(feature|fix|docs|config|refactor|revert|test).*[\\.:]$)|(\\*\\s\\w.*\\.$)|$)/gm found regex/invalid
708 | ```
709 |
710 | 4:1 error will not be reported until 3:1 is fixed.
711 |
712 | > The issue is that having an empty match does not allow the regex engine to move forward.
713 |
714 | ### Error report
715 |
716 | The 'Short pattern definition' errors are reported with the following structure:
717 |
718 | Given `someRegex`, the following message will be shown on error:
719 |
720 | ```
721 | Invalid regular expression /someRegex/gm found
722 | ```
723 |
724 | or
725 |
726 | ```
727 | Required regular expression /someRegex/gm not found in file
728 | ```
729 |
730 | The 'Detailed pattern definition' errors are reported with the following rules:
731 |
732 | A . If `message` is present then that **exact message is reported**.
733 | B . If `id` is present then:
734 |
735 | Given `"id": "someRegexId"`, the following message will be shown on error:
736 |
737 | ```
738 | Invalid regular expression 'someRegexId' found
739 | ```
740 |
741 | or
742 |
743 | ```
744 | Required regular expression 'someRegexId' not found in file
745 | ```
746 |
747 | C . If neither `message` nor `id` is present then the 'Short pattern definition' error message is shown.
748 |
749 | > * `message` takes precedence over `id`.
750 | > * Although `id` is a quick solution (and useful when creating and testing a rule), using `message` will give more information to the team about the issue.
751 |
752 | ### Mixing
753 |
754 | #### Mixing pattern types
755 |
756 | It is possible to use both type of definitions, 'Short pattern definition' with 'Detailed pattern definition', in the array of patterns.
757 |
758 | `.eslintrc.json`:
759 |
760 | ```json
761 | {
762 | "plugins": [
763 | "regex"
764 | ],
765 | "rules": {
766 | "regex/invalid": [
767 | "error", [
768 | "invalidRegex1",
769 | "invalidRegex2",
770 | {
771 | "regex": "invalidRegex3",
772 | "message": "errorMessage1",
773 | "files": {
774 | "inspect": "inspectFilesRegex1"
775 | }
776 | },
777 | {
778 | "id": "regexIdN",
779 | "regex": "invalidRegexN",
780 | "files": {
781 | "ignore": "ignoreFilesRegexN"
782 | }
783 | }
784 | ]
785 | ]
786 | }
787 | }
788 | ```
789 |
790 | * `invalidRegex1` and `invalidRegex2` are 'Short pattern definition'.
791 | * `invalidRegex3` and `invalidRegexN` are 'Detailed pattern definition'.
792 |
793 | #### Mixing rules
794 |
795 | ##### Mixing error levels
796 |
797 | Rules names have synonyms:
798 |
799 | `regex/invalid` = `regex/invalid-warn` = `regex/invalid-error` = `regex/another-invalid` = `regex/other-invalid`.
800 |
801 | `regex/required` = `regex/required-warn` = `regex/required-error` = `regex/another-required` = `regex/other-required`.
802 |
803 | * Synonyms are just that, synonyms, do not imply any level of error, but some are provided to increase readability, e.g. `regex/invalid-warn` does not imply `warn` level.
804 | * This will allow to mix different error levels.
805 |
806 | It is possible to set different error level: `error`, `warn` and `off`. For this use a synonym for the regex rule name:
807 |
808 | `.eslintrc.json`:
809 |
810 | ```json
811 | {
812 | "plugins": [
813 | "regex"
814 | ],
815 | "rules": {
816 | "regex/invalid": [
817 | "error",
818 | [
819 | "invalidRegex1",
820 | "invalidRegexN"
821 | ]
822 | ],
823 | "regex/required": [
824 | "error",
825 | [
826 | "requiredRegex1",
827 | "requiredRegexN"
828 | ]
829 | ],
830 | "regex/invalid-error": [
831 | "error", [
832 | "invalidRegexA1",
833 | "invalidRegexA2",
834 | {
835 | "regex": "invalidRegexA3",
836 | "message": "errorMessage1",
837 | "files": {
838 | "inspect": "inspectFilesRegexA1"
839 | }
840 | },
841 | {
842 | "id": "regexIdN",
843 | "regex": "invalidRegexN",
844 | "files": {
845 | "ignore": "ignoreFilesRegexAN"
846 | }
847 | }
848 | ],
849 | ],
850 | "regex/invalid-warn": [
851 | "warn", [
852 | "invalidRegexB1",
853 | "invalidRegexB2",
854 | {
855 | "regex": "invalidRegexB3",
856 | "message": "errorMessage1",
857 | "files": {
858 | "inspect": "inspectFilesRegex1"
859 | }
860 | },
861 | {
862 | "id": "regexIdN",
863 | "regex": "invalidRegexBN",
864 | "files": {
865 | "ignore": "ignoreFilesRegexN"
866 | }
867 | }
868 | ],
869 | ],
870 | "regex/other-invalid": [
871 | "off", [
872 | "invalidRegexC1",
873 | "invalidRegexC2",
874 | {
875 | "regex": "invalidRegexB3",
876 | "message": "errorMessage1",
877 | "files": {
878 | "inspect": "inspectFilesRegexC1"
879 | }
880 | },
881 | {
882 | "id": "regexIdN",
883 | "regex": "invalidRegexBN",
884 | "files": {
885 | "ignore": "ignoreFilesRegexCN"
886 | }
887 | }
888 | ],
889 | ],
890 | "regex/required-warn": [
891 | "warn",
892 | [
893 | "requiredRegexA1",
894 | "requiredRegexAN"
895 | ]
896 | ]
897 | }
898 | }
899 | ```
900 |
901 | * Rules with invalid patterns and `error` level: `regex/invalid` and `regex/invalid-error`.
902 | * Rules with invalid patterns and `off` level: `regex/other-invalid`.
903 | * Rules with required patterns and `error` level: `regex/required`.
904 | * Rules with required patterns and `warn` level: `regex/required-warn`.
905 |
906 | ##### Custom set of regex rules
907 |
908 | Creating and Using a Custom Set of regex rules **requires using `js` files**.
909 |
910 | ###### Named Regex Rules approach
911 |
912 | A regex rule can be named with a custom name. The Rule name can be anything that **includes `invalid`, `disuse`, `avoid`, `required` or `use`**, ignoring letter case, and with the restrictions of predefined names (`invalid`, `disuse`, `avoid`, `invalid-warn`, `invalid-error`, `another-invalid`, `other-invalid`, `required`, `use`, `required-warn`, `required-error`, `another-required` and `other-required`).
913 |
914 | * `regex/*invalid*`, `regex/*disuse*` or `regex/*avoid*` for invalid patterns.
915 | * `regex/*required*` or `regex/*use*` for required patterns.
916 |
917 | 
918 |
919 | > In the name `invalid`, `disuse` and `avoid` will take precedence over `required` and `use`, e.g. If custom regex rule name has both `avoid` and `use` in the name, then the respective regex patterns will be consider invalid patterns.
920 |
921 | **`addRegexRuleName` must be used to add the custom regex rule name to the set of `eslint-plugin-regex` rules.**
922 |
923 | ```javascript
924 | const { addRegexRuleName } = require('eslint-plugin-regex')
925 |
926 | addRegexRuleName('*invalid*')
927 | addRegexRuleName('*required*')
928 | ```
929 |
930 | * If the custom regex rule name is already defined, then an error will be shown:
931 |
932 | ```sh
933 | Error: Cannot read config file: /path/to/.eslintrc.js
934 | Error: "SomeRuleName" already defined as eslint-plugin-regex rule name
935 | ```
936 |
937 | ***Local Custom Regex rules***
938 |
939 | Create a local `.eslintrc.js`:
940 |
941 | 1 . Add rule name using `addRegexRuleName`.
942 | 2 . Define `eslint-plugin-regex` custom regex rule.
943 |
944 | ```javascript
945 | const { addRegexRuleName } = require('eslint-plugin-regex')
946 |
947 | addRegexRuleName('invalid-custom-890')
948 |
949 | module.exports = {
950 | plugins: [ 'regex' ],
951 | rules: {
952 | 'regex/invalid-custom-890': [
953 | 'error', [
954 | {
955 | regex: 'invalidRegexBN',
956 | files: {
957 | ignore: 'ignoreFilesRegexCN'
958 | }
959 | }
960 | ]
961 | ]
962 | }
963 | }
964 | ```
965 |
966 | ***Custom Regex rules package***
967 |
968 | Create a custom ESLint package and add the custom regex rules with a **"unique"** name for each regex rule defined in the package, so it can be use with other package of regex rules or local regex rules.
969 |
970 | Custom package `index.js`:
971 |
972 | ```javascript
973 | const { addRegexRuleName } = require('eslint-plugin-regex')
974 |
975 | addRegexRuleName('invalid-custom-890')
976 |
977 | module.exports = {
978 | configs: {
979 | 'someRegexRule1': {
980 | plugins: [ 'regex' ],
981 | rules: {
982 | 'regex/invalid-custom-890': [
983 | 'error', [
984 | {
985 | regex: 'invalidRegexBN',
986 | files: {
987 | ignore: 'ignoreFilesRegexCN'
988 | }
989 | }
990 | ]
991 | ]
992 | }
993 | }
994 | }
995 | }
996 | ```
997 |
998 | * This custom package defines 1 rule named `regex/invalid-custom-890` with only 1 invalid pattern with `error` as a default error level.
999 |
1000 | > An online example can be checked at [`eslint-plugin-base-style-config`](https://github.com/gmullerb/base-style-config/tree/master/js#regex-rules).
1001 | > For more information on how to create a custom ESLint package check [ESLint official documentation: Working with Plugins](https://eslint.org/docs/developer-guide/working-with-plugins)
1002 |
1003 | then use it,
1004 |
1005 | Some project `.eslintrc.json`:
1006 |
1007 | ```json
1008 | { "extends": [ "plugin:the-eslint-plugin/someRegexRule1",
1009 | ```
1010 |
1011 | to change the default error level set by the package:
1012 |
1013 | ```json
1014 | {
1015 | "extends": [ "plugin:the-eslint-plugin/someRegexRule1" ],
1016 | "rules": {
1017 | "regex/invalid-custom-890": "warn"
1018 |
1019 | ```
1020 |
1021 | mixing with other regex rules:
1022 |
1023 | ```json
1024 | {
1025 | "extends": [ "plugin:the-eslint-plugin/someRegexRule1" ],
1026 | "rules": {
1027 | "regex/invalid-custom-890": "warn",
1028 | "regex/required": [
1029 | "error",
1030 | [
1031 | "requiredRegex1",
1032 | "requiredRegexN"
1033 | ]
1034 | ],
1035 | "regex/invalid-error": [
1036 | "error", [
1037 | "invalidRegexA1",
1038 | "invalidRegexA2",
1039 | {
1040 | "regex": "invalidRegexA3",
1041 | "message": "errorMessage1",
1042 | "files": {
1043 | "inspect": "inspectFilesRegexA1"
1044 | }
1045 | },
1046 | {
1047 | "id": "regexIdN",
1048 | "regex": "invalidRegexN",
1049 | "files": {
1050 | "ignore": "ignoreFilesRegexAN"
1051 | }
1052 | }
1053 | ],
1054 | ],
1055 | ```
1056 |
1057 | ***Advantages***
1058 |
1059 | * Using Named Regex rule name will allow to have a **set of different regex rule**:
1060 | * Each rule with totally different settings.
1061 | * Allow to mix different regular expressions.
1062 | * Allow to mix different error levels.
1063 | * etc.
1064 | * Easily create custom regex rules package.
1065 | * When using Named Regex Rules, shown **errors will be even more specific**, e.g.:
1066 |
1067 | ```javascript
1068 | const { addRegexRuleName } = require('eslint-plugin-regex')
1069 |
1070 | addRegexRuleName('required-custom-896')
1071 | ```
1072 |
1073 | then, if an error happens, the output will be something similar to:
1074 |
1075 | ```sh
1076 | /path/to/some.js
1077 | 1:1 error Required regular expression /requiredRegex/gm not found in file regex/required-custom-896
1078 | ```
1079 |
1080 | instead of
1081 |
1082 | ```sh
1083 | /path/to/some.js
1084 | 1:1 error Required regular expression /requiredRegex/gm not found in file regex/required
1085 | ```
1086 |
1087 | ###### Import/Export approach
1088 |
1089 | Create a custom npm package using either with `json` or `js` files and add the custom regex rules.
1090 |
1091 | Custom package `index.js`:
1092 |
1093 | with complete rule definition:
1094 |
1095 | ```javascript
1096 | module.exports = {
1097 | regex: 'invalidRegexBN',
1098 | files: {
1099 | ignore: 'ignoreFilesRegexCN'
1100 | }
1101 | }
1102 | ```
1103 |
1104 | or
1105 |
1106 | ```javascript
1107 | module.exports = {
1108 | regex: 'invalidRegexBN',
1109 | }
1110 | ```
1111 |
1112 | or with only regex definition:
1113 |
1114 | ```javascript
1115 | module.exports = 'invalidRegexBN'
1116 | ```
1117 |
1118 | or with multiple complete rule definition:
1119 |
1120 | ```javascript
1121 | module.exports = {
1122 | ruleName1: {
1123 | regex: 'invalidRegex1',
1124 | files: {
1125 | ignore: 'ignoreFilesRegex1'
1126 | }
1127 | },
1128 | ruleNameN: {
1129 | regex: 'invalidRegexN',
1130 | files: {
1131 | ignore: 'ignoreFilesRegexN'
1132 | }
1133 | }
1134 | }
1135 | ```
1136 |
1137 | or
1138 |
1139 | ```javascript
1140 | module.exports = {
1141 | ruleName1: {
1142 | regex: 'invalidRegex1',
1143 | },
1144 | ruleNameN: {
1145 | regex: 'invalidRegexN',
1146 | }
1147 | }
1148 | ```
1149 |
1150 | or with multiple only regex definition:
1151 |
1152 | ```javascript
1153 | module.exports = {
1154 | ruleName1: 'invalidRegex1',
1155 | ruleNameN: 'invalidRegexN'
1156 | }
1157 | ```
1158 |
1159 | or using `json` files:
1160 |
1161 | ```json
1162 | {
1163 | "regex": "invalidRegexBN",
1164 | "files": {
1165 | "ignore": "ignoreFilesRegexCN"
1166 | }
1167 | }
1168 | ```
1169 |
1170 | or
1171 |
1172 | ```json
1173 | {
1174 | "regex": "invalidRegexBN",
1175 | }
1176 | ```
1177 |
1178 | or
1179 |
1180 | ```json
1181 | {
1182 | "ruleName1": {
1183 | "regex": "invalidRegex1",
1184 | "files": {
1185 | "ignore": "ignoreFilesRegex1"
1186 | }
1187 | },
1188 | "ruleNameN": {
1189 | "regex": "invalidRegexN",
1190 | "files": {
1191 | "ignore": "ignoreFilesRegexN"
1192 | }
1193 | }
1194 | }
1195 | ```
1196 |
1197 | or
1198 |
1199 | ```json
1200 | {
1201 | "ruleName1": {
1202 | "regex": "invalidRegex1",
1203 | },
1204 | "ruleNameN": {
1205 | "regex": "invalidRegexN",
1206 | }
1207 | }
1208 | ```
1209 |
1210 | or
1211 |
1212 | ```json
1213 | {
1214 | "ruleName1": "invalidRegex1",
1215 | "ruleNameN": "invalidRegexN"
1216 | }
1217 | ```
1218 |
1219 | > Different approaches can be defined, these are only a glance.
1220 | > For more information on how to create a custom npm package check [Contributing packages to the registry](https://docs.npmjs.com/packages-and-modules/contributing-packages-to-the-registry)
1221 |
1222 | Then use the custom package:
1223 |
1224 | Some project `.eslintrc.js`:
1225 |
1226 | ```javascript
1227 | import * as SomeESLintSetOfRegexRulesPackage1 from 'the-custom-package1'
1228 | import * as SomeESLintSetOfRegexRulesPackage2 from 'the-custom-package2'
1229 |
1230 | module.exports = {
1231 | plugins: ["regex"],
1232 | rules: {
1233 | "regex/invalid": [
1234 | 'error', [
1235 | SomeESLintSetOfRegexRulesPackage1.ruleName1,
1236 | SomeESLintSetOfRegexRulesPackage1.ruleNameN,
1237 | SomeESLintSetOfRegexRulesPackage2.ruleName1,
1238 | SomeESLintSetOfRegexRulesPackage2.ruleNameN
1239 | ]
1240 | ],
1241 | ```
1242 |
1243 | or using synonyms to mix error levels:
1244 |
1245 | ```javascript
1246 | import * as SomeESLintSetOfRegexRulesPackage1 from 'the-custom-package1'
1247 | import * as SomeESLintSetOfRegexRulesPackage2 from 'the-custom-package2'
1248 |
1249 | module.exports = {
1250 | plugins: ["regex"],
1251 | rules: {
1252 | 'regex/invalid-error': [
1253 | 'error', [
1254 | SomeESLintSetOfRegexRulesPackage1.ruleNameN,
1255 | SomeESLintSetOfRegexRulesPackage2.ruleName1
1256 | ]
1257 | ],
1258 | 'regex/invalid-warn': [
1259 | 'warn', [
1260 | SomeESLintSetOfRegexRulesPackage1.ruleName1,
1261 | SomeESLintSetOfRegexRulesPackage2.ruleNameN
1262 | ]
1263 | ]
1264 | ```
1265 |
1266 | ### `regex/invalid` vs `regex/required`
1267 |
1268 | Both rule were design with *binary* approach:
1269 |
1270 | * `regex/invalid`: pattern **is not present** => any presence of the *specific* pattern in a file is invalid.
1271 | * `regex/required`: pattern **is present** => only 1 presence of the *specific* pattern in a file is required.
1272 |
1273 | Array of patterns represent different logical operation for each rule:
1274 |
1275 | * `regex/invalid`: **OR** => the presence in a file of *any* of the patterns defined in the *array* is invalid.
1276 | * `regex/required`: **AND** => the presence in file of *all* of the patterns defined in the *array* is required.
1277 |
1278 | ### Examples
1279 |
1280 | Check:
1281 |
1282 | * [invalid-regex Basic rule tests](tests/lib/rules/invalid-regex-rule.e2e-test.js)
1283 | * [invalid-regex Detailed rule tests](tests/lib/rules/invalid-regex-detailed-rule.e2e-test.js)
1284 | * [required-regex Basic rule tests](tests/lib/rules/required-regex-rule.e2e-test.js)
1285 | * [required-regex Detailed rule tests](tests/lib/rules/required-regex-detailed-rule.e2e-test.js)
1286 | * [The set of Regex Rules of `eslint-plugin-base-style-config`](https://github.com/gmullerb/base-style-config/tree/master/js#regex-rules)
1287 |
1288 | __________________
1289 |
1290 | ## Prerequisites
1291 |
1292 | * [`"eslint": ">=4.0.0"`](https://www.npmjs.com/package/eslint).
1293 |
1294 | __________________
1295 |
1296 | ## Evolution
1297 |
1298 | [`CHANGELOG.md`](CHANGELOG.md): contains the information about changes in each version, chronologically ordered ([Keep a Changelog](http://keepachangelog.com)).
1299 |
1300 | ## Extending/Developing
1301 |
1302 | [Developing](js/.readme/developing.md)
1303 |
1304 | ## Contributing
1305 |
1306 | * **Use it**.
1307 | * **Share it**.
1308 | * [Give it a Star](https://github.com/gmullerb/eslint-plugin-regex).
1309 | * [Propose changes or improvements](https://github.com/gmullerb/eslint-plugin-regex/issues).
1310 | * [Report bugs](https://github.com/gmullerb/eslint-plugin-regex/issues).
1311 |
1312 | ## License
1313 |
1314 | [MIT License](LICENSE.txt)
1315 |
1316 | __________________
1317 |
1318 | ## Remember
1319 |
1320 | * Use code style verification tools => Encourages Best Practices, Efficiency, Readability and Learnability.
1321 | * Code Review everything => Encourages Functional suitability, Performance Efficiency and Teamwork.
1322 | * If viable, Start testing early => Encourages Reliability and Maintainability.
1323 |
1324 | ## Additional words
1325 |
1326 | Don't forget:
1327 |
1328 | * **Love what you do**.
1329 | * **Learn everyday**.
1330 | * **Learn yourself**.
1331 | * **Share your knowledge**.
1332 | * **Think different!**.
1333 | * **Learn from the past, dream on the future, live and enjoy the present to the max!**.
1334 | * **Enjoy and Value the Quest** (It's where you learn and grow).
1335 |
1336 | At life:
1337 |
1338 | * Let's act, not complain.
1339 | * Be flexible.
1340 |
1341 | At work:
1342 |
1343 | * Let's give solutions, not questions.
1344 | * Aim to simplicity not intellectualism.
1345 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | title: eslint-plugin-regex
--------------------------------------------------------------------------------
/dev_resources/mixed-rules.xml:
--------------------------------------------------------------------------------
1 | 7Zpdb5swFIZ/TS4nGdtAuGxp0+2ilaZUqrQ7DztgFXDmmCXdr59ZTIL5UDOahqpCqlQ4/gCfx8evj8kMhdnuTpJ1ci8oS2cQ0N0M3cwgdBCEs/IP0Je9xfeMIZacmkpHw5L/YcYIjLXglG2sikqIVPG1bYxEnrNIWTYipdja1VYitZ+6JjFrGZYRSdvWJ05VUr0dONq/Mh4n5skImoKMVHWNYZMQKrY1E7qdoVAKofZX2S5kaem7yi37doue0sN7SZarUxrcLe7BN+o+3ih/EfpB8P0R0S+ml98kLcx4zcuql8oBuhfta31zvU24Yss1icqSrcatbYnKUn3n6MuNkuL54CQ9vusVT9OnyjmlISFRUkh2Vza9mZsaoUiF1Pe5yJkxLc3jTf2y82emosR6UtVuBhFgczAv+zPDYVKxXa+fnIP39axlImNKvugqVQMY7JuYCYvR/nZ7pI98wzSpkXdcYyRmxsWHro9U9IUB8x+QYAuSZLEeH1xI9qvgktFlsVrxnR4XkIV2XBOhdoZqua3m7oapxnQlcmViEuLq3nRclpee5jpYrlIe59qWcUrLwmtiDJH2OpOm5YJkPC3dGoqMR/odlyTf6H/3S1OhjhQEgQ6ysyCFHraQem2kh4CuI8XvRRS1iK6J0n7KnRY7KYqcMmr8/bYQxF0haAxXefzvkdhtTArN4idgiHnNKHyVaH+Y1qaV450pbKuYNIwdpwMyuiRk3Af5YYI8EDKCyIYM8ciQ3Y8toFSozavqSRjW+7TzEHKRrZ5odPX0etWT59rIjXjCzySeYQjAucTT8xyLqDu2ePp96+rVh1xXV4QG1H37unqI0ndYV915QzzB2OI574P8Y4I8ELKPGuLpjC2ezpR+tpdbYIcirEJzPAV1Tk1A8WfS0HMmoD7y7F0RaEO9rIg6vSkonnLQwbGL7a0ScjswX3aF7U1C8ZSFDo7mwD5OQtUOeTzMUxraOA3yXFtFvfFV9MREFH0mET1nIgqDBtOOndGFRbQ3FUVTLjoYM2jsldDoItqbjKIpGx18lOs2Dgrx6CIaTCJqI2pkLbhjO3vpT6HgNBF9mES0myn27RQF+2OLKGwfAlXfySYRHRy6vm9hduHYIlotph2YJxEd/rXNPix00fuJaHnad/ix0r+y2i++0O1f
2 |
--------------------------------------------------------------------------------
/dev_resources/rules.xml:
--------------------------------------------------------------------------------
1 | 7Zhdb5swFIZ/DZeTDIYELlvaZLvIpCmVKu3OwydgFXDmOEu6X79DcyB8FLVr06WaKkUKfn1s4/fx8Wnq8LjYz41YZwstIXc8JvcOv3I8z+We51QfJu8PSlALqVGSgo7CUv0GEhmpWyVh0wm0WudWrbtiossSEtvRhDF61w1b6by76lqkMBCWiciH6q2SNqvfjh31z6DSjFYOSS9EHUrCJhNS71oSv3Z4bLS2h6diH0NeWVe7chg3G+ltXstAaZ8zYD5bsC8yuLmy01k8jaJvN1x+oll+iXxL26WXtff1/nEWtBobl7tMWViuRVL17JA2apktcmy5+LixRt81HuH+Llcqz29rbyohE0m2NTCvhl6FFBHrXBtsl7oEkpa0PMVXk9+BTbLOSvU4x+MMQhZW89F2wFjYj/rkNu7joQVdgDX3GFIPqA8enVcvjA7t3ZE+n1JM1iLvBiQKOnFpM/cRCz4Qmb+g5A0oGUhxg97MwM+tMiCx12zRsj48tMEODGsZ3ZNaNFe6tJSMnl+3aeKqv/JYYZZc5CotUSuUlFXnpSAhQb/B0MiZKFReGRrrQiX4jktRbvBrsaSANkwWRZhdp4EZhR2YnA1hNqnchum/FUs+YLkWFo0q3QE8o7elBEmGvy77/Meyj4SLMn1Y0g96pwJh/GDAYdJPwCeRjmdo61y5kxNBDtwu5OARyPxfQvbHIH/9gPxCyJz5Xcj1FXw2yMH7rp1S282ThVOAj3+hnYZQ4PUK5/TshXMyWjhViaL6r+pmHDN2qroZRFGXZXTuujkdu1Iv3uWVuhIyksHrr9QmQd/gSg0mvbrpn7tuhmOQv39AfiHkqdurm5O3q5vVD5Lm9+xDX+t/Avz6Dw==
2 |
--------------------------------------------------------------------------------
/docs/rules/invalid-regex-rule.md:
--------------------------------------------------------------------------------
1 | # Looks for Invalid regular expressions to be reported for each file
2 |
3 | ## Rule Details
4 |
5 | This rule looks for **Invalid regular expressions** to be reported for each file.
6 |
7 | Example of **incorrect** code for this rule:
8 |
9 | ```js
10 | /* eslint regex/invalid: ['error', ['"']] */
11 |
12 | const text = 'Hello "My Friend"'
13 | ```
14 |
15 | Example of **correct** code for this rule:
16 |
17 | ```js
18 | /* eslint regex/invalid: ['error', ['"']] */
19 |
20 | const text = 'Hello \'My Friend\''
21 | ```
22 |
23 | ## Options
24 |
25 | * **array** of patterns definitions to analyze. [REQUIRED]
26 | * Each pattern definition can be 'Short' or 'Detailed'.
27 | * a **string** representing the regular expression for ignoring files for all patterns. [OPTIONAL]
28 | * Slashes (`/`) are not required in the string, e.g. To get the following regex `/.*test\.js/` define the following string `".*test\.js"` when using `.eslintrc.js` or `".*test\\.js"` when using `.eslintrc.json` (backslash needs to de double in a json file).
29 |
30 | ### Short pattern definition
31 |
32 | It is specified by just a regular expression `string`, i.e. `"regex"`
33 |
34 | * Slashes (`/`) are not required in the string, e.g. To get the following regex `/\bhttp:/` define the following string `"\bhttp:"` when using `.eslintrc.js` or `"\\bhttp:"` when using `.eslintrc.json` (backslash needs to de double in a json file).
35 |
36 | `.eslintrc.json`:
37 |
38 | ```json
39 | {
40 | "plugins": [
41 | "regex"
42 | ],
43 | "rules": {
44 | "regex/invalid": [
45 | "error", [
46 | "invalidRegex1",
47 | "invalidRegexN"
48 | ],
49 | ".*test\\.js"
50 | ]
51 | }
52 | }
53 | ```
54 |
55 | ### Detailed pattern definition
56 |
57 | It is specified by an `object`, with the following fields:
58 |
59 | * `regex`: A **required** `string` for `regex/required` and `regex/invalid` representing the **Regular expression to look for**. [REQUIRED]
60 | * `flags`: A combination of flags, `i`, `s` and/or `u`, to be used by the Regular Expression. [OPTIONAL]
61 | * `replacement` for `regex/invalid` [1]: [OPTIONAL]
62 | * An optional `string` used to replace the **invalid** found pattern, or
63 | * An optional `object` that establish how the **invalid** found pattern will be replaced:
64 | * `function`: used to replace the **invalid** found pattern.
65 | * It will receive 3 parameters: `text`, `captured` and `$`, that can be used as desired.
66 | * It must return a `string` value, if not, return value will be ignored.
67 | * Its definition must be only the body of the function.
68 | * One must be defined, either the `string` or `function`.
69 | * `id`: An optional `string` representing the **Pattern Id**. [OPTIONAL]
70 | * `message`: An optional `string` specifying the **Message to be shown when an error happens** (invalid `regex` is found or required `regex` is not found). [OPTIONAL]
71 | * `files`: An optional `object` specifying which files to analyze: [OPTIONAL]
72 | * `ignore`: A `string` representing **Regular expression of the files to be ignored** when validating this specific pattern.
73 | * `inspect`: A `string` representing **Regular expression of the files to be inspected** when validating this specific pattern.
74 |
75 | ```json
76 | {
77 | "id": "regexId",
78 | "regex": "regex",
79 | "flags": "isu",
80 | "replacement": "replacementString",
81 | "message": "errorMessage",
82 | "files": {
83 | "ignore": "ignoreFilesRegex",
84 | "inspect": "inspectFilesRegex"
85 | }
86 | }
87 | ```
88 |
89 | > * `regex` is the only Required field. Slashes (`/`) are not required in the string, e.g. To get the following regex `/\bhttp:/`:
90 | > * when using `.eslintrc.js`, define the following string `"\bhttp:"`, or
91 | > * when using `.eslintrc.json`, define `"\\bhttp:"` (backslash needs to de double in a json file).
92 | > * When `ignore` and `inspect` are present, `ignore` takes precedence.
93 | > * Global ignore file pattern, takes precedence over `files` patterns.
94 |
95 | > [1] In order to fix issue eslint must be run with `--fix` option.
96 |
97 | `.eslintrc.json`:
98 |
99 | ```json
100 | {
101 | "plugins": [
102 | "regex"
103 | ],
104 | "rules": {
105 | "regex/invalid": [
106 | "error", [{
107 | "regex": "invalidRegex1",
108 | "message": "errorMessage1",
109 | "replacement": "newValue"
110 | }, {
111 | "id": "regexIdN",
112 | "regex": "invalidRegexN",
113 | "files": {
114 | "ignore": "ignoreFilesRegexN"
115 | }
116 | }
117 | ]
118 | ]
119 | }
120 | }
121 | ```
122 |
123 | #### Definition of the Function used to replace the invalid found pattern
124 |
125 | Definition of the function must be done as a `string` in 1 line, and the following rules apply:
126 |
127 | * It must return a `string` value, if not, return value will be ignored, i.e. it will silently fail.
128 | * Its definition must be **only the body of the function**.
129 | * For "simple" functions where the `return` is found at the beginning of the body of the function and the **exact** word *return* is not present, `return` can be omitted.
130 | * If the function has invalid Javascript code, the function will be ignored, i.e. it will silently fail.
131 |
132 | Function will receive 3 parameters, to be used as desired:
133 |
134 | * `text`: a `string` with the value of the invalid text found.
135 | * `captured`: an `array` of strings with the values of the captured groups for the regex.
136 | * `$`: an `array` of strings, with the value of the invalid text found plus the values of the captured groups for the regex.
137 | * `$[0]` = `text`: a `string` with the value of the invalid text found.
138 | * `$[1..]` = `captured`: an `array` of strings with the values of the captured groups for the regex.
139 | * `$[1]` = `captured[0]` and so on.
140 | * It allows smaller definitions.
141 |
142 | **e.g. Using parameter `text`**
143 |
144 | `"return text.trim()"` => only the body of the function + returns a `string` value based on `text`
145 |
146 | Having the following rule in `.eslintrc.json`:
147 |
148 | ```json
149 | {
150 | "id": "regexIdN",
151 | "regex": "\\serror\\w*\\s",
152 | "replacement": {
153 | "function": "return text.trim()"
154 | }
155 | }
156 | ```
157 |
158 | or using `$`:
159 |
160 | ```json
161 | {
162 | "id": "regexIdN",
163 | "regex": "\\serror\\w*\\s",
164 | "replacement": {
165 | "function": "return $[0].trim()"
166 | }
167 | }
168 | ```
169 |
170 | then, given:
171 |
172 | `example.js`
173 |
174 | ```js
175 | const exception = " error19 "
176 | ```
177 |
178 | when linting with fix, the result will be:
179 |
180 | ```js
181 | const exception = "error19"
182 | ```
183 |
184 | As the body of the function is "simple", i.e. the `return` is found at the beginning of the body of the function, and besides, the word *return* is not present, then the definition could be done as:
185 |
186 | ```json
187 | {
188 | "id": "regexIdN",
189 | "regex": "\\serror\\w*\\s",
190 | "replacement": {
191 | "function": "text.trim()"
192 | }
193 | }
194 | ```
195 |
196 | or
197 |
198 | ```json
199 | {
200 | "id": "regexIdN",
201 | "regex": "\\serror\\w*\\s",
202 | "replacement": {
203 | "function": "$[0].trim()"
204 | }
205 | }
206 | ```
207 |
208 | **e.g. Using parameter `captured`**
209 |
210 | `"return captured[0]"` => only the body of the function + returns a `string` value based on `captured`
211 |
212 | Having the following rule in `.eslintrc.json`:
213 |
214 | ```json
215 | {
216 | "id": "regexIdN",
217 | "regex": "\\serror(\\w*)\\s",
218 | "replacement": {
219 | "function": "return captured[0]"
220 | }
221 | }
222 | ```
223 |
224 | or using `$`:
225 |
226 | ```json
227 | {
228 | "id": "regexIdN",
229 | "regex": "\\serror(\\w*)\\s",
230 | "replacement": {
231 | "function": "return $[1]"
232 | }
233 | }
234 | ```
235 |
236 | then, given:
237 |
238 | `example.js`
239 |
240 | ```js
241 | const exception = " error19 "
242 | ```
243 |
244 | when linting with fix, the result will be:
245 |
246 | ```js
247 | const exception = "19"
248 | ```
249 |
250 | As the body of the function is "simple", i.e. the `return` is found at the beginning of the body of the function, and besides, the word *return* is not present, then the definition could be done as:
251 |
252 | ```json
253 | {
254 | "id": "regexIdN",
255 | "regex": "\\serror(\\w*)\\s",
256 | "replacement": {
257 | "function": "captured[0]"
258 | }
259 | }
260 | ```
261 |
262 | or
263 |
264 | ```json
265 | {
266 | "id": "regexIdN",
267 | "regex": "\\serror(\\w*)\\s",
268 | "replacement": {
269 | "function": "$[1]"
270 | }
271 | }
272 | ```
273 |
274 | **e.g. Using parameters `text` and `captured`**
275 |
276 | `"return text + ' = ' + captured[0] + ' + ' + captured[1] + ' = ' + (parseInt(captured[0]) + parseInt(captured[1]))"` => only the body of the function + returns a `string` value based on `text` and `captured`
277 |
278 | Having the following rule in `.eslintrc.json`:
279 |
280 | ```json
281 | {
282 | "id": "regexIdN",
283 | "regex": "(\\d+)\\+(\\d+)",
284 | "replacement": {
285 | "function": "return text + ' = ' + captured[0] + ' + ' + captured[1] + ' = ' + (parseInt(captured[0]) + parseInt(captured[1]))"
286 | }
287 | }
288 | ```
289 |
290 | or using `$`:
291 |
292 | ```json
293 | {
294 | "id": "regexIdN",
295 | "regex": "(\\d+)\\+(\\d+)",
296 | "replacement": {
297 | "function": "return $[0] + ' = ' + $[1] + ' + ' + $[2] + ' = ' + (parseInt($[1]) + parseInt($[2]))"
298 | }
299 | }
300 | ```
301 |
302 | or :
303 |
304 | ```json
305 | {
306 | "id": "regexIdN",
307 | "regex": "(\\d+)\\+(\\d+)",
308 | "replacement": {
309 | "function": "return text + ' = ' + $[1] + ' + ' + $[2] + ' = ' + (parseInt($[1]) + parseInt($[2]))"
310 | }
311 | }
312 | ```
313 |
314 | or :
315 |
316 | ```json
317 | {
318 | "id": "regexIdN",
319 | "regex": "(\\d+)\\+(\\d+)",
320 | "replacement": {
321 | "function": "return `${text} = ${captured[0]} + ${captured[1]} = ${parseInt($[1]) + parseInt($[2])}`"
322 | }
323 | }
324 | ```
325 |
326 | then, given:
327 |
328 | `example.js`
329 |
330 | ```js
331 | const sum = "4+5"
332 | ```
333 |
334 | when linting with fix, the result will be:
335 |
336 | ```js
337 | const sum = "4+5 = 4 + 5 = 9"
338 | ```
339 |
340 | As the body of the function is "simple", i.e. the `return` is found at the beginning of the body of the function, and besides, the word *return* is not present, then the definition could be done as:
341 |
342 | ```json
343 | {
344 | "id": "regexIdN",
345 | "regex": "(\\d+)\\+(\\d+)",
346 | "replacement": {
347 | "function": "text + ' = ' + $[1] + ' + ' + $[2] + ' = ' + (parseInt($[1]) + parseInt($[2]))"
348 | }
349 | }
350 | ```
351 |
352 | or :
353 |
354 | ```json
355 | {
356 | "id": "regexIdN",
357 | "regex": "(\\d+)\\+(\\d+)",
358 | "replacement": {
359 | "function": "`${text} = ${captured[0]} + ${captured[1]} = ${parseInt($[1]) + parseInt($[2])}`"
360 | }
361 | }
362 | ```
363 |
364 | **e.g. `return` required**
365 |
366 | e.g. `const result = text === 'superb' ? 'Superb' : text; return result` => only the body of the function + returns a `string` value based on `text`.
367 |
368 | Since the `return` is not found at the beginning of the body of the function, `return` cannot be omitted, then rule definition will be as usual:
369 |
370 | ```json
371 | {
372 | "id": "regexIdN",
373 | "regex": "\\w+",
374 | "replacement": {
375 | "function": "const result = text === 'superb' ? 'Superb' : text; return result"
376 | }
377 | }
378 | ```
379 |
380 | > Some cases may use Comma operator, e.g. `"function": "result = text === 'superb' ? 'Superb' : text, result"`
381 |
382 | e.g. `return text === 'return' ? 'Return' : text` => only the body of the function + returns a `string` value based on `text`.
383 |
384 | Since the *exact* word *return* is present, this will **required** `return`, then rule definition will be as usual:
385 |
386 | ```json
387 | {
388 | "id": "regexIdN",
389 | "regex": "\\w+",
390 | "replacement": {
391 | "function": "return text === 'return' ? 'Return' : text"
392 | }
393 | }
394 | ```
395 |
396 | Following case does not required `return`:
397 |
398 | e.g. `return text === 'Return' ? 'RETURN' : text` => only the body of the function + returns a `string` value based on `text`.
399 |
400 | Since the **exact** word *return* is not present, this will allow the following rule definition to be:
401 |
402 | ```json
403 | {
404 | "id": "regexIdN",
405 | "regex": "\\w+",
406 | "replacement": {
407 | "function": "text === 'Return' ? 'RETURN' : text"
408 | }
409 | }
410 | ```
411 |
412 | ##### Debugging of the Replacement Function for invalid found pattern
413 |
414 | * It is possible to add `console` statements to print some information in the Replacement Function.
415 |
416 | ```json
417 | {
418 | regex: '\\serror(\\w*)\\s',
419 | replacement: {
420 | function: 'const extract = captured[0]; console.log(extract); return extract'
421 | }
422 | }
423 | ```
424 |
425 | ### String to Regular expression conversion
426 |
427 | Internally, each string from the array will be converted into a Regular Expression with `global` and `multiline` options, e.g.:
428 |
429 | `"invalidRegex1"` will be transformed into `/invalidRegex1/gm`
430 |
431 | When the pattern is found, the error message will reflect the exact location, e.g.:
432 |
433 | ```bash
434 | 34:25 error Invalid regular expression /invalidRegex1/gm found regex/invalid
435 | ```
436 |
437 | ### Examples
438 |
439 | Check:
440 |
441 | * [invalid-regex Basic rule tests](tests/lib/rules/invalid-regex-rule.e2e-test.js)
442 | * [invalid-regex Detailed rule tests](tests/lib/rules/invalid-regex-detailed-rule.e2e-test.js)
443 | * [The set of Regex Rules of `eslint-plugin-base-style-config`](https://github.com/gmullerb/base-style-config/tree/master/js#regex-rules)
444 |
445 | ## Related Rules
446 |
447 | * [`regex/required`](docs/rules/required-regex-rule.md).
448 |
449 | ## More information
450 |
451 | * [`eslint-plugin-regex`](../README.md)
452 | * [For a set of Regex Rules examples check `eslint-plugin-base-style-config`](https://github.com/gmullerb/base-style-config/tree/master/js#regex-rules)
453 |
--------------------------------------------------------------------------------
/docs/rules/required-regex-rule.md:
--------------------------------------------------------------------------------
1 | # Looks for Required regular expressions that must be present in each file
2 |
3 | ## Rule Details
4 |
5 | This rule looks for **Required regular expressions** that must be present in each file.
6 |
7 | Example of **incorrect** code for this rule:
8 |
9 | ```js
10 | /* eslint regex/required: ["error", ["^// Copyright My Friend"]] */
11 |
12 | const text = 'Hello "My Friend"'
13 | ```
14 |
15 | Example of **correct** code for this rule:
16 |
17 | ```js
18 | /* eslint regex/required: ["error", ["^// Copyright My Friend"]] */
19 |
20 | // Copyright My Friend
21 | const text = 'Hello "My Friend"'
22 | ```
23 |
24 | ## Options
25 |
26 | * **array** of patterns definitions to analyze. [REQUIRED]
27 | * Each pattern definition can be 'Short' or 'Detailed'.
28 | * a **string** representing the regular expression for ignoring files for all patterns. [OPTIONAL]
29 | * Slashes (`/`) are not required in the string, e.g. To get the following regex `/.*test\.js/` define the following string `".*test\.js"` when using `.eslintrc.js` or `".*test\\.js"` when using `.eslintrc.json` (backslash needs to de double in a json file).
30 |
31 | ### Short pattern definition
32 |
33 | It is specified by just a regular expression `string`, i.e. `"regex"`
34 |
35 | * Slashes (`/`) are not required in the string, e.g. To get the following regex `/\bhttp:/` define the following string `"\bhttp:"` when using `.eslintrc.js` or `"\\bhttp:"` when using `.eslintrc.json` (backslash needs to de double in a json file).
36 |
37 | `.eslintrc.json`:
38 |
39 | ```json
40 | {
41 | "plugins": [
42 | "regex"
43 | ],
44 | "rules": {
45 | "regex/required": [
46 | "error", [
47 | "requiredRegex1",
48 | "requiredRegexN"
49 | ],
50 | ".*test\\.js"
51 | ]
52 | }
53 | }
54 | ```
55 |
56 | ### Detailed pattern definition
57 |
58 | It is specified by an `object`, with the following fields:
59 |
60 | * `regex`: A **required** `string` for `regex/required` and `regex/invalid` representing the **Regular expression to look for**. [REQUIRED]
61 | * `flags`: A combination of flags, `i`, `s` and/or `u`, to be used by the Regular Expression. [OPTIONAL]
62 | * `id`: An optional `string` representing the **Pattern Id**. [OPTIONAL]
63 | * `message`: An optional `string` specifying the **Message to be shown when an error happens** (invalid `regex` is found or required `regex` is not found). [OPTIONAL]
64 | * `files`: An optional `object` specifying which files to analyze: [OPTIONAL]
65 | * `ignore`: A `string` representing **Regular expression of the files to be ignored** when validating this specific pattern.
66 | * `inspect`: A `string` representing **Regular expression of the files to be inspected** when validating this specific pattern.
67 |
68 | ```json
69 | {
70 | "id": "regexId",
71 | "regex": "regex",
72 | "flags": "isu",
73 | "message": "errorMessage",
74 | "files": {
75 | "ignore": "ignoreFilesRegex",
76 | "inspect": "inspectFilesRegex"
77 | }
78 | }
79 | ```
80 |
81 | > * `regex` is the only Required field. Slashes (`/`) are not required in the string, e.g. To get the following regex `/\bhttp:/`:
82 | > * when using `.eslintrc.js`, define the following string `"\bhttp:"`, or
83 | > * when using `.eslintrc.json`, define `"\\bhttp:"` (backslash needs to de double in a json file).
84 | > * When `ignore` and `inspect` are present, `ignore` takes precedence.
85 | > * Global ignore file pattern, takes precedence over `files` patterns.
86 |
87 | `.eslintrc.json`:
88 |
89 | ```json
90 | {
91 | "plugins": [
92 | "regex"
93 | ],
94 | "rules": {
95 | "regex/required": [
96 | "error", [{
97 | "id": "regexId1",
98 | "regex": "requiredRegex1",
99 | "files": {
100 | "inspect": "inspectFilesRegex1"
101 | }
102 | }, {
103 | "regex": "requiredRegexN",
104 | "message": "errorMessageN",
105 | "files": {
106 | "ignore": "ignoreFilesRegexN",
107 | "inspect": "inspectFilesRegexN"
108 | }
109 | }
110 | ]
111 | ]
112 | }
113 | }
114 | ```
115 |
116 | ### String to Regular expression conversion
117 |
118 | Internally, each string from the array will be converted into a Regular Expression with `global` and `multiline` options, e.g.:
119 |
120 | `"requiredRegex1"` will be transformed into `/requiredRegex1/gm`
121 |
122 | ### Examples
123 |
124 | Check:
125 |
126 | * [required-regex Basic rule tests](tests/lib/rules/required-regex-rule.e2e-test.js)
127 | * [required-regex Detailed rule tests](tests/lib/rules/required-regex-detailed-rule.e2e-test.js)
128 | * [The set of Regex Rules of `eslint-plugin-base-style-config`](https://github.com/gmullerb/base-style-config/tree/master/js#regex-rules)
129 |
130 | ## Related Rules
131 |
132 | * [`regex/invalid`](docs/rules/invalid-regex-rule.md).
133 |
134 | ## More information
135 |
136 | * [`eslint-plugin-regex`](../README.md)
137 | * [For a set of Regex Rules examples check `eslint-plugin-base-style-config`](https://github.com/gmullerb/base-style-config/tree/master/js#regex-rules)
138 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Gonzalo Müller Bravo.
2 | // Licensed under the MIT License (MIT), see LICENSE.txt
3 | /* eslint-disable global-require */
4 |
5 | const rulesNames = ['required', 'use', 'required-warn', 'required-error', 'another-required', 'other-required', 'invalid', 'disuse', 'avoid', 'invalid-warn', 'invalid-error', 'another-invalid', 'other-invalid']
6 |
7 | module.exports = {
8 | addRegexRuleName: (ruleName) => {
9 | if (/use|avoid|disuse|invalid|required/i.test(ruleName)) {
10 | const candidate = ruleName.toUpperCase()
11 | if (rulesNames.findIndex(ruleName => ruleName.toUpperCase() === candidate) !== -1) {
12 | throw new Error(`"${ruleName}" already defined as eslint-plugin-regex rule name`)
13 | }
14 | rulesNames.push(ruleName)
15 | }
16 | },
17 | rules: new Proxy({}, {
18 | ownKeys: () => rulesNames,
19 | getOwnPropertyDescriptor: () => ({ configurable: true, enumerable: true }),
20 | get(rules, ruleName) {
21 | return /avoid|disuse|invalid/i.test(ruleName)
22 | ? require('./rules/invalid-regex-rule.js')
23 | : /use|required/i.test(ruleName)
24 | ? require('./rules/required-regex-rule.js')
25 | : undefined
26 | }
27 | })
28 | }
29 |
--------------------------------------------------------------------------------
/lib/rules/common-fields-definitions.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2022 Gonzalo Müller Bravo.
2 | // Licensed under the MIT License (MIT), see LICENSE.txt
3 |
4 | module.exports = {
5 | REGEX_FLAGS_FIELD_DEFINITION: {
6 | title: 'Regular expression allowed flags',
7 | description: 'A combination of "i", "s" and "u" regular expression flags',
8 | type: 'string',
9 | pattern: '^[isuISU]{1,3}$'
10 | },
11 | FILES_FIELD_DEFINITION: {
12 | type: 'object',
13 | properties: {
14 | ignore: {
15 | title: 'Ignore file pattern',
16 | description: 'Regular expression of the files to be ignored when validating this specific pattern',
17 | type: 'string',
18 | minLength: 2
19 | },
20 | inspect: {
21 | title: 'Inspect file pattern',
22 | description: 'Regular expression of the files to be inspected when validating this specific pattern',
23 | type: 'string',
24 | minLength: 2
25 | }
26 | },
27 | minProperties: 1,
28 | maxProperties: 2
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib/rules/invalid-regex-rule.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Gonzalo Müller Bravo.
2 | // Licensed under the MIT License (MIT), see LICENSE.txt
3 | const { buildCreateFunction } = require('../utils/create-utils.js')
4 | const { formatReportMessage } = require('../utils/report-utils.js')
5 | const { shouldCheck } = require('../utils/check-utils.js')
6 |
7 | const { REGEX_FLAGS_FIELD_DEFINITION, FILES_FIELD_DEFINITION } = require('./common-fields-definitions.js')
8 |
9 | /**
10 | * @param {string} source Text that was checked.
11 | * @param {number} foundAt Start position where the regex was found.
12 | * @returns {{ line: number, column: number }} Location required to report the error.
13 | */
14 | function foundStartLocation(source, foundAt) {
15 | return Array.from(source.substring(0, foundAt))
16 | .reduce((result, char) => char !== '\n'
17 | ? { line: result.line, column: result.column + 1 }
18 | : { line: result.line + 1, column: 0 },
19 | { line: 1, column: 0 })
20 | }
21 |
22 | /**
23 | * @typedef {{ $: [], matchStart: number, nextChar: number }} MatchDetail
24 | */
25 |
26 | /**
27 | * @param {string} source Text to inspect
28 | * @param {{ regex: RegExp, id: string | undefined, message: string | undefined }} pattern Information of the pattern to be use to inspect.
29 | * @param {*} replace Replacement function
30 | * @param {Function} report Report function
31 | */
32 | function checkRegex(source, pattern, replace, report) {
33 | /**
34 | * @param {string} source Text to be checked.
35 | * @param {RegExp} regex Regular Expression use to check.
36 | * @returns {MatchDetail} Result from the check.
37 | */
38 | const checkRegexInSource = () => {
39 | const foundDetail = pattern.regex.exec(source)
40 | return !!foundDetail && {
41 | $: foundDetail,
42 | matchStart: foundDetail.index,
43 | nextChar: pattern.regex.lastIndex
44 | }
45 | }
46 | const message = formatReportMessage(
47 | pattern,
48 | from => `Invalid regular expression ${from} found`
49 | )
50 | pattern.regex.test('')
51 | let matchDetail = { matchStart: -1 }
52 | while(matchDetail.matchStart !== matchDetail.nextChar && (matchDetail = checkRegexInSource())) {
53 | report({
54 | loc: { start: foundStartLocation(source, matchDetail.matchStart) },
55 | message,
56 | fix: replace(0, matchDetail)
57 | })
58 | }
59 | }
60 |
61 | function buildReplacementFunction(replacement) {
62 | try {
63 | const replacementFunction = new Function('text', 'captured', '$', /\breturn\b/.test(replacement) ? replacement : `return ${replacement}`) // eslint-disable-line no-new-func
64 | return $ => {
65 | try {
66 | const replacement = replacementFunction($[0], $.slice(1), $)
67 | if (typeof replacement === 'string') {
68 | return replacement
69 | }
70 | }
71 | catch(e) {}
72 | return $[0]
73 | }
74 | }
75 | catch(e) {
76 | return null
77 | }
78 | }
79 |
80 | /**
81 | * @callback ReplacementFunction
82 | * @param {number} from Index of the source where the replacement start
83 | * @param {MatchDetail} matchDetail Information for replacement
84 | */
85 |
86 | /**
87 | * @param {string | { function: string } | undefined} replacement Data to create the replacement function.
88 | * @returns {ReplacementFunction} Replacement function
89 | */
90 | function createReplacement(replacement) {
91 | switch (typeof replacement) {
92 | case 'string':
93 | return (from, matchDetail) => fixer => fixer.replaceTextRange([from + matchDetail.matchStart, from + matchDetail.nextChar], replacement)
94 | case 'object':{
95 | const replacementFunction = buildReplacementFunction(replacement.function)
96 | if (typeof replacementFunction === 'function') {
97 | return (from, matchDetail) =>
98 | fixer => fixer.replaceTextRange(
99 | [from + matchDetail.matchStart, from + matchDetail.nextChar],
100 | replacementFunction(Array.from(matchDetail.$))
101 | )
102 | }
103 | }
104 | }
105 | return () => undefined
106 | }
107 |
108 | function checkPatterns(fileName, source, patterns, report) {
109 | patterns.forEach(pattern => shouldCheck(pattern.files, fileName) &&
110 | checkRegex(source, pattern, createReplacement(pattern.details.replacement), report))
111 | }
112 |
113 | module.exports = {
114 | meta: {
115 | type: 'suggestion',
116 | fixable: 'code',
117 | docs: {
118 | description: 'Invalid regular expressions to be reported',
119 | category: 'Stylistic Issues',
120 | url: 'https://eslint-plugin-regex.github.io/docs/rules/invalid-regex-rule.html'
121 | },
122 | schema: [{
123 | title: 'Invalid regular expressions',
124 | description: 'Invalid regular expressions settings',
125 | type: 'array',
126 | items: {
127 | oneOf: [{
128 | title: 'Invalid pattern',
129 | description: 'Invalid pattern to be reported',
130 | type: 'string',
131 | minLength: 1
132 | }, {
133 | title: 'Invalid detailed pattern',
134 | description: 'Invalid pattern to be looked with possible custom message, custom ignored file pattern and custom inspect file pattern',
135 | type: 'object',
136 | properties: {
137 | id: {
138 | title: 'Invalid pattern Id',
139 | description: 'Invalid pattern Id to be reported',
140 | type: 'string',
141 | minLength: 2
142 | },
143 | regex: {
144 | title: 'Invalid pattern',
145 | description: 'Invalid regular expression to look for',
146 | type: 'string',
147 | minLength: 1
148 | },
149 | flags: REGEX_FLAGS_FIELD_DEFINITION,
150 | replacement: {
151 | oneOf:[{
152 | title: 'Replacement',
153 | description: 'Replacement for invalid pattern',
154 | type: 'string'
155 | }, {
156 | title: 'Detailed replacement',
157 | description: 'Detailed replacements for invalid patterns',
158 | type: 'object',
159 | properties: {
160 | function: {
161 | title: 'Replacement function',
162 | description: 'Function used to replace the found pattern. It receives the found text and must return the replacement text',
163 | type: 'string',
164 | minLength: 1
165 | }
166 | },
167 | minProperties: 1,
168 | maxProperties: 1
169 | }]
170 | },
171 | message: {
172 | title: 'Invalid message',
173 | description: 'Message to be shown when Invalid pattern is found',
174 | type: 'string',
175 | minLength: 3
176 | },
177 | files: FILES_FIELD_DEFINITION
178 | },
179 | required: ['regex']
180 | }]
181 | },
182 | minItems: 1
183 | }, {
184 | title: 'Ignore file pattern',
185 | description: 'Regular expressions of the files to be ignored when validating all the defined patterns',
186 | type: 'string',
187 | minLength: 2
188 | }]
189 | },
190 | create: buildCreateFunction(checkPatterns)
191 | }
192 |
--------------------------------------------------------------------------------
/lib/rules/required-regex-rule.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Gonzalo Müller Bravo.
2 | // Licensed under the MIT License (MIT), see LICENSE.txt
3 | const { buildCreateFunction } = require('../utils/create-utils.js')
4 | const { formatReportMessage } = require('../utils/report-utils.js')
5 | const { shouldCheck } = require('../utils/check-utils.js')
6 |
7 | const { REGEX_FLAGS_FIELD_DEFINITION, FILES_FIELD_DEFINITION } = require('./common-fields-definitions.js')
8 |
9 | function checkPatterns(fileName, source, patterns, report, node) {
10 | patterns.forEach(pattern => {
11 | if (shouldCheck(pattern.files, fileName) && !pattern.regex.test(source)) {
12 | report({
13 | node,
14 | message: formatReportMessage(
15 | pattern,
16 | from => `Required regular expression ${from} not found in file`
17 | )
18 | })
19 | }
20 | })
21 | }
22 |
23 | module.exports = {
24 | meta: {
25 | type: 'suggestion',
26 | docs: {
27 | description: 'Required regular expressions to be looked',
28 | category: 'Stylistic Issues',
29 | url: 'https://eslint-plugin-regex.github.io/docs/rules/required-regex-rule.html'
30 | },
31 | schema: [{
32 | title: 'Required regular expressions',
33 | description: 'Required regular expressions settings',
34 | type: 'array',
35 | items: {
36 | oneOf: [{
37 | title: 'Required pattern',
38 | description: 'Required pattern to be looked',
39 | type: 'string',
40 | minLength: 1
41 | }, {
42 | title: 'Required detailed pattern',
43 | description: 'Required pattern to be looked with possible custom message, custom ignored file pattern and custom inspect file pattern',
44 | type: 'object',
45 | properties: {
46 | id: {
47 | title: 'Required pattern Id',
48 | description: 'Required pattern Id to be reported',
49 | type: 'string',
50 | minLength: 2
51 | },
52 | regex: {
53 | title: 'Required pattern',
54 | description: 'Required regular expression to be looked',
55 | type: 'string',
56 | minLength: 1
57 | },
58 | flags: REGEX_FLAGS_FIELD_DEFINITION,
59 | message: {
60 | title: 'Required message',
61 | description: 'Message to be shown when Required pattern is not found',
62 | type: 'string',
63 | minLength: 3
64 | },
65 | files: FILES_FIELD_DEFINITION
66 | },
67 | required: ['regex']
68 | }]
69 | },
70 | minItems: 1
71 | }, {
72 | title: 'Ignore file pattern',
73 | description: 'Regular expressions of the files to be ignored when validating all the defined patterns',
74 | type: 'string'
75 | }]
76 | },
77 | create: buildCreateFunction(checkPatterns)
78 | }
79 |
--------------------------------------------------------------------------------
/lib/utils/check-utils.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Gonzalo Müller Bravo.
2 | // Licensed under the MIT License (MIT), see LICENSE.txt
3 |
4 | /**
5 | * @param {{ignore: RegExp, inspect: RegExp} | false} [files] Patterns that indicate which files to inspect and which to ignore.
6 | * @param {string} fileName Name of the file.
7 | * @returns {boolean} true if the file should be checked.
8 | */
9 | module.exports.shouldCheck = function (files, fileName) {
10 | return !files || (!files.ignore.test(fileName) && files.inspect.test(fileName))
11 | }
12 |
--------------------------------------------------------------------------------
/lib/utils/create-utils.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Gonzalo Müller Bravo.
2 | // Licensed under the MIT License (MIT), see LICENSE.txt
3 | const { fromOptions } = require('../utils/options-utils.js')
4 |
5 | module.exports.buildCreateFunction = function (checkPatterns) {
6 | return (context) => {
7 | const options = fromOptions(context.options)
8 | return {
9 | Program: function (node) {
10 | const fileName = context.getFilename()
11 | if (!options.ignoreFilePattern.test(context.getFilename())) {
12 | checkPatterns(fileName, context.getSourceCode(node).getText(), options.patterns, context.report, node)
13 | }
14 | }
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/lib/utils/options-utils.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Gonzalo Müller Bravo.
2 | // Licensed under the MIT License (MIT), see LICENSE.txt
3 |
4 | function fitIgnoreFilePattern(pattern) {
5 | return new RegExp(pattern || '^$')
6 | }
7 |
8 | /**
9 | * @typedef {{ ignore: RegExp, inspect: RegExp }} FilesRegex
10 | */
11 |
12 | /**
13 | * @param {{ ignore: string, inspect: string } | undefined} filesPatterns Information to create Regular Expressions.
14 | * @returns {FilesRegex | false} Regular Expressions for filtering files.
15 | */
16 | function filesRegex(filesPatterns) {
17 | return !!filesPatterns && {
18 | ignore: fitIgnoreFilePattern(filesPatterns.ignore),
19 | inspect: new RegExp(filesPatterns.inspect || '^.*$')
20 | }
21 | }
22 |
23 | const VALID_FLAGS_REGEX = /[isu]/
24 |
25 | /**
26 | * @param {string | undefined} flagsSource required flags for regex.
27 | * @returns {string} flags for regex to be used for linting.
28 | */
29 | function buildRegExpFlags(flagsSource) {
30 | let flags = 'gm'
31 | if (flagsSource) {
32 | const E = flagsSource.length
33 | for(let e = 0;e !== E;e++) {
34 | const flag = flagsSource.charAt(e).toLowerCase()
35 | if(VALID_FLAGS_REGEX.test(flag) && flags.indexOf(flag) === -1) {
36 | flags += flag
37 | }
38 | }
39 | }
40 | return flags
41 | }
42 |
43 | /**
44 | * @param {string | {regex: string, flags: string}} regexSource source for pattern creation, obtained from rule configuration (e.g. .eslintrc.json).
45 | * @returns {{regex: RegExp, details: (Object | undefined), files: (FilesRegex | false)}} pattern to be used for linting.
46 | */
47 | function extractPattern(regexSource) {
48 | return typeof regexSource !== 'string'
49 | ? {
50 | regex: new RegExp(regexSource.regex, buildRegExpFlags(regexSource.flags)),
51 | details: regexSource,
52 | files: filesRegex(regexSource.files)
53 | }
54 | : {
55 | regex: new RegExp(regexSource, 'gm'),
56 | details: {}
57 | }
58 | }
59 |
60 | /**
61 | * @param {*[]} options settings coming from rule configuration (e.g. .eslintrc.json).
62 | * @returns {Object} internal settings required by eslint-plugin-regex to work properly.
63 | */
64 | module.exports.fromOptions = function (options) {
65 | return {
66 | ignoreFilePattern: fitIgnoreFilePattern(options[1]),
67 | patterns: options[0].map(extractPattern)
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/lib/utils/report-utils.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Gonzalo Müller Bravo.
2 | // Licensed under the MIT License (MIT), see LICENSE.txt
3 |
4 | /**
5 | * @param {{ regex: RegExp, id: string | undefined, message: string | undefined }} pattern Information of the pattern used to inspect.
6 | * @param {Function} createMsg This function will create the message for Invalid or Required pattern.
7 | * @returns {string} Message to report an error.
8 | */
9 | module.exports.formatReportMessage = function (pattern, createMsg) {
10 | return !!pattern.details.message
11 | ? pattern.details.message
12 | : !!pattern.details.id
13 | ? createMsg(`'${pattern.details.id}'`)
14 | : createMsg(`${pattern.regex.toString()}`)
15 | }
16 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "eslint-plugin-regex",
3 | "description": "ESLint rules using Regular Expression",
4 | "version": "1.10.0",
5 | "license": "MIT",
6 | "author": "Gonzalo Müller Bravo",
7 | "main": "lib/index.js",
8 | "files": [
9 | "lib"
10 | ],
11 | "homepage": "https://eslint-plugin-regex.github.io",
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/gmullerb/eslint-plugin-regex"
15 | },
16 | "bugs": {
17 | "url": "https://github.com/gmullerb/eslint-plugin-regex/issues"
18 | },
19 | "keywords": [
20 | "eslint-plugin-regex",
21 | "eslint",
22 | "regex-rule",
23 | "regex",
24 | "regexp",
25 | "eslint-regex",
26 | "eslint-rule",
27 | "eslint-regex-rule",
28 | "rule",
29 | "expression",
30 | "regular-expression",
31 | "eslint-regular-expression",
32 | "eslint-regular-expression-rule",
33 | "eslintrule",
34 | "regular-expression-rule",
35 | "eslintplugin",
36 | "eslint-plugin",
37 | "plugin",
38 | "coding style",
39 | "coding standards",
40 | "code checker",
41 | "code linter",
42 | "code quality",
43 | "code standards",
44 | "code style",
45 | "syntax",
46 | "lint",
47 | "linter",
48 | "jshint",
49 | "js-hint",
50 | "jslint",
51 | "js-lint",
52 | "js lint",
53 | "js quality",
54 | "js rules",
55 | "js rules",
56 | "js standards",
57 | "js style",
58 | "style guide",
59 | "style checker",
60 | "style linter",
61 | "standard",
62 | "standard style",
63 | "style"
64 | ],
65 | "scripts": {
66 | "lint": "npm run lint.common && npm run lint.source",
67 | "lint.common": "npm run title --title=\"Lint Common\" -s && eslint --config .eslintrc-any.json \"**/[\\.a-zA-Z]*.+(js|jsx|ts|tsx|json|yml|xml|sh|txt|md|svg|properties|gradle|java|cpp|c|html|css|groovy)\" \"**/.+(|gitignore|npmignore)\" --no-eslintrc --ignore-pattern \"build\" --ignore-pattern \"tests/lib/rules/case-*.*\"",
68 | "lint.source": "npm run title --title=\"Lint Source Code\" -s && eslint --color \"**/*.js\" --ignore-pattern \"tests/lib/rules/case-*.js\"",
69 | "test.only": "node tests/tests.js && node tests/lib/rules/e2e-tests.js",
70 | "test": "npm run title --title=\"Test With Coverage\" -s && mkdir -p build && nyc npm run test.only",
71 | "check": "npm run title --title=\"Install\" -s && npm install && npm run lint && npm test && mkdir -p build && cd build && npm pack ../",
72 | "postcheck": "echo \"check OK\"",
73 | "check.all": "npm run check && npm audit",
74 | "prepublishOnly": "npm run check",
75 | "prepack": "npm run title --title=\"Build Package\" -s",
76 | "title": "echo ==== \"\\033[1;;33m\"$npm_config_title\"\\033[0m\" ===="
77 | },
78 | "engines": {
79 | "node": ">=6.0.0"
80 | },
81 | "peerDependencies": {
82 | "eslint": ">=4.0.0"
83 | },
84 | "devDependencies": {
85 | "any-eslint-parser": "^1.0.1",
86 | "eslint": "^8.13.0",
87 | "eslint-plugin-base-style-config": "^2.9.2",
88 | "eslint-plugin-import": "^2.25.4",
89 | "jasmine": "^4.0.2",
90 | "nyc": "^15.1.0"
91 | },
92 | "nyc": {
93 | "check-coverage": true,
94 | "branches": 85,
95 | "functions": 50,
96 | "statements": 25,
97 | "reporter": [
98 | "lcov",
99 | "cobertura",
100 | "text",
101 | "text-summary"
102 | ],
103 | "report-dir": "build/coverage",
104 | "temp-dir": "build/coverage"
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/tests/lib/index.test.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2022 Gonzalo Müller Bravo.
2 | // Licensed under the MIT License (MIT), see LICENSE.txt
3 | const { addRegexRuleName, rules } = require('../../lib')
4 | const invalidRegexRule = require('../../lib/rules/invalid-regex-rule.js')
5 | const requiredRegexRule = require('../../lib/rules/required-regex-rule.js')
6 |
7 | describe('index tests', () => {
8 | it('should add invalid regex rule name when valid', () => {
9 | addRegexRuleName('someInvalid')
10 |
11 | expect(Object.keys(rules).find(rule => rule === 'someInvalid')).toBe('someInvalid')
12 | expect(rules.someInvalid).toBe(invalidRegexRule)
13 | })
14 |
15 | it('should add invalid disuse regex rule name when valid', () => {
16 | addRegexRuleName('some-disuse')
17 |
18 | expect(Object.keys(rules).find(rule => rule === 'some-disuse')).toBe('some-disuse')
19 | expect(rules['some-disuse']).toBe(invalidRegexRule)
20 | })
21 |
22 | it('should add invalid avoid regex rule name when valid', () => {
23 | addRegexRuleName('avoid some')
24 |
25 | expect(Object.keys(rules).find(rule => rule === 'avoid some')).toBe('avoid some')
26 | expect(rules['avoid some']).toBe(invalidRegexRule)
27 | })
28 |
29 | it('should add required regex rule name when valid', () => {
30 | addRegexRuleName('someREQUIRED')
31 |
32 | expect(Object.keys(rules).find(rule => rule === 'someREQUIRED')).toBe('someREQUIRED')
33 | expect(rules.someREQUIRED).toBe(requiredRegexRule)
34 | })
35 |
36 | it('should add required use regex rule name when valid', () => {
37 | addRegexRuleName('someUSE')
38 |
39 | expect(Object.keys(rules).find(rule => rule === 'someUSE')).toBe('someUSE')
40 | expect(rules.someUSE).toBe(requiredRegexRule)
41 | })
42 |
43 | it('should not add regex rule name when invalid name', () => {
44 | addRegexRuleName('some')
45 |
46 | expect(Object.keys(rules).find(rule => rule === 'some')).toBeUndefined()
47 | })
48 |
49 | describe('invalid rule names', () => {
50 | const rulesNames = ['required', 'required-warn', 'required-error', 'another-required', 'other-required', 'invalid', 'invalid-warn', 'invalid-error', 'another-invalid', 'other-invalid']
51 |
52 | for(const ruleName of rulesNames) {
53 | const testRuleName = ruleName.toUpperCase()
54 | it(`should not add regex rule name when equal to ${ruleName}`, () => {
55 | try {
56 | addRegexRuleName(testRuleName)
57 | fail(`Should not add "${ruleName}" name`)
58 | }
59 | catch(error) {
60 | expect(error.toString()).toBe(`Error: "${testRuleName}" already defined as eslint-plugin-regex rule name`)
61 | }
62 | })
63 | }
64 |
65 | it('should fail adding same rule name more than once', () => {
66 | addRegexRuleName('Same Required Rule Name')
67 | try {
68 | addRegexRuleName('Same Required Rule Name')
69 | fail('Should not add "Same Required Rule Name" name')
70 | }
71 | catch(error) {
72 | expect(error.toString()).toBe('Error: "Same Required Rule Name" already defined as eslint-plugin-regex rule name')
73 | }
74 | })
75 | })
76 | })
77 |
--------------------------------------------------------------------------------
/tests/lib/rules/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "global-require": "off",
4 | "no-console": "off",
5 | "no-throw-literal": "off",
6 | "max-lines": "off"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/tests/lib/rules/case-001-minified.js:
--------------------------------------------------------------------------------
1 |
2 | (function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V