index.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. /*
  2. CSV Parse
  3. Please look at the [project documentation](https://csv.js.org/parse/) for
  4. additional information.
  5. */
  6. import { Transform } from 'stream';
  7. import {is_object} from './utils/is_object.js';
  8. import {transform} from './api/index.js';
  9. import {CsvError} from './api/CsvError.js';
  10. class Parser extends Transform {
  11. constructor(opts = {}){
  12. super({...{readableObjectMode: true}, ...opts, encoding: null});
  13. this.api = transform(opts);
  14. this.api.options.on_skip = (err, chunk) => {
  15. this.emit('skip', err, chunk);
  16. };
  17. // Backward compatibility
  18. this.state = this.api.state;
  19. this.options = this.api.options;
  20. this.info = this.api.info;
  21. }
  22. // Implementation of `Transform._transform`
  23. _transform(buf, _, callback){
  24. if(this.state.stop === true){
  25. return;
  26. }
  27. const err = this.api.parse(buf, false, (record) => {
  28. this.push(record);
  29. }, () => {
  30. this.push(null);
  31. this.end();
  32. // Fix #333 and break #410
  33. // ko: api.stream.iterator.coffee
  34. // ko with v21.4.0, ok with node v20.5.1: api.stream.finished # aborted (with generate())
  35. // ko: api.stream.finished # aborted (with Readable)
  36. // this.destroy()
  37. // Fix #410 and partially break #333
  38. // ok: api.stream.iterator.coffee
  39. // ok: api.stream.finished # aborted (with generate())
  40. // broken: api.stream.finished # aborted (with Readable)
  41. this.on('end', this.destroy);
  42. });
  43. if(err !== undefined){
  44. this.state.stop = true;
  45. }
  46. callback(err);
  47. }
  48. // Implementation of `Transform._flush`
  49. _flush(callback){
  50. if(this.state.stop === true){
  51. return;
  52. }
  53. const err = this.api.parse(undefined, true, (record) => {
  54. this.push(record);
  55. }, () => {
  56. this.push(null);
  57. this.on('end', this.destroy);
  58. });
  59. callback(err);
  60. }
  61. }
  62. const parse = function(){
  63. let data, options, callback;
  64. for(const i in arguments){
  65. const argument = arguments[i];
  66. const type = typeof argument;
  67. if(data === undefined && (typeof argument === 'string' || Buffer.isBuffer(argument))){
  68. data = argument;
  69. }else if(options === undefined && is_object(argument)){
  70. options = argument;
  71. }else if(callback === undefined && type === 'function'){
  72. callback = argument;
  73. }else{
  74. throw new CsvError('CSV_INVALID_ARGUMENT', [
  75. 'Invalid argument:',
  76. `got ${JSON.stringify(argument)} at index ${i}`
  77. ], options || {});
  78. }
  79. }
  80. const parser = new Parser(options);
  81. if(callback){
  82. const records = options === undefined || options.objname === undefined ? [] : {};
  83. parser.on('readable', function(){
  84. let record;
  85. while((record = this.read()) !== null){
  86. if(options === undefined || options.objname === undefined){
  87. records.push(record);
  88. }else{
  89. records[record[0]] = record[1];
  90. }
  91. }
  92. });
  93. parser.on('error', function(err){
  94. callback(err, undefined, parser.api.__infoDataSet());
  95. });
  96. parser.on('end', function(){
  97. callback(undefined, records, parser.api.__infoDataSet());
  98. });
  99. }
  100. if(data !== undefined){
  101. const writer = function(){
  102. parser.write(data);
  103. parser.end();
  104. };
  105. // Support Deno, Rollup doesnt provide a shim for setImmediate
  106. if(typeof setImmediate === 'function'){
  107. setImmediate(writer);
  108. }else{
  109. setTimeout(writer, 0);
  110. }
  111. }
  112. return parser;
  113. };
  114. // export default parse
  115. export { parse, Parser, CsvError };