import { createRegExp, exactly } from 'magic-regexp'
const regExp = createRegExp(exactly('foo/test.js').after('bar/'))
console.log(regExp)
// /(?<=bar\/)foo\/test\.js/
createRegExp
Every pattern you create with the library should be wrapped in createRegExp
, which enables the build-time transform.
createRegExp
accepts an arbitrary number of arguments of type string
or Input
(built up using helpers from magic-regexp
), and an optional final argument of an array of flags or a flags string. It creates a MagicRegExp
, which concatenates all the patterns from the arguments that were passed in.
import { createRegExp, global, multiline, exactly } from 'magic-regexp'
createRegExp(exactly('foo').or('bar'))
createRegExp('string-to-match', [global, multiline])
// you can also pass flags directly as strings or Sets
createRegExp('string-to-match', ['g', 'm'])
// or pass in multiple `string` and `input patterns`,
// all inputs will be concatenated to one RegExp pattern
createRegExp(
'foo',
maybe('bar').groupedAs('g1'),
'baz',
[global, multiline]
)
// equivalent to /foo(?<g1>(?:bar)?)baz/gm
magic-regexp
assume that input that is passed should be escaped - so no special RegExp characters apply. So createRegExp('foo?\d')
will not match food3
but only foo?\d
exactly.Creating inputs
There are a range of helpers that can be used to activate pattern matching, and they can be chained. Each one of these returns an object of type Input
that can be passed directly to new RegExp
, createRegExp
, to another helper or chained to produce more complex patterns.
charIn , charNotIn | this matches or doesn't match any character in the string provided. |
anyOf | this takes a variable number of inputs and matches any of them. |
char , word , wordChar , wordBoundary , digit , whitespace , letter , letter.lowercase , letter.uppercase , tab , linefeed and carriageReturn | these are helpers for specific RegExp characters. |
not | this can prefix word , wordChar , wordBoundary , digit , whitespace , letter , letter.lowercase , letter.uppercase , tab , linefeed or carriageReturn . For example createRegExp(not.letter) . |
maybe | equivalent to ? - this takes a variable number of inputs and marks them as optional. |
oneOrMore | Equivalent to + - this takes a variable number of inputs and marks them as repeatable, any number of times but at least once. |
exactly | This takes a variable number of inputs and concatenate their patterns, and escapes string inputs to match it exactly. |
string
and Input
are variadic functions, so you can pass in one or multiple arguments of string
or Input
to them and they will be concatenated to one pattern. for example, exactly('foo', maybe('bar'))
is equivalent to exactly('foo').and(maybe('bar'))
.Chaining inputs
All of the helpers above return an object of type Input
that can be chained with the following helpers:
and | this takes a variable number of inputs and adds them as new pattern to the current input, or you can use and.referenceTo(groupName) to adds a new pattern referencing to a named group. |
or | this takes a variable number of inputs and provides as an alternative to the current input. |
after , before , notAfter and notBefore | these takes a variable number of inputs and activate positive/negative lookahead/lookbehinds. Make sure to check browser support 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(x) to indicate it must repeat at least x times, times.atMost(x) to indicate it must repeat at most x times or times.any() to indicate it can repeat any number of times, including none. |
optionally | this is a function you can call to mark the current input as optional. |
as | alias for groupedAs |
groupedAs | this defines the entire input so far as a named capture group. You will get type safety when using the resulting RegExp with String.match() . |
grouped | this defines the entire input so far as an anonymous group. |
at | this allows you to match beginning/ends of lines with at.lineStart() and at.lineEnd() . |
anyOf
, maybe
, oneOrMore
, and chaining input helpers such as or
, times(.between/atLeast/any)
, or optionally
will wrap the input in a non-capturing group with (?:)
. You can use chaining input helper grouped
after any Input
type to capture it as an anonymous group.Debugging
When using magic-regexp
, a TypeScript generic is generated for you that should show the RegExp that you are constructing, as you go.
This is true not just for the final RegExp, but also for the pieces you create along the way.
So, for example:
import { exactly } from 'magic-regexp'
exactly('test.mjs')
// (alias) exactly<"test.mjs">(input: "test.mjs"): Input<"test\\.mjs", never>
exactly('test.mjs').or('something.else')
// (property) Input<"test\\.mjs", never>.or: <"something.else">(input: "something.else") => Input<"(?:test\\.mjs|something\\.else)", never>
Each function, if you hover over it, shows what's going in, and what's coming out by way of regular expression
You can also call .toString()
on any input to see the same information at runtime.
Type-Level match result (experimental)
We also provide an experimental feature that allows you to obtain the type-level results of a RegExp match or replace in string literals. To try this feature, please import all helpers from a subpath export magic-regexp/further-magic
instead of magic-regexp
.
import { createRegExp, exactly, digit } from 'magic-regexp/further-magic'
This feature is especially useful when you want to obtain the type of the matched groups or test if your RegExp matches and captures from a given string as expected.
This feature works best for matching literal strings such as
'foo'.match(createRegExp(exactly('foo').groupedAs('g1')))
which will return a matched result of type ['foo', 'foo']
. result.groups
of type { g1: 'foo' }
, result.index
of type 0
and result.length
of type 2
.
If matching with dynamic string, such as
myString.match(createRegExp(exactly('foo').or('bar').groupedAs('g1')))
the type of the matched result will be null
, or array of union of possible matches ["bar", "bar"] | ["foo", "foo"]
and result.groups
will be type { g1: "bar" } | { g1: "foo" }
.