A compiled-away, type-safe, readable RegExp alternative
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
- More TypeScript guard-rails
- More complex RegExp features/syntax
- Instrumentation for accurately getting coverage on RegExps
- Hybrid/partially-compiled RegExps for better dynamic support
Install package:
# npm
npm install magic-regexp
# yarn
yarn add magic-regexp
# pnpm
pnpm install magic-regexpimport { createRegExp, exactly } from 'magic-regexp'
const regExp = createRegExp(exactly('foo/test.js').after('bar/'))
console.log(regExp)
// /(?<=bar\/)foo\/test\.js/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])
// you can also pass flags directly as strings
createRegExp('string-to-match', ['g', 'm'])Note By default, all helpers from
magic-regexpassume that input that is passed should be escaped - so no special RegExp characters apply. SocreateRegExp('foo?\d')will not matchfood3but onlyfoo?\dexactly.
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,linefeedandcarriageReturn- these are helpers for specific RegExp characters.not- this can prefixword,digit,whitespace,letter,tab,linefeedorcarriageReturn. For examplecreateRegExp(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,notAfterandnotBefore- 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 usetimes.between(min, max)to specify a range,times.atLeast(num)to indicate it must repeat x times ortimes.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 withString.match().at- this allows you to match beginning/ends of lines withat.lineStart()andat.lineEnd().
The best way to use magic-regexp is by making use of the included transform.
const regExp = createRegExp(exactly('foo/test.js').after('bar/'))
// => gets _compiled_ to
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))import { defineNuxtConfig } from 'nuxt'
// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
// This will also enable auto-imports of magic-regexp helpers
modules: ['magic-regexp/nuxt'],
})import { defineConfig } from 'vite'
import { MagicRegExpTransformPlugin } from 'magic-regexp/transform'
export default defineConfig({
plugins: [MagicRegExpTransformPlugin.vite()],
})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
},
}import { defineBuildConfig } from 'unbuild'
import { MagicRegExpTransformPlugin } from 'magic-regexp/transform'
export default defineBuildConfig({
hooks: {
'rollup:options': (options, config) => {
config.plugins.push(MagicRegExpTransformPlugin.rollup())
},
},
})import { createRegExp, exactly, oneOrMore, digit } from 'magic-regexp'
// Quick-and-dirty semver
createRegExp(
oneOrMore(digit)
.as('major')
.and('.')
.and(oneOrMore(digit).as('minor'))
.and(exactly('.').and(oneOrMore(char).as('patch')).optionally())
)
// /(?<major>(\d)+)\.(?<minor>(\d)+)(\.(?<patch>(.)+))?/- Clone this repository
- Enable Corepack using
corepack enable(usenpm i -g corepackfor Node.js < 16.10) - Install dependencies using
pnpm install - Run interactive tests using
pnpm dev
Made with ❤️
Published under MIT License.