You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					192 lines
				
				5.6 KiB
			
		
		
			
		
	
	
					192 lines
				
				5.6 KiB
			| 
											2 years ago
										 | const fs = require('fs'); | ||
|  | const fastCsv = require('fast-csv'); | ||
|  | const customParseFormat = require('dayjs/plugin/customParseFormat'); | ||
|  | const utc = require('dayjs/plugin/utc'); | ||
|  | const dayjs = require('dayjs').extend(customParseFormat).extend(utc); | ||
|  | const StreamBuf = require('../utils/stream-buf'); | ||
|  | 
 | ||
|  | const { | ||
|  |   fs: {exists}, | ||
|  | } = require('../utils/utils'); | ||
|  | 
 | ||
|  | /* eslint-disable quote-props */ | ||
|  | const SpecialValues = { | ||
|  |   true: true, | ||
|  |   false: false, | ||
|  |   '#N/A': {error: '#N/A'}, | ||
|  |   '#REF!': {error: '#REF!'}, | ||
|  |   '#NAME?': {error: '#NAME?'}, | ||
|  |   '#DIV/0!': {error: '#DIV/0!'}, | ||
|  |   '#NULL!': {error: '#NULL!'}, | ||
|  |   '#VALUE!': {error: '#VALUE!'}, | ||
|  |   '#NUM!': {error: '#NUM!'}, | ||
|  | }; | ||
|  | /* eslint-ensable quote-props */ | ||
|  | 
 | ||
|  | class CSV { | ||
|  |   constructor(workbook) { | ||
|  |     this.workbook = workbook; | ||
|  |     this.worksheet = null; | ||
|  |   } | ||
|  | 
 | ||
|  |   async readFile(filename, options) { | ||
|  |     options = options || {}; | ||
|  |     if (!(await exists(filename))) { | ||
|  |       throw new Error(`File not found: ${filename}`); | ||
|  |     } | ||
|  |     const stream = fs.createReadStream(filename); | ||
|  |     const worksheet = await this.read(stream, options); | ||
|  |     stream.close(); | ||
|  |     return worksheet; | ||
|  |   } | ||
|  | 
 | ||
|  |   read(stream, options) { | ||
|  |     options = options || {}; | ||
|  | 
 | ||
|  |     return new Promise((resolve, reject) => { | ||
|  |       const worksheet = this.workbook.addWorksheet(options.sheetName); | ||
|  | 
 | ||
|  |       const dateFormats = options.dateFormats || [ | ||
|  |         'YYYY-MM-DD[T]HH:mm:ssZ', | ||
|  |         'YYYY-MM-DD[T]HH:mm:ss', | ||
|  |         'MM-DD-YYYY', | ||
|  |         'YYYY-MM-DD', | ||
|  |       ]; | ||
|  |       const map = | ||
|  |         options.map || | ||
|  |         function(datum) { | ||
|  |           if (datum === '') { | ||
|  |             return null; | ||
|  |           } | ||
|  |           const datumNumber = Number(datum); | ||
|  |           if (!Number.isNaN(datumNumber) && datumNumber !== Infinity) { | ||
|  |             return datumNumber; | ||
|  |           } | ||
|  |           const dt = dateFormats.reduce((matchingDate, currentDateFormat) => { | ||
|  |             if (matchingDate) { | ||
|  |               return matchingDate; | ||
|  |             } | ||
|  |             const dayjsObj = dayjs(datum, currentDateFormat, true); | ||
|  |             if (dayjsObj.isValid()) { | ||
|  |               return dayjsObj; | ||
|  |             } | ||
|  |             return null; | ||
|  |           }, null); | ||
|  |           if (dt) { | ||
|  |             return new Date(dt.valueOf()); | ||
|  |           } | ||
|  |           const special = SpecialValues[datum]; | ||
|  |           if (special !== undefined) { | ||
|  |             return special; | ||
|  |           } | ||
|  |           return datum; | ||
|  |         }; | ||
|  | 
 | ||
|  |       const csvStream = fastCsv | ||
|  |         .parse(options.parserOptions) | ||
|  |         .on('data', data => { | ||
|  |           worksheet.addRow(data.map(map)); | ||
|  |         }) | ||
|  |         .on('end', () => { | ||
|  |           csvStream.emit('worksheet', worksheet); | ||
|  |         }); | ||
|  | 
 | ||
|  |       csvStream.on('worksheet', resolve).on('error', reject); | ||
|  | 
 | ||
|  |       stream.pipe(csvStream); | ||
|  |     }); | ||
|  |   } | ||
|  | 
 | ||
|  |   /** | ||
|  |    * @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
 | ||
|  |    */ | ||
|  |   createInputStream() { | ||
|  |     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' | ||
|  |     ); | ||
|  |   } | ||
|  | 
 | ||
