Skip to content

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 →