@gruhn/regex-utils
    Preparing search index...

    Class RegexBuilder

    A wrapper class for JavaScript regular expressions that exposes the utility functions of this library and also provides a DSL for constructing regular expression.

    Index

    Methods

    • Constructs the intersection of two regex. This is useful to combine several constraints into one. For example, to build a regular expression that can validate a new password:

      Parameters

      Returns RegexBuilder

      const passwordRegex = RB(/.{12,}/) // 12 letters or more
      .and(/[0-9]/) // at least one number
      .and(/[A-Z]/) // at least one upper case letter
      .and(/[a-z]/) // at least one lower case letter
      .toRegExp()

      function isValidPassword(str: string) {
      return passwordRegex.test(str)
      }

      In most cases it's simpler and more efficient to match each RegExp individually:

      function isValidPassword(str: string) {
      return /.{12,}/.test(str) && /[0-9]/.test(str) && /[A-Z]/.test(str) && /[a-z]/.test(str)
      }

      However, this is not always possible. For example, when a third-party interface expect a single RegExp as input like:

      • Express.js - for route parameter matching and path specifications
      • Yup/Joi/Zod - for string pattern validation
      • Webpack - in various configuration options like test, include, and exclude patterns
      • fast-check - for random string generation during fuzzing / property based testing
    • A generator function that returns a (potentially infinite) stream of strings that match the given RegExp. This can be useful for testing regular expressions.

      Returns Generator<string, any, any>

      const emailRegex = /^[a-z]+@[a-z]+\.[a-z]{2,}$/

      for (const matchedStr of RB(emailRegex).enumerate()) {
      console.log(matchedStr)
      }
      a@a.aa
      b@a.aa
      aa@a.aa
      c@a.aa
      ba@a.aa
      a@b.aa
      d@a.aa
      ca@a.aa
      b@b.aa
      ab@a.aa
      e@a.aa
      da@a.aa
      c@b.aa
      bb@a.aa
      aa@b.aa
      f@a.aa
      ea@a.aa
      d@b.aa
      cb@a.aa
      ba@b.aa
      a@aa.aa
      g@a.aa
      ...
      Warning

      If the regular expression matches infinitely many strings then a loop like above won't terminate.

      Tip

      Use the new Iterator helpers to only get the first N matches, e.g RB(emailRegex).enumerate().take(100).

      The generator produces a fair enumeration. That means every string that matches the regular expression is eventually enumerated. To illustrate, an unfair enumeration of /^(a+|b+)$/ would be:

      "a", "aa", "aaa", "aaaa", "aaaaa", ...
      

      because it never produces any strings of b's. A possible fair enumeration is:

      "a", "b", "aa", "bb", "aaa", "bbb", "aaaa", "bbbb", ...
      
    • Checks if the regex matches no strings at all.

      Returns boolean

      RB('a').isEmpty() // false
      RB('').isEmpty() // false
      RB('a').and('b').isEmpty() // true
      RB(/$.^/).isEmpty() // true
    • Checks if two regular expressions are semantically equivalent, i.e. they match the exact same set of strings.

      Parameters

      Returns boolean

      RB(/a{1,}/).isEquivalent(/a+/) // true
      
    • Constructs the regex complement, i.e. the regex that matches exactly the strings that the current regex is not matching.

      TODO: examples.

      Returns RegexBuilder

    • Constructs quantified regular expressions, subsuming all these regex operators: *, +, {n,m}, ?.

      Parameters

      • bounds: RepeatBounds = ...

      Returns RegexBuilder

      RB('a').repeat(4) // a{4}
      RB('a').repeat({ min: 3, max: 5 }) // a{3,5}
      RB('a').repeat({ max: 5 }) // a{,5}
      RB('a').repeat({ min: 3 }) // a{3,}
      RB('a').repeat({ min: 0, max: 1 }) // a?
      RB('a').repeat({ min: 0 }) // a*
      RB('a').repeat() // a*
    • Returns the number of strings that match the regex or undefined if there are infinitely many matches.

      Returns undefined | bigint

      RB(/^[a-z]$/).size() === 26n

      RB(/^[a-z][0-9]$/).size() === 260n

      // this one has infinitely many matches:
      RB(/^[a-z]*$/).size() === undefined

      // that's why the return type is `bigint`;
      RB(/^[a-z]{60}/).size() === 7914088058189701615326255069116716194962212229317838559326167922356251403772678373376n
      Note

      Double counting is often avoided. For example, RB(/^(hello|hello)$/).size() is only 1n and not 2n. But it probably still happens. The value should always be an upper bound though.

    • Converts back to a native JavaScript RegExp.

      Returns RegExp

      The generated RegExp can be very large if it was constructed with .and(...) or .not(...).

    • Constructs the difference of the current regex and re. That is, this returns a new regex which matches all strings that the current regex matches EXCEPT everything that re matches.

      Parameters

      Returns RegexBuilder

      RB(/^a*$/).without(/^a{5}$/) // /^(a{0,4}|a{6,})$/