Comparing version 0.1.7 to 1.0.0
@@ -1,168 +0,164 @@ | ||
var Promise = require('node-promise'), | ||
defer = Promise.defer, | ||
when = Promise.when, | ||
all = Promise.all, | ||
_ = require('underscore'); | ||
import fs from 'fs'; | ||
import Stream from 'stream'; | ||
import unzip from 'unzipper'; | ||
import xpath from 'xpath'; | ||
import XMLDOM from 'xmldom'; | ||
const ns = { a: 'http://schemas.openxmlformats.org/spreadsheetml/2006/main' }; | ||
const select = xpath.useNamespaces(ns); | ||
function extractFiles(path, sheet) { | ||
var unzip = require('unzip2'), | ||
deferred = defer(); | ||
const files = { | ||
strings: {}, | ||
sheet: {}, | ||
'xl/sharedStrings.xml': 'strings', | ||
[`xl/worksheets/sheet${sheet}.xml`]: 'sheet' | ||
}; | ||
var files = { | ||
strings: { | ||
deferred: defer() | ||
}, | ||
sheet: { | ||
deferred: defer() | ||
}, | ||
'xl/sharedStrings.xml': 'strings' | ||
}; | ||
files['xl/worksheets/sheet' + sheet + '.xml'] = 'sheet'; | ||
var noop = function () {}; | ||
var srcStream = path instanceof require('stream') ? | ||
path : | ||
require('fs').createReadStream(path); | ||
const stream = path instanceof Stream ? path : fs.createReadStream(path); | ||
srcStream | ||
.pipe(unzip.Parse()) | ||
.on('error', function(err) { | ||
deferred.reject(err); | ||
}) | ||
.on('end', function(){ | ||
deferred.resolve(); | ||
}) | ||
.on('entry', function(entry) { | ||
if (files[entry.path]) { | ||
var contents = ''; | ||
entry.on('data', function(data) { | ||
contents += data.toString(); | ||
}).on('end', function() { | ||
files[files[entry.path]].contents = contents; | ||
files[files[entry.path]].deferred.resolve(); | ||
}); | ||
} else { | ||
entry.on('data', noop); // otherwise unzip.Parse() will hang forever on this entry on some xlsx files | ||
} | ||
}); | ||
return new Promise((resolve, reject) => { | ||
const filePromises = []; | ||
when(all(_.pluck(files, 'deferred')), function() { | ||
deferred.resolve(files); | ||
}); | ||
return deferred.promise; | ||
stream | ||
.pipe(unzip.Parse()) | ||
.on('error', reject) | ||
.on('close', () => { | ||
Promise.all(filePromises).then(() => resolve(files)); | ||
}) | ||
// For some reason `end` event is not emitted. | ||
// .on('end', () => { | ||
// Promise.all(filePromises).then(() => resolve(files)); | ||
// }) | ||
.on('entry', (entry) => { | ||
const file = files[entry.path]; | ||
if (file) { | ||
let contents = ''; | ||
filePromises.push(new Promise((resolve) => { | ||
entry | ||
.on('data', data => contents += data.toString()) | ||
.on('end', () => { | ||
files[file].contents = contents; | ||
resolve(); | ||
}); | ||
})); | ||
} else { | ||
entry.autodrain(); | ||
} | ||
}); | ||
}); | ||
} | ||
function calculateDimensions (cells) { | ||
var comparator = function (a, b) { return a-b; }; | ||
var allRows = _(cells).map(function (cell) { return cell.row; }).sort(comparator), | ||
allCols = _(cells).map(function (cell) { return cell.column; }).sort(comparator), | ||
minRow = allRows[0], | ||
maxRow = _.last(allRows), | ||
minCol = allCols[0], | ||
maxCol = _.last(allCols); | ||
const comparator = (a, b) => a - b; | ||
const allRows = cells.map(cell => cell.row).sort(comparator); | ||
const allCols = cells.map(cell => cell.column).sort(comparator); | ||
const minRow = allRows[0]; | ||
const maxRow = allRows[allRows.length - 1]; | ||
const minCol = allCols[0]; | ||
const maxCol = allCols[allCols.length - 1]; | ||
return [ | ||
{row: minRow, column: minCol}, | ||
{row: maxRow, column: maxCol} | ||
]; | ||
return [ | ||
{ row: minRow, column: minCol }, | ||
{ row: maxRow, column: maxCol } | ||
]; | ||
} | ||
function extractData(files) { | ||
try { | ||
var libxmljs = require('libxmljs'), | ||
sheet = libxmljs.parseXml(files.sheet.contents), | ||
strings = libxmljs.parseXml(files.strings.contents), | ||
ns = {a: 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'}, | ||
data = []; | ||
} catch(parseError){ | ||
return []; | ||
} | ||
let sheet; | ||
let values; | ||
const data = []; | ||
var colToInt = function(col) { | ||
var letters = ["", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]; | ||
col = col.trim().split(''); | ||
var n = 0; | ||
try { | ||
sheet = new XMLDOM.DOMParser().parseFromString(files.sheet.contents); | ||
const valuesDoc = new XMLDOM.DOMParser().parseFromString(files.strings.contents); | ||
values = select('//a:si', valuesDoc) | ||
.map(string => select('.//a:t[not(ancestor::a:rPh)]', string).map(t => t.textContent).join('')); | ||
} catch(parseError){ | ||
return []; | ||
} | ||
for (var i = 0; i < col.length; i++) { | ||
n *= 26; | ||
n += letters.indexOf(col[i]); | ||
} | ||
function colToInt(col) { | ||
const letters = ["", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]; | ||
col = col.trim().split(''); | ||
return n; | ||
}; | ||
let n = 0; | ||
var CellCoords = function(cell) { | ||
cell = cell.split(/([0-9]+)/); | ||
this.row = parseInt(cell[1]); | ||
this.column = colToInt(cell[0]); | ||
}; | ||
for (let i = 0; i < col.length; i++) { | ||
n *= 26; | ||
n += letters.indexOf(col[i]); | ||
} | ||
var na = { value: function() { return ''; }, | ||
text: function() { return ''; } }; | ||
return n; | ||
}; | ||
var Cell = function(cellNode) { | ||
var r = cellNode.attr('r').value(), | ||
type = (cellNode.attr('t') || na).value(), | ||
value = (cellNode.get('a:v', ns) || na ).text(), | ||
coords = new CellCoords(r); | ||
const na = { | ||
textContent: '' | ||
}; | ||
this.column = coords.column; | ||
this.row = coords.row; | ||
this.value = value; | ||
this.type = type; | ||
}; | ||
class CellCoords { | ||
constructor(cell) { | ||
cell = cell.split(/([0-9]+)/); | ||
this.row = parseInt(cell[1]); | ||
this.column = colToInt(cell[0]); | ||
} | ||
} | ||
var cellNodes = sheet.find('/a:worksheet/a:sheetData/a:row/a:c', ns); | ||
var cells = _(cellNodes).map(function (node) { | ||
return new Cell(node); | ||
}); | ||
class Cell { | ||
constructor(cellNode) { | ||
const r = cellNode.getAttribute('r'); | ||
const type = cellNode.getAttribute('t') || ''; | ||
const value = (select('a:v', cellNode, 1) || na).textContent; | ||
const coords = new CellCoords(r) | ||
var d = sheet.get('//a:dimension/@ref', ns); | ||
if (d) { | ||
d = _.map(d.value().split(':'), function(v) { return new CellCoords(v); }); | ||
} else { | ||
d = calculateDimensions(cells) | ||
} | ||
this.column = coords.column; | ||
this.row = coords.row; | ||
this.value = value; | ||
this.type = type; | ||
} | ||
} | ||
var cols = d[1].column - d[0].column + 1, | ||
rows = d[1].row - d[0].row + 1; | ||
const cells = select('/a:worksheet/a:sheetData/a:row/a:c', sheet).map(node => new Cell(node)); | ||
_(rows).times(function() { | ||
var _row = []; | ||
_(cols).times(function() { _row.push(''); }); | ||
data.push(_row); | ||
}); | ||
let d = select('//a:dimension/@ref', sheet, 1); | ||
if (d) { | ||
d = d.textContent.split(':').map(_ => new CellCoords(_)); | ||
} else { | ||
d = calculateDimensions(cells); | ||
} | ||
_.each(cells, function(cell) { | ||
var value = cell.value; | ||
const cols = d[1].column - d[0].column + 1; | ||
const rows = d[1].row - d[0].row + 1; | ||
if (cell.type == 's') { | ||
values = strings.find('//a:si[' + (parseInt(value) + 1) + ']//a:t[not(ancestor::a:rPh)]', ns) | ||
value = ""; | ||
for (var i = 0; i < values.length; i++) { | ||
value += values[i].text(); | ||
} | ||
} | ||
if (data[cell.row - d[0].row]) { | ||
data[cell.row - d[0].row][cell.column - d[0].column] = value; | ||
} | ||
}); | ||
times(rows, () => { | ||
const row = []; | ||
times(cols, () => row.push('')); | ||
data.push(row); | ||
}); | ||
return data; | ||
for (const cell of cells) { | ||
let value = cell.value; | ||
if (cell.type == 's') { | ||
value = values[parseInt(value)]; | ||
} | ||
if (data[cell.row - d[0].row]) { | ||
data[cell.row - d[0].row][cell.column - d[0].column] = value; | ||
} | ||
} | ||
return data; | ||
} | ||
module.exports = function parseXlsx(path, sheet, cb) { | ||
if (typeof cb === 'undefined') { | ||
cb = sheet; | ||
sheet = '1'; | ||
} | ||
extractFiles(path, sheet).then(function(files) { | ||
cb(null, extractData(files)); | ||
}, | ||
function(err) { | ||
cb(err); | ||
}); | ||
export default function parseXlsx(path, sheet = '1') { | ||
return extractFiles(path, sheet).then((files) => extractData(files)); | ||
}; | ||
function times(n, action) { | ||
let i = 0; | ||
while (i < n) { | ||
action(); | ||
i++; | ||
} | ||
} |
{ | ||
"name": "excel", | ||
"version": "0.1.7", | ||
"version": "1.0.0", | ||
"description": "Simple NodeJS XLSX parser.", | ||
"main": "excelParser.js", | ||
"main": "commonjs/excelParser.js", | ||
"contributors": [ | ||
@@ -14,15 +14,23 @@ { | ||
"email": "fabian@starting-point.nl" | ||
}, | ||
{ | ||
"name": "Cat Amphetamine", | ||
"email": "purecatamphetamine@gmail.com" | ||
} | ||
], | ||
"scripts": { | ||
"test": "mocha --reporter list" | ||
"build": "babel ./excelParser.js --out-dir ./commonjs --source-maps", | ||
"test": "npm run build && mocha --reporter list test/test.js", | ||
"prepublish": "npm run test" | ||
}, | ||
"dependencies": { | ||
"node-promise": "~0.5.3", | ||
"libxmljs": "~0.18.0", | ||
"underscore": "~1.3.3", | ||
"unzip2": "0.2.5" | ||
"libxmljs": "^0.18.0", | ||
"unzipper": "^0.8.11", | ||
"xmldom": "^0.1.27", | ||
"xpath": "0.0.27" | ||
}, | ||
"devDependencies": { | ||
"mocha": "~1.17.1" | ||
"babel-cli": "^6.26.0", | ||
"babel-preset-env": "^1.6.1", | ||
"mocha": "^1.17.1" | ||
}, | ||
@@ -29,0 +37,0 @@ "repository": { |
Excel.js [![Build Status](https://travis-ci.org/trevordixon/excel.js.svg?branch=master)](https://travis-ci.org/trevordixon/excel.js) | ||
======== | ||
Native node.js Excel file parser. Only supports xlsx for now. | ||
Native node.js Excel file parser. Only supports `*.xlsx` files for now. | ||
Install | ||
======= | ||
npm install excel | ||
```js | ||
npm install excel | ||
``` | ||
Use | ||
==== | ||
var parseXlsx = require('excel'); | ||
=== | ||
parseXlsx('Spreadsheet.xlsx', function(err, data) { | ||
if(err) throw err; | ||
// data is an array of arrays | ||
}); | ||
```js | ||
import parseXlsx from 'excel'; | ||
parseXlsx('Spreadsheet.xlsx').then((data) => { | ||
// data is an array of arrays | ||
}); | ||
``` | ||
If you have multiple sheets in your spreadsheet, | ||
parseXlsx('Spreadsheet.xlsx', '2', function(err, data) { | ||
if(err) throw err; | ||
// data is an array of arrays | ||
}); | ||
```js | ||
parseXlsx('Spreadsheet.xlsx', '2').then((data) => { | ||
// data is an array of arrays | ||
}); | ||
```` | ||
@@ -26,0 +32,0 @@ Test |
@@ -1,8 +0,17 @@ | ||
var parseXlsx = require('../excelParser'); | ||
var parseExcel = require('../commonjs/excelParser').default; | ||
var assert = require('assert'); | ||
function parseXlsx(path, sheet, callback) { | ||
if (typeof callback === 'undefined') { | ||
callback = sheet; | ||
sheet = '1'; | ||
} | ||
parseExcel(path, sheet).then((data) => callback(null, data), callback); | ||
} | ||
var sheetsDir = __dirname + '/spreadsheets'; | ||
var sheets = { | ||
'excel_mac_2011-basic.xlsx': [ [ 'One', 'Two' ], [ 'Three', 'Four' ] ], | ||
'excel_mac_2011-formatting.xlsx': [ [ 'Hey', 'now', 'so' ], [ 'cool', '', '' ] ] | ||
'excel_mac_2011-formatting.xlsx': [ [ 'Hey', 'now', 'so' ], [ 'cool', '', '' ] ], | ||
'excel_multiple_text_nodes.xlsx': [ [ 'id', 'memo' ], [ '1.0', 'abc def ghi ' ], [ '2.0', 'pqr stu' ] ] | ||
}; | ||
@@ -9,0 +18,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
84951
14
361
1
39
3
3
1
+ Addedunzipper@^0.8.11
+ Addedxmldom@^0.1.27
+ Addedxpath@0.0.27
+ Addedbig-integer@1.6.52(transitive)
+ Addedbluebird@3.4.7(transitive)
+ Addedbuffer-indexof-polyfill@1.0.2(transitive)
+ Addedbuffer-shims@1.0.0(transitive)
+ Addedduplexer2@0.1.4(transitive)
+ Addedfstream@1.0.12(transitive)
+ Addedgraceful-fs@4.2.11(transitive)
+ Addedlistenercount@1.0.1(transitive)
+ Addedprocess-nextick-args@1.0.7(transitive)
+ Addedreadable-stream@2.1.5(transitive)
+ Addedunzipper@0.8.14(transitive)
+ Addedxmldom@0.1.31(transitive)
+ Addedxpath@0.0.27(transitive)
- Removednode-promise@~0.5.3
- Removedunderscore@~1.3.3
- Removedunzip2@0.2.5
- Removedfstream@0.1.31(transitive)
- Removedgraceful-fs@3.0.12(transitive)
- Removedisarray@0.0.1(transitive)
- Removedmatch-stream@0.0.2(transitive)
- Removednatives@1.1.6(transitive)
- Removednode-promise@0.5.14(transitive)
- Removedover@0.0.5(transitive)
- Removedpullstream@0.4.1(transitive)
- Removedreadable-stream@1.0.34(transitive)
- Removedslice-stream@1.0.0(transitive)
- Removedunderscore@1.3.3(transitive)
- Removedunzip2@0.2.5(transitive)
Updatedlibxmljs@^0.18.0