@n8n/eslint-plugin-community-nodes 0.35.1 → 0.36.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/README.md
CHANGED
|
@@ -83,7 +83,7 @@ export default [
|
|
|
83
83
|
| [require-node-api-error](docs/rules/require-node-api-error.md) | Require NodeApiError or NodeOperationError for error wrapping in catch blocks. Raw errors lose HTTP context in the n8n UI. | ✅ ☑️ | | | | |
|
|
84
84
|
| [require-node-description-fields](docs/rules/require-node-description-fields.md) | Node class description must define all required fields: icon, subtitle | ✅ ☑️ | | | | |
|
|
85
85
|
| [require-version](docs/rules/require-version.md) | Require a valid "version" field in community node package.json | ✅ ☑️ | | | | |
|
|
86
|
-
| [resource-operation-pattern](docs/rules/resource-operation-pattern.md) | Enforce proper resource/operation pattern for better UX in n8n nodes |
|
|
86
|
+
| [resource-operation-pattern](docs/rules/resource-operation-pattern.md) | Enforce proper resource/operation pattern for better UX in n8n nodes | ✅ ☑️ | | | | |
|
|
87
87
|
| [trigger-node-conventions](docs/rules/trigger-node-conventions.md) | Trigger nodes (class name ends with `Trigger`) must label themselves consistently as triggers | ✅ ☑️ | | | | |
|
|
88
88
|
| [valid-author](docs/rules/valid-author.md) | Require a non-empty author name and email in package.json | ✅ ☑️ | | | | |
|
|
89
89
|
| [valid-credential-references](docs/rules/valid-credential-references.md) | Ensure credentials referenced in node descriptions exist as credential classes in the package | ✅ ☑️ | | | 💡 | |
|
package/dist/plugin.d.ts
CHANGED
|
@@ -38,7 +38,7 @@ declare const configs: {
|
|
|
38
38
|
'@n8n/community-nodes/no-unsafe-connection-type-cast': "error";
|
|
39
39
|
'@n8n/community-nodes/icon-validation': "error";
|
|
40
40
|
'@n8n/community-nodes/icon-prefer-themed-variants': "warn";
|
|
41
|
-
'@n8n/community-nodes/resource-operation-pattern': "
|
|
41
|
+
'@n8n/community-nodes/resource-operation-pattern': "error";
|
|
42
42
|
'@n8n/community-nodes/trigger-node-conventions': "error";
|
|
43
43
|
'@n8n/community-nodes/credential-documentation-url': "error";
|
|
44
44
|
'@n8n/community-nodes/cred-class-field-icon-missing': "error";
|
|
@@ -96,7 +96,7 @@ declare const configs: {
|
|
|
96
96
|
'@n8n/community-nodes/icon-validation': "error";
|
|
97
97
|
'@n8n/community-nodes/icon-prefer-themed-variants': "warn";
|
|
98
98
|
'@n8n/community-nodes/credential-documentation-url': "error";
|
|
99
|
-
'@n8n/community-nodes/resource-operation-pattern': "
|
|
99
|
+
'@n8n/community-nodes/resource-operation-pattern': "error";
|
|
100
100
|
'@n8n/community-nodes/trigger-node-conventions': "error";
|
|
101
101
|
'@n8n/community-nodes/cred-class-field-icon-missing': "error";
|
|
102
102
|
'@n8n/community-nodes/cred-class-oauth2-naming': "error";
|
|
@@ -163,7 +163,7 @@ declare const pluginWithConfigs: {
|
|
|
163
163
|
'@n8n/community-nodes/no-unsafe-connection-type-cast': "error";
|
|
164
164
|
'@n8n/community-nodes/icon-validation': "error";
|
|
165
165
|
'@n8n/community-nodes/icon-prefer-themed-variants': "warn";
|
|
166
|
-
'@n8n/community-nodes/resource-operation-pattern': "
|
|
166
|
+
'@n8n/community-nodes/resource-operation-pattern': "error";
|
|
167
167
|
'@n8n/community-nodes/trigger-node-conventions': "error";
|
|
168
168
|
'@n8n/community-nodes/credential-documentation-url': "error";
|
|
169
169
|
'@n8n/community-nodes/cred-class-field-icon-missing': "error";
|
|
@@ -221,7 +221,7 @@ declare const pluginWithConfigs: {
|
|
|
221
221
|
'@n8n/community-nodes/icon-validation': "error";
|
|
222
222
|
'@n8n/community-nodes/icon-prefer-themed-variants': "warn";
|
|
223
223
|
'@n8n/community-nodes/credential-documentation-url': "error";
|
|
224
|
-
'@n8n/community-nodes/resource-operation-pattern': "
|
|
224
|
+
'@n8n/community-nodes/resource-operation-pattern': "error";
|
|
225
225
|
'@n8n/community-nodes/trigger-node-conventions': "error";
|
|
226
226
|
'@n8n/community-nodes/cred-class-field-icon-missing': "error";
|
|
227
227
|
'@n8n/community-nodes/cred-class-oauth2-naming': "error";
|
|
@@ -289,7 +289,7 @@ declare const n8nCommunityNodesPlugin: {
|
|
|
289
289
|
'@n8n/community-nodes/no-unsafe-connection-type-cast': "error";
|
|
290
290
|
'@n8n/community-nodes/icon-validation': "error";
|
|
291
291
|
'@n8n/community-nodes/icon-prefer-themed-variants': "warn";
|
|
292
|
-
'@n8n/community-nodes/resource-operation-pattern': "
|
|
292
|
+
'@n8n/community-nodes/resource-operation-pattern': "error";
|
|
293
293
|
'@n8n/community-nodes/trigger-node-conventions': "error";
|
|
294
294
|
'@n8n/community-nodes/credential-documentation-url': "error";
|
|
295
295
|
'@n8n/community-nodes/cred-class-field-icon-missing': "error";
|
|
@@ -347,7 +347,7 @@ declare const n8nCommunityNodesPlugin: {
|
|
|
347
347
|
'@n8n/community-nodes/icon-validation': "error";
|
|
348
348
|
'@n8n/community-nodes/icon-prefer-themed-variants': "warn";
|
|
349
349
|
'@n8n/community-nodes/credential-documentation-url': "error";
|
|
350
|
-
'@n8n/community-nodes/resource-operation-pattern': "
|
|
350
|
+
'@n8n/community-nodes/resource-operation-pattern': "error";
|
|
351
351
|
'@n8n/community-nodes/trigger-node-conventions': "error";
|
|
352
352
|
'@n8n/community-nodes/cred-class-field-icon-missing': "error";
|
|
353
353
|
'@n8n/community-nodes/cred-class-oauth2-naming': "error";
|
package/dist/plugin.js
CHANGED
|
@@ -40,7 +40,7 @@ const configs = {
|
|
|
40
40
|
'@n8n/community-nodes/no-unsafe-connection-type-cast': 'error',
|
|
41
41
|
'@n8n/community-nodes/icon-validation': 'error',
|
|
42
42
|
'@n8n/community-nodes/icon-prefer-themed-variants': 'warn',
|
|
43
|
-
'@n8n/community-nodes/resource-operation-pattern': '
|
|
43
|
+
'@n8n/community-nodes/resource-operation-pattern': 'error',
|
|
44
44
|
'@n8n/community-nodes/trigger-node-conventions': 'error',
|
|
45
45
|
'@n8n/community-nodes/credential-documentation-url': 'error',
|
|
46
46
|
'@n8n/community-nodes/cred-class-field-icon-missing': 'error',
|
|
@@ -91,7 +91,7 @@ const configs = {
|
|
|
91
91
|
'@n8n/community-nodes/icon-validation': 'error',
|
|
92
92
|
'@n8n/community-nodes/icon-prefer-themed-variants': 'warn',
|
|
93
93
|
'@n8n/community-nodes/credential-documentation-url': 'error',
|
|
94
|
-
'@n8n/community-nodes/resource-operation-pattern': '
|
|
94
|
+
'@n8n/community-nodes/resource-operation-pattern': 'error',
|
|
95
95
|
'@n8n/community-nodes/trigger-node-conventions': 'error',
|
|
96
96
|
'@n8n/community-nodes/cred-class-field-icon-missing': 'error',
|
|
97
97
|
'@n8n/community-nodes/cred-class-oauth2-naming': 'error',
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Enforce proper resource/operation pattern for better UX in n8n nodes (`@n8n/community-nodes/resource-operation-pattern`)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
💼 This rule is enabled in the following configs: ✅ `recommended`, ☑️ `recommendedWithoutN8nCloudSupport`.
|
|
4
4
|
|
|
5
5
|
<!-- end auto-generated rule header -->
|
|
6
6
|
|
|
7
7
|
## Rule Details
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Reports an error when a node has more than 5 operations without resources. Use resources to group related operations. This pattern makes complex nodes easier to navigate.
|
|
10
10
|
|
|
11
11
|
When you have many operations, users benefit from having them organized into logical resource groups (e.g., "User", "Project", "File") rather than seeing a long flat list of operations.
|
|
12
12
|
|
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.36.0",
|
|
5
5
|
"main": "./dist/plugin.js",
|
|
6
6
|
"types": "./dist/plugin.d.ts",
|
|
7
7
|
"exports": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@n8n/utils": "1.
|
|
14
|
+
"@n8n/utils": "1.51.0",
|
|
15
15
|
"@typescript-eslint/typescript-estree": "8.70.0",
|
|
16
16
|
"@typescript-eslint/utils": "8.70.0",
|
|
17
17
|
"fastest-levenshtein": "1.0.16"
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"@n8n/typescript-config": "1.12.0",
|
|
21
21
|
"@n8n/vitest-config": "1.24.0",
|
|
22
22
|
"@types/node": "24.10.1",
|
|
23
|
-
"n8n-workflow": "2.
|
|
23
|
+
"n8n-workflow": "2.44.0",
|
|
24
24
|
"@typescript-eslint/rule-tester": "8.70.0",
|
|
25
25
|
"eslint-doc-generator": "^2.2.2",
|
|
26
26
|
"eslint-plugin-eslint-plugin": "^7.0.0",
|
package/src/plugin.ts
CHANGED
|
@@ -44,7 +44,7 @@ const configs = {
|
|
|
44
44
|
'@n8n/community-nodes/no-unsafe-connection-type-cast': 'error',
|
|
45
45
|
'@n8n/community-nodes/icon-validation': 'error',
|
|
46
46
|
'@n8n/community-nodes/icon-prefer-themed-variants': 'warn',
|
|
47
|
-
'@n8n/community-nodes/resource-operation-pattern': '
|
|
47
|
+
'@n8n/community-nodes/resource-operation-pattern': 'error',
|
|
48
48
|
'@n8n/community-nodes/trigger-node-conventions': 'error',
|
|
49
49
|
'@n8n/community-nodes/credential-documentation-url': 'error',
|
|
50
50
|
'@n8n/community-nodes/cred-class-field-icon-missing': 'error',
|
|
@@ -95,7 +95,7 @@ const configs = {
|
|
|
95
95
|
'@n8n/community-nodes/icon-validation': 'error',
|
|
96
96
|
'@n8n/community-nodes/icon-prefer-themed-variants': 'warn',
|
|
97
97
|
'@n8n/community-nodes/credential-documentation-url': 'error',
|
|
98
|
-
'@n8n/community-nodes/resource-operation-pattern': '
|
|
98
|
+
'@n8n/community-nodes/resource-operation-pattern': 'error',
|
|
99
99
|
'@n8n/community-nodes/trigger-node-conventions': 'error',
|
|
100
100
|
'@n8n/community-nodes/cred-class-field-icon-missing': 'error',
|
|
101
101
|
'@n8n/community-nodes/cred-class-oauth2-naming': 'error',
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { RuleTester } from '@typescript-eslint/rule-tester';
|
|
2
|
+
import { Linter } from 'eslint';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
2
4
|
|
|
5
|
+
import { configs } from '../plugin.js';
|
|
3
6
|
import { ResourceOperationPatternRule } from './resource-operation-pattern.js';
|
|
4
7
|
|
|
5
8
|
const ruleTester = new RuleTester();
|
|
@@ -215,3 +218,61 @@ ruleTester.run('resource-operation-pattern', ResourceOperationPatternRule, {
|
|
|
215
218
|
},
|
|
216
219
|
],
|
|
217
220
|
});
|
|
221
|
+
|
|
222
|
+
const resourceOperationRuleId = '@n8n/community-nodes/resource-operation-pattern';
|
|
223
|
+
|
|
224
|
+
function nodeWithSixOperations(hasResource: boolean): string {
|
|
225
|
+
return `
|
|
226
|
+
class TestNode extends Node {
|
|
227
|
+
description = {
|
|
228
|
+
properties: [
|
|
229
|
+
${hasResource ? "{ name: 'resource', type: 'options', options: [{ name: 'User', value: 'user' }] }," : ''}
|
|
230
|
+
{
|
|
231
|
+
name: 'operation',
|
|
232
|
+
type: 'options',
|
|
233
|
+
options: [
|
|
234
|
+
{ name: 'Get', value: 'get' },
|
|
235
|
+
{ name: 'Create', value: 'create' },
|
|
236
|
+
{ name: 'Update', value: 'update' },
|
|
237
|
+
{ name: 'Delete', value: 'delete' },
|
|
238
|
+
{ name: 'List', value: 'list' },
|
|
239
|
+
{ name: 'Search', value: 'search' },
|
|
240
|
+
],
|
|
241
|
+
},
|
|
242
|
+
],
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
`;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
describe.each([
|
|
249
|
+
['recommended', configs.recommended],
|
|
250
|
+
['recommendedWithoutN8nCloudSupport', configs.recommendedWithoutN8nCloudSupport],
|
|
251
|
+
] as const)('%s config', (_name, config) => {
|
|
252
|
+
const linter = new Linter();
|
|
253
|
+
const lint = (code: string) =>
|
|
254
|
+
linter.verify(
|
|
255
|
+
code,
|
|
256
|
+
{
|
|
257
|
+
files: ['**/*.node.ts'],
|
|
258
|
+
plugins: config.plugins,
|
|
259
|
+
rules: { [resourceOperationRuleId]: config.rules[resourceOperationRuleId] },
|
|
260
|
+
},
|
|
261
|
+
{ filename: 'TestNode.node.ts' },
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
// CE-3284: The resource finding must fail lint even when it is the only finding.
|
|
265
|
+
it('reports missing resources as the only error', () => {
|
|
266
|
+
expect(lint(nodeWithSixOperations(false))).toEqual([
|
|
267
|
+
expect.objectContaining({
|
|
268
|
+
ruleId: resourceOperationRuleId,
|
|
269
|
+
messageId: 'tooManyOperationsWithoutResources',
|
|
270
|
+
severity: 2,
|
|
271
|
+
}),
|
|
272
|
+
]);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('accepts operations organized by resource', () => {
|
|
276
|
+
expect(lint(nodeWithSixOperations(true))).toEqual([]);
|
|
277
|
+
});
|
|
278
|
+
});
|