@n8n/eslint-plugin-community-nodes 0.28.0 → 0.30.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.
@@ -2,11 +2,16 @@ import { TSESTree } from '@typescript-eslint/utils';
2
2
 
3
3
  import {
4
4
  isNodeTypeClass,
5
+ isTriggerNode,
5
6
  findClassProperty,
6
7
  findObjectProperty,
7
8
  createRule,
8
9
  } from '../utils/index.js';
9
10
 
11
+ function isSetToTrue(property: TSESTree.Property | null): property is TSESTree.Property {
12
+ return property?.value.type === TSESTree.AST_NODE_TYPES.Literal && property.value.value === true;
13
+ }
14
+
10
15
  export const NodeUsableAsToolRule = createRule({
11
16
  name: 'node-usable-as-tool',
12
17
  meta: {
@@ -17,6 +22,10 @@ export const NodeUsableAsToolRule = createRule({
17
22
  messages: {
18
23
  missingUsableAsTool:
19
24
  'Node class should have usableAsTool property. When in doubt, set it to true.',
25
+ triggerUsableAsTool:
26
+ 'Trigger nodes must not set usableAsTool: true. Trigger nodes cannot be invoked as AI tools and doing so pollutes the tool picker. Remove this property.',
27
+ aiOnlyUsableAsTool:
28
+ 'AI-only nodes (non-main output, no inputs) must not set usableAsTool: true. These nodes are only usable through their AI connection type, not as a generic tool, and doing so pollutes the tool picker. Remove this property.',
20
29
  },
21
30
  fixable: 'code',
22
31
  schema: [],
@@ -40,6 +49,17 @@ export const NodeUsableAsToolRule = createRule({
40
49
  }
41
50
 
42
51
  const usableAsToolProperty = findObjectProperty(descriptionValue, 'usableAsTool');
52
+
53
+ if (isTriggerNode(node, descriptionValue)) {
54
+ if (isSetToTrue(usableAsToolProperty)) {
55
+ context.report({
56
+ node: usableAsToolProperty,
57
+ messageId: 'triggerUsableAsTool',
58
+ });
59
+ }
60
+ return;
61
+ }
62
+
43
63
  const outputsProperty = findObjectProperty(descriptionValue, 'outputs');
44
64
  const inputsProperty = findObjectProperty(descriptionValue, 'inputs');
45
65
  if (
@@ -59,6 +79,14 @@ export const NodeUsableAsToolRule = createRule({
59
79
  });
60
80
  const isEmptyInputs = inputsProperty?.value?.elements?.length === 0;
61
81
  if (isAiOutput && isEmptyInputs) {
82
+ // These nodes are only ever consumed via their AI connection type (e.g. as an
83
+ // Agent's memory/language model), never invoked directly like a regular tool.
84
+ if (isSetToTrue(usableAsToolProperty)) {
85
+ context.report({
86
+ node: usableAsToolProperty,
87
+ messageId: 'aiOnlyUsableAsTool',
88
+ });
89
+ }
62
90
  return;
63
91
  }
64
92
  }
@@ -2,6 +2,7 @@ import { TSESTree } from '@typescript-eslint/utils';
2
2
 
3
3
  import {
4
4
  isNodeTypeClass,
5
+ isTriggerNodeClass,
5
6
  findClassProperty,
6
7
  findObjectProperty,
7
8
  getStringLiteralValue,
@@ -37,7 +38,7 @@ export const TriggerNodeConventionsRule = createRule({
37
38
  return;
38
39
  }
39
40
 
40
- if (!node.id?.name.endsWith('Trigger')) {
41
+ if (!isTriggerNodeClass(node)) {
41
42
  return;
42
43
  }
43
44
 
@@ -29,6 +29,32 @@ export function isCredentialTypeClass(node: TSESTree.ClassDeclaration): boolean
29
29
  return implementsInterface(node, 'ICredentialType');
30
30
  }
31
31
 
32
+ /** Matches this plugin's convention for identifying trigger nodes: class name ends with `Trigger`. */
33
+ export function isTriggerNodeClass(node: TSESTree.ClassDeclaration): boolean {
34
+ return node.id?.name.endsWith('Trigger') ?? false;
35
+ }
36
+
37
+ function hasTriggerGroup(descriptionValue: TSESTree.ObjectExpression): boolean {
38
+ const groupArray = findArrayLiteralProperty(descriptionValue, 'group');
39
+ return (
40
+ groupArray?.elements.some(
41
+ (element) => element?.type === AST_NODE_TYPES.Literal && element.value === 'trigger',
42
+ ) ?? false
43
+ );
44
+ }
45
+
46
+ /**
47
+ * `group` is the authoritative signal for "is this a trigger node" (also catches e.g. Cron,
48
+ * Webhook, versioned `*TriggerV1` classes); the name suffix is kept as a fallback for dynamic
49
+ * `group` values.
50
+ */
51
+ export function isTriggerNode(
52
+ node: TSESTree.ClassDeclaration,
53
+ descriptionValue: TSESTree.ObjectExpression,
54
+ ): boolean {
55
+ return hasTriggerGroup(descriptionValue) || isTriggerNodeClass(node);
56
+ }
57
+
32
58
  export function findClassProperty(
33
59
  node: TSESTree.ClassDeclaration,
34
60
  propertyName: string,
@@ -1,4 +0,0 @@
1
-
2
- > @n8n/eslint-plugin-community-nodes@0.28.0 build /home/runner/work/n8n/n8n/packages/@n8n/eslint-plugin-community-nodes
3
- > tsc --project tsconfig.build.json
4
-