|  |   write(stream, options) { | ||
|  |     return new Promise((resolve, reject) => { | ||
|  |       options = options || {}; | ||
|  |       // const encoding = options.encoding || 'utf8';
 | ||
|  |       // const separator = options.separator || ',';
 | ||
|  |       // const quoteChar = options.quoteChar || '\'';
 | ||
|  | 
 | ||
|  |       const worksheet = this.workbook.getWorksheet(options.sheetName || options.sheetId); | ||
|  | 
 | ||
|  |       const csvStream = fastCsv.format(options.formatterOptions); | ||
|  |       stream.on('finish', () => { | ||
|  |         resolve(); | ||
|  |       }); | ||
|  |       csvStream.on('error', reject); | ||
|  |       csvStream.pipe(stream); | ||
|  | 
 | ||
|  |       const {dateFormat, dateUTC} = options; | ||
|  |       const map = | ||
|  |         options.map || | ||
|  |         (value => { | ||
|  |           if (value) { | ||
|  |             if (value.text || value.hyperlink) { | ||
|  |               return value.hyperlink || value.text || ''; | ||
|  |             } | ||
|  |             if (value.formula || value.result) { | ||
|  |               return value.result || ''; | ||
|  |             } | ||
|  |             if (value instanceof Date) { | ||
|  |               if (dateFormat) { | ||
|  |                 return dateUTC | ||
|  |                   ? dayjs.utc(value).format(dateFormat) | ||
|  |                   : dayjs(value).format(dateFormat); | ||
|  |               } | ||
|  |               return dateUTC ? dayjs.utc(value).format() : dayjs(value).format(); | ||
|  |             } | ||
|  |             if (value.error) { | ||
|  |               return value.error; | ||
|  |             } | ||
|  |             if (typeof value === 'object') { | ||
|  |               return JSON.stringify(value); | ||
|  |             } | ||
|  |           } | ||
|  |           return value; | ||
|  |         }); | ||
|  | 
 | ||
|  |       const includeEmptyRows = options.includeEmptyRows === undefined || options.includeEmptyRows; | ||
|  |       let lastRow = 1; | ||
|  |       if (worksheet) { | ||
|  |         worksheet.eachRow((row, rowNumber) => { | ||
|  |           if (includeEmptyRows) { | ||
|  |             while (lastRow++ < rowNumber - 1) { | ||
|  |               csvStream.write([]); | ||
|  |             } | ||
|  |           } | ||
|  |           const {values} = row; | ||
|  |           values.shift(); | ||
|  |           csvStream.write(values.map(map)); | ||
|  |           lastRow = rowNumber; | ||
|  |         }); | ||
|  |       } | ||
|  |       csvStream.end(); | ||
|  |     }); | ||
|  |   } | ||
|  | 
 | ||
|  |   writeFile(filename, options) { | ||
|  |     options = options || {}; | ||
|  | 
 | ||
|  |     const streamOptions = { | ||
|  |       encoding: options.encoding || 'utf8', | ||
|  |     }; | ||
|  |     const stream = fs.createWriteStream(filename, streamOptions); | ||
|  | 
 | ||
|  |     return this.write(stream, options); | ||
|  |   } | ||
|  | 
 | ||
|  |   async writeBuffer(options) { | ||
|  |     const stream = new StreamBuf(); | ||
|  |     await this.write(stream, options); | ||
|  |     return stream.read(); | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | module.exports = CSV; |