🦄 magic-regexp
A compiled-away, type-safe, readable RegExp alternative
Features
⚠️ magic-regexp
is currently a work in progress. ⚠️
- Runtime is zero-dependency and ultra-minimal
- Ships with transform for compiling runtime to pure RegExp
- Supports automatically typed capture groups
- Packed with useful utilities:
charIn
, charNotIn
, anyOf
, char
, word
, digit
, whitespace
, letter
, tab
, linefeed
, carriageReturn
, not
, maybe
, exactly
, oneOrMore
- All chainable with
and
, or
, after
, before
, notAfter
, notBefore
, times
, as
, at
, optionally
Future ideas
Setup
Install package:
npm install magic-regexp
yarn add magic-regexp
pnpm install magic-regexp
import { createRegExp, exactly } from 'magic-regexp'
const regExp = createRegExp(exactly('foo/test.js').after('bar/'))
console.log(regExp)
Usage
Every pattern you create with the library should be wrapped in createRegExp
. It also takes a second argument, which is an array of flags.
import { createRegExp, global, multiline } from 'magic-regexp'
createRegExp('string-to-match', [global, multiline])
createRegExp('string-to-match', ['g', 'm'])
Note
By default, all helpers from 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.
There are a range of helpers that can be used to activate pattern matching, and they can be chained.
They are:
charIn
, charNotIn
- this matches or doesn't match any character in the string provided.anyOf
- this takes an array of inputs and matches any of them.char
, word
, digit
, whitespace
, letter
, tab
, linefeed
and carriageReturn
- these are helpers for specific RegExp characters.not
- this can prefix word
, digit
, whitespace
, letter
, tab
, linefeed
or carriageReturn
. For example createRegExp(not.letter)
.maybe
- equivalent to ?
- this marks the input as optional.oneOrMore
- equivalent to +
- this marks the input as repeatable, any number of times but at least once.exactly
- this escapes a string input to match it exactly.
All of these helpers return an object of type Input
that can be chained with the following helpers:
and
- this adds a new pattern to the current input.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 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.optionally
- this is a function you can call to mark the current input as optional.as
- 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()
.at
- this allows you to match beginning/ends of lines with at.lineStart()
and at.lineEnd()
.
Compilation at build
The best way to use magic-regexp
is by making use of the included transform.
const regExp = createRegExp(exactly('foo/test.js').after('bar/'))
const regExp = /(?<=bar\/)foo\/test\.js/
Of course, this only works with non-dynamic regexps. Within the createRegExp
block you have to include all the helpers you are using from magic-regexp
- and not rely on any external variables. This, for example, will not statically compile into a RegExp, although it will still continue to work with a minimal runtime:
const someString = 'test'
const regExp = createRegExp(exactly(someString))
Nuxt
import { defineNuxtConfig } from 'nuxt'
export default defineNuxtConfig({
modules: ['magic-regexp/nuxt'],
})
Vite
import { defineConfig } from 'vite'
import { MagicRegExpTransformPlugin } from 'magic-regexp/transform'
export default defineConfig({
plugins: [MagicRegExpTransformPlugin.vite()],
})
Next.js
For Next, you will need to ensure you are using next.config.mjs
or have set "type": "module"
in your `package.json.
import { MagicRegExpTransformPlugin } from 'magic-regexp/transform'
export default {
webpack(config) {
config.plugins = config.plugins || []
config.plugins.push(MagicRegExpTransformPlugin.webpack())
return config
},
}
unbuild
import { defineBuildConfig } from 'unbuild'
import { MagicRegExpTransformPlugin } from 'magic-regexp/transform'
export default defineBuildConfig({
hooks: {
'rollup:options': (options, config) => {
config.plugins.push(MagicRegExpTransformPlugin.rollup())
},
},
})
Examples
import { createRegExp, exactly, oneOrMore, digit } from 'magic-regexp'
createRegExp(
oneOrMore(digit)
.as('major')
.and('.')
.and(oneOrMore(digit).as('minor'))
.and(exactly('.').and(oneOrMore(char).as('patch')).optionally())
)
💻 Development
- Clone this repository
- Enable Corepack using
corepack enable
(use npm i -g corepack
for Node.js < 16.10) - Install dependencies using
pnpm install
- Run interactive tests using
pnpm dev
Similar packages
License
Made with ❤️
Published under MIT License.