Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions packages/csv-stringify/lib/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,15 +253,10 @@ const stringifier = function (options, state, info) {
quotedString ||
quotedMatch;
if (shouldQuote === true && containsEscape === true) {
const regexp =
escape === "\\"
? new RegExp(escape + escape, "g")
: new RegExp(escape, "g");
value = value.replace(regexp, escape + escape);
value = value.replaceAll(escape, () => escape + escape);
}
if (containsQuote === true) {
const regexp = new RegExp(quote, "g");
value = value.replace(regexp, escape + quote);
value = value.replaceAll(quote, () => escape + quote);
}
if (shouldQuote === true) {
value = quote + value + quote;
Expand Down
26 changes: 26 additions & 0 deletions packages/csv-stringify/test/option.escape.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import "should";
import dedent from "dedent";
import { stringify } from "../lib/index.js";
import { stringify as stringifySync } from "../lib/sync.js";

describe("Option `escape`", function () {
it("default", function (next) {
Expand Down Expand Up @@ -82,4 +83,29 @@ describe("Option `escape`", function () {
},
);
});

it("regexp metacharacter escape is doubled literally (fix #494)", function () {
// The escape character was interpolated into `new RegExp(escape, "g")`.
// Metacharacters (| . * + ? ( [ { ^ $ ...) broke the doubling: "|" and
// "." matched everywhere, "*" threw "Nothing to repeat", "$" anchored.
// A field that must be quoted and contains the escape char must have every
// literal escape occurrence doubled, whatever the character.
// Before #494, the returned value was `"||a|||||b||,||c||"`
stringifySync([["a|b,c"]], { escape: "|", eof: false }).should.eql(
'"a||b,c"',
);
// Before #494, the returned value was `..........`
stringifySync([["a.b,c"]], { escape: ".", eof: false }).should.eql(
'"a..b,c"',
);
// Before #494, an error was thrown `Invalid regular expression: /*/g: Nothing to repeat`
stringifySync([["a*b,c"]], { escape: "*", eof: false }).should.eql(
'"a**b,c"',
);
// "$" is also special in the replacement string, not only the pattern.
// Before #494, the returned value was `"a$b,c$"`
stringifySync([["a$b,c"]], { escape: "$", eof: false }).should.eql(
'"a$$b,c"',
);
});
});
15 changes: 15 additions & 0 deletions packages/csv-stringify/test/option.quote.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import "should";
import dedent from "dedent";
import { stringify } from "../lib/index.js";
import { stringify as stringifySync } from "../lib/sync.js";

describe("Option `quote`", function () {
it("default", function (next) {
Expand Down Expand Up @@ -225,4 +226,18 @@ describe("Option `quote`", function () {
},
);
});

it("regex metacharacter quote is doubled literally (fix #494)", function () {
// See "Option `escape` - regexp metacharacter escape is doubled literally (fix #494)"
// Same class of bug on `new RegExp(quote, "g")` when the quote character is
// a regexp metacharacter and appears inside the field.
// Before #494, the returned value was `."."."..`
stringifySync([["a.b"]], { quote: ".", eof: false }).should.eql('.a".b.');
// Before #494, the returned value was `|\|a\||\|b\||`
stringifySync([["a|b"]], {
quote: "|",
escape: "\\",
eof: false,
}).should.eql("|a\\|b|");
});
});