Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions lib/__tests__/json-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,68 @@ import {
escapeForCode,
parseJson5Compat,
diffLines,
validate,
} from "../json-utils";

const BIG = '{"id":12345678901234567890,"pi":3.141592653589793238,"n":42}';

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", () => {
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(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);
}
});

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);
Expand Down
19 changes: 18 additions & 1 deletion lib/json-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -46,11 +48,26 @@ function parseSyntaxError(raw: string, err: unknown): JsonError {
} else if (lineMatch) {
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<true> {
if (!raw.trim()) {
return {
Expand Down
Loading