-
-
Notifications
You must be signed in to change notification settings - Fork 56
/
Copy pathinputs.ts
98 lines (86 loc) · 3.57 KB
/
inputs.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import { createInput, Input } from './internal'
import type { GetValue, EscapeChar } from './types/escape'
import type { Join } from './types/join'
import type {
MapToGroups,
MapToValues,
InputSource,
GetGroup,
MapToCapturedGroupsArr,
GetCapturedGroupsArr,
} from './types/sources'
import { IfUnwrapped, wrap } from './wrap'
export type { Input }
const ESCAPE_REPLACE_RE = /[.*+?^${}()|[\]\\/]/g
/** This matches any character in the string provided */
export const charIn = <T extends string>(chars: T) =>
createInput(`[${chars.replace(/[-\\^\]]/g, '\\$&')}]`) as Input<`[${EscapeChar<T>}]`>
/** This matches any character that is not in the string provided */
export const charNotIn = <T extends string>(chars: T) =>
createInput(`[^${chars.replace(/[-\\^\]]/g, '\\$&')}]`) as Input<`[^${EscapeChar<T>}]`>
/** This takes an array of inputs and matches any of them */
export const anyOf = <New extends InputSource[]>(...args: New) =>
createInput(`(?:${args.map(a => exactly(a)).join('|')})`) as Input<
`(?:${Join<MapToValues<New>>})`,
MapToGroups<New>,
MapToCapturedGroupsArr<New>
>
export const char = createInput('.')
export const word = createInput('\\b\\w+\\b')
export const wordChar = createInput('\\w')
export const wordBoundary = createInput('\\b')
export const digit = createInput('\\d')
export const whitespace = createInput('\\s')
export const letter = Object.assign(createInput('[a-zA-Z]'), {
lowercase: createInput('[a-z]'),
uppercase: createInput('[A-Z]'),
})
export const tab = createInput('\\t')
export const linefeed = createInput('\\n')
export const carriageReturn = createInput('\\r')
export const not = {
wordChar: createInput('\\W'),
wordBoundary: createInput('\\B'),
digit: createInput('\\D'),
whitespace: createInput('\\S'),
letter: Object.assign(createInput('[^a-zA-Z]'), {
lowercase: createInput('[^a-z]'),
uppercase: createInput('[^A-Z]'),
}),
tab: createInput('[^\\t]'),
linefeed: createInput('[^\\n]'),
carriageReturn: createInput('[^\\r]'),
}
/** Equivalent to `?` - this marks the input as optional */
export const maybe = <New extends InputSource>(str: New) =>
createInput(`${wrap(exactly(str))}?`) as Input<
IfUnwrapped<GetValue<New>, `(?:${GetValue<New>})?`, `${GetValue<New>}?`>,
GetGroup<New>,
GetCapturedGroupsArr<New>
>
/** Equivalent to `??` - this marks the input as (Lazy) optional */
export const maybeLazy = <New extends InputSource>(str: New) =>
createInput(`${wrap(exactly(str))}??`) as Input<
IfUnwrapped<GetValue<New>, `(?:${GetValue<New>})??`, `${GetValue<New>}??`>,
GetGroup<New>,
GetCapturedGroupsArr<New>
>
/** This escapes a string input to match it exactly */
export const exactly = <New extends InputSource>(
input: New
): Input<GetValue<New>, GetGroup<New>, GetCapturedGroupsArr<New>> =>
typeof input === 'string' ? (createInput(input.replace(ESCAPE_REPLACE_RE, '\\$&')) as any) : input
/** Equivalent to `+` - this marks the input as repeatable, any number of times but at least once */
export const oneOrMore = <New extends InputSource>(str: New) =>
createInput(`${wrap(exactly(str))}+`) as Input<
IfUnwrapped<GetValue<New>, `(?:${GetValue<New>})+`, `${GetValue<New>}+`>,
GetGroup<New>,
GetCapturedGroupsArr<New>
>
/** Equivalent to `+?` - this marks the input as repeatable, any number of times but at least once (Lazy) */
export const oneOrMoreLazy = <New extends InputSource>(str: New) =>
createInput(`${wrap(exactly(str))}+?`) as Input<
IfUnwrapped<GetValue<New>, `(?:${GetValue<New>})+?`, `${GetValue<New>}+?`>,
GetGroup<New>,
GetCapturedGroupsArr<New>
>