Skip to content

Commit 3a3af7c

Browse files
committed
Fix exceljs#3028: make getWorksheet case-insensitive to match addWorksheet
Workbook.addWorksheet enforces case-insensitive name uniqueness (and Excel itself treats sheet names case-insensitively), but Workbook.getWorksheet was matching with a case-sensitive ===. As a result, after addWorksheet("Sheet1"), getWorksheet("sheet1") returned undefined even though addWorksheet("sheet1") would throw "Worksheet name already exists". Lowercase both sides of the string comparison in getWorksheet so any casing of an existing sheet name resolves to that sheet. Numeric-id and no-arg branches are unchanged. Adds spec/integration/issues/issue-3028-getworksheet-case-insensitive.spec.js covering: mixed casings resolve to the same sheet, unknown names still return undefined, numeric id and no-arg shortcuts preserved, and the addWorksheet/getWorksheet contracts now agree. Drive-by: collapse pre-existing multi-line constructs in the same function (two console.trace deprecation warnings and an Object.assign + a reduce->for loop) so prettier no longer wraps them with a trailing comma that eslint then rejects. The repo's .prettierrc (trailingComma: "all") conflicts with .eslintrc (comma-dangle functions: "never"), and these were the spots prettier wanted to rewrite around the edited region. Behavior is unchanged.
1 parent 4c9e73c commit 3a3af7c

3 files changed

Lines changed: 71 additions & 14 deletions

File tree

FORK.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,16 @@ This AI-assisted workflow enables rapid response to community issues while maint
2828

2929
## Fork Release History
3030

31+
### Unreleased
32+
33+
**Bug Fix: `workbook.getWorksheet(name)` is now case-insensitive** ([upstream #3028](https://github.com/exceljs/exceljs/issues/3028))
34+
35+
`Workbook.addWorksheet(name)` already enforces case-insensitive uniqueness (and Excel itself treats sheet names case-insensitively), but `Workbook.getWorksheet(name)` was matching with a case-sensitive `===`. The two APIs disagreed: a workbook would refuse to add `"sheet1"` because `"Sheet1"` exists, yet `getWorksheet("sheet1")` would return `undefined` for that same sheet.
36+
37+
`getWorksheet` now lowercases both sides of the comparison so any casing of an existing sheet name resolves to that sheet. Numeric-id and no-arg branches are unchanged.
38+
39+
Minor behavior change: code that relied on a casing mismatch returning `undefined` will now find the sheet. We consider this a bug fix — the previous behavior contradicted `addWorksheet`'s own constraint.
40+
3141
### 4.4.0-protobi.9 (2026-02-01)
3242

3343
**New Feature: Pivot Table & Chart Round-Trip Preservation** ([#41](https://github.com/cjnoname/excelts/issues/41))

lib/doc/workbook.js

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -57,20 +57,20 @@ class Workbook {
5757
// if options is a color, call it tabColor (and signal deprecated message)
5858
if (options) {
5959
if (typeof options === 'string') {
60+
const argbMsg =
61+
'tabColor argument is now deprecated. Please use workbook.addWorksheet(name, {properties: { tabColor: { argb: "rbg value" } }';
6062
// eslint-disable-next-line no-console
61-
console.trace(
62-
'tabColor argument is now deprecated. Please use workbook.addWorksheet(name, {properties: { tabColor: { argb: "rbg value" } }'
63-
);
63+
console.trace(argbMsg);
6464
options = {
6565
properties: {
6666
tabColor: {argb: options},
6767
},
6868
};
6969
} else if (options.argb || options.theme || options.indexed) {
70+
const tabMsg =
71+
'tabColor argument is now deprecated. Please use workbook.addWorksheet(name, {properties: { tabColor: { ... } }';
7072
// eslint-disable-next-line no-console
71-
console.trace(
72-
'tabColor argument is now deprecated. Please use workbook.addWorksheet(name, {properties: { tabColor: { ... } }'
73-
);
73+
console.trace(tabMsg);
7474
options = {
7575
properties: {
7676
tabColor: options,
@@ -79,13 +79,12 @@ class Workbook {
7979
}
8080
}
8181

82-
const lastOrderNo = this._worksheets.reduce((acc, ws) => ((ws && ws.orderNo) > acc ? ws.orderNo : acc), 0);
83-
const worksheetOptions = Object.assign({}, options, {
84-
id,
85-
name,
86-
orderNo: lastOrderNo + 1,
87-
workbook: this,
88-
});
82+
let lastOrderNo = 0;
83+
for (const w of this._worksheets) {
84+
if (w && w.orderNo > lastOrderNo) lastOrderNo = w.orderNo;
85+
}
86+
const orderNo = lastOrderNo + 1;
87+
const worksheetOptions = Object.assign({}, options, {id, name, orderNo, workbook: this});
8988

9089
const worksheet = new Worksheet(worksheetOptions);
9190

@@ -112,7 +111,9 @@ class Workbook {
112111
return this._worksheets[id];
113112
}
114113
if (typeof id === 'string') {
115-
return this._worksheets.find(worksheet => worksheet && worksheet.name === id);
114+
// Case-insensitive to match addWorksheet's uniqueness rule (and Excel itself).
115+
const target = id.toLowerCase();
116+
return this._worksheets.find(ws => ws && ws.name.toLowerCase() === target);
116117
}
117118
return undefined;
118119
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
const ExcelJS = verquire('exceljs');
2+
3+
describe('github issues', () => {
4+
describe('issue 3028 - workbook.getWorksheet should be case-insensitive', () => {
5+
it('returns the same worksheet regardless of name casing', () => {
6+
const wb = new ExcelJS.Workbook();
7+
const ws = wb.addWorksheet('TestSheet');
8+
9+
expect(wb.getWorksheet('TestSheet')).to.equal(ws);
10+
expect(wb.getWorksheet('testsheet')).to.equal(ws);
11+
expect(wb.getWorksheet('TESTSHEET')).to.equal(ws);
12+
expect(wb.getWorksheet('tEsTsHeEt')).to.equal(ws);
13+
});
14+
15+
it('still returns undefined for genuinely unknown names', () => {
16+
const wb = new ExcelJS.Workbook();
17+
wb.addWorksheet('TestSheet');
18+
19+
expect(wb.getWorksheet('OtherSheet')).to.equal(undefined);
20+
});
21+
22+
it('preserves numeric id lookup', () => {
23+
const wb = new ExcelJS.Workbook();
24+
const ws = wb.addWorksheet('TestSheet');
25+
26+
expect(wb.getWorksheet(ws.id)).to.equal(ws);
27+
});
28+
29+
it('preserves the no-arg first-worksheet shortcut', () => {
30+
const wb = new ExcelJS.Workbook();
31+
const ws = wb.addWorksheet('TestSheet');
32+
33+
expect(wb.getWorksheet()).to.equal(ws);
34+
});
35+
36+
it('aligns getWorksheet with addWorksheet case-insensitive uniqueness', () => {
37+
// addWorksheet rejects "testsheet" as a duplicate of "TestSheet";
38+
// therefore getWorksheet("testsheet") must locate the existing sheet.
39+
const wb = new ExcelJS.Workbook();
40+
const ws = wb.addWorksheet('TestSheet');
41+
42+
expect(() => wb.addWorksheet('testsheet')).to.throw(/already exists/);
43+
expect(wb.getWorksheet('testsheet')).to.equal(ws);
44+
});
45+
});
46+
});

0 commit comments

Comments
 (0)