Skip to content

Commit b619a72

Browse files
committed
feat: ansiRegex, ar
Signed-off-by: Lexus Drumgold <unicornware@flexdevelopment.llc>
1 parent b1e3d5a commit b619a72

File tree

34 files changed

+4774
-17
lines changed

34 files changed

+4774
-17
lines changed

.dictionary.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,22 @@ dedupe
55
devlop
66
fbca
77
gpgsign
8+
hhome
89
hmarr
910
jchen
11+
jclear
1012
kaisugi
13+
mbold
14+
mempty
15+
mhello
16+
mmany
17+
mred
18+
mreset
19+
mtoo
1120
nvmrc
1221
shfmt
1322
unstub
1423
vates
1524
vitest
1625
yarnrc
26+
zunknown

README.md

Lines changed: 250 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,15 @@ Regular expression for matching [ANSI escape codes][ansi-escape-code]
2121
- [Install](#install)
2222
- [Use](#use)
2323
- [API](#api)
24+
- [`ansiRegex([flags])`](#ansiregexflags)
25+
- [`ar`](#ar)
2426
- [Types](#types)
27+
- [`Flags`](#flags)
2528
- [Contribute](#contribute)
2629

2730
## What is this?
2831

29-
**TODO**: what is this?
32+
This is a tiny, but useful package for matching [ANSI escape codes][ansi-escape-code] in strings.
3033

3134
## Install
3235

@@ -55,7 +58,7 @@ In browsers with [`esm.sh`][esmsh]:
5558

5659
```html
5760
<script type="module">
58-
import { ansiRegex } from 'https://esm.sh/@flex-development/ansi-regex'
61+
import { ar } from 'https://esm.sh/@flex-development/ansi-regex'
5962
</script>
6063
```
6164

@@ -73,17 +76,249 @@ bun add @flex-development/ansi-regex
7376

7477
## Use
7578

76-
**TODO**: use
79+
[`example.mjs`](./example.mjs):
80+
81+
```js
82+
import { ansiRegex } from '@flex-development/ansi-regex'
83+
import c from '@flex-development/colors'
84+
import { ok } from 'devlop'
85+
86+
const emojis = '🦄🦾🚀'
87+
const hello = c.bgBlue(c.bold('hello world 🌎'))
88+
89+
console.log(`${JSON.stringify(emojis)}:`, ansiRegex().test(emojis))
90+
console.log(`${JSON.stringify(hello)}:`, ansiRegex().test(hello))
91+
92+
for (const match of hello.matchAll(ansiRegex({ d: true }))) {
93+
const { groups, index, indices } = match
94+
95+
ok(groups, 'expected `groups`')
96+
console.dir({ groups, index, indices: [...indices] }, { sorted: true })
97+
}
98+
```
99+
100+
...yields
101+
102+
```sh
103+
"🦄🦾🚀": false
104+
"\u001b[44m\u001b[1mhello world 🌎\u001b[22m\u001b[49m": true
105+
{
106+
groups: {
107+
ansi: '\x1B[44m',
108+
csi: '\x1B[44m',
109+
csi_final: 'm',
110+
csi_intermediate: '',
111+
csi_introducer: '\x1B',
112+
csi_params: '44',
113+
esc: undefined,
114+
esc_final: undefined,
115+
osc: undefined,
116+
osc_command: undefined,
117+
osc_data: undefined,
118+
osc_introducer: undefined,
119+
osc_sep: undefined,
120+
osc_terminator: undefined
121+
},
122+
index: 0,
123+
indices: [
124+
[ 0, 5 ],
125+
[ 0, 5 ],
126+
undefined,
127+
undefined,
128+
undefined,
129+
undefined,
130+
undefined,
131+
undefined,
132+
[ 0, 5 ],
133+
[ 0, 1 ],
134+
[ 2, 4 ],
135+
[ 4, 4 ],
136+
[ 4, 5 ],
137+
undefined,
138+
undefined
139+
]
140+
}
141+
{
142+
groups: {
143+
ansi: '\x1B[1m',
144+
csi: '\x1B[1m',
145+
csi_final: 'm',
146+
csi_intermediate: '',
147+
csi_introducer: '\x1B',
148+
csi_params: '1',
149+
esc: undefined,
150+
esc_final: undefined,
151+
osc: undefined,
152+
osc_command: undefined,
153+
osc_data: undefined,
154+
osc_introducer: undefined,
155+
osc_sep: undefined,
156+
osc_terminator: undefined
157+
},
158+
index: 5,
159+
indices: [
160+
[ 5, 9 ],
161+
[ 5, 9 ],
162+
undefined,
163+
undefined,
164+
undefined,
165+
undefined,
166+
undefined,
167+
undefined,
168+
[ 5, 9 ],
169+
[ 5, 6 ],
170+
[ 7, 8 ],
171+
[ 8, 8 ],
172+
[ 8, 9 ],
173+
undefined,
174+
undefined
175+
]
176+
}
177+
{
178+
groups: {
179+
ansi: '\x1B[22m',
180+
csi: '\x1B[22m',
181+
csi_final: 'm',
182+
csi_intermediate: '',
183+
csi_introducer: '\x1B',
184+
csi_params: '22',
185+
esc: undefined,
186+
esc_final: undefined,
187+
osc: undefined,
188+
osc_command: undefined,
189+
osc_data: undefined,
190+
osc_introducer: undefined,
191+
osc_sep: undefined,
192+
osc_terminator: undefined
193+
},
194+
index: 23,
195+
indices: [
196+
[ 23, 28 ],
197+
[ 23, 28 ],
198+
undefined,
199+
undefined,
200+
undefined,
201+
undefined,
202+
undefined,
203+
undefined,
204+
[ 23, 28 ],
205+
[ 23, 24 ],
206+
[ 25, 27 ],
207+
[ 27, 27 ],
208+
[ 27, 28 ],
209+
undefined,
210+
undefined
211+
]
212+
}
213+
{
214+
groups: {
215+
ansi: '\x1B[49m',
216+
csi: '\x1B[49m',
217+
csi_final: 'm',
218+
csi_intermediate: '',
219+
csi_introducer: '\x1B',
220+
csi_params: '49',
221+
esc: undefined,
222+
esc_final: undefined,
223+
osc: undefined,
224+
osc_command: undefined,
225+
osc_data: undefined,
226+
osc_introducer: undefined,
227+
osc_sep: undefined,
228+
osc_terminator: undefined
229+
},
230+
index: 28,
231+
indices: [
232+
[ 28, 33 ],
233+
[ 28, 33 ],
234+
undefined,
235+
undefined,
236+
undefined,
237+
undefined,
238+
undefined,
239+
undefined,
240+
[ 28, 33 ],
241+
[ 28, 29 ],
242+
[ 30, 32 ],
243+
[ 32, 32 ],
244+
[ 32, 33 ],
245+
undefined,
246+
undefined
247+
]
248+
}
249+
```
77250

78251
## API
79252

80-
**TODO**: api
253+
This package exports the following identifiers:
254+
255+
- [`ansiRegex`](#ansiregexflags)
256+
- [`ar`](#ar)
257+
258+
The default export is [`ar`](#ar).
259+
260+
### `ansiRegex([flags])`
261+
262+
Create a regular expression matching ANSI escape codes.
263+
264+
#### Parameters
265+
266+
- `flags` ([`Flags`](#flags) | `null` | `undefined`)
267+
— an object representing the regular expression flags to apply
268+
269+
#### Returns
270+
271+
(`RegExp`) New regular expression matching ANSI escape codes
272+
273+
### `ar`
274+
275+
(`RegExp`)
276+
277+
The default regular expression matching ANSI escape codes.
81278

82279
## Types
83280

84281
This package is fully typed with [TypeScript][].
85282

86-
**TODO**: types
283+
### `Flags`
284+
285+
Record, where each key is a regular expression flag
286+
and each truthy value indicates if the flag should be applied to the regular expression (`interface`).
287+
288+
```ts
289+
interface Flags {/* see code */}
290+
```
291+
292+
When developing extensions that use additional flags, augment `Flags` to register custom flags:
293+
294+
```ts
295+
declare module '@flex-development/ansi-regex' {
296+
interface Flags {
297+
i?: boolean | null | undefined
298+
}
299+
}
300+
```
301+
302+
#### Properties
303+
304+
- `d?` (`boolean` | `null` | `undefined`)
305+
— whether to generate indices for substring matches
306+
> 👀 [`RegExp#hasIndices`][regexp-d]
307+
- `g?` (`boolean` | `null` | `undefined`)
308+
— whether to perform global search
309+
> 👀 [`RegExp#global`][regexp-g]
310+
- `u?` (`boolean` | `null` | `undefined`)
311+
— whether to treat a pattern as a sequence of unicode code points
312+
> 👀 [`RegExp#unicode`][regexp-u]
313+
- `v?` (`boolean` | `null` | `undefined`)
314+
— whether to treat a pattern as a sequence of unicode code points.\\
315+
> 👉 the `v` flag is an "upgrade" to the `u` flag that enables additional unicode-related features.
316+
> because `u` and `v` interpret the same regex in incompatible ways,
317+
> enabling both flags at once results in a `SyntaxError`\
318+
> 👀 [`RegExp#unicodeSets`][regexp-v]
319+
- `y?` (`boolean` | `null` | `undefined`)
320+
— whether to start matches at the current position in the target string
321+
> 👀 [`RegExp#sticky`][regexp-y]
87322
88323
## Contribute
89324

@@ -100,6 +335,16 @@ community you agree to abide by its terms.
100335

101336
[esmsh]: https://esm.sh
102337

338+
[regexp-d]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices
339+
340+
[regexp-g]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp/global
341+
342+
[regexp-u]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicode
343+
344+
[regexp-v]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicodeSets
345+
346+
[regexp-y]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp/sticky
347+
103348
[typescript]: https://www.typescriptlang.org
104349

105350
[yarn]: https://yarnpkg.com

__tests__/plugins/chai-each.mts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
* @file Plugins - chaiEach
3+
* @module tests/plugins/chaiEach
4+
*/
5+
6+
export default plugin
7+
8+
/**
9+
* A list.
10+
*
11+
* @template {any} [T=unknown]
12+
* List item type
13+
*/
14+
type List<T = unknown> = ReadonlySet<T> | readonly T[]
15+
16+
/**
17+
* Chai plugin to allow for assertions on each item in a list.
18+
*
19+
* @see {@linkcode Chai.ChaiStatic}
20+
* @see {@linkcode Chai.ChaiUtils}
21+
*
22+
* @param {ChaiStatic} chai
23+
* `chai` export
24+
* @param {Chai.ChaiUtils} utils
25+
* `chai` utilities
26+
* @return {undefined}
27+
*/
28+
function plugin(chai: Chai.ChaiStatic, utils: Chai.ChaiUtils): undefined {
29+
return void chai.Assertion.addProperty('each', each)
30+
31+
/**
32+
* @this {Chai.AssertionStatic}
33+
*
34+
* @return {undefined}
35+
*/
36+
function each(this: Chai.AssertionStatic): undefined {
37+
/**
38+
* Subject of assertion.
39+
*
40+
* @const {unknown} object
41+
*/
42+
const object: unknown = utils.flag(this, 'object')
43+
44+
new chai.Assertion(object).satisfies(list)
45+
46+
for (const item of object as List) {
47+
/**
48+
* Assertion for list item.
49+
*
50+
* @const {Chai.Assertion} that
51+
*/
52+
const that: Chai.Assertion = Object.create(this)
53+
54+
utils.transferFlags(this as unknown as Chai.Assertion, that, false)
55+
utils.flag(that, 'object', item)
56+
57+
new chai.Assertion(that)
58+
}
59+
60+
return void undefined
61+
}
62+
63+
/**
64+
* @this {void}
65+
*
66+
* @param {unknown} thing
67+
* The value to check
68+
* @return {List}
69+
* `true` if `thing` is an array or set
70+
*/
71+
function list(this: void, thing: unknown): thing is List {
72+
return Array.isArray(thing) || thing instanceof Set
73+
}
74+
}

0 commit comments

Comments
 (0)