Skip to content

Instantly share code, notes, and snippets.

@rickosborne
Created January 7, 2025 23:25
Show Gist options
  • Save rickosborne/c4ade38a471764863193c07be117db71 to your computer and use it in GitHub Desktop.
Save rickosborne/c4ade38a471764863193c07be117db71 to your computer and use it in GitHub Desktop.
Concatenate regular expressions
import { expect } from "chai";
import { describe, it } from "mocha";
import { concatRegExp } from "../concat-reg-exp.js";
describe(concatRegExp.name, () => {
it("works for simple cases", () => {
expect(concatRegExp(/foo/, /bar/)).eql(/foobar/);
expect(concatRegExp(/^foo$/, /^bar$/)).eql(/^foobar$/);
expect(concatRegExp(/foo/, /^bar$/)).eql(/foobar$/);
expect(concatRegExp(/^foo$/, /bar/)).eql(/^foobar/);
expect(concatRegExp(/^foo\$/, /\^bar/)).eql(/^foo\$\^bar/);
expect(concatRegExp(/^foo\\$/, /bar/)).eql(/^foo\\bar/);
});
it("combines flags", () => {
expect(concatRegExp(/foo/is, /bar/ig)).eql(/foobar/isg);
});
});
/**
* Concatenate the patterns (and flags) of regular expressions so they will
* match all at once. This does not attempt to fix any problems, such as
* duplicate named capture groups or incorrectly-numbered backreferences.
*/
export const concatRegExp = (...patterns: RegExp[]): RegExp => {
let joined = "";
const flagSet = new Set<string>();
const addFlags = (flags: string): void => {
flags.split("").forEach((flag) => flagSet.add(flag));
};
let index = 0;
const lastIndex = patterns.length - 1;
for (const pattern of patterns) {
let source = pattern.source;
if (index < lastIndex) {
source = source.replace(/(?<=[^\\]|\\\\)\$$/, "");
}
if (index > 0) {
source = source.replace(/^\^/, "");
}
joined = joined.concat(source);
addFlags(pattern.flags);
index++;
}
return new RegExp(joined, Array.from(flagSet).join(""));
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment