From 679e114423867bb58c90f437e2044a866fa5d59d Mon Sep 17 00:00:00 2001 From: Sameer Akhtar Date: Fri, 16 Oct 2026 01:00:39 +0530 Subject: [PATCH 1/5] Fix: Update parseSyntaxError to use lossless-json for accurate error locations --- lib/__tests__/json-utils.test.ts | 51 ++++++++++++++++++++++++++++++++ lib/json-utils.ts | 29 ++++++++++++++---- 2 files changed, 75 insertions(+), 5 deletions(-) diff --git a/lib/__tests__/json-utils.test.ts b/lib/__tests__/json-utils.test.ts index 0ca5d19..189e270 100644 --- a/lib/__tests__/json-utils.test.ts +++ b/lib/__tests__/json-utils.test.ts @@ -15,6 +15,57 @@ import { const BIG = '{"id":12345678901234567890,"pi":3.141592653589793238,"n":42}'; +import { validate } from "../json-utils"; + +describe("JSON Formatter Error Locations", () => { + it("extracts correct location for unquoted values", () => { + const result = validate('{"a": x}'); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error.line).toBe(1); + expect(result.error.column).toBe(7); + } + }); + + it("extracts correct location for multi-line JSON", () => { + // The issue specifies '{"a": 1, "b": tru/}' on three lines + const input = '{\n "a": 1, "b": tru/\n}'; + const result = validate(input); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error.line).toBe(2); + expect(result.error.column).toBe(16); // Note: Adjust column based on your exact spacing to match the target + } + }); + + it("extracts correct location for unexpected end of input", () => { + const result = validate('{"a": '); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error.line).toBe(1); + expect(result.error.column).toBe(7); + } + }); + + it("extracts correct location for undefined in array", () => { + const result = validate('[1, 2, undefined]'); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error.line).toBe(1); + expect(result.error.column).toBe(8); + } + }); + + it("keeps existing correct locations intact", () => { + const result = validate('{"a":1,}'); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error.line).toBe(1); + expect(result.error.column).toBe(8); + } + }); +}); + describe("lossless numbers", () => { it("preserves large integers and long decimals when formatting", () => { const r = format(BIG, 2); diff --git a/lib/json-utils.ts b/lib/json-utils.ts index 5b9db4f..ab286bc 100644 --- a/lib/json-utils.ts +++ b/lib/json-utils.ts @@ -35,22 +35,41 @@ function parseSyntaxError(raw: string, err: unknown): JsonError { const msg = err instanceof Error ? err.message : String(err); const posMatch = msg.match(/position (\d+)/i); const lineMatch = msg.match(/line (\d+) column (\d+)/i); + //let message; + + let line = 1; let column = 1; let position = 0; - if (posMatch) { + if (posMatch && lineMatch==null) { position = parseInt(posMatch[1], 10); const lc = computeLineCol(raw, position); line = lc.line; column = lc.column; - } else if (lineMatch) { + } else if (lineMatch && posMatch==null) { line = parseInt(lineMatch[1], 10); column = parseInt(lineMatch[2], 10); - } + } + else{ + try{ + llParse(raw) + } + catch(llError) { + if (llError instanceof Error) { + const llPosMatch = llError.message.match(/position (\d+)/i); + if (llPosMatch) { + position = parseInt(llPosMatch[1], 10); + const lc = computeLineCol(raw, position); + line = lc.line; + column = lc.column; + } + } + }} + const suggestion = suggestFix(raw); return { message: msg.replace(/in JSON.*$/, "").trim() || "Invalid JSON", line, column, position, suggestion }; -} - + } + export function validate(raw: string): Result { if (!raw.trim()) { return { From aa79fe533a315dc86ae076d8e94d6cd94903daf0 Mon Sep 17 00:00:00 2001 From: Sameer Akhtar Date: Fri, 16 Oct 2026 15:03:58 +0530 Subject: [PATCH 2/5] Refactored: review feedback on fallback logic and test cases --- lib/__tests__/json-utils.test.ts | 19 +++++++++++++------ lib/json-utils.ts | 28 ++++++++++++++-------------- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/lib/__tests__/json-utils.test.ts b/lib/__tests__/json-utils.test.ts index 189e270..92a43b3 100644 --- a/lib/__tests__/json-utils.test.ts +++ b/lib/__tests__/json-utils.test.ts @@ -11,15 +11,14 @@ import { escapeForCode, parseJson5Compat, diffLines, + validate, } from "../json-utils"; const BIG = '{"id":12345678901234567890,"pi":3.141592653589793238,"n":42}'; -import { validate } from "../json-utils"; - describe("JSON Formatter Error Locations", () => { it("extracts correct location for unquoted values", () => { - const result = validate('{"a": x}'); + const result = validate('{"a": x}'); expect(result.ok).toBe(false); if (!result.ok) { expect(result.error.line).toBe(1); @@ -28,13 +27,21 @@ describe("JSON Formatter Error Locations", () => { }); it("extracts correct location for multi-line JSON", () => { - // The issue specifies '{"a": 1, "b": tru/}' on three lines - const input = '{\n "a": 1, "b": tru/\n}'; + const input = '{"a": 1,\n"b": tru\n}'; const result = validate(input); expect(result.ok).toBe(false); if (!result.ok) { expect(result.error.line).toBe(2); - expect(result.error.column).toBe(16); // Note: Adjust column based on your exact spacing to match the target + expect(result.error.column).toBe(6); + } + }); + + it("keeps original location for duplicate keys with trailing comma", () => { + const result = validate('{"a":1,"a":2,}'); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error.line).toBe(1); + expect(result.error.column).toBe(14); } }); diff --git a/lib/json-utils.ts b/lib/json-utils.ts index ab286bc..42a2712 100644 --- a/lib/json-utils.ts +++ b/lib/json-utils.ts @@ -35,36 +35,36 @@ function parseSyntaxError(raw: string, err: unknown): JsonError { const msg = err instanceof Error ? err.message : String(err); const posMatch = msg.match(/position (\d+)/i); const lineMatch = msg.match(/line (\d+) column (\d+)/i); - //let message; - let line = 1; let column = 1; let position = 0; - if (posMatch && lineMatch==null) { + + if (posMatch) { position = parseInt(posMatch[1], 10); const lc = computeLineCol(raw, position); line = lc.line; column = lc.column; - } else if (lineMatch && posMatch==null) { + } else if (lineMatch) { line = parseInt(lineMatch[1], 10); column = parseInt(lineMatch[2], 10); } else{ try{ - llParse(raw) + llParse(raw); } - catch(llError) { - if (llError instanceof Error) { - const llPosMatch = llError.message.match(/position (\d+)/i); - if (llPosMatch) { - position = parseInt(llPosMatch[1], 10); - const lc = computeLineCol(raw, position); - line = lc.line; - column = lc.column; + catch(llError) { + if (llError instanceof Error) { + const llPosMatch = llError.message.match(/position (\d+)/i); + if (llPosMatch) { + position = parseInt(llPosMatch[1], 10); + const lc = computeLineCol(raw, position); + line = lc.line; + column = lc.column; + } } } - }} + } const suggestion = suggestFix(raw); return { message: msg.replace(/in JSON.*$/, "").trim() || "Invalid JSON", line, column, position, suggestion }; From cb28935febbea07903061ef253c3f0d1e7fa23aa Mon Sep 17 00:00:00 2001 From: Sameer Akhtar <73059128+sameerakhtar406@users.noreply.github.com> Date: Sat, 17 Oct 2026 10:25:05 +0530 Subject: [PATCH 3/5] Apply batched suggestions from code review Co-authored-by: Anurag Verma <78868769+anurag629@users.noreply.github.com> --- lib/__tests__/json-utils.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/__tests__/json-utils.test.ts b/lib/__tests__/json-utils.test.ts index 92a43b3..1635d3c 100644 --- a/lib/__tests__/json-utils.test.ts +++ b/lib/__tests__/json-utils.test.ts @@ -27,7 +27,7 @@ describe("JSON Formatter Error Locations", () => { }); it("extracts correct location for multi-line JSON", () => { - const input = '{"a": 1,\n"b": tru\n}'; + const input = '{"a": 1,\n"b": tru\n}'; const result = validate(input); expect(result.ok).toBe(false); if (!result.ok) { From 0f1bd4c5bdc9ad2aaa737fdb4e23bdf9a7360767 Mon Sep 17 00:00:00 2001 From: Sameer Akhtar <73059128+sameerakhtar406@users.noreply.github.com> Date: Sat, 17 Oct 2026 10:25:22 +0530 Subject: [PATCH 4/5] Apply batched suggestions from code review Co-authored-by: Anurag Verma <78868769+anurag629@users.noreply.github.com> --- lib/json-utils.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/json-utils.ts b/lib/json-utils.ts index 42a2712..30b29ae 100644 --- a/lib/json-utils.ts +++ b/lib/json-utils.ts @@ -35,11 +35,9 @@ function parseSyntaxError(raw: string, err: unknown): JsonError { const msg = err instanceof Error ? err.message : String(err); const posMatch = msg.match(/position (\d+)/i); const lineMatch = msg.match(/line (\d+) column (\d+)/i); - let line = 1; let column = 1; let position = 0; - if (posMatch) { position = parseInt(posMatch[1], 10); const lc = computeLineCol(raw, position); From a840583640a87ddbe19c20fe2d8670f66f37a748 Mon Sep 17 00:00:00 2001 From: Sameer Akhtar Date: Sat, 17 Oct 2026 15:28:36 +0530 Subject: [PATCH 5/5] style: apply formatting fix for else try catch block --- lib/json-utils.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/json-utils.ts b/lib/json-utils.ts index 30b29ae..5c8e9fe 100644 --- a/lib/json-utils.ts +++ b/lib/json-utils.ts @@ -35,9 +35,11 @@ function parseSyntaxError(raw: string, err: unknown): JsonError { const msg = err instanceof Error ? err.message : String(err); const posMatch = msg.match(/position (\d+)/i); const lineMatch = msg.match(/line (\d+) column (\d+)/i); + let line = 1; let column = 1; let position = 0; + if (posMatch) { position = parseInt(posMatch[1], 10); const lc = computeLineCol(raw, position); @@ -46,12 +48,10 @@ function parseSyntaxError(raw: string, err: unknown): JsonError { } else if (lineMatch) { line = parseInt(lineMatch[1], 10); column = parseInt(lineMatch[2], 10); - } - else{ - try{ + } else { + try { llParse(raw); - } - catch(llError) { + } catch (llError) { if (llError instanceof Error) { const llPosMatch = llError.message.match(/position (\d+)/i); if (llPosMatch) { @@ -63,10 +63,10 @@ function parseSyntaxError(raw: string, err: unknown): JsonError { } } } - + const suggestion = suggestFix(raw); return { message: msg.replace(/in JSON.*$/, "").trim() || "Invalid JSON", line, column, position, suggestion }; - } +} export function validate(raw: string): Result { if (!raw.trim()) {