fix(core): amend typing for jsonParse() options (#4423)

* 📘 Amend typing for `jsonParse()` options

* ✏️ Update rule message and description

* 🔀 Cherrypick Adi's work

* 🐛 Account for falsy fallback values

* ♻️ Use `else if`

*  Add explicit error message as type

*  Consolidate utils tests

* ♻️ Use optional chaining

* 🔥 Remove patchy type error

Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
This commit is contained in:
Iván Ovejero 2022-10-24 12:48:16 +02:00 committed by GitHub
parent d7c8fefe19
commit 1732324965
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 32 additions and 12 deletions

View file

@ -30,12 +30,14 @@ module.exports = {
meta: {
type: 'problem',
docs: {
description: 'Calls to JSON.parse() must be surrounded with a try/catch block.',
description:
'Calls to `JSON.parse()` must be replaced with `jsonParse()` from `n8n-workflow` or surrounded with a try/catch block.',
recommended: 'error',
},
schema: [],
messages: {
noUncaughtJsonParse: 'Surround the JSON.parse() call with a try/catch block.',
noUncaughtJsonParse:
'Use `jsonParse()` from `n8n-workflow` or surround the `JSON.parse()` call with a try/catch block.',
},
},
defaultOptions: [],

View file

@ -30,22 +30,23 @@ export const deepCopy = <T>(source: T): T => {
return clone;
};
// eslint-enable
type ErrorMessage = { errorMessage: string };
type FallbackValue<T> = { fallbackValue: T };
export const jsonParse = <T>(
jsonString: string,
options: ErrorMessage | FallbackValue<T> | {} = {},
): T => {
type MutuallyExclusive<T, U> =
| (T & { [k in Exclude<keyof U, keyof T>]?: never })
| (U & { [k in Exclude<keyof T, keyof U>]?: never });
type JSONParseOptions<T> = MutuallyExclusive<{ errorMessage: string }, { fallbackValue: T }>;
export const jsonParse = <T>(jsonString: string, options?: JSONParseOptions<T>): T => {
try {
return JSON.parse(jsonString) as T;
} catch (error) {
if ('fallbackValue' in options) {
if (options?.fallbackValue !== undefined) {
return options.fallbackValue;
}
if ('errorMessage' in options) {
} else if (options?.errorMessage) {
throw new Error(options.errorMessage);
}
throw error;
}
};

View file

@ -1,4 +1,21 @@
import { deepCopy } from './utils';
import { jsonParse, deepCopy } from '../src/utils';
describe('jsonParse', () => {
it('parses JSON', () => {
expect(jsonParse('[1, 2, 3]')).toEqual([1, 2, 3]);
expect(jsonParse('{ "a": 1 }')).toEqual({ a: 1 });
});
it('optionally throws `errorMessage', () => {
expect(() => {
jsonParse('', { errorMessage: 'Invalid JSON' });
}).toThrow('Invalid JSON');
});
it('optionally returns a `fallbackValue`', () => {
expect(jsonParse('', { fallbackValue: { foo: 'bar' } })).toEqual({ foo: 'bar' });
});
});
describe('deepCopy', () => {
it('should deep copy an object', () => {