Skip to content
Open
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
12 changes: 12 additions & 0 deletions packages/csv-stringify/lib/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,17 @@ const stringifier = function (options, state, info) {
const containsQuote = quote !== "" && value.indexOf(quote) >= 0;
const containsEscape = value.indexOf(escape) >= 0 && escape !== quote;
const containsRecordDelimiter = value.indexOf(record_delimiter) >= 0;
// With a newline-based record delimiter, a field containing either
// line-break character must be quoted: the parser treats both `\r`
// and `\n` as record delimiters, so an unquoted value (e.g. a lone
// `\r` when the delimiter is `\n`) would be split apart on the next
// parse. A custom, non-newline delimiter leaves line breaks as data.
const recordDelimiterIsNewline =
record_delimiter.indexOf("\n") >= 0 ||
record_delimiter.indexOf("\r") >= 0;
const containsLineBreak =
recordDelimiterIsNewline &&
(value.indexOf("\r") >= 0 || value.indexOf("\n") >= 0);
const quotedString = quoted_string && typeof field === "string";
let quotedMatch =
quoted_match &&
Expand Down Expand Up @@ -232,6 +243,7 @@ const stringifier = function (options, state, info) {
containsQuote === true ||
containsdelimiter ||
containsRecordDelimiter ||
containsLineBreak ||
quoted ||
quotedString ||
quotedMatch;
Expand Down
3 changes: 2 additions & 1 deletion packages/csv-stringify/test/option.escape_formulas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ describe("Option `escape_formulas`", function () {
"'-c,3",
"'@d,4",
"'\te,5",
"'\rf,6",
// The carriage return forces quoting so the field round-trips.
"\"'\rf\",6",
"g,7",
"'\uFF1Dh,8",
"'\uFF0Bi,9",
Expand Down
10 changes: 10 additions & 0 deletions packages/csv-stringify/test/option.quote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,16 @@ describe("Option `quote`", function () {
);
});

it("quotes a field containing a carriage return", function (next) {
// A lone "\r" is treated as a record delimiter by the parser, so it must
// be quoted to survive a round trip (it previously leaked through unquoted).
stringify([["a\rb"]], { eof: false }, (err, data) => {
if (err) return next(err);
data.should.eql('"a\rb"');
next();
});
});

it("field where quote string is empty", function (next) {
stringify(
[
Expand Down