@n8n/eslint-plugin-community-nodes 0.24.0 → 0.25.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.
Files changed (106) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/README.md +39 -52
  3. package/dist/plugin.d.ts +0 -78
  4. package/dist/plugin.d.ts.map +1 -1
  5. package/dist/plugin.js +0 -26
  6. package/dist/plugin.js.map +1 -1
  7. package/dist/rules/index.d.ts +0 -13
  8. package/dist/rules/index.d.ts.map +1 -1
  9. package/dist/rules/index.js +0 -26
  10. package/dist/rules/index.js.map +1 -1
  11. package/dist/typecheck.tsbuildinfo +1 -1
  12. package/docs/rules/no-forbidden-lifecycle-scripts.md +1 -1
  13. package/package.json +4 -4
  14. package/src/plugin.ts +0 -26
  15. package/src/rules/index.ts +0 -26
  16. package/dist/rules/cred-class-name-field-conventions.d.ts +0 -2
  17. package/dist/rules/cred-class-name-field-conventions.d.ts.map +0 -1
  18. package/dist/rules/cred-class-name-field-conventions.js +0 -89
  19. package/dist/rules/cred-class-name-field-conventions.js.map +0 -1
  20. package/dist/rules/cred-class-name-suffix.d.ts +0 -2
  21. package/dist/rules/cred-class-name-suffix.d.ts.map +0 -1
  22. package/dist/rules/cred-class-name-suffix.js +0 -53
  23. package/dist/rules/cred-class-name-suffix.js.map +0 -1
  24. package/dist/rules/cred-filename-against-convention.d.ts +0 -2
  25. package/dist/rules/cred-filename-against-convention.d.ts.map +0 -1
  26. package/dist/rules/cred-filename-against-convention.js +0 -42
  27. package/dist/rules/cred-filename-against-convention.js.map +0 -1
  28. package/dist/rules/no-asterisk-in-option-names.d.ts +0 -2
  29. package/dist/rules/no-asterisk-in-option-names.d.ts.map +0 -1
  30. package/dist/rules/no-asterisk-in-option-names.js +0 -90
  31. package/dist/rules/no-asterisk-in-option-names.js.map +0 -1
  32. package/dist/rules/no-duplicate-param-options.d.ts +0 -2
  33. package/dist/rules/no-duplicate-param-options.d.ts.map +0 -1
  34. package/dist/rules/no-duplicate-param-options.js +0 -97
  35. package/dist/rules/no-duplicate-param-options.js.map +0 -1
  36. package/dist/rules/node-class-description-name-camelcase.d.ts +0 -2
  37. package/dist/rules/node-class-description-name-camelcase.d.ts.map +0 -1
  38. package/dist/rules/node-class-description-name-camelcase.js +0 -92
  39. package/dist/rules/node-class-description-name-camelcase.js.map +0 -1
  40. package/dist/rules/node-filename-against-convention.d.ts +0 -2
  41. package/dist/rules/node-filename-against-convention.d.ts.map +0 -1
  42. package/dist/rules/node-filename-against-convention.js +0 -61
  43. package/dist/rules/node-filename-against-convention.js.map +0 -1
  44. package/dist/rules/node-operation-error-itemindex.d.ts +0 -12
  45. package/dist/rules/node-operation-error-itemindex.d.ts.map +0 -1
  46. package/dist/rules/node-operation-error-itemindex.js +0 -184
  47. package/dist/rules/node-operation-error-itemindex.js.map +0 -1
  48. package/dist/rules/options-sorted-alphabetically.d.ts +0 -2
  49. package/dist/rules/options-sorted-alphabetically.d.ts.map +0 -1
  50. package/dist/rules/options-sorted-alphabetically.js +0 -95
  51. package/dist/rules/options-sorted-alphabetically.js.map +0 -1
  52. package/dist/rules/require-community-node-keyword.d.ts +0 -2
  53. package/dist/rules/require-community-node-keyword.d.ts.map +0 -1
  54. package/dist/rules/require-community-node-keyword.js +0 -65
  55. package/dist/rules/require-community-node-keyword.js.map +0 -1
  56. package/dist/rules/require-continue-on-fail.d.ts +0 -2
  57. package/dist/rules/require-continue-on-fail.d.ts.map +0 -1
  58. package/dist/rules/require-continue-on-fail.js +0 -76
  59. package/dist/rules/require-continue-on-fail.js.map +0 -1
  60. package/dist/rules/require-mit-license.d.ts +0 -2
  61. package/dist/rules/require-mit-license.d.ts.map +0 -1
  62. package/dist/rules/require-mit-license.js +0 -57
  63. package/dist/rules/require-mit-license.js.map +0 -1
  64. package/dist/rules/require-param-default.d.ts +0 -15
  65. package/dist/rules/require-param-default.d.ts.map +0 -1
  66. package/dist/rules/require-param-default.js +0 -75
  67. package/dist/rules/require-param-default.js.map +0 -1
  68. package/docs/rules/cred-class-name-field-conventions.md +0 -38
  69. package/docs/rules/cred-class-name-suffix.md +0 -46
  70. package/docs/rules/cred-filename-against-convention.md +0 -42
  71. package/docs/rules/no-asterisk-in-option-names.md +0 -61
  72. package/docs/rules/no-duplicate-param-options.md +0 -46
  73. package/docs/rules/node-class-description-name-camelcase.md +0 -41
  74. package/docs/rules/node-filename-against-convention.md +0 -50
  75. package/docs/rules/node-operation-error-itemindex.md +0 -81
  76. package/docs/rules/options-sorted-alphabetically.md +0 -63
  77. package/docs/rules/require-community-node-keyword.md +0 -45
  78. package/docs/rules/require-continue-on-fail.md +0 -56
  79. package/docs/rules/require-mit-license.md +0 -38
  80. package/docs/rules/require-param-default.md +0 -60
  81. package/src/rules/cred-class-name-field-conventions.test.ts +0 -121
  82. package/src/rules/cred-class-name-field-conventions.ts +0 -102
  83. package/src/rules/cred-class-name-suffix.test.ts +0 -74
  84. package/src/rules/cred-class-name-suffix.ts +0 -57
  85. package/src/rules/cred-filename-against-convention.test.ts +0 -72
  86. package/src/rules/cred-filename-against-convention.ts +0 -48
  87. package/src/rules/no-asterisk-in-option-names.test.ts +0 -185
  88. package/src/rules/no-asterisk-in-option-names.ts +0 -111
  89. package/src/rules/no-duplicate-param-options.test.ts +0 -125
  90. package/src/rules/no-duplicate-param-options.ts +0 -125
  91. package/src/rules/node-class-description-name-camelcase.test.ts +0 -121
  92. package/src/rules/node-class-description-name-camelcase.ts +0 -114
  93. package/src/rules/node-filename-against-convention.test.ts +0 -115
  94. package/src/rules/node-filename-against-convention.ts +0 -76
  95. package/src/rules/node-operation-error-itemindex.test.ts +0 -280
  96. package/src/rules/node-operation-error-itemindex.ts +0 -223
  97. package/src/rules/options-sorted-alphabetically.test.ts +0 -468
  98. package/src/rules/options-sorted-alphabetically.ts +0 -129
  99. package/src/rules/require-community-node-keyword.test.ts +0 -82
  100. package/src/rules/require-community-node-keyword.ts +0 -78
  101. package/src/rules/require-continue-on-fail.test.ts +0 -129
  102. package/src/rules/require-continue-on-fail.ts +0 -88
  103. package/src/rules/require-mit-license.test.ts +0 -67
  104. package/src/rules/require-mit-license.ts +0 -65
  105. package/src/rules/require-param-default.test.ts +0 -227
  106. package/src/rules/require-param-default.ts +0 -89
