2026-10-18 · 8 min read
- regex
- javascript
- parsing
A name on a capturing group is an alias for a number. (?<id>\d+) is group 1, and a successful match also exposes the text as the property id. Code can read either form. Flashman's regex tester shows only the numbered form: it builds a RegExp, runs matchAll, and prints the index plus the group array. The names, and the index pairs from the d flag, are not on the panel.
That split causes a false bug report. The panel line for a12 b34 with (?<id>\d+) and flag g is @1 groups ["12"] and @5 groups ["34"]. The engine had id on both matches. The tester did not drop the capture. It never rendered the name.
Make the pattern global before you read the list
matchAll rejects a non-global expression. An empty flag string, or d without g, throws String.prototype.matchAll called with a non-global RegExp argument. Add g. The d flag may stay beside it if you want indexes in your own code; dg still prints only the numbered groups here. The tester also stops the list at 50 matches.
A non-capturing group does not create a slot. (?:\d+) with g on a12 prints the match 12 and no groups line. If you expected a capture and the line has none, check for ?: before you add a name. A name on a non-capturing group is not valid syntax.
- Include g in the flag box.
- Count opening capturing parentheses from the left.
- Write each name next to its number in the fixture.
- Ignore the d flag when you are reading this panel.
Keep nulls and absent names apart
(?<word>\w+)-(?<num>\d+) on user-12 prints ["user","12"]. word is slot 1 and num is slot 2. The order is the order of the opening parentheses, not alphabetical order. Renaming a group does not move the slot. Inserting a new pair of parentheses in front of it does.
Alternatives are where the panel and the names object diverge. (?<a>a)|(?<b>b) on ab prints ["a",null] and then [null,"b"]. Every capturing pair has a numbered slot, and the pair that did not match is null. The names object on those matches is {"a":"a"} and then {"b":"b"}. The name that did not participate is omitted. If you build a names object by zipping the panel's nulls to a fixed list of names, you will store a null that JavaScript's groups object does not have.
Repeated names are a syntax error here
(?<a>a)|(?<a>b) throws Invalid regular expression with Duplicate capture group name under gu and under gv. Do not plan on one Unicode mode accepting the duplicate. Give the branches different names, or capture them under one name outside the alternation if the grammar allows it.
A syntax error produces no match list. Diffing an empty list against a previous run hides the exception. Copy the error text into the fixture next to the pattern and the flags.
Store the name map with the panel output
Save the pattern, the flags, the text, the panel lines, and a JSON object of the names you intend, in numbered order. For the id example the object is {"1":"id"}. For the word example it is {"1":"word","2":"num"}. A later reader can apply those names to the printed array without rerunning the tester.
Use the regex tester for the numbered list, the JSON formatter for that map, and diff when two versions of the pattern disagree. Changing a name without changing parentheses does not change the panel. Changing parentheses without changing the map makes the map lie. Update both in the same edit.