Coverage

75%
297
224
73

csv.js

76%
218
167
51
LineHitsSource
1// Generated by CoffeeScript 1.3.3
2/*
3Module CSV - Copyright David Worms <open@adaltas.com> (BSD Licensed)
4
5 |---------------| |---------------|---------------| |---------------|
6 | | | | | | |
7 | | | CSV | | |
8 | | | | | | |
9 | Stream | | Writer | Reader | | Stream |
10 | Reader | .pipe( | API | API | ).pipe( | Writer | )
11 | | | | | | |
12 | | | | | | |
13 |---------------| |---------------|---------------| |---------------|
14
15 fs.createReadStream('in'.pipe( csv() ).pipe( fs.createWriteStream('out') )
16*/
17
181var from, options, state, stream, to;
19
201stream = require('stream');
21
221state = require('./state');
23
241options = require('./options');
25
261from = require('./from');
27
281to = require('./to');
29
301module.exports = function() {
3111 var CSV, csv, error, parse, transform, write;
3211 CSV = function() {
3311 this.readable = true;
3411 this.writable = true;
3511 this.state = state();
3611 this.options = options();
3711 this.from = from(this);
3811 this.to = to(this);
3911 return this;
40 };
4111 CSV.prototype.__proto__ = stream.prototype;
42 /*
43
44 `write(data, [preserve])`: Write data
45 -------------------------------------
46
47 Implementation of the StreamWriter API with a larger signature. Data
48 may be a string, a buffer, an array or an object.
49
50 If data is a string or a buffer, it could span multiple lines. If data
51 is an object or an array, it must represent a single line.
52 Preserve is for line which are not considered as CSV data.
53 */
54
5511 CSV.prototype.write = function(data, preserve) {
5612 if (!this.writable) {
570 return;
58 }
5912 if (typeof data === 'string' && !preserve) {
6012 return parse(data);
610 } else if (Array.isArray(data) && !this.state.transforming) {
620 this.state.line = data;
630 return transform();
64 }
650 if (this.state.count === 0 && this.options.to.header === true) {
660 write(this.options.to.columns || this.options.from.columns);
67 }
680 write(data, preserve);
690 if (!this.state.transforming && !preserve) {
700 return this.state.count++;
71 }
72 };
73 /*
74
75 `end()`: Terminate the parsing
76 -------------------------------
77
78 Call this method when no more csv data is to be parsed. It
79 implement the StreamWriter API by setting the `writable`
80 property to "false" and emitting the `end` event.
81 */
82
8311 CSV.prototype.end = function() {
8411 if (!this.writable) {
850 return;
86 }
8711 if (this.state.quoted) {
880 return error(new Error('Quoted field not terminated'));
89 }
9011 if (this.state.field || this.state.lastC === this.options.from.delimiter || this.state.lastC === this.options.from.quote) {
911 if (this.options.from.trim || this.options.from.rtrim) {
920 this.state.field = this.state.field.trimRight();
93 }
941 this.state.line.push(this.state.field);
951 this.state.field = '';
96 }
9711 if (this.state.line.length > 0) {
981 transform();
99 }
10011 if (this.writeStream) {
10111 if (this.state.bufferPosition !== 0) {
10211 this.writeStream.write(this.state.buffer.slice(0, this.state.bufferPosition));
103 }
10411 if (this.options.to.end) {
10511 return this.writeStream.end();
106 } else {
1070 this.emit('end', this.state.count);
1080 return this.readable = false;
109 }
110 } else {
1110 this.emit('end', this.state.count);
1120 return this.readable = false;
113 }
114 };
115 /*
116
117 `transform(callback)`: Register the transformer callback
118 --------------------------------------------------------
119
120 User provided function call on each line to filter, enrich or modify
121 the dataset. The callback is called asynchronously.
122 */
123
12411 CSV.prototype.transform = function(callback) {
1258 this.transformer = callback;
1268 return this;
127 };
12811 csv = new CSV();
129 /*
130 Parse a string which may hold multiple lines.
131 Private state object is enriched on each character until
132 transform is called on a new line
133 */
134
13511 parse = function(chars) {
13612 var c, escapeIsQuoted, i, isEscaped, isQuoted, isReallyEscaped, l, nextChar;
13712 chars = '' + chars;
13812 l = chars.length;
13912 i = 0;
14012 while (i < l) {
1413270 c = chars.charAt(i);
1423270 switch (c) {
143 case csv.options.from.escape:
144 case csv.options.from.quote:
14514 if (csv.state.commented) {
1460 break;
147 }
14814 isReallyEscaped = false;
14914 if (c === csv.options.from.escape) {
15010 nextChar = chars.charAt(i + 1);
15110 escapeIsQuoted = csv.options.from.escape === csv.options.from.quote;
15210 isEscaped = nextChar === csv.options.from.escape;
15310 isQuoted = nextChar === csv.options.from.quote;
15410 if (!(escapeIsQuoted && !csv.state.field && !csv.state.quoted) && (isEscaped || isQuoted)) {
1556 i++;
1566 isReallyEscaped = true;
1576 c = chars.charAt(i);
1586 csv.state.field += c;
159 }
160 }
16114 if (!isReallyEscaped && c === csv.options.from.quote) {
1628 if (csv.state.field && !csv.state.quoted) {
1630 csv.state.field += c;
1640 break;
165 }
1668 if (csv.state.quoted) {
1674 nextChar = chars.charAt(i + 1);
1684 if (nextChar && nextChar !== '\r' && nextChar !== '\n' && nextChar !== csv.options.from.delimiter) {
1690 return error(new Error('Invalid closing quote; found "' + nextChar + '" instead of delimiter "' + csv.options.from.delimiter + '"'));
170 }
1714 csv.state.quoted = false;
1724 } else if (csv.state.field === '') {
1734 csv.state.quoted = true;
174 }
175 }
17614 break;
177 case csv.options.from.delimiter:
178341 if (csv.state.commented) {
1790 break;
180 }
181341 if (csv.state.quoted) {
1820 csv.state.field += c;
183 } else {
184341 if (csv.options.from.trim || csv.options.from.rtrim) {
1850 csv.state.field = csv.state.field.trimRight();
186 }
187341 csv.state.line.push(csv.state.field);
188341 csv.state.field = '';
189 }
190341 break;
191 case '\n':
192 case '\r':
19368 if (csv.state.quoted) {
1940 csv.state.field += c;
1950 break;
196 }
19768 if (!csv.options.from.quoted && csv.state.lastC === '\r') {
1980 break;
199 }
20068 if (csv.options.to.lineBreaks === null) {
20111 csv.options.to.lineBreaks = c + (c === '\r' && chars.charAt(i + 1) === '\n' ? '\n' : '');
202 }
20368 if (csv.options.from.trim || csv.options.from.rtrim) {
2040 csv.state.field = csv.state.field.trimRight();
205 }
20668 csv.state.line.push(csv.state.field);
20768 csv.state.field = '';
20868 transform();
20968 break;
210 case ' ':
211 case '\t':
2120 if (csv.state.quoted || (!csv.options.from.trim && !csv.options.from.ltrim) || csv.state.field) {
2130 csv.state.field += c;
2140 break;
215 }
2160 break;
217 default:
2182847 if (csv.state.commented) {
2190 break;
220 }
2212847 csv.state.field += c;
222 }
2233270 csv.state.lastC = c;
2243270 i++;
225 }
226 };
227 /*
228 Called by the `parse` function on each line. It is responsible for
229 transforming the data and finally calling `write`.
230 */
231
23211 transform = function() {
23369 var column, columns, i, isObject, line, _i, _ref;
23469 line = null;
23569 columns = csv.options.from.columns;
23669 if (columns) {
2378 if (csv.state.count === 0 && columns === true) {
2382 csv.options.from.columns = columns = csv.state.line;
2392 csv.state.line = [];
2402 csv.state.lastC = '';
2412 return;
242 }
2436 line = {};
2446 for (i = _i = 0, _ref = columns.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
24537 column = columns[i];
24637 line[column] = csv.state.line[i] || null;
247 }
2486 csv.state.line = line;
2496 line = null;
250 }
25167 if (csv.transformer) {
25261 csv.state.transforming = true;
25361 try {
25461 line = csv.transformer(csv.state.line, csv.state.count);
255 } catch (e) {
2560 return error(e);
257 }
25861 isObject = typeof line === 'object' && !Array.isArray(line);
25961 if (csv.options.to.newColumns && !csv.options.to.columns && isObject) {
2602 Object.keys(line).filter(function(column) {
26114 return columns.indexOf(column) === -1;
262 }).forEach(function(column) {
2631 return columns.push(column);
264 });
265 }
26661 csv.state.transforming = false;
267 } else {
2686 line = csv.state.line;
269 }
27067 if (csv.state.count === 0 && csv.options.to.header === true) {
2711 write(csv.options.to.columns || columns);
272 }
27367 write(line);
27467 csv.state.count++;
27567 csv.state.line = [];
27667 return csv.state.lastC = '';
277 };
278 /*
279 Write a line to the written stream.
280 Line may be an object, an array or a string
281 Preserve is for line which are not considered as CSV data
282 */
283
28411 write = function(line, preserve) {
28568 var column, columns, containsLinebreak, containsQuote, containsdelimiter, field, i, newLine, regexp, _i, _j, _line, _ref, _ref1;
28668 if (typeof line === 'undefined' || line === null) {
2870 return;
288 }
28968 if (!preserve) {
29068 try {
29168 csv.emit('data', line, csv.state.count);
292 } catch (e) {
2930 return error(e);
294 }
295 }
29668 if (typeof line === 'object') {
29768 if (!Array.isArray(line)) {
2988 columns = csv.options.to.columns || csv.options.from.columns;
2998 _line = [];
3008 if (columns) {
3018 for (i = _i = 0, _ref = columns.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
30242 column = columns[i];
30342 _line[i] = typeof line[column] === 'undefined' || line[column] === null ? '' : line[column];
304 }
305 } else {
3060 for (column in line) {
3070 _line.push(line[column]);
308 }
309 }
3108 line = _line;
3118 _line = null;
31260 } else if (csv.options.to.columns) {
3132 line.splice(csv.options.to.columns.length);
314 }
31568 if (Array.isArray(line)) {
31668 newLine = csv.state.countWriten ? csv.options.to.lineBreaks || "\n" : '';
31768 for (i = _j = 0, _ref1 = line.length; 0 <= _ref1 ? _j < _ref1 : _j > _ref1; i = 0 <= _ref1 ? ++_j : --_j) {
318391 field = line[i];
319391 if (typeof field === 'string') {
320
3210 } else if (typeof field === 'number') {
3220 field = '' + field;
3230 } else if (typeof field === 'boolean') {
3240 field = field ? '1' : '';
3250 } else if (field instanceof Date) {
3260 field = '' + field.getTime();
327 }
328391 if (field) {
329381 containsdelimiter = field.indexOf(csv.options.to.delimiter || csv.options.from.delimiter) >= 0;
330381 containsQuote = field.indexOf(csv.options.to.quote || csv.options.from.quote) >= 0;
331381 containsLinebreak = field.indexOf("\r") >= 0 || field.indexOf("\n") >= 0;
332381 if (containsQuote) {
3334 regexp = new RegExp(csv.options.to.quote || csv.options.from.quote, 'g');
3344 field = field.replace(regexp, (csv.options.to.escape || csv.options.from.escape) + (csv.options.to.quote || csv.options.from.quote));
335 }
336381 if (containsQuote || containsdelimiter || containsLinebreak || csv.options.to.quoted) {
3374 field = (csv.options.to.quote || csv.options.from.quote) + field + (csv.options.to.quote || csv.options.from.quote);
338 }
339381 newLine += field;
340 }
341391 if (i !== line.length - 1) {
342323 newLine += csv.options.to.delimiter || csv.options.from.delimiter;
343 }
344 }
34568 line = newLine;
346 }
3470 } else if (typeof line === 'number') {
3480 line = '' + line;
349 }
35068 if (csv.state.buffer) {
35168 if (csv.state.bufferPosition + Buffer.byteLength(line, csv.options.to.encoding) > csv.options.from.bufferSize) {
3521 csv.writeStream.write(csv.state.buffer.slice(0, csv.state.bufferPosition));
3531 csv.state.buffer = new Buffer(csv.options.from.bufferSize);
3541 csv.state.bufferPosition = 0;
355 }
35668 csv.state.bufferPosition += csv.state.buffer.write(line, csv.state.bufferPosition, csv.options.to.encoding);
357 }
35868 if (!preserve) {
35968 csv.state.countWriten++;
360 }
36168 return true;
362 };
36311 error = function(e) {
3640 csv.readable = false;
3650 csv.writable = false;
3660 csv.emit('error', e);
3670 if (csv.readStream) {
3680 csv.readStream.destroy();
369 }
3700 return e;
371 };
37211 return csv;
373};

