Examples

Quick-and-dirty semver

import { char, createRegExp, digit, maybe, oneOrMore } from 'magic-regexp'

createRegExp(
  oneOrMore(digit).groupedAs('major'),
  '.',
  oneOrMore(digit).groupedAs('minor'),
  maybe('.', oneOrMore(char).groupedAs('patch'))
)
// /(?<major>\d+)\.(?<minor>\d+)(?:\.(?<patch>.+))?/

References to previously captured groups using the group name

import assert from 'node:assert'
import { char, createRegExp, oneOrMore, wordChar } from 'magic-regexp'

const TENET_RE = createRegExp(
  wordChar
    .groupedAs('firstChar')
    .and(wordChar.groupedAs('secondChar'))
    .and(oneOrMore(char))
    .and.referenceTo('secondChar')
    .and.referenceTo('firstChar')
)
// /(?<firstChar>\w)(?<secondChar>\w).+\k<secondChar>\k<firstChar>/

assert.equal(TENET_RE.test('TEN<==O==>NET'), true)

Type-level RegExp match and replace result (experimental)

When matching or replacing with literal string such as magic-regexp v3.2.5.beta.1 just release!

import {
  anyOf,
  createRegExp,
  digit,
  exactly,
  oneOrMore,
  wordChar
} from 'magic-regexp/further-magic'

const literalString = 'magic-regexp 3.2.5.beta.1 just release!'

const semverRegExp = createRegExp(
  oneOrMore(digit)
    .as('major')
    .and('.')
    .and(oneOrMore(digit).as('minor'))
    .and(
      exactly('.')
        .and(oneOrMore(anyOf(wordChar, '.')).groupedAs('patch'))
        .optionally()
    )
)

// `String.match()` example
const matchResult = literalString.match(semverRegExp)
matchResult[0] // "3.2.5.beta.1"
matchResult[3] // "5.beta.1"
matchResult.length // 4
matchResult.index // 14
matchResult.groups
//   groups: {
//     major: "3";
//     minor: "2";
//     patch: "5.beta.1";
//  }

// `String.replace()` example
const replaceResult = literalString.replace(
  semverRegExp,
  `minor version "$2" brings many great DX improvements, while patch "$<patch>" fix some bugs and it's`
)

replaceResult // "magic-regexp minor version \"2\" brings many great DX improvements, while patch \"5.beta.1\" fix some bugs and it's just release!"

When matching dynamic string, the result will be union of possible matches

const myString = 'dynamic'

const RegExp = createRegExp(exactly('foo').or('bar').groupedAs('g1'))
const matchAllResult = myString.match(RegExp)

matchAllResult
// null | RegExpMatchResult<{
//     matched: ["bar", "bar"] | ["foo", "foo"];
//     namedCaptures: ["g1", "bar"] | ["g1", "foo"];
//     input: string;
//     restInput: undefined;
// }>
matchAllResult?.[0] // ['foo', 'foo'] | ['bar', 'bar']
matchAllResult?.length // 2 | undefined
matchAllResult?.groups
// groups: {
//     g1: "foo" | "bar";
// } | undefined