From 3a3af7cad7888b773f7ab2a42a85bb0e233d7106 Mon Sep 17 00:00:00 2001 From: Bob Senoff Date: Thu, 30 Apr 2026 20:41:52 -0500 Subject: [PATCH] Fix #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. --- FORK.md | 10 ++++ lib/doc/workbook.js | 29 ++++++------ ...3028-getworksheet-case-insensitive.spec.js | 46 +++++++++++++++++++ 3 files changed, 71 insertions(+), 14 deletions(-) create mode 100644 spec/integration/issues/issue-3028-getworksheet-case-insensitive.spec.js diff --git a/FORK.md b/FORK.md index 216edd925..4002dbe47 100644 --- a/FORK.md +++ b/FORK.md @@ -28,6 +28,16 @@ This AI-assisted workflow enables rapid response to community issues while maint ## Fork Release History +### Unreleased + +**Bug Fix: `workbook.getWorksheet(name)` is now case-insensitive** ([upstream #3028](https://github.com/exceljs/exceljs/issues/3028)) + +`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. + +`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. + +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. + ### 4.4.0-protobi.9 (2026-02-01) **New Feature: Pivot Table & Chart Round-Trip Preservation** ([#41](https://github.com/cjnoname/excelts/issues/41)) diff --git a/lib/doc/workbook.js b/lib/doc/workbook.js index a6e4de920..f8f0d1574 100644 --- a/lib/doc/workbook.js +++ b/lib/doc/workbook.js @@ -57,20 +57,20 @@ class Workbook { // if options is a color, call it tabColor (and signal deprecated message) if (options) { if (typeof options === 'string') { + const argbMsg = + 'tabColor argument is now deprecated. Please use workbook.addWorksheet(name, {properties: { tabColor: { argb: "rbg value" } }'; // eslint-disable-next-line no-console - console.trace( - 'tabColor argument is now deprecated. Please use workbook.addWorksheet(name, {properties: { tabColor: { argb: "rbg value" } }' - ); + console.trace(argbMsg); options = { properties: { tabColor: {argb: options}, }, }; } else if (options.argb || options.theme || options.indexed) { + const tabMsg = + 'tabColor argument is now deprecated. Please use workbook.addWorksheet(name, {properties: { tabColor: { ... } }'; // eslint-disable-next-line no-console - console.trace( - 'tabColor argument is now deprecated. Please use workbook.addWorksheet(name, {properties: { tabColor: { ... } }' - ); + console.trace(tabMsg); options = { properties: { tabColor: options, @@ -79,13 +79,12 @@ class Workbook { } } - const lastOrderNo = this._worksheets.reduce((acc, ws) => ((ws && ws.orderNo) > acc ? ws.orderNo : acc), 0); - const worksheetOptions = Object.assign({}, options, { - id, - name, - orderNo: lastOrderNo + 1, - workbook: this, - }); + let lastOrderNo = 0; + for (const w of this._worksheets) { + if (w && w.orderNo > lastOrderNo) lastOrderNo = w.orderNo; + } + const orderNo = lastOrderNo + 1; + const worksheetOptions = Object.assign({}, options, {id, name, orderNo, workbook: this}); const worksheet = new Worksheet(worksheetOptions); @@ -112,7 +111,9 @@ class Workbook { return this._worksheets[id]; } if (typeof id === 'string') { - return this._worksheets.find(worksheet => worksheet && worksheet.name === id); + // Case-insensitive to match addWorksheet's uniqueness rule (and Excel itself). + const target = id.toLowerCase(); + return this._worksheets.find(ws => ws && ws.name.toLowerCase() === target); } return undefined; } diff --git a/spec/integration/issues/issue-3028-getworksheet-case-insensitive.spec.js b/spec/integration/issues/issue-3028-getworksheet-case-insensitive.spec.js new file mode 100644 index 000000000..c3f9d6dff --- /dev/null +++ b/spec/integration/issues/issue-3028-getworksheet-case-insensitive.spec.js @@ -0,0 +1,46 @@ +const ExcelJS = verquire('exceljs'); + +describe('github issues', () => { + describe('issue 3028 - workbook.getWorksheet should be case-insensitive', () => { + it('returns the same worksheet regardless of name casing', () => { + const wb = new ExcelJS.Workbook(); + const ws = wb.addWorksheet('TestSheet'); + + expect(wb.getWorksheet('TestSheet')).to.equal(ws); + expect(wb.getWorksheet('testsheet')).to.equal(ws); + expect(wb.getWorksheet('TESTSHEET')).to.equal(ws); + expect(wb.getWorksheet('tEsTsHeEt')).to.equal(ws); + }); + + it('still returns undefined for genuinely unknown names', () => { + const wb = new ExcelJS.Workbook(); + wb.addWorksheet('TestSheet'); + + expect(wb.getWorksheet('OtherSheet')).to.equal(undefined); + }); + + it('preserves numeric id lookup', () => { + const wb = new ExcelJS.Workbook(); + const ws = wb.addWorksheet('TestSheet'); + + expect(wb.getWorksheet(ws.id)).to.equal(ws); + }); + + it('preserves the no-arg first-worksheet shortcut', () => { + const wb = new ExcelJS.Workbook(); + const ws = wb.addWorksheet('TestSheet'); + + expect(wb.getWorksheet()).to.equal(ws); + }); + + it('aligns getWorksheet with addWorksheet case-insensitive uniqueness', () => { + // addWorksheet rejects "testsheet" as a duplicate of "TestSheet"; + // therefore getWorksheet("testsheet") must locate the existing sheet. + const wb = new ExcelJS.Workbook(); + const ws = wb.addWorksheet('TestSheet'); + + expect(() => wb.addWorksheet('testsheet')).to.throw(/already exists/); + expect(wb.getWorksheet('testsheet')).to.equal(ws); + }); + }); +});