Skip to content

Commit 613dc75

Browse files
committed
add an example in README + a test file with the example
1 parent 73871bb commit 613dc75

5 files changed

Lines changed: 51 additions & 18 deletions

File tree

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,26 @@
22

33
Fetch and parse a remote CSV file.
44

5+
## Install
6+
7+
```bash
8+
npm install remotecsv
9+
```
10+
11+
## Usage
12+
13+
To parse a remote CSV file from a URL:
14+
15+
```typescript
16+
import { parseURL } from 'remotecsv'
17+
const rows = []
18+
for await (const { row } of parseURL('https://data.source.coop/severo/csv-papaparse-test-files/sample.csv')) {
19+
rows.push(row)
20+
}
21+
console.log(rows)
22+
// Output: [ [ 'A', 'B', 'C' ], [ 'X', 'Y', 'Z' ] ]
23+
```
24+
525
## Thanks
626

727
The code is heavily inspired by [Papaparse](https://www.papaparse.com/).

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
export { parseText } from './text'
2-
export { parseUrl } from './url'
2+
export { parseURL } from './url'
33
export { testEmptyLine } from './utils'

src/url.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ interface FetchOptions {
1313
fetchChunk?: typeof fetchChunk
1414
parse?: typeof parse
1515
}
16-
interface ParseUrlOptions extends ParseOptions, FetchOptions {
16+
interface parseURLOptions extends ParseOptions, FetchOptions {
1717
delimitersToGuess?: string[]
1818
stripBOM?: boolean
1919
}
@@ -38,9 +38,9 @@ interface ParseUrlOptions extends ParseOptions, FetchOptions {
3838
* @yields Parsed rows along with metadata.
3939
* @returns An async generator that yields parsed rows.
4040
*/
41-
export async function* parseUrl(
41+
export async function* parseURL(
4242
url: string,
43-
options: ParseUrlOptions = {},
43+
options: parseURLOptions = {},
4444
): AsyncGenerator<ParseResult, void, unknown> {
4545
const chunkSize = checkIntegerGreaterOrEqualThan(options.chunkSize, 1) ?? defaultChunkSize
4646
// TODO(SL): should we accept negative values (from the end)?

tests/examples.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { describe, expect, it } from 'vitest'
2+
3+
import { parseURL } from '../src'
4+
5+
describe('README examples', () => {
6+
it('parses a remote CSV file', async () => {
7+
const rows = []
8+
for await (const { row } of parseURL('https://data.source.coop/severo/csv-papaparse-test-files/sample.csv')) {
9+
rows.push(row)
10+
}
11+
expect(rows).toEqual([['A', 'B', 'C'], ['X', 'Y', 'Z']])
12+
})
13+
})

tests/url.test.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { describe, expect, it } from 'vitest'
22

33
import type { ParseResult } from '../src/types'
4-
import { parseUrl } from '../src/url'
4+
import { parseURL } from '../src/url'
55
import { toUrl } from '../src/utils'
66
import { PARSE_TESTS } from './cases'
77

@@ -22,14 +22,14 @@ function* parseMock(text: string): Generator<ParseResult, void, unknown> {
2222
}
2323
}
2424

25-
describe('parseUrl, while mocking parse, ', () => {
25+
describe('parseURL, while mocking parse, ', () => {
2626
it.each([1, 2, 3, 10, 17, 18, 19, 20, 21, 1_000, undefined])('accepts chunk size of %s', async (chunkSize) => {
2727
const text = 'hello, csvremote!!!' // length: 19
2828
const { url, fileSize, revoke } = toUrl(text)
2929
let result = ''
3030
let bytes = 0
3131
// passing 'to: fileSize - 1' for Node.js bug: https://github.com/nodejs/node/issues/60382
32-
for await (const { row, meta: { offset, byteCount } } of parseUrl(url, { chunkSize, lastByte: fileSize - 1, parse: parseMock })) {
32+
for await (const { row, meta: { offset, byteCount } } of parseURL(url, { chunkSize, lastByte: fileSize - 1, parse: parseMock })) {
3333
result += row
3434
expect(offset).toBe(bytes)
3535
bytes += byteCount
@@ -42,7 +42,7 @@ describe('parseUrl, while mocking parse, ', () => {
4242
it.each([0, -1, 1.5, NaN, Infinity])('throws if chunkSize is invalid: %s', async (chunkSize) => {
4343
const text = 'hello, csvremote!!!'
4444
const { url, fileSize, revoke } = toUrl(text)
45-
const iterator = parseUrl(url, { chunkSize, lastByte: fileSize - 1 })
45+
const iterator = parseURL(url, { chunkSize, lastByte: fileSize - 1 })
4646
await expect(iterator.next()).rejects.toThrow()
4747
revoke()
4848
})
@@ -60,7 +60,7 @@ describe('parseUrl, while mocking parse, ', () => {
6060
const { url, revoke } = toUrl(text)
6161
let result = ''
6262
// implicit assertation in the loop: no exceptions thrown
63-
for await (const { row } of parseUrl(url, { firstByte, lastByte, parse: parseMock })) {
63+
for await (const { row } of parseURL(url, { firstByte, lastByte, parse: parseMock })) {
6464
result += row[0]
6565
}
6666
revoke()
@@ -83,7 +83,7 @@ describe('parseUrl, while mocking parse, ', () => {
8383
])('throws for invalid from: %s or lastByte: %s', async (firstByte, lastByte) => {
8484
const text = 'hello, csvremote!!!'
8585
const { url, revoke } = toUrl(text)
86-
const iterator = parseUrl(url, { chunkSize: 10, firstByte, lastByte })
86+
const iterator = parseURL(url, { chunkSize: 10, firstByte, lastByte })
8787
await expect(iterator.next()).rejects.toThrow()
8888
revoke()
8989
})
@@ -92,7 +92,7 @@ describe('parseUrl, while mocking parse, ', () => {
9292
const text = 'hello, csvremote!!!'
9393
const { url, revoke } = toUrl(text)
9494
const controller = new AbortController()
95-
const iterator = parseUrl(url, { requestInit: { signal: controller.signal } })
95+
const iterator = parseURL(url, { requestInit: { signal: controller.signal } })
9696
controller.abort()
9797
await expect(iterator.next()).rejects.toThrow(/abort/i)
9898
revoke()
@@ -116,7 +116,7 @@ describe('parseUrl, while mocking parse, ', () => {
116116
},
117117
}
118118
}
119-
const iterator = parseUrl(url, {
119+
const iterator = parseURL(url, {
120120
chunkSize: 5,
121121
parse: parseMock,
122122
})
@@ -128,10 +128,10 @@ describe('parseUrl, while mocking parse, ', () => {
128128
describe('Papaparse high-level tests', () => {
129129
PARSE_TESTS.forEach((test) => {
130130
it(test.description, async () => {
131-
const config: Parameters<typeof parseUrl>[1] = test.config || {}
131+
const config: Parameters<typeof parseURL>[1] = test.config || {}
132132
const { url, fileSize, revoke } = toUrl(test.text)
133133
const result = []
134-
for await (const r of parseUrl(url, { ...config, lastByte: fileSize - 1 })) {
134+
for await (const r of parseURL(url, { ...config, lastByte: fileSize - 1 })) {
135135
result.push(r)
136136
}
137137
revoke()
@@ -150,17 +150,17 @@ describe('Papaparse high-level tests', () => {
150150
})
151151
})
152152

153-
describe('parseUrl', () => {
153+
describe('parseURL', () => {
154154
it('throws if URL is invalid', async () => {
155-
const iterator = parseUrl('http://invalid.invalid/', { chunkSize: 10 })
155+
const iterator = parseURL('http://invalid.invalid/', { chunkSize: 10 })
156156
await expect(iterator.next()).rejects.toThrow()
157157
})
158158
it.for([1, 2, 3, 5, 9, 14])('with known delimiter and newline, accepts chunk size of %s bytes.', async (chunkSize) => {
159159
const text = 'a,b,c\n1,2,3\n4,5,6\n7,8,9\na,b,c\n1,2,3\n4,5,6\n7,8,9\na,b,c\n1,2,3\n4,5,6\n7,8,9\n'
160160
const { url, fileSize, revoke } = toUrl(text)
161161
const result = []
162162
let bytes = 0
163-
for await (const { row, meta: { offset, byteCount } } of parseUrl(url, { chunkSize, lastByte: fileSize - 1 })) {
163+
for await (const { row, meta: { offset, byteCount } } of parseURL(url, { chunkSize, lastByte: fileSize - 1 })) {
164164
result.push(row)
165165
expect(offset).toBe(bytes)
166166
bytes += byteCount
@@ -173,7 +173,7 @@ describe('parseUrl', () => {
173173
const text = '\ufeffhello, csvremote!!!'
174174
const { url, fileSize, revoke } = toUrl(text)
175175
const result = []
176-
for await (const r of parseUrl(url, { stripBOM, lastByte: fileSize - 1 })) {
176+
for await (const r of parseURL(url, { stripBOM, lastByte: fileSize - 1 })) {
177177
result.push(r)
178178
}
179179
revoke()

0 commit comments

Comments
 (0)