Skip to content

Commit

Permalink
feat: add chained input and.referenceToGroup (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
didavid61202 committed Jul 23, 2022
1 parent 040c940 commit a18fccb
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 6 deletions.
2 changes: 1 addition & 1 deletion docs/content/2.getting-started/2.usage.md
Expand Up @@ -51,7 +51,7 @@ All of the helpers above return an object of type `Input` that can be chained wi

| | |
| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `and` | this adds a new pattern to the current input. |
| `and` | this adds a new pattern to the current input, or you can use `and.referenceToGroup(groupName)` to adds a new pattern referencing to a named group. |
| `or` | this provides an alternative to the current input. |
| `after`, `before`, `notAfter` and `notBefore` | these activate positive/negative lookahead/lookbehinds. Make sure to check [browser support](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#browser_compatibility) as not all browsers support lookbehinds (notably Safari). |
| `times` | this is a function you can call directly to repeat the previous pattern an exact number of times, or you can use `times.between(min, max)` to specify a range, `times.atLeast(num)` to indicate it must repeat x times or `times.any()` to indicate it can repeat any number of times, _including none_. |
Expand Down
17 changes: 12 additions & 5 deletions src/core/internal.ts
Expand Up @@ -3,10 +3,15 @@ import type { GetValue } from './types/escape'
import type { InputSource } from './types/sources'

export interface Input<V extends string, G extends string = never> {
/** this adds a new pattern to the current input */
and: <I extends InputSource<string, any>>(
input: I
) => Input<`${V}${GetValue<I>}`, G | (I extends Input<any, infer NewGroups> ? NewGroups : never)>
and: {
/** this adds a new pattern to the current input */
<I extends InputSource<string, any>>(input: I): Input<
`${V}${GetValue<I>}`,
G | (I extends Input<any, infer NewGroups> ? NewGroups : never)
>
/** this adds a new pattern to the current input, with the pattern reference to a named group. */
referenceToGroup: <N extends G>(groupName: N) => Input<`${V}\\k<${N}>`, G>
}
/** this provides an alternative to the current input */
or: <I extends InputSource<string, any>>(
input: I
Expand Down Expand Up @@ -52,7 +57,9 @@ export const createInput = <Value extends string, Groups extends string = never>
): Input<Value, Groups> => {
return {
toString: () => s.toString(),
and: input => createInput(`${s}${exactly(input)}`),
and: Object.assign((input: InputSource<string, any>) => createInput(`${s}${exactly(input)}`), {
referenceToGroup: (groupName: string) => createInput(`${s}\\k<${groupName}>`),
}),
or: input => createInput(`(${s}|${exactly(input)})`),
after: input => createInput(`(?<=${exactly(input)})${s}`),
before: input => createInput(`${s}(?=${exactly(input)})`),
Expand Down
20 changes: 20 additions & 0 deletions test/index.test.ts
Expand Up @@ -101,4 +101,24 @@ describe('inputs', () => {
)
)?.groups?.id
})
it('named backreference to capture groups', () => {
const pattern = exactly('foo')
.as('fooGroup')
.and(exactly('bar').as('barGroup'))
.and('baz')
.and.referenceToGroup('barGroup')
.and.referenceToGroup('fooGroup')
.and.referenceToGroup('barGroup')

expect('foobarbazbarfoobar'.match(createRegExp(pattern))).toMatchInlineSnapshot(`
[
"foobarbazbarfoobar",
"foo",
"bar",
]
`)
expectTypeOf(pattern.and.referenceToGroup).toBeCallableWith('barGroup')
// @ts-expect-error
pattern.and.referenceToGroup('bazgroup')
})
})
6 changes: 6 additions & 0 deletions test/inputs.test.ts
Expand Up @@ -135,6 +135,12 @@ describe('chained inputs', () => {
expect(regexp).toMatchInlineSnapshot('/\\\\\\?test\\\\\\.js/')
expectTypeOf(extractRegExp(val)).toMatchTypeOf<'\\?test\\.js'>()
})
it('and.referenceToGroup', () => {
const val = input.as('namedGroup').and(exactly('any')).and.referenceToGroup('namedGroup')
const regexp = new RegExp(val as any)
expect(regexp).toMatchInlineSnapshot('/\\(\\?<namedGroup>\\\\\\?\\)any\\\\k<namedGroup>/')
expectTypeOf(extractRegExp(val)).toMatchTypeOf<'(?<namedGroup>\\?)any\\k<namedGroup>'>()
})
it('or', () => {
const val = input.or('test.js')
const regexp = new RegExp(val as any)
Expand Down

1 comment on commit a18fccb

@vercel
Copy link

@vercel vercel bot commented on a18fccb Jul 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.