From 31b2f91439eadb37c49963f94485ea9c9bd21812 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 11 Jul 2026 14:49:24 +0000 Subject: [PATCH 1/2] fix: validate tab_size matches how it's actually used The guard checked Number(tab_size) < 1, but the raw tab_size value was used directly in the indentation math (SPACE.repeat(tab_size * size)). A non-numeric tab_size (e.g. a JS caller passing a string, bypassing the TS type) coerced to NaN, which passed the old guard (NaN < 1 is false) and silently produced zero-indented output instead of throwing. Fractional values were silently truncated by String.prototype.repeat. Now tab_size is coerced once and validated as a finite integer >= 1; non-numeric, fractional, NaN and Infinity values all throw. --- src/lib/index.ts | 7 +++++-- test/tab-size.test.ts | 31 +++++++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/lib/index.ts b/src/lib/index.ts index 515d26f..243fcaf 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -417,8 +417,11 @@ export function format( css: string, { minify = false, tab_size = undefined }: FormatOptions = Object.create(null), ): string { - if (tab_size !== undefined && Number(tab_size) < 1) { - throw new TypeError('tab_size must be a number greater than 0') + if (tab_size !== undefined) { + tab_size = Number(tab_size) + if (!Number.isInteger(tab_size) || tab_size < 1) { + throw new TypeError('tab_size must be a whole number greater than 0') + } } const NEWLINE = minify ? EMPTY_STRING : '\n' diff --git a/test/tab-size.test.ts b/test/tab-size.test.ts index 6332db4..fd92b48 100644 --- a/test/tab-size.test.ts +++ b/test/tab-size.test.ts @@ -35,12 +35,39 @@ test('tab_size: 2', () => { }) test('invalid tab_size: 0', () => { - expect(() => format(fixture, { tab_size: 0 })).toThrow('tab_size must be a number greater than 0') + expect(() => format(fixture, { tab_size: 0 })).toThrow( + 'tab_size must be a whole number greater than 0', + ) }) test('invalid tab_size: negative', () => { expect(() => format(fixture, { tab_size: -1 })).toThrow( - 'tab_size must be a number greater than 0', + 'tab_size must be a whole number greater than 0', + ) +}) + +test('invalid tab_size: fractional', () => { + expect(() => format(fixture, { tab_size: 2.5 })).toThrow( + 'tab_size must be a whole number greater than 0', + ) +}) + +test('invalid tab_size: NaN', () => { + expect(() => format(fixture, { tab_size: NaN })).toThrow( + 'tab_size must be a whole number greater than 0', + ) +}) + +test('invalid tab_size: Infinity', () => { + expect(() => format(fixture, { tab_size: Infinity })).toThrow( + 'tab_size must be a whole number greater than 0', + ) +}) + +test('invalid tab_size: non-numeric string (bypassing TypeScript types)', () => { + // @ts-expect-error tab_size is typed as number, but JS callers can pass anything at runtime + expect(() => format(fixture, { tab_size: 'abc' })).toThrow( + 'tab_size must be a whole number greater than 0', ) }) From 9429c44e1519971c8b767b62dbf18d3a3e8be446 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 11 Jul 2026 14:58:34 +0000 Subject: [PATCH 2/2] fix: invalid tab_size falls back to default indentation instead of throwing An invalid tab_size (0, negative, fractional, NaN, Infinity, or a non-numeric value from a JS caller bypassing the TS type) is now silently ignored, falling back to the default tab indentation, rather than throwing a TypeError. --- src/lib/index.ts | 8 ++++---- test/tab-size.test.ts | 42 ++++++++++++++++++------------------------ 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/src/lib/index.ts b/src/lib/index.ts index 243fcaf..67434db 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -418,10 +418,10 @@ export function format( { minify = false, tab_size = undefined }: FormatOptions = Object.create(null), ): string { if (tab_size !== undefined) { - tab_size = Number(tab_size) - if (!Number.isInteger(tab_size) || tab_size < 1) { - throw new TypeError('tab_size must be a whole number greater than 0') - } + let normalized = Number(tab_size) + // An invalid tab_size (non-numeric, fractional, NaN, Infinity, < 1) falls + // back to the default tab indentation instead of throwing. + tab_size = Number.isInteger(normalized) && normalized >= 1 ? normalized : undefined } const NEWLINE = minify ? EMPTY_STRING : '\n' diff --git a/test/tab-size.test.ts b/test/tab-size.test.ts index fd92b48..1a2f17a 100644 --- a/test/tab-size.test.ts +++ b/test/tab-size.test.ts @@ -34,41 +34,35 @@ test('tab_size: 2', () => { expect(actual).toEqual(expected) }) -test('invalid tab_size: 0', () => { - expect(() => format(fixture, { tab_size: 0 })).toThrow( - 'tab_size must be a whole number greater than 0', - ) +let default_indentation = format(fixture) + +test('invalid tab_size: 0 falls back to default tab indentation', () => { + expect(format(fixture, { tab_size: 0 })).toEqual(default_indentation) }) -test('invalid tab_size: negative', () => { - expect(() => format(fixture, { tab_size: -1 })).toThrow( - 'tab_size must be a whole number greater than 0', - ) +test('invalid tab_size: negative falls back to default tab indentation', () => { + expect(format(fixture, { tab_size: -1 })).toEqual(default_indentation) }) -test('invalid tab_size: fractional', () => { - expect(() => format(fixture, { tab_size: 2.5 })).toThrow( - 'tab_size must be a whole number greater than 0', - ) +test('invalid tab_size: fractional falls back to default tab indentation', () => { + expect(format(fixture, { tab_size: 2.5 })).toEqual(default_indentation) }) -test('invalid tab_size: NaN', () => { - expect(() => format(fixture, { tab_size: NaN })).toThrow( - 'tab_size must be a whole number greater than 0', - ) +test('invalid tab_size: NaN falls back to default tab indentation', () => { + expect(format(fixture, { tab_size: NaN })).toEqual(default_indentation) }) -test('invalid tab_size: Infinity', () => { - expect(() => format(fixture, { tab_size: Infinity })).toThrow( - 'tab_size must be a whole number greater than 0', - ) +test('invalid tab_size: Infinity falls back to default tab indentation', () => { + expect(format(fixture, { tab_size: Infinity })).toEqual(default_indentation) }) -test('invalid tab_size: non-numeric string (bypassing TypeScript types)', () => { +test('invalid tab_size: non-numeric string falls back to default tab indentation (bypassing TypeScript types)', () => { // @ts-expect-error tab_size is typed as number, but JS callers can pass anything at runtime - expect(() => format(fixture, { tab_size: 'abc' })).toThrow( - 'tab_size must be a whole number greater than 0', - ) + expect(format(fixture, { tab_size: 'abc' })).toEqual(default_indentation) +}) + +test('invalid tab_size does not throw', () => { + expect(() => format(fixture, { tab_size: 0 }), 'invalid tab_size should not throw').not.toThrow() }) test('combine tab_size and minify', () => {