state.js

100%
2
2
0
LineHitsSource
1// Generated by CoffeeScript 1.3.3
2
31module.exports = function() {
411 return {
5 count: 0,
6 countWriten: 0,
7 field: '',
8 line: [],
9 lastC: '',
10 quoted: false,
11 commented: false,
12 buffer: null,
13 bufferPosition: 0,
14 transforming: false
15 };
16};

options.js

100%
2
2
0
LineHitsSource
1// Generated by CoffeeScript 1.3.3
2
31module.exports = function() {
411 return {
5 /*
6
7 `options.from`: Options of the CSV input source
8 -----------------------------------------------
9
10 Default values are:
11 * `delimiter` ,
12 * `quote` "
13 * `escape` "
14 * `columns` null
15 * `flags` r
16 * `encoding` utf8
17 * `bufferSize` 8 * 1024 * 1024
18 * `trim` false
19 * `ltrim` false
20 * `rtrim` false
21 */
22
23 from: {
24 delimiter: ',',
25 quote: '"',
26 escape: '"',
27 columns: null,
28 flags: 'r',
29 encoding: 'utf8',
30 bufferSize: 8388608,
31 trim: false,
32 ltrim: false,
33 rtrim: false
34 },
35 /*
36
37 `options.to`: Options of the CSV output source
38 -----------------------------------------------
39
40 Default options are:
41
42 Default options are:
43 * `delimiter`
44 * `quote`
45 * `quoted`
46 * `escape`
47 * `columns`
48 * `header`
49 * `lineBreaks` f
50 * `flags`
51 * `encoding`
52 * `bufferSize`
53 * `newColumns`
54 * `end` Call `end()` on close
55 */
56
57 to: {
58 delimiter: null,
59 quote: null,
60 quoted: false,
61 escape: null,
62 columns: null,
63 header: false,
64 lineBreaks: null,
65 flags: 'w',
66 encoding: 'utf8',
67 bufferSize: null,
68 newColumns: false,
69 end: true
70 }
71 };
72};

