Regex Tester — TypeScript source
Test regular expressions against any text with all flags (g/i/m/s/u/y). See every match, its index and capture groups, plus the constructed pattern. 100% in-browser.
This is the TypeScript implementation — the same logic the interactive tool runs, in a shareable, citable form.
/**
* Regular expression tester: match a pattern against input text and report
* each match with its capture groups and character index.
*
* Language: TypeScript
* CosmoDev polyglot showcase port of the `regex-tester` tool.
* Ported from src/tools/RegexTester.tsx - display source, part of CosmoDev's
* polyglot tool pages.
*
* Pure logic only (no React/JSX): compile the pattern with the chosen JS
* flags, walk every match, and surface the full match, its index, and the
* capture groups. Supports the flag set g, i, m, s, u, y. An empty pattern
* yields no matches and no error; a malformed pattern returns the engine's
* error message instead of throwing. The global path is the exact exec loop
* from the island's useMemo - including the zero-width lastIndex advance and
* the 2000-match safety cap.
*/
export const REGEX_FLAGS = ['g', 'i', 'm', 's', 'u', 'y'] as const;
export type RegexFlag = (typeof REGEX_FLAGS)[number];
export interface RegexMatch {
/** Full matched substring (group 0). */
match: string;
/** Character index of the match within the test string. */
index: number;
/** Capture groups (m[1..n]); an entry is `undefined` if the group did not participate. */
groups: string[];
}
export interface RegexResult {
matches: RegexMatch[];
error: string | null;
}
/**
* Run `pattern` (with `flags`) against `testStr` and collect every match.
* With the `g` flag all matches are returned; without it, only the first.
*/
export function runRegex(pattern: string, flags: string, testStr: string): RegexResult {
if (!pattern) return { matches: [], error: null };
try {
const re = new RegExp(pattern, flags);
const out: RegexMatch[] = [];
if (re.global) {
re.lastIndex = 0;
let m: RegExpExecArray | null;
let guard = 0;
while ((m = re.exec(testStr)) !== null) {
out.push({ match: m[0], index: m.index, groups: m.slice(1) });
if (m[0] === '') re.lastIndex++; // advance past zero-width matches
if (++guard > 2000) break;
}
} else {
const m = re.exec(testStr);
if (m) out.push({ match: m[0], index: m.index, groups: m.slice(1) });
}
return { matches: out, error: null };
} catch (e) {
return { matches: [], error: e instanceof Error ? e.message : 'Invalid regular expression' };
}
}
/**
* Render a result the way the tool's UI does: one numbered line per match,
* `N. "match" @ index → [g1, g2]`, or `(none)` when empty. On error the
* error text is returned verbatim.
*/
export function formatMatches(result: RegexResult): string {
if (result.error) return result.error;
if (!result.matches.length) return '(none)';
return result.matches
.map(
(m, i) =>
`${i + 1}. "${m.match}" @ ${m.index}${m.groups.length ? ` → [${m.groups.join(', ')}]` : ''}`,
)
.join('\n');
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →