csv.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. "use strict";
  2. const fs = require('fs');
  3. const fastCsv = require('fast-csv');
  4. const customParseFormat = require('dayjs/plugin/customParseFormat');
  5. const utc = require('dayjs/plugin/utc');
  6. const dayjs = require('dayjs').extend(customParseFormat).extend(utc);
  7. const StreamBuf = require('../utils/stream-buf');
  8. const {
  9. fs: {
  10. exists
  11. }
  12. } = require('../utils/utils');
  13. /* eslint-disable quote-props */
  14. const SpecialValues = {
  15. true: true,
  16. false: false,
  17. '#N/A': {
  18. error: '#N/A'
  19. },
  20. '#REF!': {
  21. error: '#REF!'
  22. },
  23. '#NAME?': {
  24. error: '#NAME?'
  25. },
  26. '#DIV/0!': {
  27. error: '#DIV/0!'
  28. },
  29. '#NULL!': {
  30. error: '#NULL!'
  31. },
  32. '#VALUE!': {
  33. error: '#VALUE!'
  34. },
  35. '#NUM!': {
  36. error: '#NUM!'
  37. }
  38. };
  39. /* eslint-ensable quote-props */
  40. class CSV {
  41. constructor(workbook) {
  42. this.workbook = workbook;
  43. this.worksheet = null;
  44. }
  45. async readFile(filename, options) {
  46. options = options || {};
  47. if (!(await exists(filename))) {
  48. throw new Error(`File not found: ${filename}`);
  49. }
  50. const stream = fs.createReadStream(filename);
  51. const worksheet = await this.read(stream, options);
  52. stream.close();
  53. return worksheet;
  54. }
  55. read(stream, options) {
  56. options = options || {};
  57. return new Promise((resolve, reject) => {
  58. const worksheet = this.workbook.addWorksheet(options.sheetName);
  59. const dateFormats = options.dateFormats || ['YYYY-MM-DD[T]HH:mm:ssZ', 'YYYY-MM-DD[T]HH:mm:ss', 'MM-DD-YYYY', 'YYYY-MM-DD'];
  60. const map = options.map || function (datum) {
  61. if (datum === '') {
  62. return null;
  63. }
  64. const datumNumber = Number(datum);
  65. if (!Number.isNaN(datumNumber) && datumNumber !== Infinity) {
  66. return datumNumber;
  67. }
  68. const dt = dateFormats.reduce((matchingDate, currentDateFormat) => {
  69. if (matchingDate) {
  70. return matchingDate;
  71. }
  72. const dayjsObj = dayjs(datum, currentDateFormat, true);
  73. if (dayjsObj.isValid()) {
  74. return dayjsObj;
  75. }
  76. return null;
  77. }, null);
  78. if (dt) {
  79. return new Date(dt.valueOf());
  80. }
  81. const special = SpecialValues[datum];
  82. if (special !== undefined) {
  83. return special;
  84. }
  85. return datum;
  86. };
  87. const csvStream = fastCsv.parse(options.parserOptions).on('data', data => {
  88. worksheet.addRow(data.map(map));
  89. }).on('end', () => {
  90. csvStream.emit('worksheet', worksheet);
  91. });
  92. csvStream.on('worksheet', resolve).on('error', reject);
  93. stream.pipe(csvStream);
  94. });
  95. }
  96. /**
  97. * @deprecated since version 4.0. You should use `CSV#read` instead. Please follow upgrade instruction: https://github.com/exceljs/exceljs/blob/master/UPGRADE-4.0.md
  98. */
  99. createInputStream() {
  100. throw new Error('`CSV#createInputStream` is deprecated. You should use `CSV#read` instead. This method will be removed in version 5.0. Please follow upgrade instruction: https://github.com/exceljs/exceljs/blob/master/UPGRADE-4.0.md');
  101. }
  102. write(stream, options) {
  103. return new Promise((resolve, reject) => {
  104. options = options || {};
  105. // const encoding = options.encoding || 'utf8';
  106. // const separator = options.separator || ',';
  107. // const quoteChar = options.quoteChar || '\'';
  108. const worksheet = this.workbook.getWorksheet(options.sheetName || options.sheetId);
  109. const csvStream = fastCsv.format(options.formatterOptions);
  110. stream.on('finish', () => {
  111. resolve();
  112. });
  113. csvStream.on('error', reject);
  114. csvStream.pipe(stream);
  115. const {
  116. dateFormat,
  117. dateUTC
  118. } = options;
  119. const map = options.map || (value => {
  120. if (value) {
  121. if (value.text || value.hyperlink) {
  122. return value.hyperlink || value.text || '';
  123. }
  124. if (value.formula || value.result) {
  125. return value.result || '';
  126. }
  127. if (value instanceof Date) {
  128. if (dateFormat) {
  129. return dateUTC ? dayjs.utc(value).format(dateFormat) : dayjs(value).format(dateFormat);
  130. }
  131. return dateUTC ? dayjs.utc(value).format() : dayjs(value).format();
  132. }
  133. if (value.error) {
  134. return value.error;
  135. }
  136. if (typeof value === 'object') {
  137. return JSON.stringify(value);
  138. }
  139. }
  140. return value;
  141. });
  142. const includeEmptyRows = options.includeEmptyRows === undefined || options.includeEmptyRows;
  143. let lastRow = 1;
  144. if (worksheet) {
  145. worksheet.eachRow((row, rowNumber) => {
  146. if (includeEmptyRows) {
  147. while (lastRow++ < rowNumber - 1) {
  148. csvStream.write([]);
  149. }
  150. }
  151. const {
  152. values
  153. } = row;
  154. values.shift();
  155. csvStream.write(values.map(map));
  156. lastRow = rowNumber;
  157. });
  158. }
  159. csvStream.end();
  160. });
  161. }
  162. writeFile(filename, options) {
  163. options = options || {};
  164. const streamOptions = {
  165. encoding: options.encoding || 'utf8'
  166. };
  167. const stream = fs.createWriteStream(filename, streamOptions);
  168. return this.write(stream, options);
  169. }
  170. async writeBuffer(options) {
  171. const stream = new StreamBuf();
  172. await this.write(stream, options);
  173. return stream.read();
  174. }
  175. }
  176. module.exports = CSV;
  177. //# sourceMappingURL=csv.js.map