From 486f35b4cc59d53d5c046760fe98923ade9201ad Mon Sep 17 00:00:00 2001
From: enghong <enghong.sron@gmail.com>
Date: Thu, 18 Jun 2020 03:34:04 +0200
Subject: [PATCH] Add option to force quotes around values in when exporting to
 csv (#2009)

---
 README.md         | 17 +++++++++--------
 bits/90_utils.js  |  2 +-
 types/index.d.ts  |  3 +++
 xlsx.flow.js      |  2 +-
 xlsx.js           |  2 +-
 xlsx.mini.flow.js |  2 +-
 xlsx.mini.js      |  2 +-
 7 files changed, 17 insertions(+), 13 deletions(-)

diff --git a/README.md b/README.md
index 6ab5dc9..4b32832 100644
--- a/README.md
+++ b/README.md
@@ -2140,14 +2140,15 @@ For the example sheet:
 As an alternative to the `writeFile` CSV type, `XLSX.utils.sheet_to_csv` also
 produces CSV output.  The function takes an options argument:
 
-| Option Name |  Default | Description                                         |
-| :---------- | :------: | :-------------------------------------------------- |
-|`FS`         |  `","`   | "Field Separator"  delimiter between fields         |
-|`RS`         |  `"\n"`  | "Record Separator" delimiter between rows           |
-|`dateNF`     |  FMT 14  | Use specified date format in string output          |
-|`strip`      |  false   | Remove trailing field separators in each record **  |
-|`blankrows`  |  true    | Include blank lines in the CSV output               |
-|`skipHidden` |  false   | Skips hidden rows/columns in the CSV output         |
+| Option Name  |  Default | Description                                         |
+| :----------- | :------: | :-------------------------------------------------- |
+|`FS`          |  `","`   | "Field Separator"  delimiter between fields         |
+|`RS`          |  `"\n"`  | "Record Separator" delimiter between rows           |
+|`dateNF`      |  FMT 14  | Use specified date format in string output          |
+|`strip`       |  false   | Remove trailing field separators in each record **  |
+|`blankrows`   |  true    | Include blank lines in the CSV output               |
+|`skipHidden`  |  false   | Skips hidden rows/columns in the CSV output         |
+|`forceQuotes` |  false   | Force quotes around fields                          |
 
 - `strip` will remove trailing commas from each line under default `FS/RS`
 - `blankrows` must be set to `false` to skip blank lines.
diff --git a/bits/90_utils.js b/bits/90_utils.js
index 34a95c9..3f329b8 100644
--- a/bits/90_utils.js
+++ b/bits/90_utils.js
@@ -99,7 +99,7 @@ function make_csv_row(sheet/*:Worksheet*/, r/*:Range*/, R/*:number*/, cols/*:Arr
 		else if(val.v != null) {
 			isempty = false;
 			txt = ''+format_cell(val, null, o);
-			for(var i = 0, cc = 0; i !== txt.length; ++i) if((cc = txt.charCodeAt(i)) === fs || cc === rs || cc === 34) {txt = "\"" + txt.replace(qreg, '""') + "\""; break; }
+			for(var i = 0, cc = 0; i !== txt.length; ++i) if((cc = txt.charCodeAt(i)) === fs || cc === rs || cc === 34 || o.forceQuotes) {txt = "\"" + txt.replace(qreg, '""') + "\""; break; }
 			if(txt == "ID") txt = '"ID"';
 		} else if(val.f != null && !val.F) {
 			isempty = false;
diff --git a/types/index.d.ts b/types/index.d.ts
index 12a2935..a34d2ef 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -623,6 +623,9 @@ export interface Sheet2CSVOpts extends DateNFOption {
 
     /** Skip hidden rows and columns in the CSV output */
     skipHidden?: boolean;
+
+    /** Force quotes around fields */
+    forceQuotes?: boolean;
 }
 
 export interface OriginOption {
diff --git a/xlsx.flow.js b/xlsx.flow.js
index 675a6c7..e2ac276 100644
--- a/xlsx.flow.js
+++ b/xlsx.flow.js
@@ -21213,7 +21213,7 @@ function make_csv_row(sheet/*:Worksheet*/, r/*:Range*/, R/*:number*/, cols/*:Arr
 		else if(val.v != null) {
 			isempty = false;
 			txt = ''+format_cell(val, null, o);
-			for(var i = 0, cc = 0; i !== txt.length; ++i) if((cc = txt.charCodeAt(i)) === fs || cc === rs || cc === 34) {txt = "\"" + txt.replace(qreg, '""') + "\""; break; }
+			for(var i = 0, cc = 0; i !== txt.length; ++i) if((cc = txt.charCodeAt(i)) === fs || cc === rs || cc === 34 || o.forceQuotes) {txt = "\"" + txt.replace(qreg, '""') + "\""; break; }
 			if(txt == "ID") txt = '"ID"';
 		} else if(val.f != null && !val.F) {
 			isempty = false;
diff --git a/xlsx.js b/xlsx.js
index 8023478..a7c8aa1 100644
--- a/xlsx.js
+++ b/xlsx.js
@@ -21082,7 +21082,7 @@ function make_csv_row(sheet, r, R, cols, fs, rs, FS, o) {
 		else if(val.v != null) {
 			isempty = false;
 			txt = ''+format_cell(val, null, o);
-			for(var i = 0, cc = 0; i !== txt.length; ++i) if((cc = txt.charCodeAt(i)) === fs || cc === rs || cc === 34) {txt = "\"" + txt.replace(qreg, '""') + "\""; break; }
+			for(var i = 0, cc = 0; i !== txt.length; ++i) if((cc = txt.charCodeAt(i)) === fs || cc === rs || cc === 34 || o.forceQuotes) {txt = "\"" + txt.replace(qreg, '""') + "\""; break; }
 			if(txt == "ID") txt = '"ID"';
 		} else if(val.f != null && !val.F) {
 			isempty = false;
diff --git a/xlsx.mini.flow.js b/xlsx.mini.flow.js
index 057c253..5210ae9 100644
--- a/xlsx.mini.flow.js
+++ b/xlsx.mini.flow.js
@@ -8396,7 +8396,7 @@ function make_csv_row(sheet/*:Worksheet*/, r/*:Range*/, R/*:number*/, cols/*:Arr
 		else if(val.v != null) {
 			isempty = false;
 			txt = ''+format_cell(val, null, o);
-			for(var i = 0, cc = 0; i !== txt.length; ++i) if((cc = txt.charCodeAt(i)) === fs || cc === rs || cc === 34) {txt = "\"" + txt.replace(qreg, '""') + "\""; break; }
+			for(var i = 0, cc = 0; i !== txt.length; ++i) if((cc = txt.charCodeAt(i)) === fs || cc === rs || cc === 34 || o.forceQuotes) {txt = "\"" + txt.replace(qreg, '""') + "\""; break; }
 			if(txt == "ID") txt = '"ID"';
 		} else if(val.f != null && !val.F) {
 			isempty = false;
diff --git a/xlsx.mini.js b/xlsx.mini.js
index a1e22d2..680be2f 100644
--- a/xlsx.mini.js
+++ b/xlsx.mini.js
@@ -8292,7 +8292,7 @@ function make_csv_row(sheet, r, R, cols, fs, rs, FS, o) {
 		else if(val.v != null) {
 			isempty = false;
 			txt = ''+format_cell(val, null, o);
-			for(var i = 0, cc = 0; i !== txt.length; ++i) if((cc = txt.charCodeAt(i)) === fs || cc === rs || cc === 34) {txt = "\"" + txt.replace(qreg, '""') + "\""; break; }
+			for(var i = 0, cc = 0; i !== txt.length; ++i) if((cc = txt.charCodeAt(i)) === fs || cc === rs || cc === 34 || o.forceQuotes) {txt = "\"" + txt.replace(qreg, '""') + "\""; break; }
 			if(txt == "ID") txt = '"ID"';
 		} else if(val.f != null && !val.F) {
 			isempty = false;