from.js

62%
35
22
13
LineHitsSource
1// Generated by CoffeeScript 1.3.3
21var fs, utils;
3
41fs = require('fs');
5
61utils = require('./utils');
7
8/*
9
10Reading data from a source
11--------------------------
12
13The `from` property provide convenient functions to read some csv input.
14*/
15
16
171module.exports = function(csv) {
1811 return {
19 /*
20
21 `from.options([options])`: Set or get options
22 ---------------------------------------------
23
24 Options are:
25
26 * `delimiter` Set the field delimiter, one character only, defaults to comma.
27 * `quote` Set the field delimiter, one character only, defaults to double quotes.
28 * `escape` Set the field delimiter, one character only, defaults to double quotes.
29 * `columns` List of fields or true if autodiscovered in the first CSV line, impact the `transform` argument and the `data` event by providing an object instead of an array, order matters, see the transform and the columns sections below.
30 * `flags`
31 * `encoding` Defaults to 'utf8', applied when a readable stream is created.
32 * `bufferSize`
33 * `trim` If true, ignore whitespace immediately around the delimiter, defaults to false.
34 * `ltrim` If true, ignore whitespace immediately following the delimiter (i.e. left-trim all fields), defaults to false.
35 * `rtrim` If true, ignore whitespace immediately preceding the delimiter (i.e. right-trim all fields), defaults to false.
36 */
37
38 options: function(options) {
3955 if (options != null) {
408 utils.merge(csv.options.from, options);
418 return csv;
42 } else {
4347 return csv.options.from;
44 }
45 },
46 /*
47
48 `from.array:(data, [options])`: Read from an array
49 --------------------------------------------------
50
51 Take an array as first argument and optionally some options
52 as a second argument. Each element of the array represents
53 a csv record. Those elements may be a string, a buffer, an
54 array or an object.
55 */
56
57 array: function(data, options) {
580 this.options(options);
590 process.nextTick(function() {
600 var i, _i, _ref;
610 for (i = _i = 0, _ref = data.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
620 csv.write(data[i]);
63 }
640 return csv.end();
65 });
660 return csv;
67 },
68 /*
69
70 `from.string:(data, [options])`: Read from a string or a buffer
71 ---------------------------------------------------------------
72
73 Take a string as first argument and optionally an object
74 of options as a second argument. The string must be the
75 complete csv data and may contains more than one line.
76 */
77
78 string: function(data, options) {
790 this.options(options);
800 process.nextTick(function() {
810 csv.write(data);
820 return csv.end();
83 });
840 return csv;
85 },
86 /*
87
88 `from.path(path, [options])`: Read from a file path
89 ---------------------------------------------------
90
91 Take a file path as first argument and optionally an object
92 of options as a second argument.
93 */
94
95 path: function(path, options) {
9611 var stream;
9711 this.options(options);
9811 stream = fs.createReadStream(path, csv.from.options());
9911 stream.setEncoding(csv.from.options().encoding);
10011 return csv.from.stream(stream, null);
101 },
102 /*
103
104 `from.stream(readStream, [options])`: Read from a stream
105 --------------------------------------------------------
106
107 Take a readable stream as first argument and optionally
108 an object of options as a second argument.
109 */
110
111 stream: function(readStream, options) {
11211 this.options(options);
11311 readStream.on('data', function(data) {
11412 return csv.write(data.toString());
115 });
11611 readStream.on('error', function(e) {
1170 return error(e);
118 });
11911 readStream.on('end', function() {
12011 return csv.end();
121 });
12211 csv.readStream = readStream;
12311 return csv;
124 }
125 };
126};

