πΉ Convert patterns from Oniguruma (the regex flavor used by Ruby, TextMate grammars, etc.) to native JavaScript RegExp
See the codeβI think [Oniguruma-To-ES] is very wonderfulβ
β K. Kosako, creator of Oniguruma
Oniguruma is a regular expression engine written in C that's used in Ruby (via a fork named Onigmo), PHP (mb_ereg, etc.), TextMate grammars (used by VS Code, Shiki, etc. for syntax highlighting), and many other tools.
Oniguruma-To-ES is an advanced Oniguruma to JavaScript regex translator that runs in the browser or the server, with support for ~99.99% of Oniguruma regexes (more details below). Use it to:
Compared to running the Oniguruma C library via WASM using vscode-oniguruma, this library is ~4% of the size and its regexes often run much faster (even including transpilation time) since they run as native JavaScript.
[!TIP] You can further reduce bundle size (and increase run-time performance) by precompiling your regexes. In many cases, that avoids the need for any run-time dependency. Conversions for regexes that use certain advanced features rely on a
RegExpsubclass, in which case the tree-shakableEmulatedRegExp(3 kB minzip) is still needed after precompilation.
Oniguruma-To-ES deeply understands the hundreds of large and small differences between Oniguruma and JavaScript regex syntax and behavior, across multiple JavaScript version targets. It's obsessive about ensuring that the emulated features it supports have exactly the same behavior, even in extreme edge cases. And it's been battle-tested on tens of thousands of real-world Oniguruma regexes used in TextMate grammars. It's built on top of oniguruma-parser and Regex+, both by the same author as this library.
toRegExp, toRegExpDetails, EmulatedRegExpaccuracy, avoidSubclass, flags, global, hasIndices, lazyCompileLength, rules, target, verboseimport {toRegExp} from 'oniguruma-to-es';
toRegExp(String.raw`(?x)
(?<n>\d) (?<n>\p{greek}) \k<n>
([0a-z&&\h]){,2}
`);
// β /(?<n>\p{Nd})(\p{sc=Greek})(?>\2|\1)(?:[[0a-z]&&\p{AHex}]){0,2}/v
Although the example above is fairly straightforward, it shows several kinds of differences being translated:
x for insignificant whitespace and comments.(?x) or the \h hex-digit shorthand. Note: ES2025 added support for flag groups like (?i:β¦).Greek, requires nested character classes for intersection of union and ranges, and doesn't allow an implicit 0 min for {β¦} quantifiers.\d is Unicode based by default, backreferences to duplicate group names match the captured value of any of the groups, and (β¦) groups are noncapturing by default if named groups are present.Many advanced features are supported that would produce more complicated transformations.
[!NOTE] The
(?>β¦)atomic group shown in the result was a simplification for readability. Since JavaScript doesn't support atomic groups, the actual result uses(?=(\2|\1))\3for the same effect, and then uses aRegExpsubclass to automatically remove the added capturing group from reported matches.
This next example shows support for Unicode case folding with mixed case-sensitivity. Notice that code points ΕΏ (U+017F) and βͺ (U+212A) are added to the second, case-insensitive range if using a target prior to ES2025, and that modern JavaScript regex features (like flag groups) are used if supported by the target.
toRegExp('[a-z](?i)[a-z]', {target: 'ES2018'});
// β /[a-z][a-zA-ZΕΏβͺ]/u
toRegExp('[a-z](?i)[a-z]', {target: 'ES2025'});
// β /[a-z](?i:[a-z])/v
npm install oniguruma-to-es
import {toRegExp} from 'oniguruma-to-es';
const str = 'β¦';
const pattern = 'β¦';
// Works with all string/regexp methods since it returns a native regexp
str.match(toRegExp(pattern));
<script src="https://cdn.jsdelivr.net/npm/oniguruma-to-es/dist/index.min.js"></script>
<script>
const {toRegExp} = OnigurumaToEs;
</script>
toRegExpAccepts an Oniguruma pattern and returns an equivalent JavaScript RegExp.
[!TIP] Try it in the demo REPL.
function toRegExp(
pattern: string,
options?: ToRegExpOptions
): RegExp | EmulatedRegExp;
ToRegExpOptionstype ToRegExpOptions = {
accuracy?: 'default' | 'strict';
avoidSubclass?: boolean;
flags?: string;
global?: boolean;
hasIndices?: boolean;
lazyCompileLength?: number;
rules?: {
allowOrphanBackrefs?: boolean;
asciiWordBoundaries?: boolean;
captureGroup?: boolean;
recursionLimit?: number;
singleline?: boolean;
};
target?: 'auto' | 'ES2025' | 'ES2024' | 'ES2018';
verbose?: boolean;
};
See Options for more details.
toRegExpDetailsAccepts an Oniguruma pattern and returns the details needed to construct an equivalent JavaScript RegExp.
function toRegExpDetails(
pattern: string,
options?: ToRegExpOptions
): {
pattern: string;
flags: string;
options?: EmulatedRegExpOptions;
};
Note that the returned flags might also be different than those provided, as a result of the emulation process. The returned pattern, flags, and options properties can be provided as arguments to the EmulatedRegExp constructor to produce the same result as toRegExp.
If the only keys returned are pattern and flags, they can optionally be provided to JavaScript's RegExp constructor instead. Setting option avoidSubclass to true ensures that this is always the case (resulting in an error for any patterns that require EmulatedRegExp's additional handling).
EmulatedRegExpWorks the same as JavaScript's native RegExp constructor in all contexts, but can be given results from toRegExpDetails to produce the same result as toRegExp.
class EmulatedRegExp extends RegExp {
constructor(pattern: string, flags?: string, options?: EmulatedRegExpOptions);
constructor(pattern: EmulatedRegExp, flags?: string);
rawOptions: EmulatedRegExpOptions;
}
The rawOptions property of EmulatedRegExp instances can be used for serialization.
EmulatedRegExpOptionstype EmulatedRegExpOptions = {
hiddenCaptures?: Array<number>;
lazyCompile?: boolean;
strategy?: string | null;
transfers?: Array<[number, Array<number>]>;
};
The following options are shared by functions toRegExp and toRegExpDetails.
accuracyOne of 'default' (default) or 'strict'.
Sets the level of emulation rigor/strictness.
target.Using default accuracy adds support for the following features, depending on target:
ES2025 and earlier):
\X using a close approximation of a Unicode extended grapheme cluster.\G that rely on subclass-based emulation.ES2024 and earlier:
ES2018:
[:graph:] and [:print:] using ASCII versions rather than the Unicode versions available for ES2024 and later. Other POSIX classes are always Unicode based.avoidSubclassDefault: false.
Disables advanced emulation that relies on returning a RegExp subclass. In cases when a subclass would otherwise have been used, this results in one of the following:
groups, and indices).flagsOniguruma flags; a string with i, m, x, D, S, W, y{g} in any order (all optional).
Flags i, m, x can also be specified via modifiers in the pattern.
[!IMPORTANT] Oniguruma and JavaScript both have an
mflag but with different meanings. Oniguruma'smis equivalent to JavaScript'ss(dotAll).
globalDefault: false.
Include JavaScript flag g (global) in the result.
hasIndicesDefault: false.
Include JavaScript flag d (hasIndices) in the result.
lazyCompileLengthDefault: Infinity. In other words, lazy compilation is off by default.
Delay regex construction until first use if the transpiled pattern is at least this length.
Although regex construction in JavaScript is fast, it can sometimes be helpful to defer the cost for extremely long patterns. Lazy compilation defers the time JavaScript spends inside the RegExp constructor (building the transpiled pattern into a regex object) until the first time the regex is used in a search. The regex object is outwardly identical before and after deferred compilation.
Lazy compilation relies on the EmulatedRegExp class.
rulesAdvanced options that override standard behavior, error checking, and flags when enabled.
allowOrphanBackrefs: Useful with TextMate grammars that merge backreferences across patterns.asciiWordBoundaries: Use ASCII \b and \B, which increases search performance of generated regexes.captureGroup: Allow unnamed captures and numbered calls (backreferences and subroutines) when using named capture.
ONIG_OPTION_CAPTURE_GROUP; on by default in vscode-oniguruma.recursionLimit: Change the recursion depth limit from Oniguruma's 20 to an integer 2β20.singleline: ^ as \A; $ as \Z. Improves search performance of generated regexes without changing the meaning if searching line by line.
ONIG_OPTION_SINGLELINE.targetOne of 'auto' (default), 'ES2025', 'ES2024', or 'ES2018'.
JavaScript version used for generated regexes. Using auto detects the best value for your environment. Later targets enable faster transpilation, simpler generated source, and support for additional features.
ES2018: Uses JS flag u.
ES2024: Uses JS flag v.
ES2025: Uses JS flag v and allows use of flag groups.
verboseDefault: false.
Disables minifications that simplify the pattern without changing the meaning.
Example: By default, unneeded noncapturing groups might be removed during transpilation. Setting this option to true disables such changes.
[!TIP] The oniguruma-parser library includes a regex optimizer that goes far beyond these basic, built-in minifications. If desired, you can call its optimizer first, and then use the result for transpilation. That isn't appropropriate in all cases (since it adds performance overhead and increases bundle size), but the benefits of optimization do pass through to the transpiled, JavaScript version of a regex.
Following are the supported features by target. The official Oniguruma syntax doc doesn't cover many of the finer details described here.
[!NOTE] Targets
ES2024andES2025have the same emulation capabilities. Resulting regexes might have different source and flags, but they match the same strings. Seetarget.
π = Syntax not available in JavaScript.
π = JavaScript uses slightly different syntax for the same concept; ex: \x{β¦} β \u{β¦}.
Even for features not marked with one of the above symbols, notice that nearly every feature below has at least subtle differences from JavaScript. Unsupported features throw an error.
| Feature | Example | ES2018 | ES2024+ | Subfeatures & JS differences | |
|---|---|---|---|---|---|
| Characters | Literal | E, ! | β | β |
β Code point based matching (same as JS with flag u, v)β Standalone ], {, } don't require escaping |
| Identity escape | \E, \! | β | β |
β Different set than JS β Allows multibyte chars | |
| Escaped metachar | \\, \. | β | β |
β Same as JS | |
| Control code escape | \t | β | β |
β The JS set plus \a, \e | |
\xNN | \x7F | β | β |
β Allows 1 hex digit β Above 7F, is UTF-8 encoded byte (β JS)β Error for invalid encoded bytes | |
\uNNNN | \uFFFF | β | β |
β Same as JS with flag u, v | |
π \x{β¦} | \x{A} | β | β |
β Allows leading 0s up to 8 total hex digits | |
| Escaped num | \20 | β | β |
β Can be backref, error, null, octal, identity escape, or any of these combined with literal digits, based on complex rules that differ from JS β Always handles escaped single digit 1-9 outside char class as backref β Allows null with 1-3 0s β Error for octal β₯ 200 | |
| Caret notation |
\cA,π \C-A
| β | β |
β With A-Za-z (JS: only \c form) | |
| Character sets | Digit | \d, \D | β | β |
β Unicode by default (β JS) |
| Word | \w, \W | β | β |
β Unicode by default (β JS) | |
| Whitespace | \s, \S | β | β |
β Unicode by default β No JS adjustments to Unicode set (β \uFEFF, +\x85) | |
| π Hex digit | \h, \H | β | β |
β ASCII | |
| Dot | . | β | β |
β Excludes only \n (β JS) | |
| π Any | \O | β | β |
β Any char (with any flags) β Identity escape in char class | |
π Not \n | \N | β | β |
β Identity escape in char class | |
| π Newline | \R | β | β |
β Matched atomically β Identity escape in char class | |
| π Grapheme | \X | βοΈ | βοΈ |
β Uses a close approximation β Matched atomically β Identity escape in char class | |
| Unicode property |
\p{L},\P{L}
| β | β |
β Binary properties β Categories β Scripts β Aliases β POSIX properties β Invert with \p{^β¦}, \P{^β¦}β Insignificant spaces, hyphens, underscores, and casing in names β \p, \P without { is an identity escapeβ Error for key prefixes β Error for props of strings β Blocks (wontfix[1]) | |
| Character classes | Base | [β¦], [^β¦] | β | β |
β Unescaped - outside of range is literal in some contexts (different than JS rules in any mode)β Leading unescaped ] is literalβ Fewer chars require escaping than JS |
| Range | [a-z] | β | β |
β Same as JS with flag u, vβ Allows \x{β¦} above 10FFFF at end of range to mean last valid code point | |
| π POSIX class |
[[:word:]],[[:^word:]]
| βοΈ[2] | β |
β All use Unicode definitions | |
| Nested class | [β¦[β¦]] | βοΈ[3] | β |
β Same as JS with flag v | |
| Intersection | [β¦&&β¦] | β | β |
β Doesn't require nested classes for intersection of union and ranges β Allows empty segments | |
| Assertions | Line start, end | ^, $ | β | β |
β Always "multiline" β Only \n as newlineβ ^ doesn't match after string-terminating \n |
| π String start, end | \A, \z | β | β |
β Same as JS ^ $ without JS flag m | |
| π String end or before terminating newline | \Z | β | β |
β Only \n as newline | |
| π Search start | \G | β | β |
β Matches at start of match attempt (not end of prev match; advances after 0-length match) | |
| Word boundary | \b, \B | β | β |
β Unicode based (β JS) | |
| Lookaround |
(?=β¦),(?!β¦),(?<=β¦),(?<!β¦)
| β | β |
β Allows variable-length quantifiers and alternation within lookbehind β Lookahead invalid within lookbehind β Capturing groups invalid within negative lookbehind β Negative lookbehind invalid within positive lookbehind | |
| Quantifiers | Greedy, lazy | *, +?, {2,}, etc. | β | β |
β Includes all JS forms β Adds {,n} for min 0β Explicit bounds have upper limit of 100,000 (unlimited in JS) β Error with assertions (same as JS with flag u, v) and directives |
| π Possessive | ?+, *+, ++, {3,2} | β | β |
β + suffix doesn't make {β¦} quantifiers possessive (creates a quantifier chain)β Reversed {β¦} ranges are possessive | |
| π Chained | **, ??+*, {2,3}+, etc. | β | β |
β Further repeats the preceding repetition | |
| Groups | Noncapturing | (?:β¦) | β | β |
β Same as JS |
| π Atomic | (?>β¦) | β | β |
β Supported | |
| Capturing | (β¦) | β | β |
β Is noncapturing if named capture present | |
| Named capturing |
(?<a>β¦),π (?'a'β¦)
| β | β |
β Duplicate names allowed (including within the same alternation path) unless directly referenced by a subroutine β Error for names invalid in Oniguruma (more permissive than JS) | |
| Backreferences | Numbered | \1 | β | β |
β Error if named capture used β Refs the most recent of a capture/subroutine set |
| π Enclosed numbered, relative |
\k<1>,\k'1',\k<-1>,\k'-1'
| β | β |
β Error if named capture used β Allows leading 0s β Refs the most recent of a capture/subroutine set β \k without < or ' is an identity escape | |
| Named |
\k<a>,π \k'a'
| β | β |
β For duplicate group names, rematch any of their matches (multiplex), atomically β Refs the most recent of a capture/subroutine set (no multiplex) β Combination of multiplex and most recent of capture/subroutine set if duplicate name is indirectly created by a subroutine β Error for backref to valid group name that includes -/+ | |
| To nonparticipating groups | βοΈ | βοΈ |
β Error if group to the right[4] β Duplicate names (and subroutines) to the right not included in multiplex β Fail to match (or don't include in multiplex) ancestor groups and groups in preceding alternation paths β Some rare cases are indeterminable at compile time and use the JS behavior of matching an empty string | ||
| Subroutines | π Numbered, relative |
\g<1>,\g'1',\g<-1>,\g'-1',\g<+1>,\g'+1'
| β | β |
β Error if named capture used β Allows leading 0s All subroutines (incl. named): β Allowed before reffed group β Can be nested (any depth) β Reuses flags from the reffed group (ignores local flags) β Replaces most recent captured values (for backrefs) β \g without < or ' is an identity escape |
| π Named |
\g<a>,\g'a'
| β | β |
β Same behavior as numbered β Error if reffed group uses duplicate name | |
| Recursion | π Full pattern |
\g<0>,\g'0'
| βοΈ[5] | βοΈ[5] |
β 20-level depth limit |
| π Numbered, relative, named |
(β¦\g<1>?β¦),(β¦\g<-1>?β¦),(?<a>β¦\g<a>?β¦), etc.
| βοΈ[5] | βοΈ[5] |
β 20-level depth limit | |
| Other | Alternation | β¦|β¦ | β | β |
β Same as JS |
| π Absence repeater[6] | (?~β¦) | β | β |
β Supported | |
| π Comment group | (?#β¦) | β | β |
β Allows escaping \), \\β Comments allowed between a token and its quantifier β Comments between a quantifier and the ?/+ that makes it lazy/possessive changes it to a quantifier chain | |
| π Fail[7] | (*FAIL) | β | β |
β Supported | |
| π Keep | \K | βοΈ | βοΈ |
β Supported at top level if no top-level alternation is used | |
| JS features unknown to Oniguruma are handled using Oniguruma syntax rules | β | β |
β \u{β¦} is an errorβ [], [^] are errorsβ [\q{β¦}] matches q, etc.β [a--b] includes the invalid reversed range a to - | ||
| Invalid Oniguruma syntax | β | β |
β Error | ||
| Flags | Supported in top-level flags and flag modifiers | ||||
| Ignore case | i | β | β |
β Unicode case folding (same as JS with flag u, v)[8] | |
| π Dot all | m | β | β |
β Equivalent to JS flag s | |
| π Extended | x | β | β |
β Unicode whitespace ignored β Line comments with #β Whitespace/comments allowed between a token and its quantifier β Whitespace/comments between a quantifier and the ?/+ that makes it lazy/possessive changes it to a quantifier chainβ Whitespace/comments separate tokens (ex: \1 0)β Whitespace and # not ignored in char classes | |
| Currently supported only in top-level flags | |||||
| π Digit is ASCII | D | β | β |
β ASCII \d, \p{Digit}, etc. | |
| π Space is ASCII | S | β | β |
β ASCII \s, \p{Space}, etc. | |
| π Word is ASCII[9] | W | β | β |
β ASCII \w, \p{Word}, \b, etc. | |
| π Text segment mode is grapheme | y{g} | β | β |
β Grapheme based \X, \y | |
| Flag modifiers | Group | (?im-x:β¦) | β | β |
β Unicode case folding for iβ Allows enabling and disabling the same flag (priority: disable) β Allows lone or multiple - |
| π Directive | (?im-x) | β | β |
β Continues until end of pattern or group (spanning alternatives) | |
| Compile-time options | ONIG_OPTION_CAPTURE_GROUP | β | β |
β Unnamed captures and numbered calls allowed when using named capture | |
ONIG_OPTION_SINGLELINE | β | β |
β ^ β \Aβ $ β \Z | ||
The table above doesn't include all aspects that Oniguruma-To-ES emulates (including error handling, subpattern details on match results, most aspects that work the same as in JavaScript, and many aspects of non-JavaScript features that work the same in the other regex flavors that support them). Where applicable, Oniguruma-To-ES follows the latest version of Oniguruma (6.9.10).
In prefix) are easily emulatable but their character data would significantly increase library weight. They're also rarely used, fundamentally flawed, and arguably unuseful given the availability of Unicode scripts and other properties.ES2018, the specific POSIX classes [:graph:] and [:print:] use ASCII versions rather than the Unicode versions available for target ES2024 and later, and they result in an error if using strict accuracy.ES2018 has limited support for nested, negated character classes.\10 or higher and not as many capturing groups are defined to the left (it's an octal or identity escape).20. Oniguruma-To-ES uses the same limit by default but allows customizing it via the rules.recursionLimit option. Two rare uses of recursion aren't yet supported: overlapping recursions, and use of backreferences when a recursed subpattern contains captures. Patterns that would trigger an infinite recursion error in Oniguruma might find a match in Oniguruma-To-ES (since recursion is bounded), but future versions will detect this and error at transpilation time.(?~| and are extremely rare. Note that absence functions behave differently in Oniguruma and Onigmo.(*β¦) and are extremely rare.i, in rare cases Oniguruma can change the length of certain matches based on Unicode case conversion rules. That behavior isn't reproduced in this library because β the rules are applied inconsistently (report) and β‘ Oniguruma planned to disable case conversion length changes by default in future versions.W and i can result in edge case Oniguruma bugs (report) that aren't reproduced in this library.The following throw errors since they aren't yet supported. They're all extremely rare.
\cx \C-x, meta \M-x \M-\C-x, octal code points \o{β¦}, and octal encoded bytes β₯ \200.\x{H H β¦} \o{O O β¦}.P (POSIX is ASCII) and y{w} (text segment mode is word), and whole-pattern flag C (don't capture group).(?(β¦)β¦), etc.I (ignore-case is ASCII) and L (find longest).(*SKIP).\y \Y.(?{β¦}), and most named callouts.See also the supported features table (above), which describes some additional, rarely-used sub-features that aren't yet supported.
Despite these gaps, ~99.99% of real-world Oniguruma regexes are supported, based on a sample of ~55k regexes used in TextMate grammars. Conditionals were used in three regexes, overlapping recursions in three regexes, and other unsupported features weren't used at all. Some Oniguruma features are so exotic that they aren't used in any public code on GitHub.
Oniguruma-To-ES fully supports mixed case-sensitivity (ex: (?i)a(?-i)a) and handles the Unicode edge cases regardless of JavaScript target.
Oniguruma-To-ES focuses on being lightweight to make it better for use in browsers. This is partly achieved by not including heavyweight Unicode character data, which imposes a few minor/rare restrictions:
ES2018. Use target ES2024 (supported by Node.js 20 and 2023-era browsers) or later if you need support for these features.ES2025, a handful of Unicode properties that target a specific character case (ex: \p{Lower}) can't be used case-insensitively in patterns that contain other characters with a specific case that are used case-sensitively.
A\p{Lower}, (?i)A\p{Lower}, (?i:A)\p{Lower}, (?i)A(?-i)\p{Lower}, and \w(?i)\p{Lower}, but not A(?i)\p{Lower}.\p{β¦} that were added in a later version of Unicode than the environment supports results in a runtime error. This is an extreme edge case since modern JavaScript environments support recent versions of Unicode.Contributions are welcome. See the guide to help you get started.
JsRegex transpiles Ruby regexes to JavaScript. Ruby uses Onigmo, a fork of Oniguruma. Although JsRegex and this library have important differences, JsRegex might be a better fit for some Ruby projects.
Oniguruma-To-ES was created by Steven Levithan and contributors.
If you use or want to support this project, I'd love your help by contributing improvements (guide), sharing it with others, or sponsoring maintenance and development.
JavaScript
100.0%
πΉ Convert patterns from Oniguruma (the regex flavor used by Ruby, TextMate grammars, etc.) to native JavaScript RegExp
See the codeβI think [Oniguruma-To-ES] is very wonderfulβ
β K. Kosako, creator of Oniguruma
Oniguruma is a regular expression engine written in C that's used in Ruby (via a fork named Onigmo), PHP (mb_ereg, etc.), TextMate grammars (used by VS Code, Shiki, etc. for syntax highlighting), and many other tools.
Oniguruma-To-ES is an advanced Oniguruma to JavaScript regex translator that runs in the browser or the server, with support for ~99.99% of Oniguruma regexes (more details below). Use it to:
Compared to running the Oniguruma C library via WASM using vscode-oniguruma, this library is ~4% of the size and its regexes often run much faster (even including transpilation time) since they run as native JavaScript.
[!TIP] You can further reduce bundle size (and increase run-time performance) by precompiling your regexes. In many cases, that avoids the need for any run-time dependency. Conversions for regexes that use certain advanced features rely on a
RegExpsubclass, in which case the tree-shakableEmulatedRegExp(3 kB minzip) is still needed after precompilation.
Oniguruma-To-ES deeply understands the hundreds of large and small differences between Oniguruma and JavaScript regex syntax and behavior, across multiple JavaScript version targets. It's obsessive about ensuring that the emulated features it supports have exactly the same behavior, even in extreme edge cases. And it's been battle-tested on tens of thousands of real-world Oniguruma regexes used in TextMate grammars. It's built on top of oniguruma-parser and Regex+, both by the same author as this library.
toRegExp, toRegExpDetails, EmulatedRegExpaccuracy, avoidSubclass, flags, global, hasIndices, lazyCompileLength, rules, target, verboseimport {toRegExp} from 'oniguruma-to-es';
toRegExp(String.raw`(?x)
(?<n>\d) (?<n>\p{greek}) \k<n>
([0a-z&&\h]){,2}
`);
// β /(?<n>\p{Nd})(\p{sc=Greek})(?>\2|\1)(?:[[0a-z]&&\p{AHex}]){0,2}/v
Although the example above is fairly straightforward, it shows several kinds of differences being translated:
x for insignificant whitespace and comments.(?x) or the \h hex-digit shorthand. Note: ES2025 added support for flag groups like (?i:β¦).Greek, requires nested character classes for intersection of union and ranges, and doesn't allow an implicit 0 min for {β¦} quantifiers.\d is Unicode based by default, backreferences to duplicate group names match the captured value of any of the groups, and (β¦) groups are noncapturing by default if named groups are present.Many advanced features are supported that would produce more complicated transformations.
[!NOTE] The
(?>β¦)atomic group shown in the result was a simplification for readability. Since JavaScript doesn't support atomic groups, the actual result uses(?=(\2|\1))\3for the same effect, and then uses aRegExpsubclass to automatically remove the added capturing group from reported matches.
This next example shows support for Unicode case folding with mixed case-sensitivity. Notice that code points ΕΏ (U+017F) and βͺ (U+212A) are added to the second, case-insensitive range if using a target prior to ES2025, and that modern JavaScript regex features (like flag groups) are used if supported by the target.
toRegExp('[a-z](?i)[a-z]', {target: 'ES2018'});
// β /[a-z][a-zA-ZΕΏβͺ]/u
toRegExp('[a-z](?i)[a-z]', {target: 'ES2025'});
// β /[a-z](?i:[a-z])/v
npm install oniguruma-to-es
import {toRegExp} from 'oniguruma-to-es';
const str = 'β¦';
const pattern = 'β¦';
// Works with all string/regexp methods since it returns a native regexp
str.match(toRegExp(pattern));
<script src="https://cdn.jsdelivr.net/npm/oniguruma-to-es/dist/index.min.js"></script>
<script>
const {toRegExp} = OnigurumaToEs;
</script>
toRegExpAccepts an Oniguruma pattern and returns an equivalent JavaScript RegExp.
[!TIP] Try it in the demo REPL.
function toRegExp(
pattern: string,
options?: ToRegExpOptions
): RegExp | EmulatedRegExp;
ToRegExpOptionstype ToRegExpOptions = {
accuracy?: 'default' | 'strict';
avoidSubclass?: boolean;
flags?: string;
global?: boolean;
hasIndices?: boolean;
lazyCompileLength?: number;
rules?: {
allowOrphanBackrefs?: boolean;
asciiWordBoundaries?: boolean;
captureGroup?: boolean;
recursionLimit?: number;
singleline?: boolean;
};
target?: 'auto' | 'ES2025' | 'ES2024' | 'ES2018';
verbose?: boolean;
};
See Options for more details.
toRegExpDetailsAccepts an Oniguruma pattern and returns the details needed to construct an equivalent JavaScript RegExp.
function toRegExpDetails(
pattern: string,
options?: ToRegExpOptions
): {
pattern: string;
flags: string;
options?: EmulatedRegExpOptions;
};
Note that the returned flags might also be different than those provided, as a result of the emulation process. The returned pattern, flags, and options properties can be provided as arguments to the EmulatedRegExp constructor to produce the same result as toRegExp.
If the only keys returned are pattern and flags, they can optionally be provided to JavaScript's RegExp constructor instead. Setting option avoidSubclass to true ensures that this is always the case (resulting in an error for any patterns that require EmulatedRegExp's additional handling).
EmulatedRegExpWorks the same as JavaScript's native RegExp constructor in all contexts, but can be given results from toRegExpDetails to produce the same result as toRegExp.
class EmulatedRegExp extends RegExp {
constructor(pattern: string, flags?: string, options?: EmulatedRegExpOptions);
constructor(pattern: EmulatedRegExp, flags?: string);
rawOptions: EmulatedRegExpOptions;
}
The rawOptions property of EmulatedRegExp instances can be used for serialization.
EmulatedRegExpOptionstype EmulatedRegExpOptions = {
hiddenCaptures?: Array<number>;
lazyCompile?: boolean;
strategy?: string | null;
transfers?: Array<[number, Array<number>]>;
};
The following options are shared by functions toRegExp and toRegExpDetails.
accuracyOne of 'default' (default) or 'strict'.
Sets the level of emulation rigor/strictness.
target.Using default accuracy adds support for the following features, depending on target:
ES2025 and earlier):
\X using a close approximation of a Unicode extended grapheme cluster.\G that rely on subclass-based emulation.ES2024 and earlier:
ES2018:
[:graph:] and [:print:] using ASCII versions rather than the Unicode versions available for ES2024 and later. Other POSIX classes are always Unicode based.avoidSubclassDefault: false.
Disables advanced emulation that relies on returning a RegExp subclass. In cases when a subclass would otherwise have been used, this results in one of the following:
groups, and indices).flagsOniguruma flags; a string with i, m, x, D, S, W, y{g} in any order (all optional).
Flags i, m, x can also be specified via modifiers in the pattern.
[!IMPORTANT] Oniguruma and JavaScript both have an
mflag but with different meanings. Oniguruma'smis equivalent to JavaScript'ss(dotAll).
globalDefault: false.
Include JavaScript flag g (global) in the result.
hasIndicesDefault: false.
Include JavaScript flag d (hasIndices) in the result.
lazyCompileLengthDefault: Infinity. In other words, lazy compilation is off by default.
Delay regex construction until first use if the transpiled pattern is at least this length.
Although regex construction in JavaScript is fast, it can sometimes be helpful to defer the cost for extremely long patterns. Lazy compilation defers the time JavaScript spends inside the RegExp constructor (building the transpiled pattern into a regex object) until the first time the regex is used in a search. The regex object is outwardly identical before and after deferred compilation.
Lazy compilation relies on the EmulatedRegExp class.
rulesAdvanced options that override standard behavior, error checking, and flags when enabled.
allowOrphanBackrefs: Useful with TextMate grammars that merge backreferences across patterns.asciiWordBoundaries: Use ASCII \b and \B, which increases search performance of generated regexes.captureGroup: Allow unnamed captures and numbered calls (backreferences and subroutines) when using named capture.
ONIG_OPTION_CAPTURE_GROUP; on by default in vscode-oniguruma.recursionLimit: Change the recursion depth limit from Oniguruma's 20 to an integer 2β20.singleline: ^ as \A; $ as \Z. Improves search performance of generated regexes without changing the meaning if searching line by line.
ONIG_OPTION_SINGLELINE.targetOne of 'auto' (default), 'ES2025', 'ES2024', or 'ES2018'.
JavaScript version used for generated regexes. Using auto detects the best value for your environment. Later targets enable faster transpilation, simpler generated source, and support for additional features.
ES2018: Uses JS flag u.
ES2024: Uses JS flag v.
ES2025: Uses JS flag v and allows use of flag groups.
verboseDefault: false.
Disables minifications that simplify the pattern without changing the meaning.
Example: By default, unneeded noncapturing groups might be removed during transpilation. Setting this option to true disables such changes.
[!TIP] The oniguruma-parser library includes a regex optimizer that goes far beyond these basic, built-in minifications. If desired, you can call its optimizer first, and then use the result for transpilation. That isn't appropropriate in all cases (since it adds performance overhead and increases bundle size), but the benefits of optimization do pass through to the transpiled, JavaScript version of a regex.
Following are the supported features by target. The official Oniguruma syntax doc doesn't cover many of the finer details described here.
[!NOTE] Targets
ES2024andES2025have the same emulation capabilities. Resulting regexes might have different source and flags, but they match the same strings. Seetarget.
π = Syntax not available in JavaScript.
π = JavaScript uses slightly different syntax for the same concept; ex: \x{β¦} β \u{β¦}.
Even for features not marked with one of the above symbols, notice that nearly every feature below has at least subtle differences from JavaScript. Unsupported features throw an error.
| Feature | Example | ES2018 | ES2024+ | Subfeatures & JS differences | |
|---|---|---|---|---|---|
| Characters | Literal | E, ! | β | β |
β Code point based matching (same as JS with flag u, v)β Standalone ], {, } don't require escaping |
| Identity escape | \E, \! | β | β |
β Different set than JS β Allows multibyte chars | |
| Escaped metachar | \\, \. | β | β |
β Same as JS | |
| Control code escape | \t | β | β |
β The JS set plus \a, \e | |
\xNN | \x7F | β | β |
β Allows 1 hex digit β Above 7F, is UTF-8 encoded byte (β JS)β Error for invalid encoded bytes | |
\uNNNN | \uFFFF | β | β |
β Same as JS with flag u, v | |
π \x{β¦} | \x{A} | β | β |
β Allows leading 0s up to 8 total hex digits | |
| Escaped num | \20 | β | β |
β Can be backref, error, null, octal, identity escape, or any of these combined with literal digits, based on complex rules that differ from JS β Always handles escaped single digit 1-9 outside char class as backref β Allows null with 1-3 0s β Error for octal β₯ 200 | |
| Caret notation |
\cA,π \C-A
| β | β |
β With A-Za-z (JS: only \c form) | |
| Character sets | Digit | \d, \D | β | β |
β Unicode by default (β JS) |
| Word | \w, \W | β | β |
β Unicode by default (β JS) | |
| Whitespace | \s, \S | β | β |
β Unicode by default β No JS adjustments to Unicode set (β \uFEFF, +\x85) | |
| π Hex digit | \h, \H | β | β |
β ASCII | |
| Dot | . | β | β |
β Excludes only \n (β JS) | |
| π Any | \O | β | β |
β Any char (with any flags) β Identity escape in char class | |
π Not \n | \N | β | β |
β Identity escape in char class | |
| π Newline | \R | β | β |
β Matched atomically β Identity escape in char class | |
| π Grapheme | \X | βοΈ | βοΈ |
β Uses a close approximation β Matched atomically β Identity escape in char class | |
| Unicode property |
\p{L},\P{L}
| β | β |
β Binary properties β Categories β Scripts β Aliases β POSIX properties β Invert with \p{^β¦}, \P{^β¦}β Insignificant spaces, hyphens, underscores, and casing in names β \p, \P without { is an identity escapeβ Error for key prefixes β Error for props of strings β Blocks (wontfix[1]) | |
| Character classes | Base | [β¦], [^β¦] | β | β |
β Unescaped - outside of range is literal in some contexts (different than JS rules in any mode)β Leading unescaped ] is literalβ Fewer chars require escaping than JS |
| Range | [a-z] | β | β |
β Same as JS with flag u, vβ Allows \x{β¦} above 10FFFF at end of range to mean last valid code point | |
| π POSIX class |
[[:word:]],[[:^word:]]
| βοΈ[2] | β |
β All use Unicode definitions | |
| Nested class | [β¦[β¦]] | βοΈ[3] | β |
β Same as JS with flag v | |
| Intersection | [β¦&&β¦] | β | β |
β Doesn't require nested classes for intersection of union and ranges β Allows empty segments | |
| Assertions | Line start, end | ^, $ | β | β |
β Always "multiline" β Only \n as newlineβ ^ doesn't match after string-terminating \n |
| π String start, end | \A, \z | β | β |
β Same as JS ^ $ without JS flag m | |
| π String end or before terminating newline | \Z | β | β |
β Only \n as newline | |
| π Search start | \G | β | β |
β Matches at start of match attempt (not end of prev match; advances after 0-length match) | |
| Word boundary | \b, \B | β | β |
β Unicode based (β JS) | |
| Lookaround |
(?=β¦),(?!β¦),(?<=β¦),(?<!β¦)
| β | β |
β Allows variable-length quantifiers and alternation within lookbehind β Lookahead invalid within lookbehind β Capturing groups invalid within negative lookbehind β Negative lookbehind invalid within positive lookbehind | |
| Quantifiers | Greedy, lazy | *, +?, {2,}, etc. | β | β |
β Includes all JS forms β Adds {,n} for min 0β Explicit bounds have upper limit of 100,000 (unlimited in JS) β Error with assertions (same as JS with flag u, v) and directives |
| π Possessive | ?+, *+, ++, {3,2} | β | β |
β + suffix doesn't make {β¦} quantifiers possessive (creates a quantifier chain)β Reversed {β¦} ranges are possessive | |
| π Chained | **, ??+*, {2,3}+, etc. | β | β |
β Further repeats the preceding repetition | |
| Groups | Noncapturing | (?:β¦) | β | β |
β Same as JS |
| π Atomic | (?>β¦) | β | β |
β Supported | |
| Capturing | (β¦) | β | β |
β Is noncapturing if named capture present | |
| Named capturing |
(?<a>β¦),π (?'a'β¦)
| β | β |
β Duplicate names allowed (including within the same alternation path) unless directly referenced by a subroutine β Error for names invalid in Oniguruma (more permissive than JS) | |
| Backreferences | Numbered | \1 | β | β |
β Error if named capture used β Refs the most recent of a capture/subroutine set |
| π Enclosed numbered, relative |
\k<1>,\k'1',\k<-1>,\k'-1'
| β | β |
β Error if named capture used β Allows leading 0s β Refs the most recent of a capture/subroutine set β \k without < or ' is an identity escape | |
| Named |
\k<a>,π \k'a'
| β | β |
β For duplicate group names, rematch any of their matches (multiplex), atomically β Refs the most recent of a capture/subroutine set (no multiplex) β Combination of multiplex and most recent of capture/subroutine set if duplicate name is indirectly created by a subroutine β Error for backref to valid group name that includes -/+ | |
| To nonparticipating groups | βοΈ | βοΈ |
β Error if group to the right[4] β Duplicate names (and subroutines) to the right not included in multiplex β Fail to match (or don't include in multiplex) ancestor groups and groups in preceding alternation paths β Some rare cases are indeterminable at compile time and use the JS behavior of matching an empty string | ||
| Subroutines | π Numbered, relative |
\g<1>,\g'1',\g<-1>,\g'-1',\g<+1>,\g'+1'
| β | β |
β Error if named capture used β Allows leading 0s All subroutines (incl. named): β Allowed before reffed group β Can be nested (any depth) β Reuses flags from the reffed group (ignores local flags) β Replaces most recent captured values (for backrefs) β \g without < or ' is an identity escape |
| π Named |
\g<a>,\g'a'
| β | β |
β Same behavior as numbered β Error if reffed group uses duplicate name | |
| Recursion | π Full pattern |
\g<0>,\g'0'
| βοΈ[5] | βοΈ[5] |
β 20-level depth limit |
| π Numbered, relative, named |
(β¦\g<1>?β¦),(β¦\g<-1>?β¦),(?<a>β¦\g<a>?β¦), etc.
| βοΈ[5] | βοΈ[5] |
β 20-level depth limit | |
| Other | Alternation | β¦|β¦ | β | β |
β Same as JS |
| π Absence repeater[6] | (?~β¦) | β | β |
β Supported | |
| π Comment group | (?#β¦) | β | β |
β Allows escaping \), \\β Comments allowed between a token and its quantifier β Comments between a quantifier and the ?/+ that makes it lazy/possessive changes it to a quantifier chain | |
| π Fail[7] | (*FAIL) | β | β |
β Supported | |
| π Keep | \K | βοΈ | βοΈ |
β Supported at top level if no top-level alternation is used | |
| JS features unknown to Oniguruma are handled using Oniguruma syntax rules | β | β |
β \u{β¦} is an errorβ [], [^] are errorsβ [\q{β¦}] matches q, etc.β [a--b] includes the invalid reversed range a to - | ||
| Invalid Oniguruma syntax | β | β |
β Error | ||
| Flags | Supported in top-level flags and flag modifiers | ||||
| Ignore case | i | β | β |
β Unicode case folding (same as JS with flag u, v)[8] | |
| π Dot all | m | β | β |
β Equivalent to JS flag s | |
| π Extended | x | β | β |
β Unicode whitespace ignored β Line comments with #β Whitespace/comments allowed between a token and its quantifier β Whitespace/comments between a quantifier and the ?/+ that makes it lazy/possessive changes it to a quantifier chainβ Whitespace/comments separate tokens (ex: \1 0)β Whitespace and # not ignored in char classes | |
| Currently supported only in top-level flags | |||||
| π Digit is ASCII | D | β | β |
β ASCII \d, \p{Digit}, etc. | |
| π Space is ASCII | S | β | β |
β ASCII \s, \p{Space}, etc. | |
| π Word is ASCII[9] | W | β | β |
β ASCII \w, \p{Word}, \b, etc. | |
| π Text segment mode is grapheme | y{g} | β | β |
β Grapheme based \X, \y | |
| Flag modifiers | Group | (?im-x:β¦) | β | β |
β Unicode case folding for iβ Allows enabling and disabling the same flag (priority: disable) β Allows lone or multiple - |
| π Directive | (?im-x) | β | β |
β Continues until end of pattern or group (spanning alternatives) | |
| Compile-time options | ONIG_OPTION_CAPTURE_GROUP | β | β |
β Unnamed captures and numbered calls allowed when using named capture | |
ONIG_OPTION_SINGLELINE | β | β |
β ^ β \Aβ $ β \Z | ||
The table above doesn't include all aspects that Oniguruma-To-ES emulates (including error handling, subpattern details on match results, most aspects that work the same as in JavaScript, and many aspects of non-JavaScript features that work the same in the other regex flavors that support them). Where applicable, Oniguruma-To-ES follows the latest version of Oniguruma (6.9.10).
In prefix) are easily emulatable but their character data would significantly increase library weight. They're also rarely used, fundamentally flawed, and arguably unuseful given the availability of Unicode scripts and other properties.ES2018, the specific POSIX classes [:graph:] and [:print:] use ASCII versions rather than the Unicode versions available for target ES2024 and later, and they result in an error if using strict accuracy.ES2018 has limited support for nested, negated character classes.\10 or higher and not as many capturing groups are defined to the left (it's an octal or identity escape).20. Oniguruma-To-ES uses the same limit by default but allows customizing it via the rules.recursionLimit option. Two rare uses of recursion aren't yet supported: overlapping recursions, and use of backreferences when a recursed subpattern contains captures. Patterns that would trigger an infinite recursion error in Oniguruma might find a match in Oniguruma-To-ES (since recursion is bounded), but future versions will detect this and error at transpilation time.(?~| and are extremely rare. Note that absence functions behave differently in Oniguruma and Onigmo.(*β¦) and are extremely rare.i, in rare cases Oniguruma can change the length of certain matches based on Unicode case conversion rules. That behavior isn't reproduced in this library because β the rules are applied inconsistently (report) and β‘ Oniguruma planned to disable case conversion length changes by default in future versions.W and i can result in edge case Oniguruma bugs (report) that aren't reproduced in this library.The following throw errors since they aren't yet supported. They're all extremely rare.
\cx \C-x, meta \M-x \M-\C-x, octal code points \o{β¦}, and octal encoded bytes β₯ \200.\x{H H β¦} \o{O O β¦}.P (POSIX is ASCII) and y{w} (text segment mode is word), and whole-pattern flag C (don't capture group).(?(β¦)β¦), etc.I (ignore-case is ASCII) and L (find longest).(*SKIP).\y \Y.(?{β¦}), and most named callouts.See also the supported features table (above), which describes some additional, rarely-used sub-features that aren't yet supported.
Despite these gaps, ~99.99% of real-world Oniguruma regexes are supported, based on a sample of ~55k regexes used in TextMate grammars. Conditionals were used in three regexes, overlapping recursions in three regexes, and other unsupported features weren't used at all. Some Oniguruma features are so exotic that they aren't used in any public code on GitHub.
Oniguruma-To-ES fully supports mixed case-sensitivity (ex: (?i)a(?-i)a) and handles the Unicode edge cases regardless of JavaScript target.
Oniguruma-To-ES focuses on being lightweight to make it better for use in browsers. This is partly achieved by not including heavyweight Unicode character data, which imposes a few minor/rare restrictions:
ES2018. Use target ES2024 (supported by Node.js 20 and 2023-era browsers) or later if you need support for these features.ES2025, a handful of Unicode properties that target a specific character case (ex: \p{Lower}) can't be used case-insensitively in patterns that contain other characters with a specific case that are used case-sensitively.
A\p{Lower}, (?i)A\p{Lower}, (?i:A)\p{Lower}, (?i)A(?-i)\p{Lower}, and \w(?i)\p{Lower}, but not A(?i)\p{Lower}.\p{β¦} that were added in a later version of Unicode than the environment supports results in a runtime error. This is an extreme edge case since modern JavaScript environments support recent versions of Unicode.Contributions are welcome. See the guide to help you get started.
JsRegex transpiles Ruby regexes to JavaScript. Ruby uses Onigmo, a fork of Oniguruma. Although JsRegex and this library have important differences, JsRegex might be a better fit for some Ruby projects.
Oniguruma-To-ES was created by Steven Levithan and contributors.
If you use or want to support this project, I'd love your help by contributing improvements (guide), sharing it with others, or sponsoring maintenance and development.
JavaScript
100.0%