arktype icon indicating copy to clipboard operation
arktype copied to clipboard

Custom error for parse.json morph

Open ssalbdivad opened this issue 1 year ago • 0 comments

Would be great to have a custom error message pointing out the specific section of JSON that failed to parse.

This is based on this suggestion in Effect's Discord by user spaethnl:

I have some parse errors that result from JSON.parse that look like this: ... │ └─ Unexpected non-whitespace character after JSON at position 560 []: # FILEPATH: /NewIssue.md []: # BEGIN: ed8c6549bwf9

It would be helpful if the context were shown to make it easier to track down the syntax error. Could parseJson in Schema.ts be updated to call a function like below (by chatGPT)? I updated it inline on my node_modules to try it out and it improved my error message to: │ └─ Unexpected non-whitespace character after JSON at position 560 ... Concern"]][["Counter ... ^

Generate a context string for JSON syntax errors.
/**
Given a SyntaxError from JSON.parse and the original JSON string,
 this function generates a two-line string that shows the error position.

 @param error - The SyntaxError object thrown by JSON.parse
 @param jsonData - The original JSON string that caused the error
 @param contextLength - The number of characters to show before and after the error position. Default is 10.
 @returns A string showing the error context in the original JSON data.

 @example
 const data = '[[1][2]]';
 try { JSON.parse(data) }
 catch (e: SyntaxError) {
 console.log('Error:', e.message)
 console.log(generateJsonErrorContext(e, data))
 }
 // Output:
 // Error: Unexpected token a in JSON at position 4
 // ...[[1][2]]
 // ...    ^
 **/
 function generateJsonErrorContext(error: SyntaxError, jsonData: string, contextLength: number = 10): string {
 const match = /position (\d+)/.exec(error.message);
 if (!match) return 'Could not determine error position.'

const position = parseInt(match[1], 10);
const start = Math.max(0, position - contextLength);
const end = Math.min(jsonData.length, position + contextLength);

const context = jsonData.substring(start, end);
const pointer = ' '.repeat(position - start) + '^';

return `...${context}\n...${pointer}\n`;
}

ssalbdivad avatar Aug 27 '23 09:08 ssalbdivad