@n8n/eslint-plugin-community-nodes 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/README.md +5 -1
- package/dist/plugin.d.ts +30 -6
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +10 -2
- package/dist/plugin.js.map +1 -1
- package/dist/rules/index.d.ts +5 -0
- package/dist/rules/index.d.ts.map +1 -1
- package/dist/rules/index.js +10 -0
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/missing-paired-item.d.ts +16 -0
- package/dist/rules/missing-paired-item.d.ts.map +1 -0
- package/dist/rules/missing-paired-item.js +121 -0
- package/dist/rules/missing-paired-item.js.map +1 -0
- package/dist/rules/no-forbidden-lifecycle-scripts.d.ts +2 -0
- package/dist/rules/no-forbidden-lifecycle-scripts.d.ts.map +1 -0
- package/dist/rules/no-forbidden-lifecycle-scripts.js +59 -0
- package/dist/rules/no-forbidden-lifecycle-scripts.js.map +1 -0
- package/dist/rules/node-class-description-icon-missing.d.ts +1 -0
- package/dist/rules/node-class-description-icon-missing.d.ts.map +1 -1
- package/dist/rules/node-class-description-icon-missing.js +6 -1
- package/dist/rules/node-class-description-icon-missing.js.map +1 -1
- package/dist/rules/require-community-node-keyword.d.ts +2 -0
- package/dist/rules/require-community-node-keyword.d.ts.map +1 -0
- package/dist/rules/require-community-node-keyword.js +65 -0
- package/dist/rules/require-community-node-keyword.js.map +1 -0
- package/dist/rules/require-continue-on-fail.d.ts +2 -0
- package/dist/rules/require-continue-on-fail.d.ts.map +1 -0
- package/dist/rules/require-continue-on-fail.js +76 -0
- package/dist/rules/require-continue-on-fail.js.map +1 -0
- package/dist/rules/require-node-description-fields.d.ts +2 -0
- package/dist/rules/require-node-description-fields.d.ts.map +1 -0
- package/dist/rules/require-node-description-fields.js +58 -0
- package/dist/rules/require-node-description-fields.js.map +1 -0
- package/docs/rules/missing-paired-item.md +70 -0
- package/docs/rules/no-forbidden-lifecycle-scripts.md +46 -0
- package/docs/rules/node-class-description-icon-missing.md +1 -1
- package/docs/rules/require-community-node-keyword.md +45 -0
- package/docs/rules/require-continue-on-fail.md +56 -0
- package/docs/rules/require-node-description-fields.md +46 -0
- package/package.json +3 -3
- package/src/plugin.ts +10 -2
- package/src/rules/index.ts +10 -0
- package/src/rules/missing-paired-item.test.ts +229 -0
- package/src/rules/missing-paired-item.ts +149 -0
- package/src/rules/no-forbidden-lifecycle-scripts.test.ts +103 -0
- package/src/rules/no-forbidden-lifecycle-scripts.ts +69 -0
- package/src/rules/node-class-description-icon-missing.ts +7 -1
- package/src/rules/require-community-node-keyword.test.ts +82 -0
- package/src/rules/require-community-node-keyword.ts +78 -0
- package/src/rules/require-continue-on-fail.test.ts +129 -0
- package/src/rules/require-continue-on-fail.ts +88 -0
- package/src/rules/require-node-description-fields.test.ts +171 -0
- package/src/rules/require-node-description-fields.ts +77 -0
- package/tsconfig.build.tsbuildinfo +1 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { AST_NODE_TYPES } from '@typescript-eslint/utils';
|
|
2
|
+
import { createRule, findJsonProperty } from '../utils/index.js';
|
|
3
|
+
const REQUIRED_KEYWORD = 'n8n-community-node-package';
|
|
4
|
+
export const RequireCommunityNodeKeywordRule = createRule({
|
|
5
|
+
name: 'require-community-node-keyword',
|
|
6
|
+
meta: {
|
|
7
|
+
type: 'problem',
|
|
8
|
+
docs: {
|
|
9
|
+
description: 'Require the "n8n-community-node-package" keyword in community node package.json',
|
|
10
|
+
},
|
|
11
|
+
fixable: 'code',
|
|
12
|
+
messages: {
|
|
13
|
+
missingKeyword: `The "keywords" array must include "${REQUIRED_KEYWORD}". This keyword is required for n8n to discover community node packages.`,
|
|
14
|
+
missingKeywordsArray: `The package.json must have a "keywords" array containing "${REQUIRED_KEYWORD}".`,
|
|
15
|
+
},
|
|
16
|
+
schema: [],
|
|
17
|
+
},
|
|
18
|
+
defaultOptions: [],
|
|
19
|
+
create(context) {
|
|
20
|
+
if (!context.filename.endsWith('package.json')) {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
ObjectExpression(node) {
|
|
25
|
+
if (node.parent?.type !== AST_NODE_TYPES.ExpressionStatement) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const keywordsProp = findJsonProperty(node, 'keywords');
|
|
29
|
+
if (!keywordsProp) {
|
|
30
|
+
context.report({
|
|
31
|
+
node,
|
|
32
|
+
messageId: 'missingKeywordsArray',
|
|
33
|
+
fix(fixer) {
|
|
34
|
+
const lastProp = node.properties[node.properties.length - 1];
|
|
35
|
+
if (!lastProp) {
|
|
36
|
+
return fixer.replaceText(node, `{ "keywords": ["${REQUIRED_KEYWORD}"] }`);
|
|
37
|
+
}
|
|
38
|
+
return fixer.insertTextAfter(lastProp, `, "keywords": ["${REQUIRED_KEYWORD}"]`);
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (keywordsProp.value.type !== AST_NODE_TYPES.ArrayExpression) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const keywordsArray = keywordsProp.value;
|
|
47
|
+
const hasRequiredKeyword = keywordsArray.elements.some((element) => element?.type === AST_NODE_TYPES.Literal && element.value === REQUIRED_KEYWORD);
|
|
48
|
+
if (!hasRequiredKeyword) {
|
|
49
|
+
context.report({
|
|
50
|
+
node: keywordsProp,
|
|
51
|
+
messageId: 'missingKeyword',
|
|
52
|
+
fix(fixer) {
|
|
53
|
+
const lastElement = keywordsArray.elements[keywordsArray.elements.length - 1];
|
|
54
|
+
if (!lastElement) {
|
|
55
|
+
return fixer.replaceText(keywordsArray, `["${REQUIRED_KEYWORD}"]`);
|
|
56
|
+
}
|
|
57
|
+
return fixer.insertTextAfter(lastElement, `, "${REQUIRED_KEYWORD}"`);
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
//# sourceMappingURL=require-community-node-keyword.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"require-community-node-keyword.js","sourceRoot":"","sources":["../../src/rules/require-community-node-keyword.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1D,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEjE,MAAM,gBAAgB,GAAG,4BAA4B,CAAC;AAEtD,MAAM,CAAC,MAAM,+BAA+B,GAAG,UAAU,CAAC;IACzD,IAAI,EAAE,gCAAgC;IACtC,IAAI,EAAE;QACL,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACL,WAAW,EACV,iFAAiF;SAClF;QACD,OAAO,EAAE,MAAM;QACf,QAAQ,EAAE;YACT,cAAc,EAAE,sCAAsC,gBAAgB,0EAA0E;YAChJ,oBAAoB,EAAE,6DAA6D,gBAAgB,IAAI;SACvG;QACD,MAAM,EAAE,EAAE;KACV;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACb,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;YAChD,OAAO,EAAE,CAAC;QACX,CAAC;QAED,OAAO;YACN,gBAAgB,CAAC,IAA+B;gBAC/C,IAAI,IAAI,CAAC,MAAM,EAAE,IAAI,KAAK,cAAc,CAAC,mBAAmB,EAAE,CAAC;oBAC9D,OAAO;gBACR,CAAC;gBAED,MAAM,YAAY,GAAG,gBAAgB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;gBAExD,IAAI,CAAC,YAAY,EAAE,CAAC;oBACnB,OAAO,CAAC,MAAM,CAAC;wBACd,IAAI;wBACJ,SAAS,EAAE,sBAAsB;wBACjC,GAAG,CAAC,KAAK;4BACR,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;4BAC7D,IAAI,CAAC,QAAQ,EAAE,CAAC;gCACf,OAAO,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,mBAAmB,gBAAgB,MAAM,CAAC,CAAC;4BAC3E,CAAC;4BACD,OAAO,KAAK,CAAC,eAAe,CAAC,QAAQ,EAAE,mBAAmB,gBAAgB,IAAI,CAAC,CAAC;wBACjF,CAAC;qBACD,CAAC,CAAC;oBACH,OAAO;gBACR,CAAC;gBAED,IAAI,YAAY,CAAC,KAAK,CAAC,IAAI,KAAK,cAAc,CAAC,eAAe,EAAE,CAAC;oBAChE,OAAO;gBACR,CAAC;gBAED,MAAM,aAAa,GAAG,YAAY,CAAC,KAAK,CAAC;gBACzC,MAAM,kBAAkB,GAAG,aAAa,CAAC,QAAQ,CAAC,IAAI,CACrD,CAAC,OAAO,EAAE,EAAE,CACX,OAAO,EAAE,IAAI,KAAK,cAAc,CAAC,OAAO,IAAI,OAAO,CAAC,KAAK,KAAK,gBAAgB,CAC/E,CAAC;gBAEF,IAAI,CAAC,kBAAkB,EAAE,CAAC;oBACzB,OAAO,CAAC,MAAM,CAAC;wBACd,IAAI,EAAE,YAAY;wBAClB,SAAS,EAAE,gBAAgB;wBAC3B,GAAG,CAAC,KAAK;4BACR,MAAM,WAAW,GAAG,aAAa,CAAC,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;4BAC9E,IAAI,CAAC,WAAW,EAAE,CAAC;gCAClB,OAAO,KAAK,CAAC,WAAW,CAAC,aAAa,EAAE,KAAK,gBAAgB,IAAI,CAAC,CAAC;4BACpE,CAAC;4BACD,OAAO,KAAK,CAAC,eAAe,CAAC,WAAW,EAAE,MAAM,gBAAgB,GAAG,CAAC,CAAC;wBACtE,CAAC;qBACD,CAAC,CAAC;gBACJ,CAAC;YACF,CAAC;SACD,CAAC;IACH,CAAC;CACD,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"require-continue-on-fail.d.ts","sourceRoot":"","sources":["../../src/rules/require-continue-on-fail.ts"],"names":[],"mappings":"AA8CA,eAAO,MAAM,yBAAyB,0JAyCpC,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { AST_NODE_TYPES } from '@typescript-eslint/utils';
|
|
2
|
+
import { isNodeTypeClass } from '../utils/index.js';
|
|
3
|
+
import { createRule } from '../utils/rule-creator.js';
|
|
4
|
+
/** Keys that are not child AST nodes (back-references, metadata). */
|
|
5
|
+
const NON_CHILD_KEYS = new Set(['parent', 'loc', 'range', 'start', 'end', 'tokens', 'comments']);
|
|
6
|
+
/**
|
|
7
|
+
* Recursively checks whether any descendant of the given AST node is a
|
|
8
|
+
* `this.continueOnFail()` call expression.
|
|
9
|
+
*/
|
|
10
|
+
function containsContinueOnFailCall(node) {
|
|
11
|
+
if (node.type === AST_NODE_TYPES.CallExpression &&
|
|
12
|
+
node.callee.type === AST_NODE_TYPES.MemberExpression &&
|
|
13
|
+
node.callee.object.type === AST_NODE_TYPES.ThisExpression &&
|
|
14
|
+
node.callee.property.type === AST_NODE_TYPES.Identifier &&
|
|
15
|
+
node.callee.property.name === 'continueOnFail') {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
for (const key of Object.keys(node)) {
|
|
19
|
+
if (NON_CHILD_KEYS.has(key))
|
|
20
|
+
continue;
|
|
21
|
+
const value = node[key];
|
|
22
|
+
if (Array.isArray(value)) {
|
|
23
|
+
for (const child of value) {
|
|
24
|
+
if (child && typeof child === 'object' && 'type' in child) {
|
|
25
|
+
if (containsContinueOnFailCall(child)) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
else if (value && typeof value === 'object' && 'type' in value) {
|
|
32
|
+
if (containsContinueOnFailCall(value)) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
export const RequireContinueOnFailRule = createRule({
|
|
40
|
+
name: 'require-continue-on-fail',
|
|
41
|
+
meta: {
|
|
42
|
+
type: 'problem',
|
|
43
|
+
docs: {
|
|
44
|
+
description: 'Require continueOnFail() handling in execute() methods of node classes',
|
|
45
|
+
},
|
|
46
|
+
messages: {
|
|
47
|
+
missingContinueOnFail: 'execute() method must handle this.continueOnFail() for proper error handling. ' +
|
|
48
|
+
'Wrap item processing in a try/catch and check this.continueOnFail() in the catch block.',
|
|
49
|
+
},
|
|
50
|
+
schema: [],
|
|
51
|
+
},
|
|
52
|
+
defaultOptions: [],
|
|
53
|
+
create(context) {
|
|
54
|
+
return {
|
|
55
|
+
ClassDeclaration(node) {
|
|
56
|
+
if (!isNodeTypeClass(node)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
for (const member of node.body.body) {
|
|
60
|
+
if (member.type !== AST_NODE_TYPES.MethodDefinition ||
|
|
61
|
+
member.key.type !== AST_NODE_TYPES.Identifier ||
|
|
62
|
+
member.key.name !== 'execute') {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (member.value.body && !containsContinueOnFailCall(member.value.body)) {
|
|
66
|
+
context.report({
|
|
67
|
+
node: member.key,
|
|
68
|
+
messageId: 'missingContinueOnFail',
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
//# sourceMappingURL=require-continue-on-fail.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"require-continue-on-fail.js","sourceRoot":"","sources":["../../src/rules/require-continue-on-fail.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAiB,MAAM,0BAA0B,CAAC;AAEzE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD,qEAAqE;AACrE,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC;AAEjG;;;GAGG;AACH,SAAS,0BAA0B,CAAC,IAAmB;IACtD,IACC,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC,cAAc;QAC3C,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,cAAc,CAAC,gBAAgB;QACpD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,cAAc,CAAC,cAAc;QACzD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,cAAc,CAAC,UAAU;QACvD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,gBAAgB,EAC7C,CAAC;QACF,OAAO,IAAI,CAAC;IACb,CAAC;IAED,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACrC,IAAI,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QAEtC,MAAM,KAAK,GAAI,IAA2C,CAAC,GAAG,CAAC,CAAC;QAEhE,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;gBAC3B,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,IAAI,KAAK,EAAE,CAAC;oBAC3D,IAAI,0BAA0B,CAAC,KAAsB,CAAC,EAAE,CAAC;wBACxD,OAAO,IAAI,CAAC;oBACb,CAAC;gBACF,CAAC;YACF,CAAC;QACF,CAAC;aAAM,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,IAAI,KAAK,EAAE,CAAC;YAClE,IAAI,0BAA0B,CAAC,KAAsB,CAAC,EAAE,CAAC;gBACxD,OAAO,IAAI,CAAC;YACb,CAAC;QACF,CAAC;IACF,CAAC;IAED,OAAO,KAAK,CAAC;AACd,CAAC;AAED,MAAM,CAAC,MAAM,yBAAyB,GAAG,UAAU,CAAC;IACnD,IAAI,EAAE,0BAA0B;IAChC,IAAI,EAAE;QACL,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACL,WAAW,EAAE,wEAAwE;SACrF;QACD,QAAQ,EAAE;YACT,qBAAqB,EACpB,gFAAgF;gBAChF,yFAAyF;SAC1F;QACD,MAAM,EAAE,EAAE;KACV;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACb,OAAO;YACN,gBAAgB,CAAC,IAAI;gBACpB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC5B,OAAO;gBACR,CAAC;gBAED,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;oBACrC,IACC,MAAM,CAAC,IAAI,KAAK,cAAc,CAAC,gBAAgB;wBAC/C,MAAM,CAAC,GAAG,CAAC,IAAI,KAAK,cAAc,CAAC,UAAU;wBAC7C,MAAM,CAAC,GAAG,CAAC,IAAI,KAAK,SAAS,EAC5B,CAAC;wBACF,SAAS;oBACV,CAAC;oBAED,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,0BAA0B,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;wBACzE,OAAO,CAAC,MAAM,CAAC;4BACd,IAAI,EAAE,MAAM,CAAC,GAAG;4BAChB,SAAS,EAAE,uBAAuB;yBAClC,CAAC,CAAC;oBACJ,CAAC;gBACF,CAAC;YACF,CAAC;SACD,CAAC;IACH,CAAC;CACD,CAAC,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const RequireNodeDescriptionFieldsRule: import("@typescript-eslint/utils/ts-eslint").RuleModule<"missingField" | "missingFields", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
2
|
+
//# sourceMappingURL=require-node-description-fields.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"require-node-description-fields.d.ts","sourceRoot":"","sources":["../../src/rules/require-node-description-fields.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,gCAAgC,mKA2D3C,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { TSESTree } from '@typescript-eslint/utils';
|
|
2
|
+
import { isNodeTypeClass, findClassProperty, findObjectProperty, isFileType, createRule, } from '../utils/index.js';
|
|
3
|
+
// Fields that are optional in the TypeScript interface but required by
|
|
4
|
+
// community node review standards. TypeScript-required fields (displayName,
|
|
5
|
+
// name, group, version, description, etc.) are already caught by tsc.
|
|
6
|
+
const REQUIRED_FIELDS = ['icon', 'subtitle'];
|
|
7
|
+
export const RequireNodeDescriptionFieldsRule = createRule({
|
|
8
|
+
name: 'require-node-description-fields',
|
|
9
|
+
meta: {
|
|
10
|
+
type: 'problem',
|
|
11
|
+
docs: {
|
|
12
|
+
description: `Node class description must define all required fields: ${REQUIRED_FIELDS.join(', ')}`,
|
|
13
|
+
},
|
|
14
|
+
messages: {
|
|
15
|
+
missingField: 'Node class description is missing required `{{field}}` property',
|
|
16
|
+
missingFields: 'Node class description is missing required properties: {{fields}}',
|
|
17
|
+
},
|
|
18
|
+
schema: [],
|
|
19
|
+
},
|
|
20
|
+
defaultOptions: [],
|
|
21
|
+
create(context) {
|
|
22
|
+
if (!isFileType(context.filename, '.node.ts')) {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
ClassDeclaration(node) {
|
|
27
|
+
if (!isNodeTypeClass(node)) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const descriptionProperty = findClassProperty(node, 'description');
|
|
31
|
+
if (!descriptionProperty?.value ||
|
|
32
|
+
descriptionProperty.value.type !== TSESTree.AST_NODE_TYPES.ObjectExpression) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const descriptionValue = descriptionProperty.value;
|
|
36
|
+
const missingFields = REQUIRED_FIELDS.filter((field) => !findObjectProperty(descriptionValue, field));
|
|
37
|
+
if (missingFields.length === 0) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (missingFields.length === 1) {
|
|
41
|
+
context.report({
|
|
42
|
+
node: descriptionProperty,
|
|
43
|
+
messageId: 'missingField',
|
|
44
|
+
data: { field: missingFields[0] },
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
context.report({
|
|
49
|
+
node: descriptionProperty,
|
|
50
|
+
messageId: 'missingFields',
|
|
51
|
+
data: { fields: missingFields.map((f) => `\`${f}\``).join(', ') },
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
//# sourceMappingURL=require-node-description-fields.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"require-node-description-fields.js","sourceRoot":"","sources":["../../src/rules/require-node-description-fields.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpD,OAAO,EACN,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,UAAU,EACV,UAAU,GACV,MAAM,mBAAmB,CAAC;AAE3B,uEAAuE;AACvE,4EAA4E;AAC5E,sEAAsE;AACtE,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,UAAU,CAAU,CAAC;AAItD,MAAM,CAAC,MAAM,gCAAgC,GAAG,UAAU,CAAC;IAC1D,IAAI,EAAE,iCAAiC;IACvC,IAAI,EAAE;QACL,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACL,WAAW,EAAE,2DAA2D,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;SACpG;QACD,QAAQ,EAAE;YACT,YAAY,EAAE,iEAAiE;YAC/E,aAAa,EAAE,mEAAmE;SAClF;QACD,MAAM,EAAE,EAAE;KACV;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACb,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,CAAC;YAC/C,OAAO,EAAE,CAAC;QACX,CAAC;QAED,OAAO;YACN,gBAAgB,CAAC,IAAI;gBACpB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC5B,OAAO;gBACR,CAAC;gBAED,MAAM,mBAAmB,GAAG,iBAAiB,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;gBACnE,IACC,CAAC,mBAAmB,EAAE,KAAK;oBAC3B,mBAAmB,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,cAAc,CAAC,gBAAgB,EAC1E,CAAC;oBACF,OAAO;gBACR,CAAC;gBAED,MAAM,gBAAgB,GAAG,mBAAmB,CAAC,KAAK,CAAC;gBAEnD,MAAM,aAAa,GAAoB,eAAe,CAAC,MAAM,CAC5D,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,kBAAkB,CAAC,gBAAgB,EAAE,KAAK,CAAC,CACvD,CAAC;gBAEF,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBAChC,OAAO;gBACR,CAAC;gBAED,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBAChC,OAAO,CAAC,MAAM,CAAC;wBACd,IAAI,EAAE,mBAAmB;wBACzB,SAAS,EAAE,cAAc;wBACzB,IAAI,EAAE,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE;qBACjC,CAAC,CAAC;gBACJ,CAAC;qBAAM,CAAC;oBACP,OAAO,CAAC,MAAM,CAAC;wBACd,IAAI,EAAE,mBAAmB;wBACzB,SAAS,EAAE,eAAe;wBAC1B,IAAI,EAAE,EAAE,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;qBACjE,CAAC,CAAC;gBACJ,CAAC;YACF,CAAC;SACD,CAAC;IACH,CAAC;CACD,CAAC,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Require pairedItem on INodeExecutionData objects in execute() methods to preserve item linking (`@n8n/community-nodes/missing-paired-item`)
|
|
2
|
+
|
|
3
|
+
💼 This rule is enabled in the following configs: ✅ `recommended`, ☑️ `recommendedWithoutN8nCloudSupport`.
|
|
4
|
+
|
|
5
|
+
<!-- end auto-generated rule header -->
|
|
6
|
+
|
|
7
|
+
## Rule Details
|
|
8
|
+
|
|
9
|
+
Every `INodeExecutionData` object returned from `execute()` should include a `pairedItem` property. Without it, downstream nodes cannot trace data lineage and expressions like `$('NodeName').item` will silently fail.
|
|
10
|
+
|
|
11
|
+
The rule detects object literals with a `json` property but no `pairedItem` inside `execute()` methods of `INodeType` classes.
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
### Incorrect
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
class MyNode implements INodeType {
|
|
19
|
+
async execute() {
|
|
20
|
+
const items = this.getInputData();
|
|
21
|
+
// Missing pairedItem
|
|
22
|
+
return [items.map((item) => ({ json: item.json }))];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
class MyNode implements INodeType {
|
|
29
|
+
async execute() {
|
|
30
|
+
const returnData: INodeExecutionData[] = [];
|
|
31
|
+
// Missing pairedItem
|
|
32
|
+
returnData.push({ json: { result: true } });
|
|
33
|
+
return [returnData];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Correct
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
class MyNode implements INodeType {
|
|
42
|
+
async execute() {
|
|
43
|
+
const items = this.getInputData();
|
|
44
|
+
return [items.map((item, index) => ({ json: item.json, pairedItem: { item: index } }))];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
class MyNode implements INodeType {
|
|
51
|
+
async execute() {
|
|
52
|
+
const returnData: INodeExecutionData[] = [];
|
|
53
|
+
returnData.push({ json: { result: true }, pairedItem: { item: 0 } });
|
|
54
|
+
return [returnData];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## When to Disable
|
|
60
|
+
|
|
61
|
+
If your node intentionally does not support item linking (e.g. it aggregates all input items into a single output), you can suppress this rule:
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
// eslint-disable-next-line @n8n/community-nodes/missing-paired-item
|
|
65
|
+
returnData.push({ json: aggregatedResult });
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Further Reading
|
|
69
|
+
|
|
70
|
+
- [n8n Paired Items Documentation](https://docs.n8n.io/integrations/creating-nodes/build/reference/paired-items/)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Ban lifecycle scripts (prepare, preinstall, postinstall, etc.) in community node packages (`@n8n/community-nodes/no-forbidden-lifecycle-scripts`)
|
|
2
|
+
|
|
3
|
+
💼 This rule is enabled in the following configs: ✅ `recommended`, ☑️ `recommendedWithoutN8nCloudSupport`.
|
|
4
|
+
|
|
5
|
+
<!-- end auto-generated rule header -->
|
|
6
|
+
|
|
7
|
+
## Rule Details
|
|
8
|
+
|
|
9
|
+
npm lifecycle scripts (`prepare`, `preinstall`, `install`, `postinstall`, `prepublish`, `preprepare`, `postprepare`) run automatically — without user confirmation — during `npm install`. In the context of n8n community nodes, this means arbitrary code executes on the n8n instance the moment a community node is installed.
|
|
10
|
+
|
|
11
|
+
n8n community nodes are distributed as pre-built npm packages. Unlike regular npm libraries, there is no legitimate reason for a community node to hook into install-time lifecycle events — the package should already contain compiled code ready to use. A `prepare` or `postinstall` script in a community node is either a misconfiguration (the author forgot to remove a build step meant for development) or a supply-chain attack vector.
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
### Incorrect
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"name": "n8n-nodes-example",
|
|
20
|
+
"scripts": {
|
|
21
|
+
"prepare": "npm run build"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"name": "n8n-nodes-example",
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc",
|
|
31
|
+
"postinstall": "node setup.js"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Correct
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"name": "n8n-nodes-example",
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsc",
|
|
43
|
+
"test": "jest"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Node class description must have an `icon` property defined (`@n8n/community-nodes/node-class-description-icon-missing`)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
❌ This rule is **deprecated**. Use [`require-node-description-fields`](require-node-description-fields.md) instead.
|
|
4
4
|
|
|
5
5
|
💡 This rule is manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions).
|
|
6
6
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Require the "n8n-community-node-package" keyword in package.json (`@n8n/community-nodes/require-community-node-keyword`)
|
|
2
|
+
|
|
3
|
+
⚠️ This rule is set to `warn` in the following configs: ✅ `recommended`, ☑️ `recommendedWithoutN8nCloudSupport`.
|
|
4
|
+
|
|
5
|
+
🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/use/command-line-interface#--fix).
|
|
6
|
+
|
|
7
|
+
<!-- end auto-generated rule header -->
|
|
8
|
+
|
|
9
|
+
## Rule Details
|
|
10
|
+
|
|
11
|
+
Validates that the `package.json` of a community node package includes `"n8n-community-node-package"` in its `keywords` array. This keyword is required for n8n to discover and list community node packages.
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
### ❌ Incorrect
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"name": "n8n-nodes-my-service",
|
|
20
|
+
"version": "1.0.0"
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"name": "n8n-nodes-my-service",
|
|
27
|
+
"keywords": ["n8n", "automation"]
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### ✅ Correct
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"name": "n8n-nodes-my-service",
|
|
36
|
+
"keywords": ["n8n-community-node-package"]
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"name": "n8n-nodes-my-service",
|
|
43
|
+
"keywords": ["n8n", "automation", "n8n-community-node-package"]
|
|
44
|
+
}
|
|
45
|
+
```
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Require continueOnFail() handling in execute() methods of node classes (`@n8n/community-nodes/require-continue-on-fail`)
|
|
2
|
+
|
|
3
|
+
💼 This rule is enabled in the following configs: ✅ `recommended`, ☑️ `recommendedWithoutN8nCloudSupport`.
|
|
4
|
+
|
|
5
|
+
<!-- end auto-generated rule header -->
|
|
6
|
+
|
|
7
|
+
## Rule Details
|
|
8
|
+
|
|
9
|
+
Ensures that `execute()` methods in node classes include a `this.continueOnFail()` check. Without this, a single item error will abort the entire workflow instead of allowing execution to continue past the failing item.
|
|
10
|
+
|
|
11
|
+
## Examples
|
|
12
|
+
|
|
13
|
+
### ❌ Incorrect
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
export class MyNode implements INodeType {
|
|
17
|
+
description: INodeTypeDescription = { /* ... */ };
|
|
18
|
+
|
|
19
|
+
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
|
20
|
+
const items = this.getInputData();
|
|
21
|
+
const returnData: INodeExecutionData[] = [];
|
|
22
|
+
for (let i = 0; i < items.length; i++) {
|
|
23
|
+
// No error handling — one bad item kills the whole workflow
|
|
24
|
+
const result = await someApiCall(items[i]);
|
|
25
|
+
returnData.push({ json: result });
|
|
26
|
+
}
|
|
27
|
+
return [returnData];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### ✅ Correct
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
export class MyNode implements INodeType {
|
|
36
|
+
description: INodeTypeDescription = { /* ... */ };
|
|
37
|
+
|
|
38
|
+
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
|
39
|
+
const items = this.getInputData();
|
|
40
|
+
const returnData: INodeExecutionData[] = [];
|
|
41
|
+
for (let i = 0; i < items.length; i++) {
|
|
42
|
+
try {
|
|
43
|
+
const result = await someApiCall(items[i]);
|
|
44
|
+
returnData.push({ json: result });
|
|
45
|
+
} catch (error) {
|
|
46
|
+
if (this.continueOnFail()) {
|
|
47
|
+
returnData.push({ json: { error: error.message }, pairedItem: { item: i } });
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return [returnData];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Node class description must define all required fields (`@n8n/community-nodes/require-node-description-fields`)
|
|
2
|
+
|
|
3
|
+
💼 This rule is enabled in the following configs: ✅ `recommended`, ☑️ `recommendedWithoutN8nCloudSupport`.
|
|
4
|
+
|
|
5
|
+
<!-- end auto-generated rule header -->
|
|
6
|
+
|
|
7
|
+
## Rule Details
|
|
8
|
+
|
|
9
|
+
Enforces that community node class descriptions include fields that are optional in the TypeScript interface but required by community node review standards.
|
|
10
|
+
|
|
11
|
+
This rule supersedes `node-class-description-icon-missing`, which only checked for the `icon` field.
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
### ❌ Incorrect
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
export class MyNode implements INodeType {
|
|
19
|
+
description: INodeTypeDescription = {
|
|
20
|
+
displayName: 'My Node',
|
|
21
|
+
name: 'myNode',
|
|
22
|
+
icon: 'file:myNode.svg',
|
|
23
|
+
group: ['transform'],
|
|
24
|
+
version: 1,
|
|
25
|
+
description: 'My node description',
|
|
26
|
+
// Missing subtitle
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### ✅ Correct
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
export class MyNode implements INodeType {
|
|
35
|
+
description: INodeTypeDescription = {
|
|
36
|
+
displayName: 'My Node',
|
|
37
|
+
name: 'myNode',
|
|
38
|
+
icon: 'file:myNode.svg',
|
|
39
|
+
group: ['transform'],
|
|
40
|
+
version: 1,
|
|
41
|
+
description: 'My node description',
|
|
42
|
+
subtitle: '={{$parameter["operation"]}}',
|
|
43
|
+
// ...
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
```
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@n8n/eslint-plugin-community-nodes",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.13.0",
|
|
5
5
|
"main": "./dist/plugin.js",
|
|
6
6
|
"types": "./dist/plugin.d.ts",
|
|
7
7
|
"exports": {
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"typescript": "6.0.2",
|
|
24
24
|
"vitest": "^4.1.1",
|
|
25
25
|
"@n8n/typescript-config": "1.4.0",
|
|
26
|
-
"
|
|
27
|
-
"n8n-
|
|
26
|
+
"n8n-workflow": "2.18.0",
|
|
27
|
+
"@n8n/vitest-config": "1.9.0"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"eslint": ">= 9",
|
package/src/plugin.ts
CHANGED
|
@@ -29,14 +29,18 @@ const configs = {
|
|
|
29
29
|
'@n8n/community-nodes/package-name-convention': 'error',
|
|
30
30
|
'@n8n/community-nodes/credential-test-required': 'error',
|
|
31
31
|
'@n8n/community-nodes/no-credential-reuse': 'error',
|
|
32
|
+
'@n8n/community-nodes/no-forbidden-lifecycle-scripts': 'error',
|
|
32
33
|
'@n8n/community-nodes/no-http-request-with-manual-auth': 'error',
|
|
33
34
|
'@n8n/community-nodes/icon-validation': 'error',
|
|
34
35
|
'@n8n/community-nodes/options-sorted-alphabetically': 'warn',
|
|
35
36
|
'@n8n/community-nodes/resource-operation-pattern': 'warn',
|
|
36
37
|
'@n8n/community-nodes/credential-documentation-url': 'error',
|
|
37
|
-
'@n8n/community-nodes/node-class-description-icon-missing': 'error',
|
|
38
38
|
'@n8n/community-nodes/cred-class-field-icon-missing': 'error',
|
|
39
39
|
'@n8n/community-nodes/node-connection-type-literal': 'error',
|
|
40
|
+
'@n8n/community-nodes/missing-paired-item': 'error',
|
|
41
|
+
'@n8n/community-nodes/require-community-node-keyword': 'warn',
|
|
42
|
+
'@n8n/community-nodes/require-continue-on-fail': 'error',
|
|
43
|
+
'@n8n/community-nodes/require-node-description-fields': 'error',
|
|
40
44
|
},
|
|
41
45
|
},
|
|
42
46
|
recommendedWithoutN8nCloudSupport: {
|
|
@@ -52,14 +56,18 @@ const configs = {
|
|
|
52
56
|
'@n8n/community-nodes/package-name-convention': 'error',
|
|
53
57
|
'@n8n/community-nodes/credential-test-required': 'error',
|
|
54
58
|
'@n8n/community-nodes/no-credential-reuse': 'error',
|
|
59
|
+
'@n8n/community-nodes/no-forbidden-lifecycle-scripts': 'error',
|
|
55
60
|
'@n8n/community-nodes/no-http-request-with-manual-auth': 'error',
|
|
56
61
|
'@n8n/community-nodes/icon-validation': 'error',
|
|
57
62
|
'@n8n/community-nodes/options-sorted-alphabetically': 'warn',
|
|
58
63
|
'@n8n/community-nodes/credential-documentation-url': 'error',
|
|
59
64
|
'@n8n/community-nodes/resource-operation-pattern': 'warn',
|
|
60
|
-
'@n8n/community-nodes/node-class-description-icon-missing': 'error',
|
|
61
65
|
'@n8n/community-nodes/cred-class-field-icon-missing': 'error',
|
|
62
66
|
'@n8n/community-nodes/node-connection-type-literal': 'error',
|
|
67
|
+
'@n8n/community-nodes/missing-paired-item': 'error',
|
|
68
|
+
'@n8n/community-nodes/require-community-node-keyword': 'warn',
|
|
69
|
+
'@n8n/community-nodes/require-continue-on-fail': 'error',
|
|
70
|
+
'@n8n/community-nodes/require-node-description-fields': 'error',
|
|
63
71
|
},
|
|
64
72
|
},
|
|
65
73
|
} satisfies Record<string, Linter.Config>;
|
package/src/rules/index.ts
CHANGED
|
@@ -6,8 +6,10 @@ import { CredentialDocumentationUrlRule } from './credential-documentation-url.j
|
|
|
6
6
|
import { CredentialPasswordFieldRule } from './credential-password-field.js';
|
|
7
7
|
import { CredentialTestRequiredRule } from './credential-test-required.js';
|
|
8
8
|
import { IconValidationRule } from './icon-validation.js';
|
|
9
|
+
import { MissingPairedItemRule } from './missing-paired-item.js';
|
|
9
10
|
import { NoCredentialReuseRule } from './no-credential-reuse.js';
|
|
10
11
|
import { NoDeprecatedWorkflowFunctionsRule } from './no-deprecated-workflow-functions.js';
|
|
12
|
+
import { NoForbiddenLifecycleScriptsRule } from './no-forbidden-lifecycle-scripts.js';
|
|
11
13
|
import { NoHttpRequestWithManualAuthRule } from './no-http-request-with-manual-auth.js';
|
|
12
14
|
import { NoRestrictedGlobalsRule } from './no-restricted-globals.js';
|
|
13
15
|
import { NoRestrictedImportsRule } from './no-restricted-imports.js';
|
|
@@ -16,6 +18,9 @@ import { NodeConnectionTypeLiteralRule } from './node-connection-type-literal.js
|
|
|
16
18
|
import { NodeUsableAsToolRule } from './node-usable-as-tool.js';
|
|
17
19
|
import { OptionsSortedAlphabeticallyRule } from './options-sorted-alphabetically.js';
|
|
18
20
|
import { PackageNameConventionRule } from './package-name-convention.js';
|
|
21
|
+
import { RequireCommunityNodeKeywordRule } from './require-community-node-keyword.js';
|
|
22
|
+
import { RequireContinueOnFailRule } from './require-continue-on-fail.js';
|
|
23
|
+
import { RequireNodeDescriptionFieldsRule } from './require-node-description-fields.js';
|
|
19
24
|
import { ResourceOperationPatternRule } from './resource-operation-pattern.js';
|
|
20
25
|
|
|
21
26
|
export const rules = {
|
|
@@ -29,6 +34,7 @@ export const rules = {
|
|
|
29
34
|
'package-name-convention': PackageNameConventionRule,
|
|
30
35
|
'credential-test-required': CredentialTestRequiredRule,
|
|
31
36
|
'no-credential-reuse': NoCredentialReuseRule,
|
|
37
|
+
'no-forbidden-lifecycle-scripts': NoForbiddenLifecycleScriptsRule,
|
|
32
38
|
'no-http-request-with-manual-auth': NoHttpRequestWithManualAuthRule,
|
|
33
39
|
'icon-validation': IconValidationRule,
|
|
34
40
|
'resource-operation-pattern': ResourceOperationPatternRule,
|
|
@@ -36,4 +42,8 @@ export const rules = {
|
|
|
36
42
|
'node-class-description-icon-missing': NodeClassDescriptionIconMissingRule,
|
|
37
43
|
'cred-class-field-icon-missing': CredClassFieldIconMissingRule,
|
|
38
44
|
'node-connection-type-literal': NodeConnectionTypeLiteralRule,
|
|
45
|
+
'missing-paired-item': MissingPairedItemRule,
|
|
46
|
+
'require-community-node-keyword': RequireCommunityNodeKeywordRule,
|
|
47
|
+
'require-continue-on-fail': RequireContinueOnFailRule,
|
|
48
|
+
'require-node-description-fields': RequireNodeDescriptionFieldsRule,
|
|
39
49
|
} satisfies Record<string, AnyRuleModule>;
|