Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
wbourne0 committed Nov 22, 2022
1 parent 9e6490a commit 9728c77
Show file tree
Hide file tree
Showing 10 changed files with 3,782 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/dist
/node_modules
3 changes: 3 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/jest.config.json
**/*.test.ts
/.gitignore
4 changes: 4 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"singleQuote": true,
"trailingComma": "all"
}
80 changes: 80 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Incremental CSV Parser

A fast & lightweight CSV parser with an incremental parsing api. No
dependencies; browser compatible. Includes ESM and CJS builds. All column
values are strings (numbers and dates will **not** automatically be parsed).

Fully compatible with [rfc4180](https://www.ietf.org/rfc/rfc4180.txt).


## Basic parsing

```js
import { parse } from 'incremental-csv-parser';


const results = await parse(`a,b,c,d
1,2,3,4
5,6,7,8
`);

console.log(results);
// [
// { a: '1', b: '2', c: '3', d: '4' },
// { a: '5', b: '6', c: '7', d: '8' },
// ]
```

## Incremental parsing

```js
import { CSVParser } from 'incremental-csv-parser';

const results = [];

const parser = new CSVParser((row) => {
results.push(row);
});

parser.process('a,b,c,d\n');
parser.process('1,2,3,4\n');
console.log(results.shift()); // { a: '1', b: '2', c: '3', d: '4' }
parser.process('5,6,7,8');
parser.flush();
console.log(results.shift()); // { a: '5', b: '6', c: '7', d: '8' }
```

## Typescript support

If column names are known ahead of time, they can be passed in via generics.

```ts
import { CSVParser } from 'incremental-csv-parser';

type ColumnName = 'a' | 'b' | 'c' | 'd';
const results: Record<ColumnName, string> = [];

const parser = new CSVParser<ColumName>((row) => {
results.push(row);
});
```

## CSV files without pre-defined headers

Both the `parse` function and `CSVParser` class have an optional second argument
for explicitly providing column names for a csv.

```ts
import { CSVParser } from 'incremental-csv-parser';

const columns = ['a', 'b', 'c', 'd'];

const results = parse(`1,2,3,4
5,6,7,8`, columns);

console.log(results);
// [
// { a: '1', b: '2', c: '3', d: '4' },
// { a: '5', b: '6', c: '7', d: '8' },
// ]
```
5 changes: 5 additions & 0 deletions jest.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"transform": {
"^.+\\.ts$": ["esbuild-jest", { "sourcemap": true }]
}
}
35 changes: 35 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "incremental-csv-parser",
"version": "1.0.0",
"description": "A simple, browser compatible, incremental CSV parser.",
"main": "dist/esm/index.js",
"repository": "https://github.com/AllAwesome497/incremental-csv-parser",
"author": "Wade Bourne <[email protected]>",
"license": "MIT",
"scripts": {
"build": "yarn build:types && yarn build:esm && yarn build:cjs",
"build:types": "tsc",
"build:esm": "esbuild src/*.ts --outdir=dist/esm --format=esm --out-extension:.js=.mjs --sourcemap",
"build:cjs": "esbuild src/*.ts --outdir=dist/cjs --format=cjs --sourcemap",
"test": "jest",
"prepack": "yarn test && yarn build"
},
"exports": {
".": {
"import": "./dist/esm/index.mjs",
"require": "./dist/cjs/index.js",
"types": "./dist/types/index.d.ts"
},
"./package.json": "./package.json"
},
"devDependencies": {
"@babel/preset-typescript": "^7.18.6",
"@jest/globals": "^29.3.1",
"esbuild": "^0.15.15",
"esbuild-jest": "^0.5.0",
"jest": "^29.3.1",
"ts-jest": "^29.0.3",
"typescript": "^4.9.3",
"prettier": "^2.7.1"
}
}
121 changes: 121 additions & 0 deletions src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/* eslint-env jest */
import { expect, describe, it } from '@jest/globals';
import { CSVParser } from '../index';

interface CSVParserTest {
name: string;
headers?: Array<string>;
chunks: Array<string>;
expected: Array<Record<string, string>>;
}

describe('SimpleCSVParser', () => {
it.each<CSVParserTest>([
{
name: 'basic',
chunks: [
`a,b,c,d
1,2,3,4
5,6,7,8
`,
],
expected: [
{ a: '1', b: '2', c: '3', d: '4' },
{ a: '5', b: '6', c: '7', d: '8' },
],
},
{
name: 'mixed quotes',
chunks: [
`a,b,c,"d"
"1",2,"3","4"
5,6,7,8
`,
],
expected: [
{ a: '1', b: '2', c: '3', d: '4' },
{ a: '5', b: '6', c: '7', d: '8' },
],
},
{
name: 'quoted newlines',
chunks: [
`a,b,c,d
"1
2",3,4,5
`,
],
expected: [{ a: '1\n2', b: '3', c: '4', d: '5' }],
},
{
name: 'empty values',
chunks: [
`a,b,c,d
,,,`,
],
expected: [{ a: '', b: '', c: '', d: '' }],
},
{
name: 'carriage returns + newlines',
chunks: [
`a,b,c,d\r
1,2,3,4\r
5,6,7,8
`,
],
expected: [
{ a: '1', b: '2', c: '3', d: '4' },
{ a: '5', b: '6', c: '7', d: '8' },
],
},
{
name: 'commas in quoted text',
chunks: [
`a,b,c,d
"1,2",3,4,5
`,
],
expected: [{ a: '1,2', b: '3', c: '4', d: '5' }],
},
{
name: 'basic chunks',
chunks: ['a,b,c,d\n', '1,', '2,', '3,', '4\n'],

expected: [{ a: '1', b: '2', c: '3', d: '4' }],
},
{
name: 'chunks with newlines',
chunks: ['a,b,c\n', '"1\n', '\n', '\n', '"', ',2,3\n'],
expected: [{ a: '1\n\n\n', b: '2', c: '3' }],
},
{
name: 'chunks with commas and quotes',
chunks: ['a,b,c\n', ',', '",,,', '",'],
expected: [{ a: '', b: ',,,', c: '' }],
},
{
name: 'manually set headers',
chunks: [
`1,2,3,4
5,6,7,8`,
],
headers: ['a', 'b', 'c', 'd'],
expected: [
{ a: '1', b: '2', c: '3', d: '4' },
{ a: '5', b: '6', c: '7', d: '8' },
],
},
])('$name', ({ chunks, expected, headers }) => {
const results: Array<Record<string, string>> = [];

const parser = new CSVParser((data) => results.push(data), headers);

for (const chunk of chunks) {
parser.process(chunk);
}

parser.flush();

expect(results).toEqual(expected);
});
});
Loading

0 comments on commit 9728c77

Please sign in to comment.