Skip to content

fix(pipeline/template): preserve commas in replace() filter replacement value#1917

Open
Chen17-sq wants to merge 1 commit into
jackwener:mainfrom
Chen17-sq:fix/pipeline-template-replace-comma
Open

fix(pipeline/template): preserve commas in replace() filter replacement value#1917
Chen17-sq wants to merge 1 commit into
jackwener:mainfrom
Chen17-sq:fix/pipeline-template-replace-comma

Conversation

@Chen17-sq

Copy link
Copy Markdown
Contributor

Problem

The replace(old, new) pipeline template filter truncates any replacement value that contains a comma. For example:

item.x | replace('a', 'x,y')   // with item.x === 'a'

returns 'x' instead of the expected 'x,y'.

Root cause

src/pipeline/template.ts:103 (pre-fix) split the raw filter arguments on every comma:

const parts = rawArgs?.split(',').map(s => s.trim().replace(/^['"]|['"]$/g, '')) ?? [];
return parts.length >= 2 ? value.replaceAll(parts[0], parts[1]) : value;

replace('a', 'x,y') has rawArgs === "'a', 'x,y'", which split(',') turns into ["'a'", " 'x", "y'"]. Only parts[0] (a) and parts[1] (x) are used, so the replacement is silently truncated at the first comma in the value.

Fix

Split on the first comma only, treating everything after it as the replacement value:

case 'replace': {
  if (typeof value !== 'string') return value;
  if (rawArgs == null) return value;
  const ci = rawArgs.indexOf(',');
  if (ci === -1) return value;
  const strip = (s: string) => s.trim().replace(/^['"]|['"]$/g, '');
  const oldVal = strip(rawArgs.slice(0, ci));
  const newVal = strip(rawArgs.slice(ci + 1));
  return value.replaceAll(oldVal, newVal);
}

This makes replacement values containing commas work while keeping existing behavior for simple args (single-comma usage, non-string passthrough, single-arg no-op). Quote-stripping and replaceAll semantics are unchanged.

Tests

Added five co-located unit tests in src/pipeline/template.test.ts (the evalExpr filter block had no prior replace coverage, so there is zero regression risk):

  • control: replace('foo','bar') on 'foo baz' -> 'bar baz'
  • the bug: replace('a','x,y') on 'a' -> 'x,y'
  • multiple, unquoted: replace(' ','_') on 'a b c' -> 'a_b_c'
  • non-string passthrough unchanged (42 -> 42)
  • single-arg replace('a') returns the value unchanged

npx vitest run src/pipeline/template.test.ts: 57 passing (52 baseline + 5 new). tsc --noEmit clean. The existing join(,) test is on a separate code path and is unaffected.

Scope

Single-concern: this PR fixes only the replace-filter comma splitting. Two related items are deliberately left out to keep the change surgical:

  • A literal comma needle (replace(',', ';')) remains unsupported by design — the comma is the argument separator. This fix only guarantees the replacement value may contain commas.
  • The separate single-|-in-filter-args concern (e.g. join(|), handled by the pipe tokenizer at template.ts:43) is out of scope; addressing it would require a paren-aware change to the hot-path tokenizer and turn this into a two-concern parser PR. It can be a follow-up if there is demand.

A full quoted-arg parser was intentionally avoided to keep the diff small and low-risk.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant