GithubNPM

RXP Guide

Import the Library

RXP can be installed directly through NPM / Yarn:

npm i rxp
yarn add rxp

Once installed, the RXP object can be imported, which contains every aspect of the library

import { RXP } from "rxp"
const { init, optional, either } = RXP

If you want to install specific functions directly, you can specify them in the import:

import { init, optional, either } from "rxp"

Alternatively, you can bypass importing the library altogether and just use the live code editor on the playground page to construct a regex and then copy it directly into your application

Initialize the Constructor

The RXP init function works by accepting text to search for, creating an object with methods to modify the search conditions, and then converts it to a standard regex through the construct command.

init("sample").construct() // 	/sample/
init("sample").atStart.construct() // 	/^(?:sample)/
init("sample").occurs(5).and.atStart.construct("g") //  /^(?:(?:sample){5})/g

The init function combines any number of arguments into a text to search for. String, regex, and even other RXP constructors can be provided to init. String arguments will automatically be escaped, so you can enter the text exactly as you expect to see it:

init("search", " for ", "me").construct() // /search for me/

init("() and []").construct() // /\(\) and \[\]/

const sample = init("sample")
init("test ", /out /, sample).construct() // /test out sample/

Once the search text has been specified with init, alternative text can be specified with or. Any RXP methods used after or will then be applied to both options:

const thisOrThat = init("this").or("that")
thisOrThat.construct()
//  result: /(?:this)|(?:that)/

thisOrThat.occurs(5).construct()
//  result: /(?:(?:this)|(?:that)){5}/

Using this pattern, regex built through RXP become modular and composable, making it easy to reuse or extend them:

const name = init("Jeff")
const namePattern = init(
    name.or("Jefe"), 
    "some stuff", 
    name.isOptional);

const patternsAndPatternsOhMy = init(
    namePattern, 
    namePattern, 
    namePattern.occurs(999)
  ).construct()

Modify Search Conditions

After creating the RXP constructor object, the provided text can be modified with regex search conditions:

sample.occurs(2).construct() // /(?:sample){2}/ 
sample.precededBy("ID: ").construct() // /(?<=ID: )sample/
sample.atStart.construct() // /^(?:sample)/ 
sample.isOptional.construct() // /(?:sample)?/

There are a large number of modifiers available to give full coverage to regex search conditions. You can review these individually on the API page.

By default, groups are noncapturing and searches are lazy, but both settings can be overridden:

sample.isCaptured.construct() // /(sample)/ 
sample.occursOnceOrMore.construct() // /(?:sample)+?/ 
sample.occursOnceOrMore.and.isGreedy.construct() // /(?:sample)+/

Regex variables can be defined with the isVariable method and then passed to other RXP constructors through init as often as needed. When using the construct method, the variables will be rewritten in order of appearance to work as expected. Here is an example using the preset anyDigit (covered below):

const regexVar = anyDigit.occurs(3).and.isVariable("var")
init(regexVar, " with ", regexVar).construct() 
// /(?<var>d{3}) with //k<var>/

Convert to Regex

The construct function can be passed arguments to define the search flags:

sample.construct("g") // /sample/g 
sample.construct("global", "s", "I") // /sample/gsi

If init is passed a regex argument including a flag, the flag will be stripped from it and would need to be added back with construct:

init(/search/g, " for me").construct() // /search for me/

Presets

To use special characters, such as \d to match a digit or \w to match a letter, you can use the presets provided with RXP, such as anyDigit or anyLetter. These are preloaded RXP constructor objects with all the functionality built in:

anyDigit.occurs(3).construct() // /(?:[0123456789]){3}/ 
anyLetter.atEnd.construct() // /(?:\w)$/

Since the init function will automatically escape special characters, if you want to pass them in directly without using presets then you would need to use a standard regex:

// incorrect:
init("\d").construct() // result: /\\d/

// correct:
init(/\d/).construct() // result: /\d/

A full list of presets can be found on the API page

Shorthands

shorthands are a group of functions that create an RXP object and immediately apply a desired search condition. These are provided to improve readability, and there is no functional difference between something like optional("text") and init("text").isOptional.

init(
    optional("("), 
    "text", 
    optional(")")
  .construct() // /(?:()?text(?:))?/

A variety of shorthands are available and can be found on the API page. All of these produce the standard RXP constructor object with the unique exceptions of withBoundaries and wrapRXP.

Error Handling

RXP has been designed with a degree of error handling built in. When combining two search conditions with and would result in an invalid regex, the constructor will not make the subsequent option available, allowing only valid combinations.

This is designed to be intuitive for the user thanks to intellisense, and is not something you should have to think about. The options presented will naturally guide you into creating valid regex.

The actual search conditions are structured into levels, with each one locking out previous levels:

  1. occurs, occursAtLeast, etc.
  2. followedBy, notFollowedBy, etc.
  3. atStart, atEnd
  4. isOptional, isCaptured, isVariable

By applying this structure, it is not possible to follow a method like atStart with options such as occursand precededBy, both of which would result in invalid regex:

/^(?:(?<=this )won't work)/
/(^nor will this){5}/

There is one important caveat here - when using regex or RXP constructors as arguments in init or related functions, the resulting RXP constructor will provide all of the possible options initially, so it is still possible to write invalid regex:

const example = init("text").precededBy("stuff")
example.atEnd // works
example.atStart // X - unavailable

init(example).atStart // invalid regex, but due to RXP composition
// the constructor will still allow this

RXP does a lot to deter mistakes, but it is not fullproof and regex should still be built in a careful manner