From 5f4cbaa8544475cc09804620df26e9c5352d12e7 Mon Sep 17 00:00:00 2001 From: Rick Love Date: Fri, 12 Jan 2024 11:55:21 -0600 Subject: [PATCH 1/5] Allow match replacement functions --- .vscode/settings.json | 3 +++ src/index.ts | 6 +++--- src/stringReplaceStream.ts | 19 +++++++++++++------ 3 files changed, 19 insertions(+), 9 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..00ad71f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "typescript.tsdk": "node_modules\\typescript\\lib" +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 2b5705d..bd8c241 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,10 @@ import { NextFunction, Request, Response } from 'express'; import hijackResponse from 'hijackresponse'; -import stringReplaceStream from './stringReplaceStream'; +import stringReplaceStream, { MatchReplacement } from './stringReplaceStream'; export type Options = Record<'contentTypeFilterRegexp', RegExp>; -export type ReplaceFunction = (req: Request, res: Response) => string; +export type ReplaceFunction = (req: Request, res: Response) => MatchReplacement; const defaultOptions: Options = { contentTypeFilterRegexp: /^text\/|^application\/json$|^application\/xml$/, @@ -39,7 +39,7 @@ export const stringReplace = ( } res.removeHeader('content-length'); - let scopedReplacements: Record; + let scopedReplacements: Record; if (hasFunctionReplacements) { // If we have dynamic replacements, calculate for this request scopedReplacements = { ...stringReplacements }; diff --git a/src/stringReplaceStream.ts b/src/stringReplaceStream.ts index d6a3416..9e3b69c 100644 --- a/src/stringReplaceStream.ts +++ b/src/stringReplaceStream.ts @@ -1,13 +1,17 @@ import { Transform, TransformCallback } from 'stream'; import escapeStringRegexp from 'escape-string-regexp'; +export type MatchReplacement = + | string + | ((substring: string, ...args: any[]) => string); + type Options = { encoding: BufferEncoding; ignoreCase: boolean; }; type Replacer = { matcher: RegExp; - replace: string; + replace: MatchReplacement; }; const defaultOptions: Options = { @@ -16,7 +20,7 @@ const defaultOptions: Options = { }; function buildReplacers( - replacements: Record, + replacements: Record, opts: Options ): Replacer[] { return Object.keys(replacements) @@ -30,7 +34,9 @@ function buildReplacers( })); } -function getMaxSearchLength(replacements: Record): number { +function getMaxSearchLength( + replacements: Record +): number { return Object.keys(replacements).reduce( (acc, search) => Math.max(acc, search.length), 0 @@ -38,7 +44,7 @@ function getMaxSearchLength(replacements: Record): number { } export default function StringReplaceStream( - replacements: Record, + replacements: Record, options: Partial = {} ) { const opts: Options = { ...defaultOptions, ...options }; @@ -65,7 +71,7 @@ export default function StringReplaceStream( body = body .slice(0, replaceBefore) - .replace(replacer.matcher, replacer.replace) + + .replace(replacer.matcher, replacer.replace as string) + body.slice(replaceBefore); }); @@ -98,7 +104,8 @@ export default function StringReplaceStream( } const body = replacers.reduce( - (acc, replacer) => acc.replace(replacer.matcher, replacer.replace), + (acc, replacer) => + acc.replace(replacer.matcher, replacer.replace as string), tail ); cb(null, body); From 43072e066a067a0fed1378923e669a49e1b622b6 Mon Sep 17 00:00:00 2001 From: Rick Love Date: Fri, 12 Jan 2024 12:02:25 -0600 Subject: [PATCH 2/5] improve naming of args --- src/stringReplaceStream.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/stringReplaceStream.ts b/src/stringReplaceStream.ts index 9e3b69c..09835c3 100644 --- a/src/stringReplaceStream.ts +++ b/src/stringReplaceStream.ts @@ -1,9 +1,10 @@ import { Transform, TransformCallback } from 'stream'; import escapeStringRegexp from 'escape-string-regexp'; +/** The replacer string or function passed to string.replace(text: string, replacer: MatchReplacement) */ export type MatchReplacement = | string - | ((substring: string, ...args: any[]) => string); + | ((matchedSubstring: string, ...capturedGroups: string[]) => string); type Options = { encoding: BufferEncoding; From 10834f30ed4450091269107dfae03758b4086596 Mon Sep 17 00:00:00 2001 From: Rick Love Date: Fri, 12 Jan 2024 12:03:39 -0600 Subject: [PATCH 3/5] Improve docs --- src/stringReplaceStream.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stringReplaceStream.ts b/src/stringReplaceStream.ts index 09835c3..b07048d 100644 --- a/src/stringReplaceStream.ts +++ b/src/stringReplaceStream.ts @@ -1,7 +1,7 @@ import { Transform, TransformCallback } from 'stream'; import escapeStringRegexp from 'escape-string-regexp'; -/** The replacer string or function passed to string.replace(text: string, replacer: MatchReplacement) */ +/** The replacer string or function passed to string.replace(text: string, replacer: *MatchReplacement*) */ export type MatchReplacement = | string | ((matchedSubstring: string, ...capturedGroups: string[]) => string); From 473cb676fa1df8be03b21b75b0c13d64050f9bb3 Mon Sep 17 00:00:00 2001 From: Rick Love Date: Fri, 12 Jan 2024 16:28:52 -0600 Subject: [PATCH 4/5] useRegExp match --- src/index.ts | 14 ++++++++++---- src/stringReplaceStream.ts | 12 +++++++----- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/index.ts b/src/index.ts index bd8c241..0c4c8b7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,13 +2,13 @@ import { NextFunction, Request, Response } from 'express'; import hijackResponse from 'hijackresponse'; import stringReplaceStream, { MatchReplacement } from './stringReplaceStream'; -export type Options = Record<'contentTypeFilterRegexp', RegExp>; - export type ReplaceFunction = (req: Request, res: Response) => MatchReplacement; -const defaultOptions: Options = { +const defaultOptions = { contentTypeFilterRegexp: /^text\/|^application\/json$|^application\/xml$/, + useRegExp: false, }; +type Options = typeof defaultOptions; export const stringReplace = ( replacements: Record, @@ -51,7 +51,13 @@ export const stringReplace = ( scopedReplacements = stringReplacements; } - res.pipe(stringReplaceStream(scopedReplacements)).pipe(res); + res + .pipe( + stringReplaceStream(scopedReplacements, { + useRegExp: options.useRegExp, + }) + ) + .pipe(res); } else { return res.unhijack(); } diff --git a/src/stringReplaceStream.ts b/src/stringReplaceStream.ts index b07048d..83d0e36 100644 --- a/src/stringReplaceStream.ts +++ b/src/stringReplaceStream.ts @@ -6,18 +6,20 @@ export type MatchReplacement = | string | ((matchedSubstring: string, ...capturedGroups: string[]) => string); -type Options = { - encoding: BufferEncoding; - ignoreCase: boolean; -}; type Replacer = { matcher: RegExp; replace: MatchReplacement; }; +type Options = { + encoding: BufferEncoding; + ignoreCase: boolean; + useRegExp: boolean; +}; const defaultOptions: Options = { encoding: 'utf8', ignoreCase: true, + useRegExp: false, }; function buildReplacers( @@ -28,7 +30,7 @@ function buildReplacers( .sort((a, b) => b.length - a.length) .map(search => ({ matcher: new RegExp( - escapeStringRegexp(search), + opts.useRegExp ? search : escapeStringRegexp(search), opts.ignoreCase ? 'gmi' : 'gm' ), replace: replacements[search], From eef8e9d443a20b442a403da38471760ddff044e0 Mon Sep 17 00:00:00 2001 From: Rick Love Date: Fri, 12 Jan 2024 16:43:37 -0600 Subject: [PATCH 5/5] update version --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index e3f4aaa..e9cfa79 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "string-replace-middleware", - "version": "1.1.0", + "version": "1.2.0", "description": "Express middleware to replace strings in response stream on the fly", "license": "MIT", "repository": "bfncs/string-replace-middleware", @@ -51,4 +51,4 @@ "singleQuote": true, "trailingComma": "es5" } -} +} \ No newline at end of file