Quick-and-dirty semver
import { createRegExp, exactly, maybe, oneOrMore, digit, char } 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 { createRegExp, wordChar, char, oneOrMore } 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)
This feature is still experimental, to try it please import
createRegExp
and all Input
helpers from magic-regexp/further-magic
instead of magic-regexp
.When matching or replacing with literal string such as magic-regexp v3.2.5.beta.1 just release!
import {
createRegExp,
oneOrMore,
exactly,
anyOf,
digit,
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
let 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