Added Types and Unit tests
This commit is contained in:
61
.gitignore
vendored
Normal file
61
.gitignore
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
# next.js build output
|
||||
.next
|
18
README.md
18
README.md
@ -5,10 +5,26 @@ efficient and no dependencies.
|
||||
## Usage
|
||||
Only one method is provided;
|
||||
```js
|
||||
const csv = 'Name,Age,Location\nDom,26,Sydney\nSteve,30,New York';
|
||||
const csv = 'Name,Age,Location\nDom,26,"Sydney, Australia"\nSteve,30,"New York, USA"';
|
||||
parseCSV(csv, function(headers, line, index) {
|
||||
console.log(index, '-', line[headers.indexOf('Name')]);//1 - Dom
|
||||
return true;//Return false to break loop
|
||||
})
|
||||
```
|
||||
To stay efficient we ask for a callback that is fired for every line parsed.
|
||||
|
||||
## Options
|
||||
Options can be provided if necessary;
|
||||
```js
|
||||
parseCSV(csv, (headers,line,index) => true, {
|
||||
skipBlanks: false
|
||||
});
|
||||
```
|
||||
|
||||
Current Options;
|
||||
```
|
||||
skipBlanks - True or False, whether or not to skip blank lines
|
||||
```
|
||||
|
||||
## Testing
|
||||
Tests are written in jest. Run `yarn test`
|
9
index.d.ts
vendored
Normal file
9
index.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
type CSVOptions = {
|
||||
skipBlanks?:boolean;
|
||||
};
|
||||
|
||||
type CSVCallback = (headers:string[], line:string[], index:number) => boolean;
|
||||
|
||||
type CSVParser = (csv:string, onLine:CSVCallback, options?:CSVOptions)=>void;
|
||||
|
||||
export const { parseCSV:CSVParser }
|
17
package.json
Normal file
17
package.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "micro-csv",
|
||||
"version": "1.0.0",
|
||||
"main": "src/index.js",
|
||||
"repository": "https://github.com/YourWishes/Micro-CSV.git",
|
||||
"author": "Dominic Masters <dominic@domsplace.com>",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"jest": "^26.6.3"
|
||||
},
|
||||
"jest": {
|
||||
"testMatch": [ "**/test/**/*.js" ]
|
||||
}
|
||||
}
|
21
src/index.js
21
src/index.js
@ -1,15 +1,25 @@
|
||||
function parseCSV(csv, onLine) {
|
||||
/**
|
||||
* Parses a CSV string and runs a callback for each line found.
|
||||
*
|
||||
* @param csv CSV string to parse
|
||||
* @param onLine Callback to be fired for each line in the CSV
|
||||
* @param options Modify how the callback is called
|
||||
*/
|
||||
function parseCSV(csv, onLine, options) {
|
||||
//Split lines by delim, don't worry about \r here
|
||||
const lines = csv.split('\n');
|
||||
let headers = null;
|
||||
let index = 0;
|
||||
options = options || {
|
||||
skipBlanks: true
|
||||
};
|
||||
|
||||
//For each line...
|
||||
for(let i = 0; i < lines.length; i++) {
|
||||
//Shave off trailing whitespaces
|
||||
let line = lines[i].trim();
|
||||
if(!line.replace(/\s/g, '').length) continue;//Skip blank lines
|
||||
index++;//Count real lines
|
||||
|
||||
if(options.skipBlanks && !line.replace(/\s/g, '').length) continue;//Skip blank lines
|
||||
|
||||
//Setup line array
|
||||
let lineArray;
|
||||
@ -39,15 +49,20 @@ function parseCSV(csv, onLine) {
|
||||
} else {
|
||||
lineArray = line.split(',');
|
||||
}
|
||||
if(options.skipBlanks && (!lineArray.length || lineArray.every(e => !e.length))) continue;
|
||||
|
||||
//Is this the headers line?
|
||||
if(!headers) {
|
||||
headers = lineArray;
|
||||
continue;
|
||||
}
|
||||
index++;//Count real lines
|
||||
|
||||
//Callback
|
||||
let r = onLine(headers, lineArray, index);
|
||||
if(!r) break;//If r != true then break loop
|
||||
}
|
||||
}
|
||||
|
||||
//CommonJS only, don't copy this into google script
|
||||
module.exports = { parseCSV };
|
75
test/index.js
Normal file
75
test/index.js
Normal file
@ -0,0 +1,75 @@
|
||||
const { expect, it } = require('@jest/globals');
|
||||
const { parseCSV } = require('./../src/index');
|
||||
|
||||
describe('parseCSV', () => {
|
||||
it('should parse a csv file', () => {
|
||||
const csv = 'Name,Age,Location\nDom,26,"Sydney, Australia"\nSteve,30,"New York, USA"';
|
||||
|
||||
parseCSV(csv, (headers,line,index) => {
|
||||
//Headers are parsed as arrays of strings
|
||||
expect(headers).toEqual([ 'Name', 'Age', 'Location' ]);
|
||||
|
||||
switch(index) {
|
||||
case 1:
|
||||
expect(line).toEqual([ 'Dom', '26', 'Sydney, Australia' ]);
|
||||
break;
|
||||
case 2:
|
||||
expect(line).toEqual([ 'Steve', '30', 'New York, USA' ]);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
});
|
||||
|
||||
it('should skip blank lines', () => {
|
||||
//Line 2 is blank, Line 3 ends with a return carraige
|
||||
const csv = 'Name,Age,Location\n,,\nSteve,30,"New York, USA"\n\rDom,26,"Sydney, Australia"';
|
||||
|
||||
parseCSV(csv, (headers,line,index) => {
|
||||
expect(headers).toEqual([ 'Name', 'Age', 'Location' ]);
|
||||
|
||||
switch(index) {
|
||||
case 1:
|
||||
expect(line).toEqual([ 'Steve', '30', 'New York, USA' ]);
|
||||
break;
|
||||
case 2:
|
||||
expect(line).toEqual([ 'Dom', '26', 'Sydney, Australia' ]);
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
});
|
||||
});
|
||||
|
||||
it('should break when returning false', () => {
|
||||
const csv = 'Name,Age,Location\nDom,26,"Sydney, Australia"\nSteve,30,"New York, USA"';
|
||||
|
||||
parseCSV(csv, (headers,line,index) => {
|
||||
expect(line).toEqual([ 'Dom', '26', 'Sydney, Australia' ]);
|
||||
expect(index).not.toEqual(2);
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
it('should be allowed to not skip blanks', () => {
|
||||
|
||||
//Line 2 is blank, Line 3 ends with a return carraige
|
||||
const csv = 'Name,Age,Location\n,,\nSteve,30,"New York, USA"\n\rDom,26,"Sydney, Australia"';
|
||||
|
||||
parseCSV(csv, (headers,line,index) => {
|
||||
expect(headers).toEqual([ 'Name', 'Age', 'Location' ]);
|
||||
|
||||
switch(index) {
|
||||
case 1:
|
||||
expect(line).toEqual([ '', '', '' ]);
|
||||
break;
|
||||
case 2:
|
||||
expect(line).toEqual([ 'Steve', '30', 'New York, USA' ]);
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}, { skipBlanks: false });
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user