Skip to content

Commit b34ae26

Browse files
Add HTML tag regex utilities (#230)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent ff8ca87 commit b34ae26

5 files changed

Lines changed: 136 additions & 3 deletions

File tree

.changeset/small-trees-mix.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@js-utils-kit/regex': minor
3+
'@js-utils-kit/core': minor
4+
js-utils-kit: minor
5+
---
6+
7+
Add `HTML_TAG_REGEX` and `STRICT_HTML_TAG_REGEX` regexes

exports.json

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
{
22
"summary": {
3-
"totalExports": 220,
3+
"totalExports": 222,
44
"deprecatedExports": 0,
55
"classExports": 0,
66
"functionExports": 89,
7-
"variableExports": 105,
7+
"variableExports": 107,
88
"typeExports": 25,
9-
"valueExports": 194,
9+
"valueExports": 196,
1010
"totalPackages": 14
1111
},
1212
"exportNames": {
@@ -137,6 +137,7 @@
137137
"HAS_WHITESPACE_REGEX",
138138
"homeDir",
139139
"HOUR",
140+
"HTML_TAG_REGEX",
140141
"HTTP_METHODS",
141142
"HTTP_STATUS",
142143
"IS_ALPHA_REGEX",
@@ -198,6 +199,7 @@
198199
"SQRT_1_2",
199200
"SQRT_2",
200201
"STARTS_WITH_UPPERCASE_REGEX",
202+
"STRICT_HTML_TAG_REGEX",
201203
"TAB",
202204
"TAU",
203205
"TON_TO_KG",
@@ -1402,6 +1404,22 @@
14021404
"filePath": "packages/@js-utils-kit/regex/src/hasWhitespace.ts",
14031405
"line": 23
14041406
},
1407+
{
1408+
"name": "HTML_TAG_REGEX",
1409+
"declarationKind": "VariableDeclaration",
1410+
"exportKind": "variable",
1411+
"deprecated": false,
1412+
"filePath": "packages/@js-utils-kit/regex/src/html.ts",
1413+
"line": 18
1414+
},
1415+
{
1416+
"name": "STRICT_HTML_TAG_REGEX",
1417+
"declarationKind": "VariableDeclaration",
1418+
"exportKind": "variable",
1419+
"deprecated": false,
1420+
"filePath": "packages/@js-utils-kit/regex/src/html.ts",
1421+
"line": 37
1422+
},
14051423
{
14061424
"name": "NPM_PACKAGE_NAME_REGEX",
14071425
"declarationKind": "VariableDeclaration",
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* Matches any HTML/XML-like tag, including React fragments.
3+
*
4+
* Intended for stripping markup from a string, not for validating HTML.
5+
*
6+
* @example
7+
* ```ts
8+
* "<p>Hello</p>".replace(HTML_TAG_REGEX, "");
9+
* // "Hello"
10+
* ```
11+
*
12+
* @example
13+
* ```ts
14+
* "<><div>Hello</div></>".replace(HTML_TAG_REGEX, "");
15+
* // "Hello"
16+
* ```
17+
*/
18+
export const HTML_TAG_REGEX = /<\/?[^>]*>/g;
19+
20+
/**
21+
* Matches standard HTML tags whose tag names begin with a letter.
22+
*
23+
* Unlike {@link HTML_TAG_REGEX}, this pattern does not match React fragment tags (`<>` and `</>`).
24+
*
25+
* @example
26+
* ```ts
27+
* "<div>Hello</div>".replace(STRICT_HTML_TAG_REGEX, "");
28+
* // "Hello"
29+
* ```
30+
*
31+
* @example
32+
* ```ts
33+
* "<></>".match(STRICT_HTML_TAG_REGEX);
34+
* // null
35+
* ```
36+
*/
37+
export const STRICT_HTML_TAG_REGEX = /<\/?[a-z][^>]*>/gi;

packages/@js-utils-kit/regex/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ export * from './cases';
88
export * from './email';
99
export * from './endsWithPunctuation';
1010
export * from './hasWhitespace';
11+
export * from './html';
1112
export * from './pm';
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { HTML_TAG_REGEX, STRICT_HTML_TAG_REGEX } from '../src';
3+
4+
describe('HTML_TAG_REGEX', () => {
5+
it('matches opening tags', () => {
6+
expect('<div>'.match(HTML_TAG_REGEX)).toEqual(['<div>']);
7+
});
8+
9+
it('matches closing tags', () => {
10+
expect('</div>'.match(HTML_TAG_REGEX)).toEqual(['</div>']);
11+
});
12+
13+
it('matches self-closing tags', () => {
14+
expect('<img src="a.png" />'.match(HTML_TAG_REGEX)).toEqual(['<img src="a.png" />']);
15+
});
16+
17+
it('matches nested tags', () => {
18+
expect('<div><span>Hello</span></div>'.match(HTML_TAG_REGEX)).toEqual([
19+
'<div>',
20+
'<span>',
21+
'</span>',
22+
'</div>',
23+
]);
24+
});
25+
26+
it('does not match plain text', () => {
27+
expect('Hello, world!'.match(HTML_TAG_REGEX)).toBeNull();
28+
});
29+
30+
it('matches malformed tags', () => {
31+
expect('<>'.match(HTML_TAG_REGEX)).toEqual(['<>']);
32+
expect('</>'.match(HTML_TAG_REGEX)).toEqual(['</>']);
33+
});
34+
});
35+
36+
describe('STRICT_HTML_TAG_REGEX', () => {
37+
it('matches opening tags', () => {
38+
expect('<div>'.match(STRICT_HTML_TAG_REGEX)).toEqual(['<div>']);
39+
});
40+
41+
it('matches closing tags', () => {
42+
expect('</div>'.match(STRICT_HTML_TAG_REGEX)).toEqual(['</div>']);
43+
});
44+
45+
it('matches self-closing tags', () => {
46+
expect('<img src="a.png" />'.match(STRICT_HTML_TAG_REGEX)).toEqual(['<img src="a.png" />']);
47+
});
48+
49+
it('matches uppercase tag names', () => {
50+
expect('<DIV>'.match(STRICT_HTML_TAG_REGEX)).toEqual(['<DIV>']);
51+
});
52+
53+
it('does not match React fragments', () => {
54+
expect('<>'.match(STRICT_HTML_TAG_REGEX)).toBeNull();
55+
expect('</>'.match(STRICT_HTML_TAG_REGEX)).toBeNull();
56+
});
57+
58+
it('does not match plain text', () => {
59+
expect('Hello'.match(STRICT_HTML_TAG_REGEX)).toBeNull();
60+
});
61+
62+
it('matches nested tags', () => {
63+
expect('<div><span>Hello</span></div>'.match(STRICT_HTML_TAG_REGEX)).toEqual([
64+
'<div>',
65+
'<span>',
66+
'</span>',
67+
'</div>',
68+
]);
69+
});
70+
});

0 commit comments

Comments
 (0)