diff --git a/apps/mocksi-lite-next/src/pages/content/mocksi-extension.tsx b/apps/mocksi-lite-next/src/pages/content/mocksi-extension.tsx index a349acd..819e0b6 100644 --- a/apps/mocksi-lite-next/src/pages/content/mocksi-extension.tsx +++ b/apps/mocksi-lite-next/src/pages/content/mocksi-extension.tsx @@ -78,6 +78,7 @@ chrome.runtime.onMessage.addListener((request) => { async function findReplaceAll( find: string, replace: string, + flags: string, highlight: boolean, ) { const modification: ModificationRequest = { @@ -85,7 +86,7 @@ chrome.runtime.onMessage.addListener((request) => { modifications: [ { action: "replaceAll", - content: `/${find}/${replace}/`, + content: `/${find}/${replace}/${flags}`, selector: "body", }, ], @@ -118,8 +119,8 @@ chrome.runtime.onMessage.addListener((request) => { } if (request.message === "NEW_EDIT") { if (request.data) { - const { find, highlightEdits, replace } = request.data; - await findReplaceAll(find, replace, highlightEdits); + const { find, highlightEdits, replace, flags } = request.data; + await findReplaceAll(find, replace, flags, highlightEdits); data = Array.from(reactor.getAppliedModifications()).map( (mod) => mod.modificationRequest, ); diff --git a/packages/reactor/modifications/replaceAll.ts b/packages/reactor/modifications/replaceAll.ts index 61c1152..f3c4aa5 100644 --- a/packages/reactor/modifications/replaceAll.ts +++ b/packages/reactor/modifications/replaceAll.ts @@ -159,7 +159,7 @@ function replaceText( addModifiedElement: (element: Element) => string, addHighlightNode: (node: Node) => void, ): (node: Node) => TreeChange | null { - const { patternRegexp, replacement } = toRegExpPattern(pattern); + const { patternRegexp, replacement, flags } = toRegExpPattern(pattern); return (node: Node) => { let split = node.nodeValue?.split(patternRegexp) || []; @@ -167,7 +167,7 @@ function replaceText( if (index % 2 === 0) { return part; } - return replaceFirstLetterCaseAndPlural(replacement)(part); + return replaceFirstLetterCaseAndPlural(replacement, flags)(part); }); const parentElement = node.parentElement; @@ -213,7 +213,7 @@ function replaceText( }; } -function replaceFirstLetterCaseAndPlural(value: string) { +function replaceFirstLetterCaseAndPlural(value: string, flags: Partial> = {}) { return (match: string) => { let out = value; @@ -225,7 +225,7 @@ function replaceFirstLetterCaseAndPlural(value: string) { } // if the match is plural, add an s - if (match.endsWith("s")) { + if ((flags[PatternFlag.Plurals] ?? false) && match.endsWith("s")) { out = `${out}s`; } @@ -233,18 +233,47 @@ function replaceFirstLetterCaseAndPlural(value: string) { }; } +enum PatternFlag { + CaseInsensitive = "i", + WordBoundary = "w", + Plurals = "p", +} + // Take pattern in the form of /pattern/replacement/ and return {patternRegexp, replacement} function toRegExpPattern(pattern: string): { patternRegexp: RegExp; replacement: string; + flags: Partial>; } { - const match = /\/(.+)\/(.+)\//.exec(pattern); - if (!match || match.length !== 3 || !match[1] || !match[2]) { + const match = /\/(.+)\/(.+)\/(.*)/.exec(pattern); + if (!match || match.length < 3 || !match[1] || !match[2]) { throw new Error(`Invalid pattern: ${pattern}`); } + let reFlags = "g"; + let rePattern = match[1]; + const flags = match[3]; + + const patternFlags: Partial> = {}; + + if (flags?.includes('p')) { + rePattern += "s?"; + patternFlags[PatternFlag.Plurals] = true; + } + + if (flags?.includes('w')) { + rePattern = `\\b${rePattern}\\b`; + patternFlags[PatternFlag.WordBoundary] = true; + } + + if (flags?.includes('i')) { + reFlags += "i"; + patternFlags[PatternFlag.CaseInsensitive] = true; + } + return { - patternRegexp: new RegExp(`(\\b${match[1]}s?\\b)`, "gi"), + patternRegexp: new RegExp(`(${rePattern})`, reFlags), replacement: match[2], + flags: patternFlags, }; } diff --git a/packages/reactor/tests/main.test.ts b/packages/reactor/tests/main.test.ts index 1d360a9..33e9320 100644 --- a/packages/reactor/tests/main.test.ts +++ b/packages/reactor/tests/main.test.ts @@ -36,7 +36,7 @@ describe("modifyHtml", () => { { xpath: "//html/body/div", action: "replaceAll", - content: "/train/brain/", + content: "/train/brain/i", }, ], }); diff --git a/packages/reactor/tests/modifications.test.ts b/packages/reactor/tests/modifications.test.ts index 2067017..62cfc28 100644 --- a/packages/reactor/tests/modifications.test.ts +++ b/packages/reactor/tests/modifications.test.ts @@ -45,7 +45,7 @@ describe("Utils", () => { it("should replace all content correctly", async () => { const modification: Modification = { action: "replaceAll", - content: "/old/new/", + content: "/old/new/i", }; const element = doc.createElement("div"); @@ -74,7 +74,7 @@ describe("Utils", () => { it("should preserve capitals in replacement", async () => { const modification: Modification = { action: "replaceAll", - content: "/old/new/", + content: "/old/new/i", }; const element = doc.createElement("div"); @@ -88,7 +88,7 @@ describe("Utils", () => { it("should preserve plurals in replacement", async () => { const modification: Modification = { action: "replaceAll", - content: "/train/brain/", + content: "/train/brain/ip", }; const element = doc.createElement("div"); @@ -104,7 +104,7 @@ describe("Utils", () => { it("should only replace whole words", async () => { const modification: Modification = { action: "replaceAll", - content: "/train/brain/", + content: "/train/brain/wip", }; const element = doc.createElement("div"); @@ -121,7 +121,7 @@ describe("Utils", () => { it("should handle more complicated HTML", async () => { const modification: Modification = { action: "replaceAll", - content: "/train/brain/", + content: "/train/brain/wip", }; const element = doc.createElement("div"); @@ -156,7 +156,7 @@ describe("Utils", () => { it("should work with multiple text nodes", async () => { const modification: Modification = { action: "replaceAll", - content: "/train/brain/", + content: "/train/brain/wip", }; const element = doc.createElement("div"); diff --git a/packages/reactor/tests/mutation.test.ts b/packages/reactor/tests/mutation.test.ts index ca9cadd..f6a6678 100644 --- a/packages/reactor/tests/mutation.test.ts +++ b/packages/reactor/tests/mutation.test.ts @@ -29,7 +29,7 @@ describe("test mutation listeners", {}, () => { { selector: "body", action: "replaceAll", - content: "/train/brain/", + content: "/train/brain/wip", } ], };