Skip to content

Commit 4cede49

Browse files
authored
Prefer public ExcelJS APIs (#255)
* Use Object.create(null) * Prefer public ExcelJS APIs * Use latest tap API
1 parent fe6a1b4 commit 4cede49

File tree

4 files changed

+519
-61
lines changed

4 files changed

+519
-61
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
},
3333
"devDependencies": {
3434
"eslint": "^7.18.0",
35+
"exceljs": "^4.3.0",
3536
"husky": "^4.3.8",
3637
"node-fetch": "^2.6.1",
3738
"rollup": "^2.37.1",

src/xlsx.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ function extract(sheet, {range, headers} = {}) {
3535
const output = new Array(r1 - r0 + 1);
3636
for (let r = r0; r <= r1; r++) {
3737
const row = (output[r - r0] = Object.create(null, {"#": {value: r + 1}}));
38-
const _row = sheet._rows[r]; // is this an internal ExcelJS API? why not sheet.getRow(r)?
39-
if (_row && _row.hasValues)
38+
const _row = sheet.getRow(r + 1);
39+
if (_row.hasValues)
4040
for (let c = c0; c <= c1; c++) {
41-
const value = valueOf(_row._cells[c]); // internal ExcelJS API?
41+
const value = valueOf(_row.findCell(c + 1));
4242
if (value != null) row[names[c + 1]] = value;
4343
}
4444
}

test/xlsx-test.js

Lines changed: 25 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,31 @@
11
import {test} from "tap";
22
import {Workbook} from "../src/xlsx.js";
3+
import ExcelJS from "exceljs";
34

4-
function mockWorkbook(contents, overrides = {}) {
5-
return {
6-
worksheets: Object.keys(contents).map((name) => ({name})),
7-
getWorksheet(name) {
8-
const _rows = contents[name];
9-
return Object.assign(
10-
{
11-
_rows: _rows.map((row) => ({
12-
_cells: row.map((cell) => ({value: cell})),
13-
hasValues: !!row.length,
14-
})),
15-
rowCount: _rows.length,
16-
columnCount: Math.max(..._rows.map((r) => r.length)),
17-
},
18-
overrides
19-
);
20-
},
21-
};
5+
function exceljs(contents) {
6+
const workbook = new ExcelJS.Workbook();
7+
for (const [sheet, rows] of Object.entries(contents)) {
8+
const ws = workbook.addWorksheet(sheet);
9+
for (const row of rows) ws.addRow(row);
10+
}
11+
return workbook;
2212
}
2313

2414
test("FileAttachment.xlsx reads sheet names", (t) => {
25-
const workbook = new Workbook(mockWorkbook({Sheet1: []}));
15+
const workbook = new Workbook(exceljs({Sheet1: []}));
2616
t.same(workbook.sheetNames, ["Sheet1"]);
2717
t.end();
2818
});
2919

3020
test("FileAttachment.xlsx sheet(name) throws on unknown sheet name", (t) => {
31-
const workbook = new Workbook(mockWorkbook({Sheet1: []}));
21+
const workbook = new Workbook(exceljs({Sheet1: []}));
3222
t.throws(() => workbook.sheet("bad"));
3323
t.end();
3424
});
3525

3626
test("FileAttachment.xlsx reads sheets", (t) => {
3727
const workbook = new Workbook(
38-
mockWorkbook({
28+
exceljs({
3929
Sheet1: [
4030
["one", "two", "three"],
4131
[1, 2, 3],
@@ -50,13 +40,15 @@ test("FileAttachment.xlsx reads sheets", (t) => {
5040
{A: "one", B: "two", C: "three"},
5141
{A: 1, B: 2, C: 3},
5242
]);
43+
t.equal(workbook.sheet(0)[0]["#"], 1);
44+
t.equal(workbook.sheet(0)[1]["#"], 2);
5345
t.end();
5446
});
5547

5648
test("FileAttachment.xlsx reads sheets with different types", (t) => {
5749
t.same(
5850
new Workbook(
59-
mockWorkbook({
51+
exceljs({
6052
Sheet1: [
6153
[],
6254
[null, undefined],
@@ -79,7 +71,7 @@ test("FileAttachment.xlsx reads sheets with different types", (t) => {
7971
);
8072
t.same(
8173
new Workbook(
82-
mockWorkbook({
74+
exceljs({
8375
Sheet1: [
8476
[
8577
{richText: [{text: "two"}, {text: "three"}]}, // A
@@ -112,7 +104,7 @@ test("FileAttachment.xlsx reads sheets with different types", (t) => {
112104
);
113105
t.same(
114106
new Workbook(
115-
mockWorkbook({
107+
exceljs({
116108
Sheet1: [
117109
[
118110
{formula: "=B2*5", result: 10},
@@ -131,7 +123,7 @@ test("FileAttachment.xlsx reads sheets with different types", (t) => {
131123

132124
test("FileAttachment.xlsx reads sheets with headers", (t) => {
133125
const workbook = new Workbook(
134-
mockWorkbook({
126+
exceljs({
135127
Sheet1: [
136128
[null, "one", "one", "two", "A", "0"],
137129
[1, null, 3, 4, 5, "zero"],
@@ -156,9 +148,10 @@ test("FileAttachment.xlsx reads sheets with headers", (t) => {
156148
});
157149

158150
test("FileAttachment.xlsx throws on invalid ranges", (t) => {
159-
const workbook = new Workbook(mockWorkbook({Sheet1: []}));
151+
const workbook = new Workbook(exceljs({Sheet1: []}));
160152
const malformed = new Error("Malformed range specifier");
161153

154+
t.throws(() => t.same(workbook.sheet(0, {range: 0})), malformed);
162155
t.throws(() => t.same(workbook.sheet(0, {range: ""})), malformed);
163156
t.throws(() => t.same(workbook.sheet(0, {range: "-:"})), malformed);
164157
t.throws(() => t.same(workbook.sheet(0, {range: " :"})), malformed);
@@ -174,7 +167,7 @@ test("FileAttachment.xlsx throws on invalid ranges", (t) => {
174167

175168
test("FileAttachment.xlsx reads sheet ranges", (t) => {
176169
const workbook = new Workbook(
177-
mockWorkbook({
170+
exceljs({
178171
Sheet1: [
179172
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
180173
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
@@ -248,41 +241,22 @@ test("FileAttachment.xlsx reads sheet ranges", (t) => {
248241
t.end();
249242
});
250243

251-
test("FileAttachment.xlsx throws on unknown range specifier", (t) => {
252-
const workbook = new Workbook(mockWorkbook({Sheet1: []}));
253-
t.throws(() => workbook.sheet(0, {range: 0}));
254-
t.end();
255-
});
256-
257244
test("FileAttachment.xlsx derives column names such as A AA AAA…", (t) => {
258-
const l0 = 26 * 26 * 26 + 26 * 26 + 26;
245+
const l0 = 26 * 26 * 23;
259246
const workbook = new Workbook(
260-
mockWorkbook({
247+
exceljs({
261248
Sheet1: [Array.from({length: l0}).fill(1)],
262249
})
263250
);
264251
t.same(
265-
workbook.sheet(0, {headers: false}).columns.filter((d) => d.match(/^A*$/)),
252+
workbook.sheet(0).columns.filter((d) => d.match(/^A+$/)),
266253
["A", "AA", "AAA"]
267254
);
268-
const workbook1 = new Workbook(
269-
mockWorkbook({
270-
Sheet1: [Array.from({length: l0 + 1}).fill(1)],
271-
})
272-
);
273-
t.same(
274-
workbook1.sheet(0, {headers: false}).columns.filter((d) => d.match(/^A*$/)),
275-
["A", "AA", "AAA", "AAAA"]
276-
);
277255
t.end();
278256
});
279257

280258
test("FileAttachment.sheet headers protects __proto__ of row objects", (t) => {
281-
const workbook = new Workbook(
282-
mockWorkbook({
283-
Sheet1: [["__proto__"], [{a: 1}]],
284-
})
285-
);
286-
t.notEqual(workbook.sheet(0, {headers: true})[0].a, 1);
259+
const workbook = new Workbook(exceljs({Sheet1: [["__proto__"], [{a: 1}]]}));
260+
t.not(workbook.sheet(0, {headers: true})[0].a, 1);
287261
t.end();
288262
});

0 commit comments

Comments
 (0)