From 1776ae1834987eba58a0d3b03d8350bc40a6c3bb Mon Sep 17 00:00:00 2001 From: Johann Schopplich Date: Wed, 29 Oct 2025 09:01:43 +0100 Subject: [PATCH] refactor: use specific error types in decoders and parser --- src/decoders.ts | 12 ++++++------ src/index.ts | 2 +- src/parser.ts | 14 +++++++------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/decoders.ts b/src/decoders.ts index 1718668..17eeac1 100644 --- a/src/decoders.ts +++ b/src/decoders.ts @@ -29,7 +29,7 @@ import { export function decodeValueFromLines(cursor: LineCursor, options: ResolvedDecodeOptions): JsonValue { const first = cursor.peek() if (!first) { - throw new Error('No content to decode') + throw new ReferenceError('No content to decode') } // Check for root array @@ -230,7 +230,7 @@ function decodeListArray( if (options.strict && !cursor.atEnd()) { const nextLine = cursor.peek() if (nextLine && nextLine.depth === itemDepth && nextLine.content.startsWith(LIST_ITEM_PREFIX)) { - throw new Error(`Expected ${header.length} list array items, but found more`) + throw new RangeError(`Expected ${header.length} list array items, but found more`) } } @@ -284,7 +284,7 @@ function decodeTabularArray( if (!hasColon) { // No colon = data row (for single-field tables) - throw new Error(`Expected ${header.length} tabular rows, but found more`) + throw new RangeError(`Expected ${header.length} tabular rows, but found more`) } else if (hasDelimiter) { // Has both colon and delimiter - check which comes first @@ -292,7 +292,7 @@ function decodeTabularArray( const delimiterPos = nextLine.content.indexOf(header.delimiter) if (delimiterPos < colonPos) { // Delimiter before colon = data row - throw new Error(`Expected ${header.length} tabular rows, but found more`) + throw new RangeError(`Expected ${header.length} tabular rows, but found more`) } // Colon before delimiter = key-value pair, OK } @@ -315,7 +315,7 @@ function decodeListItem( ): JsonValue { const line = cursor.next() if (!line) { - throw new Error('Expected list item') + throw new ReferenceError('Expected list item') } const afterHyphen = line.content.slice(LIST_ITEM_PREFIX.length) @@ -382,7 +382,7 @@ function decodeFirstFieldOnHyphen( function assertExpectedCount(actual: number, expected: number, what: string, options: ResolvedDecodeOptions): void { if (options.strict && actual !== expected) { - throw new Error(`Expected ${expected} ${what}, but got ${actual}`) + throw new RangeError(`Expected ${expected} ${what}, but got ${actual}`) } } diff --git a/src/index.ts b/src/index.ts index 90e8e8a..a18cb51 100644 --- a/src/index.ts +++ b/src/index.ts @@ -36,7 +36,7 @@ export function decode(input: string, options?: DecodeOptions): JsonValue { const lines = toParsedLines(input, resolved.indent) if (lines.length === 0) { - throw new Error('Cannot decode empty input') + throw new TypeError('Cannot decode empty input: input must be a non-empty string') } const cursor = new LineCursor(lines) diff --git a/src/parser.ts b/src/parser.ts index f064eea..35cb42d 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -256,7 +256,7 @@ export function parseStringLiteral(token: string): string { if (trimmed[i] === DOUBLE_QUOTE) { // Found closing quote if (i !== trimmed.length - 1) { - throw new Error('Unexpected characters after closing quote') + throw new SyntaxError('Unexpected characters after closing quote') } const content = trimmed.slice(1, i) return unescapeString(content) @@ -265,7 +265,7 @@ export function parseStringLiteral(token: string): string { } // If we get here, no closing quote was found - throw new Error('Unterminated string: missing closing quote') + throw new SyntaxError('Unterminated string: missing closing quote') } return trimmed @@ -278,7 +278,7 @@ export function unescapeString(value: string): string { while (i < value.length) { if (value[i] === BACKSLASH) { if (i + 1 >= value.length) { - throw new Error('Invalid escape sequence: backslash at end of string') + throw new SyntaxError('Invalid escape sequence: backslash at end of string') } const next = value[i + 1] @@ -308,7 +308,7 @@ export function unescapeString(value: string): string { continue } - throw new Error(`Invalid escape sequence: \\${next}`) + throw new SyntaxError(`Invalid escape sequence: \\${next}`) } result += value[i] @@ -326,7 +326,7 @@ export function parseUnquotedKey(content: string, start: number): { key: string, // Validate that a colon was found if (end >= content.length || content[end] !== COLON) { - throw new Error('Missing colon after key') + throw new SyntaxError('Missing colon after key') } const key = content.slice(start, end).trim() @@ -355,7 +355,7 @@ export function parseQuotedKey(content: string, start: number): { key: string, e // Validate and skip colon after quoted key if (end >= content.length || content[end] !== COLON) { - throw new Error('Missing colon after key') + throw new SyntaxError('Missing colon after key') } end++ @@ -366,7 +366,7 @@ export function parseQuotedKey(content: string, start: number): { key: string, e i++ } - throw new Error('Unterminated quoted key') + throw new SyntaxError('Unterminated quoted key') } export function parseKeyToken(content: string, start: number): { key: string, end: number } {