I'd like to reduce bugs related to a regex and make it safer to write a regex.
> [email protected] lint:js
> eslint . --cache --max-warnings=0 "-f" "codeframe"
error: Capturing group number 1 is defined but never used (regexp/no-unused-capturing-group) at lib/__tests__/standalone-syntax.test.js:23:39:
21 | expect(results).toHaveLength(6);
22 |
> 23 | const safeParserExtensionsTest = /\.(css|pcss|postcss)$/i;
| ^
24 |
25 | results
26 | .filter((result) => !safeParserExtensionsTest.test(result.source))
error: Capturing group number 1 is defined but never used (regexp/no-unused-capturing-group) at lib/rules/color-hex-alpha/index.js:16:16:
14 | });
15 |
> 16 | const HEX = /^#([\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$/i;
| ^
17 |
18 | /** @type {import('stylelint').StylelintRule} */
19 | const rule = (primary) => {
error: Unexpected character class range '0-9'. Use '\d' instead (regexp/prefer-d) at lib/rules/color-hex-case/index.js:18:17:
16 | });
17 |
> 18 | const HEX = /^#[0-9A-Za-z]+/;
| ^
19 | const IGNORED_FUNCTIONS = new Set(['url']);
20 |
21 | /** @type {import('stylelint').StylelintRule} */
error: Unexpected character class range '0-9'. Use '\d' instead (regexp/prefer-d) at lib/rules/color-hex-length/index.js:18:17:
16 | });
17 |
> 18 | const HEX = /^#[0-9A-Za-z]+/;
| ^
19 | const IGNORED_FUNCTIONS = new Set(['url']);
20 |
21 | /** @type {import('stylelint').StylelintRule} */
error: Unexpected character class range '0-9'. Use '\d' instead (regexp/prefer-d) at lib/rules/color-no-hex/index.js:17:17:
15 | });
16 |
> 17 | const HEX = /^#[0-9A-Za-z]+/;
| ^
18 | const IGNORED_FUNCTIONS = new Set(['url']);
19 |
20 | /** @type {import('stylelint').StylelintRule} */
error: Unexpected character class range '0-9'. Use '\d' instead (regexp/prefer-d) at lib/rules/color-no-invalid-hex/index.js:31:26:
29 | if (type !== 'word') return;
30 |
> 31 | const hexMatch = /^#[0-9A-Za-z]+/.exec(value);
| ^
32 |
33 | if (!hexMatch) return;
34 |
error: '\r?' can be removed because it is already included by '\s*' (regexp/optimal-quantifier-concatenation) at lib/rules/declaration-colon-newline-after/index.js:67:14:
65 | const betweenAfter = between.slice(sliceIndex);
66 |
> 67 | if (/^\s*\r?\n/.test(betweenAfter)) {
| ^
68 | decl.raws.between = betweenBefore + betweenAfter.replace(/^[^\S\r\n]*/, '');
69 | } else {
70 | decl.raws.between = betweenBefore + context.newline + betweenAfter;
error: Capturing group number 1 is defined but never used (regexp/no-unused-capturing-group) at lib/rules/font-family-name-quotes/index.js:55:12:
53 | function quotesRequired(family) {
54 | return family.split(/\s+/).some((word) => {
> 55 | return /^(-?\d|--)/.test(word) || !/^[-_a-zA-Z0-9\u{00A0}-\u{10FFFF}]+$/u.test(word);
| ^
56 | });
57 | }
58 |
error: Unexpected character class ranges '[_a-zA-Z0-9]'. Use '\w' instead (regexp/prefer-w) at lib/rules/font-family-name-quotes/index.js:55:40:
53 | function quotesRequired(family) {
54 | return family.split(/\s+/).some((word) => {
> 55 | return /^(-?\d|--)/.test(word) || !/^[-_a-zA-Z0-9\u{00A0}-\u{10FFFF}]+$/u.test(word);
| ^
56 | });
57 | }
58 |
error: Unexpected character class range '0-9'. Use '\d' instead (regexp/prefer-d) at lib/rules/font-family-name-quotes/index.js:55:49:
53 | function quotesRequired(family) {
54 | return family.split(/\s+/).some((word) => {
> 55 | return /^(-?\d|--)/.test(word) || !/^[-_a-zA-Z0-9\u{00A0}-\u{10FFFF}]+$/u.test(word);
| ^
56 | });
57 | }
58 |
error: Unescaped source character '{' (regexp/strict) at lib/rules/function-calc-no-unspaced-operator/index.js:244:38:
242 | */
243 | function blurVariables(source) {
> 244 | return source.replace(/[$@][^)\s]+|#{.+?}/g, '0');
| ^
245 | }
246 |
247 | /**
error: Unescaped source character '}' (regexp/strict) at lib/rules/function-calc-no-unspaced-operator/index.js:244:42:
242 | */
243 | function blurVariables(source) {
> 244 | return source.replace(/[$@][^)\s]+|#{.+?}/g, '0');
| ^
245 | }
246 |
247 | /**
error: Unescaped source character '{' (regexp/strict) at lib/rules/indentation/index.js:306:36:
304 | }
305 |
> 306 | const followsOpeningBrace = /{[ \t]*$/.test(source.slice(0, newlineIndex));
| ^
307 |
308 | if (followsOpeningBrace) {
309 | parentheticalDepth += 1;
error: Unescaped source character '}' (regexp/strict) at lib/rules/indentation/index.js:312:44:
310 | }
311 |
> 312 | const startingClosingBrace = /^[ \t]*}/.test(source.slice(match.startIndex + 1));
| ^
313 |
314 | if (startingClosingBrace) {
315 | parentheticalDepth -= 1;
error: Capturing group number 1 is defined but never used (regexp/no-unused-capturing-group) at lib/rules/indentation/index.js:610:17:
608 |
609 | source = source.replace(/^[^\r\n]+/, (firstLine) => {
> 610 | if (/(?:^|\n)([ \t]*)$/.test(root.raws.beforeStart)) {
| ^
611 | return RegExp.$1 + firstLine;
612 | }
613 |
error: 'RegExp.$1' static property is forbidden (regexp/no-legacy-features) at lib/rules/indentation/index.js:611:12:
609 | source = source.replace(/^[^\r\n]+/, (firstLine) => {
610 | if (/(?:^|\n)([ \t]*)$/.test(root.raws.beforeStart)) {
> 611 | return RegExp.$1 + firstLine;
| ^
612 | }
613 |
614 | return '';
error: Capturing group number 1 is defined but never used (regexp/no-unused-capturing-group) at lib/rules/max-empty-lines/index.js:164:12:
162 | const emptyCRLFLines = '\r\n'.repeat(repeatTimes);
163 |
> 164 | return /(\r\n)+/g.test(str)
| ^
165 | ? str.replace(/(\r\n)+/g, ($1) => {
166 | if ($1.length / 2 > repeatTimes) {
167 | return emptyCRLFLines;
warning: The 'g' flag is unnecessary because the regex is used only once in 'RegExp.prototype.test' (regexp/no-useless-flag) at lib/rules/max-empty-lines/index.js:164:20:
162 | const emptyCRLFLines = '\r\n'.repeat(repeatTimes);
163 |
> 164 | return /(\r\n)+/g.test(str)
| ^
165 | ? str.replace(/(\r\n)+/g, ($1) => {
166 | if ($1.length / 2 > repeatTimes) {
167 | return emptyCRLFLines;
error: '\r?' can be removed because it is already included by '\s*' (regexp/optimal-quantifier-concatenation) at lib/rules/media-query-list-comma-newline-after/index.js:68:19:
66 |
67 | if (primary.startsWith('always')) {
> 68 | params = /^\s*\r?\n/.test(afterComma)
| ^
69 | ? beforeComma + afterComma.replace(/^[^\S\r\n]*/, '')
70 | : beforeComma + context.newline + afterComma;
71 | } else if (primary.startsWith('never')) {
error: Capturing group number 2 is defined but never used (regexp/no-unused-capturing-group) at lib/rules/mediaQueryListCommaWhitespaceChecker.js:32:40:
30 | }
31 |
> 32 | if ((execResult = /^([^\S\r\n]*\/\/([\s\S]*?))\r?\n/.exec(params.slice(index + 1)))) {
| ^
33 | index += execResult[1].length;
34 | }
35 | }
error: The quantifier '\d*?' can exchange characters with '0+'. Using any string accepted by /0+/, this can be exploited to cause at least polynomial backtracking (regexp/no-super-linear-backtracking) at lib/rules/number-no-trailing-zeros/index.js:55:23:
53 | }
54 |
> 55 | const match = /\.(\d*?)(0+)(?:\D|$)/.exec(valueNode.value);
| ^
56 |
57 | // match[1] is any numbers between the decimal and our trailing zero, could be empty
58 | // match[2] is our trailing zero(s)
error: Unescaped source character ']' (regexp/strict) at lib/rules/selector-disallowed-list/__tests__/index.js:7:33:
5 | testRule({
6 | ruleName,
> 7 | config: ['a > .foo', /\[data-.+]/],
| ^
8 |
9 | accept: [
10 | {
error: The quantifier '.*' can exchange characters with '.*'. Using any string accepted by />+/, this can be exploited to cause at least polynomial backtracking. This might cause exponential backtracking (regexp/no-super-linear-backtracking) at lib/rules/selector-disallowed-list/__tests__/index.js:63:17:
61 | testRule({
62 | ruleName,
> 63 | config: [/\.foo.*>.*\.bar/],
| ^
64 |
65 | accept: [
66 | {
error: Capturing group number 1 is defined but never used (regexp/no-unused-capturing-group) at lib/rules/string-no-newline/index.js:15:20:
13 |
14 | const ruleName = 'string-no-newline';
> 15 | const reNewLine = /(\r?\n)/;
| ^
16 |
17 | const messages = ruleMessages(ruleName, {
18 | rejected: 'Unexpected newline in string',
error: 'RegExp.leftContext' static property is forbidden (regexp/no-legacy-features) at lib/rules/string-no-newline/index.js:65:7:
63 | attributeNode.operator,
64 | // length of the contents before newline
> 65 | RegExp.leftContext,
| ^
66 | ].reduce(
67 | (index, str) => index + str.length,
68 | // index of the start of our attribute node in our source
error: 'RegExp.leftContext' static property is forbidden (regexp/no-legacy-features) at lib/rules/string-no-newline/index.js:99:6:
97 | valueNode.quote,
98 | // length of the contents before newline
> 99 | RegExp.leftContext,
| ^
100 | ].reduce((index, str) => index + str.length, valueNode.sourceIndex);
101 |
102 | report({
error: Capturing group number 2 is defined but never used (regexp/no-unused-capturing-group) at lib/rules/unit-disallowed-list/index.js:28:11:
26 | const value = mediaFeatureNode.value.toLowerCase();
27 |
> 28 | return /((-?\w*)*)/i.exec(value)[1];
| ^
29 | };
30 |
31 | function rule(listInput, options) {
warning: The 'i' flag is unnecessary because the pattern only contains case-invariant characters (regexp/no-useless-flag) at lib/rules/unit-disallowed-list/index.js:28:21:
26 | const value = mediaFeatureNode.value.toLowerCase();
27 |
> 28 | return /((-?\w*)*)/i.exec(value)[1];
| ^
29 | };
30 |
31 | function rule(listInput, options) {
error: Unexpected character class '[0-9]'. Use '\d' instead (regexp/prefer-d) at lib/utils/checkInvalidCLIOptions.js:54:48:
52 | */
53 | const kebabCase = (opt) => {
> 54 | const matches = opt.match(/[A-Z]?[a-z]+|[A-Z]|[0-9]+/g);
| ^
55 |
56 | if (matches) {
57 | return matches.map((s) => s.toLowerCase()).join('-');
error: Unescaped source character '{' (regexp/strict) at lib/utils/hasLessInterpolation.js:10:11:
8 | */
9 | module.exports = function (string) {
> 10 | return /@{.+?}/.test(string);
| ^
11 | };
12 |
error: Unescaped source character '}' (regexp/strict) at lib/utils/hasLessInterpolation.js:10:15:
8 | */
9 | module.exports = function (string) {
> 10 | return /@{.+?}/.test(string);
| ^
11 | };
12 |
error: Unescaped source character '{' (regexp/strict) at lib/utils/hasScssInterpolation.js:9:11:
7 | */
8 | module.exports = function (string) {
> 9 | return /#{.+?}/.test(string);
| ^
10 | };
11 |
error: Unescaped source character '}' (regexp/strict) at lib/utils/hasScssInterpolation.js:9:15:
7 | */
8 | module.exports = function (string) {
> 9 | return /#{.+?}/.test(string);
| ^
10 | };
11 |
error: Unescaped source character '{' (regexp/strict) at lib/utils/hasTplInterpolation.js:10:10:
8 | */
9 | module.exports = function (string) {
> 10 | return /{.+?}/.test(string);
| ^
11 | };
12 |
error: Unescaped source character '}' (regexp/strict) at lib/utils/hasTplInterpolation.js:10:14:
8 | */
9 | module.exports = function (string) {
> 10 | return /{.+?}/.test(string);
| ^
11 | };
12 |
error: The quantifier '\d+' can exchange characters with '\d*'. Using any string accepted by /\d+/, this can be exploited to cause at least polynomial backtracking (regexp/no-super-linear-backtracking) at lib/utils/isKeyframeSelector.js:17:11:
15 |
16 | // Percentages
> 17 | if (/^(?:\d+\.?\d*|\d*\.?\d+)%$/.test(selector)) {
| ^
18 | return true;
19 | }
20 |
error: The quantifier '\d*' can exchange characters with '\d+'. Using any string accepted by /\d+/, this can be exploited to cause at least polynomial backtracking (regexp/no-super-linear-backtracking) at lib/utils/isKeyframeSelector.js:17:21:
15 |
16 | // Percentages
> 17 | if (/^(?:\d+\.?\d*|\d*\.?\d+)%$/.test(selector)) {
| ^
18 | return true;
19 | }
20 |
error: Unescaped source character '{' (regexp/strict) at lib/utils/isStandardSyntaxMediaFeatureName.js:11:8:
9 | module.exports = function (mediaFeatureName) {
10 | // SCSS interpolation
> 11 | if (/#{.+?}|\$.+?/.test(mediaFeatureName)) {
| ^
12 | return false;
13 | }
14 |
error: Unescaped source character '}' (regexp/strict) at lib/utils/isStandardSyntaxMediaFeatureName.js:11:12:
9 | module.exports = function (mediaFeatureName) {
10 | // SCSS interpolation
> 11 | if (/#{.+?}|\$.+?/.test(mediaFeatureName)) {
| ^
12 | return false;
13 | }
14 |
warning: The quantifier can be removed because the quantifier is lazy and has a minimum of 1 (regexp/no-lazy-ends) at lib/utils/isStandardSyntaxMediaFeatureName.js:11:16:
9 | module.exports = function (mediaFeatureName) {
10 | // SCSS interpolation
> 11 | if (/#{.+?}|\$.+?/.test(mediaFeatureName)) {
| ^
12 | return false;
13 | }
14 |
error: Capturing group number 1 is defined but never used (regexp/no-unused-capturing-group) at lib/utils/isStandardSyntaxSelector.js:28:14:
26 |
27 | // Less :extend()
> 28 | if (/:extend(\(.*?\))?/.test(selector)) {
| ^
29 | return false;
30 | }
31 |
warning: The 'i' flag is unnecessary because the pattern only contains case-invariant characters (regexp/no-useless-flag) at lib/utils/isStandardSyntaxSelector.js:33:24:
31 |
32 | // Less mixin with resolved nested selectors (e.g. .foo().bar or .foo(@a, @b)[bar])
> 33 | if (/\.[\w-]+\(.*\).+/i.test(selector)) {
| ^
34 | return false;
35 | }
36 |
error: Unexpected obscure character range. The characters of '+-/' (U+002b - U+002f) are not obvious (regexp/no-obscure-range) at lib/utils/isStandardSyntaxUrl.js:43:35:
41 | // But in this case it is allowed to use only specific characters
42 | // Also forbidden "/" at the end of url
> 43 | if (url.includes('$') && /^[$\s\w+-/*'"/]+$/.test(url) && !url.endsWith('/')) {
| ^
44 | return false;
45 | }
46 |
error: '/' (U+002f) is already included in '+-/' (U+002b - U+002f) (regexp/no-dupe-characters-character-class) at lib/utils/isStandardSyntaxUrl.js:43:41:
41 | // But in this case it is allowed to use only specific characters
42 | // Also forbidden "/" at the end of url
> 43 | if (url.includes('$') && /^[$\s\w+-/*'"/]+$/.test(url) && !url.endsWith('/')) {
| ^
44 | return false;
45 | }
46 |
error: Unexpected character class range '0-9'. Use '\d' instead (regexp/prefer-d) at lib/utils/isValidHex.js:10:16:
8 | */
9 | module.exports = function (value) {
> 10 | return /^#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(value);
| ^
11 | };
12 |
error: Unexpected character class range '0-9'. Use '\d' instead (regexp/prefer-d) at lib/utils/isValidHex.js:10:33:
8 | */
9 | module.exports = function (value) {
> 10 | return /^#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(value);
| ^
11 | };
12 |
error: Unexpected character class range '0-9'. Use '\d' instead (regexp/prefer-d) at lib/utils/isValidHex.js:10:48:
8 | */
9 | module.exports = function (value) {
> 10 | return /^#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(value);
| ^
11 | };
12 |
error: '\r?' can be removed because it is already included by '\s*' (regexp/optimal-quantifier-concatenation) at lib/utils/removeEmptyLinesAfter.js:12:69:
10 | */
11 | module.exports = function removeEmptyLinesAfter(node, newline) {
> 12 | node.raws.after = node.raws.after ? node.raws.after.replace(/(\r?\n\s*\r?\n)+/g, newline) : '';
| ^
13 |
14 | return node;
15 | };
error: '\r?' can be removed because it is already included by '\s*' (regexp/optimal-quantifier-concatenation) at lib/utils/removeEmptyLinesBefore.js:12:72:
10 | */
11 | module.exports = function removeEmptyLinesBefore(node, newline) {
> 12 | node.raws.before = node.raws.before ? node.raws.before.replace(/(\r?\n\s*\r?\n)+/g, newline) : '';
| ^
13 |
14 | return node;
15 | };
45 errors and 4 warnings found.
31 errors and 3 warnings potentially fixable with the `--fix` option.
45 errors and 4 warnings found.
31 errors and 3 warnings potentially fixable with the `--fix` option.