用法

import { createRegExp, exactly } from 'magic-regexp'

const regExp = createRegExp(exactly('foo/test.js').after('bar/'))
console.log(regExp)

// /(?<=bar\/)foo\/test\.js/

createRegExp

使用该库创建的每个模式都应包裹在 createRegExp 中,这将启用构建时转换。

createRegExp 接受任意数量的 stringInput 类型参数(使用 magic-regexp 中的 helper 构建),以及一个可选的最终参数,该参数为 flags 数组或 flags 字符串。它创建一个 MagicRegExp,该 RegExp 将传入的所有参数中的模式连接起来。

import { createRegExp, exactly, global, maybe, multiline } from 'magic-regexp'

createRegExp(exactly('foo').or('bar'))

createRegExp('string-to-match', [global, multiline])
// 你也可以直接将 flags 作为字符串或 Set 传入
createRegExp('string-to-match', ['g', 'm'])

// 或者传入多个 `string` 和 `input patterns`,
// 所有输入将被连接成一个 RegExp 模式
createRegExp(
  'foo',
  maybe('bar').groupedAs('g1'),
  'baz',
  [global, multiline]
)
// 等同于 /foo(?<g1>(?:bar)?)baz/gm

创建输入

有一系列 helper 可用于激活模式匹配,并且它们可以链式调用。这些 helper 中的每一个都会返回一个 Input 类型对象,可以直接传递给 new RegExpcreateRegExp、另一个 helper,或者链式调用以产生更复杂的模式。

charIn, charNotIn这个匹配或不匹配提供的字符串中的任何字符。
anyOf这个接受可变数量的输入,并匹配其中的任意一个。
char, word, wordChar, wordBoundary, digit, whitespace, letter, letter.lowercase, letter.uppercase, tab, linefeedcarriageReturn这些是特定 RegExp 字符的 helper。
not这个可以前缀 wordwordCharwordBoundarydigitwhitespaceletterletter.lowercaseletter.uppercasetablinefeedcarriageReturn。例如 createRegExp(not.letter)
maybe等同于 ? - 这个接受可变数量的输入,并将它们标记为可选。
oneOrMore等同于 + - 这个接受可变数量的输入,并将它们标记为可重复,任意次数但至少一次。
exactly这个接受可变数量的输入并连接它们的模式,并转义字符串输入以精确匹配它。

链式输入

上述所有 helper 都会返回一个 Input 类型对象,可以与以下 helper 链式调用:

and这个接受可变数量的输入,并将它们作为新模式添加到当前输入中,或者你可以使用 and.referenceTo(groupName) 来添加一个引用命名组的新模式。
or这个接受可变数量的输入,并将其作为当前输入的备选方案。
after, before, notAfternotBefore这些接受可变数量的输入,并激活正向/负向前瞻/后顾。确保检查 浏览器支持,因为并非所有浏览器都支持后顾(特别是 Safari)。
times这个是一个你可以直接调用的函数,用于将前一个模式重复精确次数,或者你可以使用 times.between(min, max) 指定范围、times.atLeast(x) 表示必须至少重复 x 次、times.atMost(x) 表示必须最多重复 x 次,或 times.any() 表示可以重复任意次数,包括零次。
optionally这个是一个你可以调用的函数,用于将当前输入标记为可选。
asgroupedAs 的别名
groupedAs这个将迄今为止的整个输入定义为一个命名捕获组。当使用生成的 RegExp 与 String.match() 时,你将获得类型安全。
grouped这个将迄今为止的整个输入定义为一个匿名组。
at这个允许你使用 at.lineStart()at.lineEnd() 匹配行首/行尾。

调试

在使用 magic-regexp 时,会为你生成一个 TypeScript 泛型,它应该显示你正在构建的 RegExp,随着你的进行。

这不仅适用于最终的 RegExp,还适用于你沿途创建的各个部分。

因此,例如:

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>

每个函数,如果你悬停在它上面,都会显示输入和输出是什么样的正则表达式。

你也可以在任何输入上调用 .toString() 来在运行时查看相同的信息。

类型级匹配结果(实验性)

我们还提供了一个实验性功能,允许你在字符串字面量中获取 RegExp 匹配或替换的类型级结果。要尝试此功能,请从子路径导出 magic-regexp/further-magic 而非 magic-regexp 导入所有 helper。

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

此功能在你想获取匹配组的类型或测试你的 RegExp 是否如预期般匹配并从给定字符串捕获时特别有用。

此功能最适合匹配字符串字面量,例如

'foo'.match(createRegExp(exactly('foo').groupedAs('g1')))

这将返回类型为 ['foo', 'foo'] 的匹配结果。result.groups 的类型为 { g1: 'foo' }result.index 的类型为 0result.length 的类型为 2

如果使用动态字符串进行匹配,例如

myString.match(createRegExp(exactly('foo').or('bar').groupedAs('g1')))

匹配结果的类型将是 null,或可能的匹配联合数组 ["bar", "bar"] | ["foo", "foo"]result.groups 的类型将是 { g1: "bar" } | { g1: "foo" }