feat(eslint-config): add custom eslint rule 'no-uncaught-json-parse' (#4087)

feat(eslint-config): add custom eslint rule 'no-uncaugh-json-parse'

Co-authored-by: Iván Ovejero <ivov.src@gmail.com>
This commit is contained in:
Mike Arvela 2022-09-26 11:08:59 +03:00 committed by GitHub
parent 7aa40bf95d
commit 31391a5b19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 2753 additions and 5 deletions

2629
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -40,6 +40,12 @@ module.exports = {
* https://github.com/paleite/eslint-plugin-diff#plugindiffdiff-recommended
*/
'eslint-plugin-diff',
/*
* Plugin to allow specifying local ESLint rules.
* https://github.com/ivov/eslint-plugin-n8n-local-rules
*/
'eslint-plugin-n8n-local-rules',
],
extends: [
@ -324,6 +330,13 @@ module.exports = {
*/
'import/order': 'error',
// ----------------------------------
// eslint-plugin-n8n-local-rules
// ----------------------------------
// TODO: set to `error` and fix offenses
'n8n-local-rules/no-uncaught-json-parse': 'warn',
// ******************************************************************
// overrides to base ruleset
// ******************************************************************

View file

@ -0,0 +1,86 @@
'use strict';
/**
* This file contains any locally defined ESLint rules. They are picked up by
* eslint-plugin-n8n-local-rules and exposed as 'n8n-local-rules/<rule-name>'.
*/
module.exports = {
/**
* A rule to detect calls to JSON.parse() that are not wrapped inside try/catch blocks.
*
* Valid:
* ```js
* try { JSON.parse(foo) } catch(err) { baz() }
* ```
*
* Invalid:
* ```js
* JSON.parse(foo)
* ```
*
* The pattern where an object is cloned with JSON.parse(JSON.stringify()) is allowed
* (abundant in the n8n codebase):
*
* Valid:
* ```js
* JSON.parse(JSON.stringify(foo))
* ```
*/
'no-uncaught-json-parse': {
meta: {
type: 'problem',
docs: {
description: 'Calls to JSON.parse() must be surrounded with a try/catch block.',
recommended: 'error',
},
schema: [],
messages: {
noUncaughtJsonParse: 'Surround the JSON.parse() call with a try/catch block.',
},
},
defaultOptions: [],
create(context) {
return {
CallExpression(node) {
if (!isJsonParseCall(node)) {
return;
}
if (isDeepCloneOperation(node)) {
return;
}
if (context.getAncestors().find((node) => node.type === 'TryStatement') !== undefined) {
return;
}
// Found a JSON.parse() call not wrapped into a try/catch, so report it
context.report({
messageId: 'noUncaughtJsonParse',
node,
});
},
};
},
},
};
const isJsonParseCall = (node) =>
node.callee.type === 'MemberExpression' &&
node.callee.object.type === 'Identifier' &&
node.callee.object.name === 'JSON' &&
node.callee.property.type === 'Identifier' &&
node.callee.property.name === 'parse';
const isDeepCloneOperation = (node) => {
const parseArg = node.arguments?.[0];
return (
parseArg !== undefined &&
parseArg.type === 'CallExpression' &&
parseArg.callee.type === 'MemberExpression' &&
parseArg.callee.object.type === 'Identifier' &&
parseArg.callee.object.name === 'JSON' &&
parseArg.callee.property.type === 'Identifier' &&
parseArg.callee.property.name === 'stringify'
);
};

View file

@ -0,0 +1,23 @@
'use strict';
const rules = require('./local-rules'),
RuleTester = require('eslint').RuleTester;
const ruleTester = new RuleTester();
ruleTester.run('no-uncaught-json-parse', rules['no-uncaught-json-parse'], {
valid: [
{
code: 'try { JSON.parse(foo) } catch (e) {}',
},
{
code: 'JSON.parse(JSON.stringify(foo))',
},
],
invalid: [
{
code: 'JSON.parse(foo)',
errors: [{ messageId: 'noUncaughtJsonParse' }],
},
],
});

View file

@ -11,7 +11,12 @@
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-diff": "^2.0.1",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-n8n-local-rules": "^1.0.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^9.4.0"
"eslint-plugin-vue": "^9.4.0",
"jest": "^28.1.3"
},
"scripts": {
"test": "jest"
}
}