Skip to content

Commit 443ba8b

Browse files
fix: char class ^- edge case (#42)
1 parent de93567 commit 443ba8b

File tree

4 files changed

+23
-10
lines changed

4 files changed

+23
-10
lines changed

README.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,20 @@ This library allows users to create regular expressions in a structured way, mak
1313
const hexColor = /^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/;
1414

1515
// After
16-
const hexDigit = charClass(charRange('a', 'f'), charRange('A', 'F'), charRange('0', '9'));
16+
const hexDigit = charClass(
17+
charRange('a', 'f'), //
18+
charRange('A', 'F'),
19+
charRange('0', '9'),
20+
);
1721

18-
// prettier-ignore
1922
const hexColor = buildRegex(
2023
startOfString,
2124
optionally('#'),
2225
capture(
2326
choiceOf(
24-
repeat({ count: 6 }, hexDigit),
25-
repeat({ count: 3 }, hexDigit),
26-
)
27+
repeat({ count: 6 }, hexDigit), // #rrggbb
28+
repeat({ count: 3 }, hexDigit), // #rgb
29+
),
2730
),
2831
endOfString,
2932
);
@@ -73,11 +76,11 @@ const currencyAmount = buildRegex([
7376
choiceOf(
7477
'$',
7578
'',
76-
repeat({ count: 3 }, charRange('A', 'Z')) // ISO currency code
79+
repeat({ count: 3 }, charRange('A', 'Z')), // ISO currency code
7780
),
7881
capture(
7982
oneOrMore(digit), // Integer part
80-
optionally(['.', repeat({ count: 2 }, digit)]) // Fractional part
83+
optionally(['.', repeat({ count: 2 }, digit)]), // Fractional part
8184
),
8285
]);
8386
```

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@
144144
"quoteProps": "consistent",
145145
"singleQuote": true,
146146
"tabWidth": 2,
147-
"trailingComma": "es5",
147+
"trailingComma": "all",
148148
"useTabs": false
149149
},
150150
"react-native-builder-bob": {

src/components/__tests__/character-class.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ test('`anyOf` moves hyphen to the last position', () => {
8888
expect(anyOf('a-bc')).toHavePattern(/[abc-]/);
8989
});
9090

91+
test('`anyOf` edge case caret and hyphen', () => {
92+
expect(anyOf('^-')).toHavePattern(/[\^-]/);
93+
expect(anyOf('-^')).toHavePattern(/[\^-]/);
94+
expect(anyOf('-^a')).toHavePattern(/[a^-]/);
95+
});
96+
9197
test('`anyOf` throws on empty text', () => {
9298
expect(() => anyOf('')).toThrowErrorMatchingInlineSnapshot(
9399
`"\`anyOf\` should received at least one character"`

src/components/character-class.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,13 +129,17 @@ function encodeCharacterClass(this: CharacterClass): EncodeOutput {
129129
// first (or last) place in order to treat it as hyphen character and not a range.
130130
// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions/Character_classes#types
131131
const hyphen = this.chars.includes('-') ? '-' : '';
132-
const otherChars = this.chars.filter((c) => c !== '-').join('');
132+
const caret = this.chars.includes('^') ? '^' : '';
133+
const otherChars = this.chars.filter((c) => c !== '-' && c !== '^').join('');
133134
const ranges = this.ranges.map(({ start, end }) => `${start}-${end}`).join('');
134135
const isInverted = this.isInverted ? '^' : '';
135136

137+
let pattern = `[${isInverted}${ranges}${otherChars}${caret}${hyphen}]`;
138+
if (pattern === '[^-]') pattern = '[\\^-]';
139+
136140
return {
137141
precedence: 'atom',
138-
pattern: `[${isInverted}${ranges}${otherChars}${hyphen}]`,
142+
pattern,
139143
};
140144
}
141145

0 commit comments

Comments
 (0)