utils.js

100%
6
6
0
LineHitsSource
1// Generated by CoffeeScript 1.3.3
2
31module.exports = {
4 merge: function(obj1, obj2) {
523 var key, r;
623 r = obj1 || {};
723 for (key in obj2) {
8145 r[key] = obj2[key];
9 }
1023 return r;
11 }
12};

to.js

73%
34
25
9
LineHitsSource
1// Generated by CoffeeScript 1.3.3
21var fs, utils;
3
41fs = require('fs');
5
61utils = require('./utils');
7
8/*
9
10Writing data to a source
11--------------------------
12
13The `to` property provide convenient functions to write some csv output.
14*/
15
16
171module.exports = function(csv) {
1811 return {
19 /*
20
21 `to.options([options])`: Set or get options
22 -------------------------------------------
23
24 Options are:
25
26 * `delimiter` Set the field delimiter, one character only, defaults to `options.from.delimiter` which is a comma.
27 * `quote` Defaults to the quote read option.
28 * `quoted` Boolean, default to false, quote all the fields even if not required.
29 * `escape` Defaults to the escape read option.
30 * `columns` List of fields, applied when `transform` returns an object, order matters, see the transform and the columns sections below.
31 * `encoding` Defaults to 'utf8', applied when a writable stream is created.
32 * `header` Display the column names on the first line if the columns option is provided.
33
34 * `lineBreaks` String used to delimit record rows or a special value; special values are 'auto', 'unix', 'mac', 'windows', 'unicode'; defaults to 'auto' (discovered in source or 'unix' if no source is specified).
35 * `flags` Defaults to 'w', 'w' to create or overwrite an file, 'a' to append to a file. Applied when using the `toPath` method.
36 * `bufferSize` Internal buffer holding data before being flushed into a stream. Applied when destination is a stream.
37 * `end` Prevent calling `end` on the destination, so that destination is no longer writable, similar to passing `{end: false}` option in `stream.pipe()`.
38 * `newColumns` If the `columns` option is not specified (which means columns will be taken from the reader
39 options, will automatically append new columns if they are added during `transform()`.
40 */
41
42 options: function(options) {
4322 if (options != null) {
444 utils.merge(csv.options.to, options);
454 return csv;
46 } else {
4718 return csv.options.to;
48 }
49 },
50 /*
51
52 `to.stream(writeStream, [options])`: Write to a stream
53 ---------------------------------------------------
54
55 Take a readable stream as first argument and optionally on object of options as a second argument.
56 */
57
58 stream: function(writeStream, options) {
5911 this.options(options);
6011 switch (csv.options.to.lineBreaks) {
61 case 'auto':
620 csv.options.to.lineBreaks = null;
630 break;
64 case 'unix':
650 csv.options.to.lineBreaks = "\n";
660 break;
67 case 'mac':
680 csv.options.to.lineBreaks = "\r";
690 break;
70 case 'windows':
710 csv.options.to.lineBreaks = "\r\n";
720 break;
73 case 'unicode':
740 csv.options.to.lineBreaks = "\u2028";
75 }
7611 writeStream.on('close', function() {
7711 csv.emit('end', csv.state.count);
7811 csv.readable = false;
7911 return csv.writable = false;
80 });
8111 csv.writeStream = writeStream;
8211 csv.state.buffer = new Buffer(csv.options.to.bufferSize || csv.from.options().bufferSize);
8311 csv.state.bufferPosition = 0;
8411 return csv;
85 },
86 /*
87
88 `to.path(path, [options])`: Write to a path
89 ----------------------------------------
90
91 Take a file path as first argument and optionally on object of options as a second argument.
92 */
93
94 path: function(path, options) {
9511 var stream;
9611 this.options(options);
9711 options = utils.merge({}, csv.options.to);
9811 delete options.end;
9911 stream = fs.createWriteStream(path, options);
10011 return csv.to.stream(stream, null);
101 }
102 };
103};