@@ -1,63 +0,0 @@
1
- # Enforce alphabetical ordering of options arrays in n8n node properties (`@n8n/community-nodes/options-sorted-alphabetically`)
2
-
3
- ⚠️ This rule _warns_ in the following configs: ✅ `recommended`, ☑️ `recommendedWithoutN8nCloudSupport`.
4
-
5
- <!-- end auto-generated rule header -->
6
-
7
- ## Rule Details
8
-
9
- Warns when an `options`-type parameter has its options array not sorted alphabetically by name. Applies to all `type: 'options'` parameters — including `resource`, `operation`, and any other dropdowns.
10
-
11
- Alphabetical ordering is an [official n8n UI design requirement](https://docs.n8n.io/integrations/creating-nodes/plan/node-ui-design/#lists) and the most frequently flagged issue in community node reviews.
12
-
13
- Comparison is case-insensitive and locale-aware (handles non-ASCII names such as Spanish or Portuguese labels).
14
-
15
- ## Examples
16
-
17
- ### ❌ Incorrect
18
-
19
- ```typescript
20
- export class MyNode implements INodeType {
21
- description: INodeTypeDescription = {
22
- displayName: 'My Service',
23
- name: 'myService',
24
- properties: [
25
- {
26
- displayName: 'Resource',
27
- name: 'resource',
28
- type: 'options',
29
- options: [
30
- { name: 'User', value: 'user' },
31
- { name: 'Contact', value: 'contact' }, // out of order
32
- { name: 'Project', value: 'project' },
33
- ],
34
- default: 'user',
35
- },
36
- ],
37
- };
38
- }
39
- ```
40
-
41
- ### ✅ Correct
42
-
43
- ```typescript
44
- export class MyNode implements INodeType {
45
- description: INodeTypeDescription = {
46
- displayName: 'My Service',
47
- name: 'myService',
48
- properties: [
49
- {
50
- displayName: 'Resource',
51
- name: 'resource',
52
- type: 'options',
53
- options: [
54
- { name: 'Contact', value: 'contact' },
55
- { name: 'Project', value: 'project' },
56
- { name: 'User', value: 'user' },
57
- ],
58
- default: 'contact',
59
- },
60
- ],
61
- };
62
- }
63
- ```
@@ -1,45 +0,0 @@
1
- # Require the "n8n-community-node-package" keyword in community node package.json (`@n8n/community-nodes/require-community-node-keyword`)
2
-
3
- ⚠️ This rule _warns_ in the following configs: ✅ `recommended`, ☑️ `recommendedWithoutN8nCloudSupport`.
4
-
5
- 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/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
- ```
@@ -1,56 +0,0 @@
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
- ```
@@ -1,38 +0,0 @@
1
- # Require the "license" field in community node package.json to be "MIT" (`@n8n/community-nodes/require-mit-license`)
2
-
3
- 💼 This rule is enabled in the following configs: ✅ `recommended`, ☑️ `recommendedWithoutN8nCloudSupport`.
4
-
5
- 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/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 declares its `license` as `"MIT"`. Community node packages must be MIT licensed to be distributed through n8n.
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
- "license": "Apache-2.0"
28
- }
29
- ```
30
-
31
- ### ✅ Correct
32
-
33
- ```json
34
- {
35
- "name": "n8n-nodes-my-service",
36
- "license": "MIT"
37
- }
38
- ```
@@ -1,60 +0,0 @@
1
- # Require every node parameter to declare a default value (`@n8n/community-nodes/require-param-default`)
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 node parameter must declare a `default` property. A parameter is detected
10
- by its shape: an object inside the node's `description` with `displayName`,
11
- `name`, and `type` all set to string literals — the shape every
12
- `INodeProperties` entry has. Entries in an `options` array (which carry
13
- `name`/`value` but no `displayName`/`type`) are not treated as parameters.
14
-
15
- Without a `default`, n8n cannot reliably initialise the parameter's value. This
16
- leads to inconsistent behaviour in the editor (empty or `undefined` fields) and
17
- at execution time. Even non-input types such as `notice` are expected to declare
18
- `default: ''`.
19
-
20
- This rule also checks parameters nested inside `options` of `collection` and
21
- `fixedCollection` parameters.
22
-
23
- ## Examples
24
-
25
- ### Incorrect
26
-
27
- ```typescript
28
- export class MyNode implements INodeType {
29
- description: INodeTypeDescription = {
30
- displayName: 'My Node',
31
- name: 'myNode',
32
- properties: [
33
- {
34
- displayName: 'Field',
35
- name: 'field',
36
- type: 'string',
37
- },
38
- ],
39
- };
40
- }
41
- ```
42
-
43
- ### Correct
44
-
45
- ```typescript
46
- export class MyNode implements INodeType {
47
- description: INodeTypeDescription = {
48
- displayName: 'My Node',
49
- name: 'myNode',
50
- properties: [
51
- {
52
- displayName: 'Field',
53
- name: 'field',
54
- type: 'string',
55
- default: '',
56
- },
57
- ],
58
- };
59
- }
60
- ```
@@ -1,121 +0,0 @@
1
- import { RuleTester } from '@typescript-eslint/rule-tester';
2
-
3
- import { CredClassNameFieldConventionsRule } from './cred-class-name-field-conventions.js';
4
-
5
- const ruleTester = new RuleTester();
6
-
7
- const credFilePath = '/tmp/TestCredential.credentials.ts';
8
- const nonCredFilePath = '/tmp/SomeHelper.ts';
9
-
10
- function createCredentialCode(name: string): string {
11
- return `
12
- import type { ICredentialType, INodeProperties } from 'n8n-workflow';
13
-
14
- export class TestApi implements ICredentialType {
15
- name = '${name}';
16
- displayName = 'Test API';
17
- properties: INodeProperties[] = [];
18
- }`;
19
- }
20
-
21
- function createRegularClass(name: string): string {
22
- return `
23
- export class SomeHelper {
24
- name = '${name}';
25
- }`;
26
- }
27
-
28
- // Embeds the raw literal text (including its quotes) verbatim, so tests can
29
- // exercise names containing quote characters that need escaping.
30
- function createCredentialCodeWithLiteral(literal: string): string {
31
- return `
32
- import type { ICredentialType, INodeProperties } from 'n8n-workflow';
33
-
34
- export class TestApi implements ICredentialType {
35
- name = ${literal};
36
- displayName = 'Test API';
37
- properties: INodeProperties[] = [];
38
- }`;
39
- }
40
-
41
- ruleTester.run('cred-class-name-field-conventions', CredClassNameFieldConventionsRule, {
42
- valid: [
43
- {
44
- name: 'name field with Api suffix and lowercase first char',
45
- filename: credFilePath,
46
- code: createCredentialCode('githubApi'),
47
- },
48
- {
49
- name: 'OAuth2 name field is also valid',
50
- filename: credFilePath,
51
- code: createCredentialCode('githubOAuth2Api'),
52
- },
53
- {
54
- name: 'class not implementing ICredentialType is ignored',
55
- filename: credFilePath,
56
- code: createRegularClass('Github'),
57
- },
58
- {
59
- name: 'non-.credentials.ts file is ignored',
60
- filename: nonCredFilePath,
61
- code: createCredentialCode('Github'),
62
- },
63
- ],
64
- invalid: [
65
- {
66
- name: 'name field missing Api suffix',
67
- filename: credFilePath,
68
- code: createCredentialCode('github'),
69
- errors: [{ messageId: 'missingSuffix', data: { value: 'github' } }],
70
- output: createCredentialCode('githubApi'),
71
- },
72
- {
73
- name: 'name field with uppercase first char',
74
- filename: credFilePath,
75
- code: createCredentialCode('GithubApi'),
76
- errors: [{ messageId: 'uppercaseFirstChar', data: { value: 'GithubApi' } }],
77
- output: createCredentialCode('githubApi'),
78
- },
79
- {
80
- name: 'name field with both uppercase first char and missing suffix',
81
- filename: credFilePath,
82
- code: createCredentialCode('Github'),
83
- errors: [
84
- { messageId: 'uppercaseFirstChar', data: { value: 'Github' } },
85
- { messageId: 'missingSuffix', data: { value: 'Github' } },
86
- ],
87
- output: createCredentialCode('githubApi'),
88
- },
89
- {
90
- name: 'name field ending in Ap',
91
- filename: credFilePath,
92
- code: createCredentialCode('githubAp'),
93
- errors: [{ messageId: 'missingSuffix', data: { value: 'githubAp' } }],
94
- output: createCredentialCode('githubApi'),
95
- },
96
- {
97
- name: 'name field ending in A',
98
- filename: credFilePath,
99
- code: createCredentialCode('githubA'),
100
- errors: [{ messageId: 'missingSuffix', data: { value: 'githubA' } }],
101
- output: createCredentialCode('githubApi'),
102
- },
103
- {
104
- name: 'autofix escapes single quotes in the name value',
105
- filename: credFilePath,
106
- code: createCredentialCodeWithLiteral("'git\\'hub'"),
107
- errors: [{ messageId: 'missingSuffix', data: { value: "git'hub" } }],
108
- output: createCredentialCodeWithLiteral("'git\\'hubApi'"),
109
- },
110
- {
111
- name: 'autofix preserves double quotes and escapes them in the name value',
112
- filename: credFilePath,
113
- code: createCredentialCodeWithLiteral('"Git\\"hub"'),
114
- errors: [
115
- { messageId: 'uppercaseFirstChar', data: { value: 'Git"hub' } },
116
- { messageId: 'missingSuffix', data: { value: 'Git"hub' } },
117
- ],
118
- output: createCredentialCodeWithLiteral('"git\\"hubApi"'),
119
- },
120
- ],
121
- });
@@ -1,102 +0,0 @@
1
- import {
2
- createRule,
3
- findClassProperty,
4
- getStringLiteralValue,
5
- isCredentialTypeClass,
6
- isFileType,
7
- } from '../utils/index.js';
8
-
9
- function lowercaseFirstChar(name: string): string {
10
- return name.charAt(0).toLowerCase() + name.slice(1);
11
- }
12
-
13
- function addApiSuffix(name: string): string {
14
- if (name.endsWith('Api')) return name;
15
- if (name.endsWith('Ap')) return `${name}i`;
16
- if (name.endsWith('A')) return `${name}pi`;
17
- return `${name}Api`;
18
- }
19
-
20
- // Serialize a value as a string literal using the original quote character,
21
- // escaping any characters that would otherwise break the literal so the
22
- // autofix never emits invalid code (e.g. names containing quotes).
23
- function toStringLiteral(value: string, quote: string): string {
24
- const escaped = value
25
- .replace(/\\/g, '\\\\')
26
- .replace(/\n/g, '\\n')
27
- .replace(/\r/g, '\\r')
28
- .split(quote)
29
- .join(`\\${quote}`);
30
- return `${quote}${escaped}${quote}`;
31
- }
32
-
33
- export const CredClassNameFieldConventionsRule = createRule({
34
- name: 'cred-class-name-field-conventions',
35
- meta: {
36
- type: 'problem',
37
- docs: {
38
- description: 'Credential `name` field must end with `Api` and start with a lowercase letter',
39
- },
40
- messages: {
41
- missingSuffix: "Credential `name` field '{{value}}' must end with 'Api'",
42
- uppercaseFirstChar: "Credential `name` field '{{value}}' must start with a lowercase letter",
43
- },
44
- fixable: 'code',
45
- schema: [],
46
- },
47
- defaultOptions: [],
48
- create(context) {
49
- if (!isFileType(context.filename, '.credentials.ts')) {
50
- return {};
51
- }
52
-
53
- return {
54
- ClassDeclaration(node) {
55
- if (!isCredentialTypeClass(node)) {
56
- return;
57
- }
58
-
59
- const nameProperty = findClassProperty(node, 'name');
60
- if (!nameProperty?.value) {
61
- return;
62
- }
63
-
64
- const nameValue = getStringLiteralValue(nameProperty.value);
65
- if (nameValue === null) {
66
- return;
67
- }
68
-
69
- const startsLowercase = !/^[A-Z]/.test(nameValue);
70
- const endsWithApi = nameValue.endsWith('Api');
71
- if (startsLowercase && endsWithApi) {
72
- return;
73
- }
74
-
75
- // Compute the fully-corrected value once so each fix yields the
76
- // same result in a single pass, regardless of which one applies.
77
- const fixedValue = addApiSuffix(lowercaseFirstChar(nameValue));
78
- const valueNode = nameProperty.value;
79
- const quote = context.sourceCode.getText(valueNode).charAt(0);
80
- const replacement = toStringLiteral(fixedValue, quote);
81
-
82
- if (!startsLowercase) {
83
- context.report({
84
- node: valueNode,
85
- messageId: 'uppercaseFirstChar',
86
- data: { value: nameValue },
87
- fix: (fixer) => fixer.replaceText(valueNode, replacement),
88
- });
89
- }
90
-
91
- if (!endsWithApi) {
92
- context.report({
93
- node: valueNode,
94
- messageId: 'missingSuffix',
95
- data: { value: nameValue },
96
- fix: (fixer) => fixer.replaceText(valueNode, replacement),
97
- });
98
- }
99
- },
100
- };
101
- },
102
- });
@@ -1,74 +0,0 @@
1
- import { RuleTester } from '@typescript-eslint/rule-tester';
2
-
3
- import { CredClassNameSuffixRule } from './cred-class-name-suffix.js';
4
-
5
- const ruleTester = new RuleTester();
6
-
7
- const credFilePath = '/tmp/TestCredential.credentials.ts';
8
- const nonCredFilePath = '/tmp/SomeHelper.ts';
9
-
10
- function createCredentialCode(className: string): string {
11
- return `
12
- import type { ICredentialType, INodeProperties } from 'n8n-workflow';
13
-
14
- export class ${className} implements ICredentialType {
15
- name = 'testApi';
16
- displayName = 'Test API';
17
- properties: INodeProperties[] = [];
18
- }`;
19
- }
20
-
21
- function createRegularClass(className: string): string {
22
- return `
23
- export class ${className} {
24
- name = 'test';
25
- }`;
26
- }
27
-
28
- ruleTester.run('cred-class-name-suffix', CredClassNameSuffixRule, {
29
- valid: [
30
- {
31
- name: 'credential class with Api suffix',
32
- filename: credFilePath,
33
- code: createCredentialCode('TestApi'),
34
- },
35
- {
36
- name: 'credential class with OAuth2Api suffix',
37
- filename: credFilePath,
38
- code: createCredentialCode('TestOAuth2Api'),
39
- },
40
- {
41
- name: 'class not implementing ICredentialType is ignored',
42
- filename: credFilePath,
43
- code: createRegularClass('SomeHelper'),
44
- },
45
- {
46
- name: 'non-.credentials.ts file is ignored',
47
- filename: nonCredFilePath,
48
- code: createCredentialCode('TestCredential'),
49
- },
50
- ],
51
- invalid: [
52
- {
53
- name: 'credential class missing Api suffix',
54
- filename: credFilePath,
55
- code: createCredentialCode('TestCredential'),
56
- errors: [{ messageId: 'missingSuffix', data: { name: 'TestCredential' } }],
57
- output: createCredentialCode('TestCredentialApi'),
58
- },
59
- {
60
- name: 'credential class name ending in Ap',
61
- filename: credFilePath,
62
- code: createCredentialCode('TestAp'),
63
- errors: [{ messageId: 'missingSuffix', data: { name: 'TestAp' } }],
64
- output: createCredentialCode('TestApi'),
65
- },
66
- {
67
- name: 'credential class name ending in A',
68
- filename: credFilePath,
69
- code: createCredentialCode('TestA'),
70
- errors: [{ messageId: 'missingSuffix', data: { name: 'TestA' } }],
71
- output: createCredentialCode('TestApi'),
72
- },
73
- ],
74
- });
@@ -1,57 +0,0 @@
1
- import { isCredentialTypeClass, isFileType, createRule } from '../utils/index.js';
2
-
3
- function addApiSuffix(name: string): string {
4
- if (name.endsWith('Ap')) return `${name}i`;
5
- if (name.endsWith('A')) return `${name}pi`;
6
- return `${name}Api`;
7
- }
8
-
9
- export const CredClassNameSuffixRule = createRule({
10
- name: 'cred-class-name-suffix',
11
- meta: {
12
- type: 'problem',
13
- docs: {
14
- description: 'Credential class names must be suffixed with `Api`',
15
- },
16
- messages: {
17
- missingSuffix: "Credential class name '{{name}}' must end with 'Api'",
18
- },
19
- fixable: 'code',
20
- schema: [],
21
- },
22
- defaultOptions: [],
23
- create(context) {
24
- if (!isFileType(context.filename, '.credentials.ts')) {
25
- return {};
26
- }
27
-
28
- return {
29
- ClassDeclaration(node) {
30
- if (!isCredentialTypeClass(node)) {
31
- return;
32
- }
33
-
34
- const classNameNode = node.id;
35
- if (!classNameNode) {
36
- return;
37
- }
38
-
39
- const className = classNameNode.name;
40
- if (className.endsWith('Api')) {
41
- return;
42
- }
43
-
44
- const fixedName = addApiSuffix(className);
45
-
46
- context.report({
47
- node: classNameNode,
48
- messageId: 'missingSuffix',
49
- data: { name: className },
50
- fix(fixer) {
51
- return fixer.replaceText(classNameNode, fixedName);
52
- },
53
- });
54
- },
55
- };
56
- },
57
- });
@@ -1,72 +0,0 @@
1
- import { RuleTester } from '@typescript-eslint/rule-tester';
2
-
3
- import { CredFilenameAgainstConventionRule } from './cred-filename-against-convention.js';
4
-
5
- const ruleTester = new RuleTester();
6
-
7
- function createCredentialCode(className: string): string {
8
- return `
9
- import type { ICredentialType, INodeProperties } from 'n8n-workflow';
10
-
11
- export class ${className} implements ICredentialType {
12
- name = '${className.charAt(0).toLowerCase() + className.slice(1)}';
13
- displayName = '${className}';
14
- properties: INodeProperties[] = [];
15
- }`;
16
- }
17
-
18
- function createRegularClass(): string {
19
- return `
20
- export class SomeHelper {
21
- name = 'githubApi';
22
- }`;
23
- }
24
-
25
- ruleTester.run('cred-filename-against-convention', CredFilenameAgainstConventionRule, {
26
- valid: [
27
- {
28
- name: 'filename matches credential class name',
29
- filename: '/tmp/GithubApi.credentials.ts',
30
- code: createCredentialCode('GithubApi'),
31
- },
32
- {
33
- name: 'multi-word class name matches filename',
34
- filename: '/tmp/GoogleSheetsOAuth2Api.credentials.ts',
35
- code: createCredentialCode('GoogleSheetsOAuth2Api'),
36
- },
37
- {
38
- name: 'class not implementing ICredentialType is ignored',
39
- filename: '/tmp/Github.credentials.ts',
40
- code: createRegularClass(),
41
- },
42
- {
43
- name: 'non-.credentials.ts file is ignored',
44
- filename: '/tmp/GithubApi.ts',
45
- code: createCredentialCode('Mismatch'),
46
- },
47
- ],
48
- invalid: [
49
- {
50
- name: 'filename does not match class name',
51
- filename: '/tmp/GithubApi.credentials.ts',
52
- code: createCredentialCode('GitlabApi'),
53
- errors: [
54
- {
55
- messageId: 'renameFile',
56
- data: { className: 'GitlabApi', expected: 'GitlabApi.credentials.ts' },
57
- },
58
- ],
59
- },
60
- {
61
- name: 'filename has wrong casing',
62
- filename: '/tmp/githubApi.credentials.ts',
63
- code: createCredentialCode('GithubApi'),
64
- errors: [
65
- {
66
- messageId: 'renameFile',
67
- data: { className: 'GithubApi', expected: 'GithubApi.credentials.ts' },
68
- },
69
- ],
70
- },
71
- ],
72
- });