@verifyhash/csv-lite 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +154 -0
- package/index.js +228 -0
- package/package.json +37 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 <OWNER-NAME PLACEHOLDER>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# csv-lite
|
|
2
|
+
|
|
3
|
+
<!-- publish-prep -->
|
|
4
|
+
> **TODO (owner): pick the final npm name/scope before publishing.**
|
|
5
|
+
> The npm name is **not** finalized. `package.json` uses the placeholder
|
|
6
|
+
> `PLACEHOLDER-csv-lite` — replace it with the real published name/scope
|
|
7
|
+
> before any `npm publish`. Graduation to a package registry is a
|
|
8
|
+
> **needs-human** decision, not something this project does on its own.
|
|
9
|
+
|
|
10
|
+
A tiny, **zero-dependency, zero-network** Node.js library for **RFC 4180** CSV:
|
|
11
|
+
parse a CSV string into rows (or objects), and turn rows back into CSV text with
|
|
12
|
+
minimal, correct quoting. One CommonJS file (`index.js`), no install step, no
|
|
13
|
+
I/O — drop it into any project and `require` it.
|
|
14
|
+
|
|
15
|
+
Spec reference: <https://www.rfc-editor.org/rfc/rfc4180>
|
|
16
|
+
|
|
17
|
+
## Who it's for
|
|
18
|
+
|
|
19
|
+
JavaScript/Node developers who need to read or write CSV correctly — quoted
|
|
20
|
+
fields, commas and newlines inside quotes, `""` escapes, `;`/tab delimiters,
|
|
21
|
+
CRLF vs LF — **without** pulling in a larger parser like
|
|
22
|
+
[`csv-parse`](https://www.npmjs.com/package/csv-parse) or
|
|
23
|
+
[`papaparse`](https://www.npmjs.com/package/papaparse) and their footprint.
|
|
24
|
+
Good for build scripts, CLIs, small ETL glue, tests, config import/export, and
|
|
25
|
+
anywhere you want auditable, single-file CSV handling.
|
|
26
|
+
|
|
27
|
+
If you need to stream gigabyte files, sniff the delimiter automatically, coerce
|
|
28
|
+
types, or handle malformed-quote recovery, use one of the full packages above.
|
|
29
|
+
This library deliberately covers the well-formed RFC 4180 common case.
|
|
30
|
+
|
|
31
|
+
## Install / use
|
|
32
|
+
|
|
33
|
+
No install — copy `index.js`, or `require` it directly:
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
const csv = require('./index.js');
|
|
37
|
+
|
|
38
|
+
// parse -> 2D array of strings
|
|
39
|
+
csv.parse('a,"b,c",d');
|
|
40
|
+
// [['a', 'b,c', 'd']]
|
|
41
|
+
|
|
42
|
+
// parse with header -> array of objects keyed by the first row
|
|
43
|
+
csv.parse('name,age\nAlice,30\nBob,25', { header: true });
|
|
44
|
+
// [{ name: 'Alice', age: '30' }, { name: 'Bob', age: '25' }]
|
|
45
|
+
|
|
46
|
+
// stringify a 2D array (quotes only where required)
|
|
47
|
+
csv.stringify([['x', 'y,z', 'q"1']]);
|
|
48
|
+
// 'x,"y,z","q""1"'
|
|
49
|
+
|
|
50
|
+
// stringify array-of-objects with an explicit column order
|
|
51
|
+
csv.stringify(
|
|
52
|
+
[{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }],
|
|
53
|
+
{ columns: ['name', 'age'] }
|
|
54
|
+
);
|
|
55
|
+
// 'name,age\nAlice,30\nBob,25'
|
|
56
|
+
|
|
57
|
+
// custom delimiter (semicolon, tab, pipe, ...)
|
|
58
|
+
csv.parse('a;b;c', { delimiter: ';' }); // [['a', 'b', 'c']]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Complements the browser-based [/csv-to-json/](https://polyatic.com/csv-to-json/)
|
|
62
|
+
hub tool: same RFC 4180 rules, but as a reusable Node module.
|
|
63
|
+
|
|
64
|
+
## API
|
|
65
|
+
|
|
66
|
+
### `parse(text, options) → string[][] | object[]`
|
|
67
|
+
|
|
68
|
+
Parse CSV text into rows. Every value is returned as a **string** — no type
|
|
69
|
+
coercion.
|
|
70
|
+
|
|
71
|
+
Options:
|
|
72
|
+
|
|
73
|
+
| option | default | meaning |
|
|
74
|
+
| ----------- | ------- | -------------------------------------------------------------- |
|
|
75
|
+
| `delimiter` | `','` | Single-character field separator (e.g. `';'`, `'\t'`, `'\|'`). |
|
|
76
|
+
| `header` | `false` | When `true`, use the first row as keys and return objects. |
|
|
77
|
+
|
|
78
|
+
What it handles per RFC 4180:
|
|
79
|
+
|
|
80
|
+
- **Quoted fields** — a field wrapped in `"..."`; the delimiter, `CR`, `LF`, and
|
|
81
|
+
`CRLF` are literal data inside the quotes.
|
|
82
|
+
- **Embedded delimiters** — `a,"b,c",d` → `['a', 'b,c', 'd']`.
|
|
83
|
+
- **Embedded newlines** — `"line1\nline2"` stays one field; the row is not split.
|
|
84
|
+
- **Doubled-quote escapes** — `""` inside a quoted field is one literal `"`.
|
|
85
|
+
- **CRLF and LF** record separators, including mixed within one document.
|
|
86
|
+
- **A single trailing newline** is ignored — it does not produce an extra empty
|
|
87
|
+
record. (A blank line *in the middle* of the data is a real one-field row `['']`.)
|
|
88
|
+
- **Empty input** (`''`) returns `[]`.
|
|
89
|
+
|
|
90
|
+
With `header: true`, the first row becomes the object keys and the remaining
|
|
91
|
+
rows become objects. A header-only document returns `[]`.
|
|
92
|
+
|
|
93
|
+
### `stringify(rows, options) → string`
|
|
94
|
+
|
|
95
|
+
Serialize rows to CSV text. Output uses `LF` (`\n`) record separators and has
|
|
96
|
+
**no trailing newline**.
|
|
97
|
+
|
|
98
|
+
Options:
|
|
99
|
+
|
|
100
|
+
| option | default | meaning |
|
|
101
|
+
| ----------- | ------- | ------------------------------------------------------------------------- |
|
|
102
|
+
| `delimiter` | `','` | Single-character field separator. |
|
|
103
|
+
| `columns` | — | When given, `rows` is an **array of objects**; emits a header row of these names first, then projects each object onto them in order. |
|
|
104
|
+
|
|
105
|
+
Quoting rule: a field is quoted **only when it must be** — it contains the
|
|
106
|
+
delimiter, a double-quote, `CR`, or `LF`. Embedded double-quotes are escaped by
|
|
107
|
+
doubling (`"` → `""`). Non-string values are coerced with `String()`;
|
|
108
|
+
`null`/`undefined` become an empty, unquoted field.
|
|
109
|
+
|
|
110
|
+
`parse` and `stringify` are exact inverses for well-formed data — see the
|
|
111
|
+
`parse -> stringify -> parse` stability test in `test/index.test.js`.
|
|
112
|
+
|
|
113
|
+
## Limits (please read)
|
|
114
|
+
|
|
115
|
+
This is an honest, deliberately small library. It does **not** do everything a
|
|
116
|
+
full CSV package does:
|
|
117
|
+
|
|
118
|
+
- **No streaming.** `parse(text)` takes a complete string and returns the fully
|
|
119
|
+
materialized result in memory; `stringify(rows)` builds the whole output
|
|
120
|
+
string. This is explicitly **out of scope** — for files too large to fit in
|
|
121
|
+
memory, use a streaming parser such as `csv-parse`. As a rough guide, keep
|
|
122
|
+
inputs to something comfortably under available RAM (tens of MB is fine; multi-
|
|
123
|
+
gigabyte files are not what this is for).
|
|
124
|
+
- **RFC 4180 subset.** It targets well-formed RFC 4180. It does **not** attempt
|
|
125
|
+
recovery from malformed quoting (e.g. a `"` in the middle of an unquoted
|
|
126
|
+
field, or an unterminated quote — an unterminated quoted field simply runs to
|
|
127
|
+
end-of-input). There is no automatic delimiter detection, no comment-line
|
|
128
|
+
(`#`) skipping, no BOM stripping, and no quote/escape-character customization
|
|
129
|
+
(the quote character is always `"`).
|
|
130
|
+
- **Ragged rows are preserved, not normalized, in array mode.** `parse` in the
|
|
131
|
+
default (non-header) mode returns each row exactly as many fields as it had —
|
|
132
|
+
it does **not** pad or truncate rows to a uniform width, so different rows may
|
|
133
|
+
have different lengths. Only in `header: true` mode are objects shaped by the
|
|
134
|
+
header: rows with **fewer** cells than the header get `''` for the missing
|
|
135
|
+
keys, and any **extra** cells beyond the header width are dropped.
|
|
136
|
+
- **Strings only, no type coercion.** `"30"` stays the string `"30"`; there is
|
|
137
|
+
no number/boolean/date inference. Convert types yourself after parsing.
|
|
138
|
+
|
|
139
|
+
## Running the tests
|
|
140
|
+
|
|
141
|
+
One command, Node's built-in `assert` only — no test runner, no dependencies:
|
|
142
|
+
|
|
143
|
+
```sh
|
|
144
|
+
node test/index.test.js
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
It prints `csv-lite: all N tests passed.` and exits `0` on success. Fixtures
|
|
148
|
+
cover embedded delimiters, embedded newlines inside quotes, doubled-quote
|
|
149
|
+
escapes, header-mode object round-trip, custom delimiters (`;` and tab), CRLF vs
|
|
150
|
+
LF, and a `parse -> stringify -> parse` round-trip that is byte-stable.
|
|
151
|
+
|
|
152
|
+
## License
|
|
153
|
+
|
|
154
|
+
MIT — see `LICENSE`.
|
package/index.js
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* csv-lite — a zero-dependency, zero-network RFC 4180 CSV parser and
|
|
5
|
+
* stringifier for Node.js.
|
|
6
|
+
*
|
|
7
|
+
* No I/O, no streaming, no dependencies. Two pure functions over strings:
|
|
8
|
+
* parse(text, options) -> 2D array of strings, or array of objects
|
|
9
|
+
* stringify(rows, options) -> CSV text
|
|
10
|
+
*
|
|
11
|
+
* Spec reference: https://www.rfc-editor.org/rfc/rfc4180
|
|
12
|
+
*
|
|
13
|
+
* Streaming is explicitly OUT OF SCOPE: parse() takes a complete string and
|
|
14
|
+
* returns the fully materialized result. For multi-gigabyte files that will
|
|
15
|
+
* not fit in memory, use a streaming parser such as `csv-parse`.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// parse
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Parse CSV text into rows.
|
|
24
|
+
*
|
|
25
|
+
* @param {string} text The CSV document.
|
|
26
|
+
* @param {object} [options]
|
|
27
|
+
* @param {string} [options.delimiter=','] Single-character field delimiter.
|
|
28
|
+
* @param {boolean} [options.header=false] When true, treat the first row as a
|
|
29
|
+
* header and return an array of objects keyed by the header names.
|
|
30
|
+
* @returns {string[][] | Object<string,string>[]}
|
|
31
|
+
*
|
|
32
|
+
* Behavior (RFC 4180):
|
|
33
|
+
* - Fields may be wrapped in double quotes. Inside a quoted field, the
|
|
34
|
+
* delimiter, CR, LF and CRLF are literal data.
|
|
35
|
+
* - A double-quote inside a quoted field is written as two double-quotes ("").
|
|
36
|
+
* - Records are separated by CRLF or LF (both accepted, mixed is fine).
|
|
37
|
+
* - A single trailing newline at the end of the document is ignored (it does
|
|
38
|
+
* not produce an extra empty record).
|
|
39
|
+
* - Every value is returned as a string; no type coercion is performed.
|
|
40
|
+
* - Empty input ("") yields an empty array ([]).
|
|
41
|
+
*/
|
|
42
|
+
function parse(text, options) {
|
|
43
|
+
options = options || {};
|
|
44
|
+
const delimiter = options.delimiter == null ? ',' : options.delimiter;
|
|
45
|
+
const header = !!options.header;
|
|
46
|
+
|
|
47
|
+
if (typeof text !== 'string') {
|
|
48
|
+
throw new TypeError('csv-lite: parse(text) expects a string');
|
|
49
|
+
}
|
|
50
|
+
if (typeof delimiter !== 'string' || delimiter.length !== 1) {
|
|
51
|
+
throw new TypeError('csv-lite: delimiter must be a single character');
|
|
52
|
+
}
|
|
53
|
+
if (delimiter === '"' || delimiter === '\r' || delimiter === '\n') {
|
|
54
|
+
throw new TypeError('csv-lite: delimiter may not be a quote or newline');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const rows = [];
|
|
58
|
+
let field = '';
|
|
59
|
+
let row = [];
|
|
60
|
+
let inQuotes = false;
|
|
61
|
+
// Tracks whether the current record has produced any content at all, so we
|
|
62
|
+
// can distinguish a genuine empty trailing record from the ignorable
|
|
63
|
+
// trailing newline.
|
|
64
|
+
let sawAnyChar = false;
|
|
65
|
+
const len = text.length;
|
|
66
|
+
|
|
67
|
+
for (let i = 0; i < len; i++) {
|
|
68
|
+
const ch = text[i];
|
|
69
|
+
|
|
70
|
+
if (inQuotes) {
|
|
71
|
+
if (ch === '"') {
|
|
72
|
+
// Either an escaped quote ("") or the closing quote.
|
|
73
|
+
if (i + 1 < len && text[i + 1] === '"') {
|
|
74
|
+
field += '"';
|
|
75
|
+
i++; // consume the second quote of the pair
|
|
76
|
+
} else {
|
|
77
|
+
inQuotes = false;
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
field += ch;
|
|
81
|
+
}
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (ch === '"') {
|
|
86
|
+
inQuotes = true;
|
|
87
|
+
sawAnyChar = true;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (ch === delimiter) {
|
|
92
|
+
row.push(field);
|
|
93
|
+
field = '';
|
|
94
|
+
sawAnyChar = true;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (ch === '\r') {
|
|
99
|
+
// Handle CRLF as a single record separator; a lone CR also ends a record.
|
|
100
|
+
if (i + 1 < len && text[i + 1] === '\n') i++;
|
|
101
|
+
row.push(field);
|
|
102
|
+
rows.push(row);
|
|
103
|
+
field = '';
|
|
104
|
+
row = [];
|
|
105
|
+
sawAnyChar = false;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (ch === '\n') {
|
|
110
|
+
row.push(field);
|
|
111
|
+
rows.push(row);
|
|
112
|
+
field = '';
|
|
113
|
+
row = [];
|
|
114
|
+
sawAnyChar = false;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
field += ch;
|
|
119
|
+
sawAnyChar = true;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Flush the final record. We emit it unless the document ended exactly on a
|
|
123
|
+
// record separator (trailing newline) with nothing after it.
|
|
124
|
+
if (sawAnyChar || field !== '' || row.length > 0) {
|
|
125
|
+
row.push(field);
|
|
126
|
+
rows.push(row);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (!header) return rows;
|
|
130
|
+
|
|
131
|
+
// header:true -> array of objects keyed by the first row.
|
|
132
|
+
if (rows.length === 0) return [];
|
|
133
|
+
const keys = rows[0];
|
|
134
|
+
const out = [];
|
|
135
|
+
for (let r = 1; r < rows.length; r++) {
|
|
136
|
+
const src = rows[r];
|
|
137
|
+
const obj = {};
|
|
138
|
+
for (let c = 0; c < keys.length; c++) {
|
|
139
|
+
// Ragged rows: missing trailing cells become '' (empty string); this
|
|
140
|
+
// keeps every object shaped by the header rather than by the data row.
|
|
141
|
+
obj[keys[c]] = c < src.length ? src[c] : '';
|
|
142
|
+
}
|
|
143
|
+
out.push(obj);
|
|
144
|
+
}
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
// stringify
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Serialize rows to CSV text.
|
|
154
|
+
*
|
|
155
|
+
* @param {Array} rows A 2D array of cell values, OR an array of objects when
|
|
156
|
+
* `columns` is provided.
|
|
157
|
+
* @param {object} [options]
|
|
158
|
+
* @param {string} [options.delimiter=','] Single-character field delimiter.
|
|
159
|
+
* @param {string[]} [options.columns] When given, `rows` is treated as an
|
|
160
|
+
* array of objects; a header row of these column names is emitted first
|
|
161
|
+
* and each object is projected onto them in order.
|
|
162
|
+
* @returns {string} CSV text with LF (\n) record separators and no trailing
|
|
163
|
+
* newline.
|
|
164
|
+
*
|
|
165
|
+
* Quoting: a field is quoted ONLY when it must be — it contains the delimiter,
|
|
166
|
+
* a double-quote, CR, or LF. Embedded double-quotes are escaped by doubling.
|
|
167
|
+
* Non-string values are coerced with String(); null/undefined become '' (an
|
|
168
|
+
* empty, unquoted field).
|
|
169
|
+
*/
|
|
170
|
+
function stringify(rows, options) {
|
|
171
|
+
options = options || {};
|
|
172
|
+
const delimiter = options.delimiter == null ? ',' : options.delimiter;
|
|
173
|
+
const columns = options.columns;
|
|
174
|
+
|
|
175
|
+
if (!Array.isArray(rows)) {
|
|
176
|
+
throw new TypeError('csv-lite: stringify(rows) expects an array');
|
|
177
|
+
}
|
|
178
|
+
if (typeof delimiter !== 'string' || delimiter.length !== 1) {
|
|
179
|
+
throw new TypeError('csv-lite: delimiter must be a single character');
|
|
180
|
+
}
|
|
181
|
+
if (delimiter === '"' || delimiter === '\r' || delimiter === '\n') {
|
|
182
|
+
throw new TypeError('csv-lite: delimiter may not be a quote or newline');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const needsQuote = (s) =>
|
|
186
|
+
s.indexOf(delimiter) !== -1 ||
|
|
187
|
+
s.indexOf('"') !== -1 ||
|
|
188
|
+
s.indexOf('\n') !== -1 ||
|
|
189
|
+
s.indexOf('\r') !== -1;
|
|
190
|
+
|
|
191
|
+
const cell = (value) => {
|
|
192
|
+
let s;
|
|
193
|
+
if (value == null) s = '';
|
|
194
|
+
else s = String(value);
|
|
195
|
+
if (needsQuote(s)) {
|
|
196
|
+
return '"' + s.replace(/"/g, '""') + '"';
|
|
197
|
+
}
|
|
198
|
+
return s;
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const lines = [];
|
|
202
|
+
|
|
203
|
+
if (columns) {
|
|
204
|
+
if (!Array.isArray(columns)) {
|
|
205
|
+
throw new TypeError('csv-lite: columns must be an array of names');
|
|
206
|
+
}
|
|
207
|
+
// Header row.
|
|
208
|
+
lines.push(columns.map(cell).join(delimiter));
|
|
209
|
+
// Object rows projected onto columns.
|
|
210
|
+
for (const obj of rows) {
|
|
211
|
+
const src = obj || {};
|
|
212
|
+
lines.push(columns.map((key) => cell(src[key])).join(delimiter));
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
for (const row of rows) {
|
|
216
|
+
if (!Array.isArray(row)) {
|
|
217
|
+
throw new TypeError(
|
|
218
|
+
'csv-lite: without `columns`, each row must be an array'
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
lines.push(row.map(cell).join(delimiter));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return lines.join('\n');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
module.exports = { parse, stringify };
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@verifyhash/csv-lite",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Zero-dependency RFC 4180 CSV parser and stringifier for Node.js: quoted fields, embedded delimiters/newlines, doubled-quote escapes, header-object mode, custom delimiters.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"csv",
|
|
7
|
+
"rfc4180",
|
|
8
|
+
"parse",
|
|
9
|
+
"stringify",
|
|
10
|
+
"delimiter",
|
|
11
|
+
"quote",
|
|
12
|
+
"tsv",
|
|
13
|
+
"header",
|
|
14
|
+
"zero-dependency"
|
|
15
|
+
],
|
|
16
|
+
"main": "index.js",
|
|
17
|
+
"scripts": {
|
|
18
|
+
"test": "node test/index.test.js"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"files": [
|
|
22
|
+
"index.js",
|
|
23
|
+
"README.md"
|
|
24
|
+
],
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/verifyhash/libs.git",
|
|
31
|
+
"directory": "csv-lite"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://github.com/verifyhash/libs/tree/main/csv-lite#readme",
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/verifyhash/libs/issues"
|
|
36
|
+
}
|
|
37
|
+
}
|