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); + }); + }); +});