@n8n/eslint-plugin-community-nodes 0.24.0 → 0.26.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 +39 -52
- package/dist/plugin.d.ts +15 -93
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +0 -26
- package/dist/plugin.js.map +1 -1
- package/dist/rules/ai-node-package-json.d.ts +1 -1
- package/dist/rules/cred-class-field-icon-missing.d.ts +1 -1
- package/dist/rules/cred-class-oauth2-naming.d.ts +1 -1
- package/dist/rules/icon-validation.d.ts +1 -1
- package/dist/rules/index.d.ts +20 -33
- package/dist/rules/index.d.ts.map +1 -1
- package/dist/rules/index.js +0 -26
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/no-credential-reuse.d.ts +1 -1
- package/dist/rules/no-dangerous-functions.d.ts +1 -1
- package/dist/rules/no-deprecated-workflow-functions.d.ts +1 -1
- package/dist/rules/no-restricted-imports.d.ts +1 -1
- package/dist/rules/no-restricted-imports.d.ts.map +1 -1
- package/dist/rules/no-restricted-imports.js +14 -5
- package/dist/rules/no-restricted-imports.js.map +1 -1
- package/dist/rules/node-class-description-icon-missing.d.ts +1 -1
- package/dist/rules/node-connection-type-literal.js.map +1 -1
- package/dist/rules/package-name-convention.d.ts +1 -1
- package/dist/rules/require-version.d.ts +1 -1
- package/dist/rules/trigger-node-conventions.d.ts +1 -1
- package/dist/rules/valid-author.d.ts +1 -1
- package/dist/rules/valid-credential-references.d.ts +1 -1
- package/dist/rules/valid-description.d.ts +1 -1
- package/dist/rules/valid-peer-dependencies.d.ts +1 -1
- package/dist/rules/webhook-lifecycle-complete.d.ts +1 -1
- package/dist/typecheck.tsbuildinfo +1 -1
- package/dist/utils/ast-utils.js.map +1 -1
- package/dist/utils/file-utils.d.ts +7 -0
- package/dist/utils/file-utils.d.ts.map +1 -1
- package/dist/utils/file-utils.js +28 -6
- package/dist/utils/file-utils.js.map +1 -1
- package/docs/rules/no-forbidden-lifecycle-scripts.md +1 -1
- package/docs/rules/no-restricted-imports.md +9 -0
- package/package.json +6 -5
- package/src/plugin.ts +0 -26
- package/src/rules/index.ts +0 -26
- package/src/rules/no-restricted-imports.test.ts +63 -0
- package/src/rules/no-restricted-imports.ts +17 -4
- package/src/utils/file-utils.ts +27 -6
- package/tsconfig.json +1 -1
- package/dist/rules/cred-class-name-field-conventions.d.ts +0 -2
- package/dist/rules/cred-class-name-field-conventions.d.ts.map +0 -1
- package/dist/rules/cred-class-name-field-conventions.js +0 -89
- package/dist/rules/cred-class-name-field-conventions.js.map +0 -1
- package/dist/rules/cred-class-name-suffix.d.ts +0 -2
- package/dist/rules/cred-class-name-suffix.d.ts.map +0 -1
- package/dist/rules/cred-class-name-suffix.js +0 -53
- package/dist/rules/cred-class-name-suffix.js.map +0 -1
- package/dist/rules/cred-filename-against-convention.d.ts +0 -2
- package/dist/rules/cred-filename-against-convention.d.ts.map +0 -1
- package/dist/rules/cred-filename-against-convention.js +0 -42
- package/dist/rules/cred-filename-against-convention.js.map +0 -1
- package/dist/rules/no-asterisk-in-option-names.d.ts +0 -2
- package/dist/rules/no-asterisk-in-option-names.d.ts.map +0 -1
- package/dist/rules/no-asterisk-in-option-names.js +0 -90
- package/dist/rules/no-asterisk-in-option-names.js.map +0 -1
- package/dist/rules/no-duplicate-param-options.d.ts +0 -2
- package/dist/rules/no-duplicate-param-options.d.ts.map +0 -1
- package/dist/rules/no-duplicate-param-options.js +0 -97
- package/dist/rules/no-duplicate-param-options.js.map +0 -1
- package/dist/rules/node-class-description-name-camelcase.d.ts +0 -2
- package/dist/rules/node-class-description-name-camelcase.d.ts.map +0 -1
- package/dist/rules/node-class-description-name-camelcase.js +0 -92
- package/dist/rules/node-class-description-name-camelcase.js.map +0 -1
- package/dist/rules/node-filename-against-convention.d.ts +0 -2
- package/dist/rules/node-filename-against-convention.d.ts.map +0 -1
- package/dist/rules/node-filename-against-convention.js +0 -61
- package/dist/rules/node-filename-against-convention.js.map +0 -1
- package/dist/rules/node-operation-error-itemindex.d.ts +0 -12
- package/dist/rules/node-operation-error-itemindex.d.ts.map +0 -1
- package/dist/rules/node-operation-error-itemindex.js +0 -184
- package/dist/rules/node-operation-error-itemindex.js.map +0 -1
- package/dist/rules/options-sorted-alphabetically.d.ts +0 -2
- package/dist/rules/options-sorted-alphabetically.d.ts.map +0 -1
- package/dist/rules/options-sorted-alphabetically.js +0 -95
- package/dist/rules/options-sorted-alphabetically.js.map +0 -1
- package/dist/rules/require-community-node-keyword.d.ts +0 -2
- package/dist/rules/require-community-node-keyword.d.ts.map +0 -1
- package/dist/rules/require-community-node-keyword.js +0 -65
- package/dist/rules/require-community-node-keyword.js.map +0 -1
- package/dist/rules/require-continue-on-fail.d.ts +0 -2
- package/dist/rules/require-continue-on-fail.d.ts.map +0 -1
- package/dist/rules/require-continue-on-fail.js +0 -76
- package/dist/rules/require-continue-on-fail.js.map +0 -1
- package/dist/rules/require-mit-license.d.ts +0 -2
- package/dist/rules/require-mit-license.d.ts.map +0 -1
- package/dist/rules/require-mit-license.js +0 -57
- package/dist/rules/require-mit-license.js.map +0 -1
- package/dist/rules/require-param-default.d.ts +0 -15
- package/dist/rules/require-param-default.d.ts.map +0 -1
- package/dist/rules/require-param-default.js +0 -75
- package/dist/rules/require-param-default.js.map +0 -1
- package/docs/rules/cred-class-name-field-conventions.md +0 -38
- package/docs/rules/cred-class-name-suffix.md +0 -46
- package/docs/rules/cred-filename-against-convention.md +0 -42
- package/docs/rules/no-asterisk-in-option-names.md +0 -61
- package/docs/rules/no-duplicate-param-options.md +0 -46
- package/docs/rules/node-class-description-name-camelcase.md +0 -41
- package/docs/rules/node-filename-against-convention.md +0 -50
- package/docs/rules/node-operation-error-itemindex.md +0 -81
- package/docs/rules/options-sorted-alphabetically.md +0 -63
- package/docs/rules/require-community-node-keyword.md +0 -45
- package/docs/rules/require-continue-on-fail.md +0 -56
- package/docs/rules/require-mit-license.md +0 -38
- package/docs/rules/require-param-default.md +0 -60
- package/src/rules/cred-class-name-field-conventions.test.ts +0 -121
- package/src/rules/cred-class-name-field-conventions.ts +0 -102
- package/src/rules/cred-class-name-suffix.test.ts +0 -74
- package/src/rules/cred-class-name-suffix.ts +0 -57
- package/src/rules/cred-filename-against-convention.test.ts +0 -72
- package/src/rules/cred-filename-against-convention.ts +0 -48
- package/src/rules/no-asterisk-in-option-names.test.ts +0 -185
- package/src/rules/no-asterisk-in-option-names.ts +0 -111
- package/src/rules/no-duplicate-param-options.test.ts +0 -125
- package/src/rules/no-duplicate-param-options.ts +0 -125
- package/src/rules/node-class-description-name-camelcase.test.ts +0 -121
- package/src/rules/node-class-description-name-camelcase.ts +0 -114
- package/src/rules/node-filename-against-convention.test.ts +0 -115
- package/src/rules/node-filename-against-convention.ts +0 -76
- package/src/rules/node-operation-error-itemindex.test.ts +0 -280
- package/src/rules/node-operation-error-itemindex.ts +0 -223
- package/src/rules/options-sorted-alphabetically.test.ts +0 -468
- package/src/rules/options-sorted-alphabetically.ts +0 -129
- package/src/rules/require-community-node-keyword.test.ts +0 -82
- package/src/rules/require-community-node-keyword.ts +0 -78
- package/src/rules/require-continue-on-fail.test.ts +0 -129
- package/src/rules/require-continue-on-fail.ts +0 -88
- package/src/rules/require-mit-license.test.ts +0 -67
- package/src/rules/require-mit-license.ts +0 -65
- package/src/rules/require-param-default.test.ts +0 -227
- package/src/rules/require-param-default.ts +0 -89
|
@@ -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
|
-
});
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import * as path from 'node:path';
|
|
2
|
-
|
|
3
|
-
import { isCredentialTypeClass, isFileType, createRule } from '../utils/index.js';
|
|
4
|
-
|
|
5
|
-
export const CredFilenameAgainstConventionRule = createRule({
|
|
6
|
-
name: 'cred-filename-against-convention',
|
|
7
|
-
meta: {
|
|
8
|
-
type: 'problem',
|
|
9
|
-
docs: {
|
|
10
|
-
description: 'Credential filename must match the credential class name',
|
|
11
|
-
},
|
|
12
|
-
messages: {
|
|
13
|
-
renameFile:
|
|
14
|
-
'Credential filename must match the class name "{{className}}". Rename file to "{{expected}}".',
|
|
15
|
-
},
|
|
16
|
-
schema: [],
|
|
17
|
-
},
|
|
18
|
-
defaultOptions: [],
|
|
19
|
-
create(context) {
|
|
20
|
-
if (!isFileType(context.filename, '.credentials.ts')) {
|
|
21
|
-
return {};
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return {
|
|
25
|
-
ClassDeclaration(node) {
|
|
26
|
-
if (!isCredentialTypeClass(node)) {
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const classNameNode = node.id;
|
|
31
|
-
if (!classNameNode) {
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const className = classNameNode.name;
|
|
36
|
-
const actualBaseName = path.basename(context.filename, '.credentials.ts');
|
|
37
|
-
|
|
38
|
-
if (actualBaseName !== className) {
|
|
39
|
-
context.report({
|
|
40
|
-
node: classNameNode,
|
|
41
|
-
messageId: 'renameFile',
|
|
42
|
-
data: { className, expected: `${className}.credentials.ts` },
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
},
|
|
48
|
-
});
|
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
import { RuleTester } from '@typescript-eslint/rule-tester';
|
|
2
|
-
|
|
3
|
-
import { NoAsteriskInOptionNamesRule } from './no-asterisk-in-option-names.js';
|
|
4
|
-
|
|
5
|
-
const ruleTester = new RuleTester();
|
|
6
|
-
|
|
7
|
-
function createNodeCode(body: string): string {
|
|
8
|
-
return `
|
|
9
|
-
import type { INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
10
|
-
|
|
11
|
-
export class TestNode implements INodeType {
|
|
12
|
-
description: INodeTypeDescription = {
|
|
13
|
-
${body}
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
`;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
ruleTester.run('no-asterisk-in-option-names', NoAsteriskInOptionNamesRule, {
|
|
20
|
-
valid: [
|
|
21
|
-
{
|
|
22
|
-
name: 'class that does not implement INodeType',
|
|
23
|
-
filename: '/tmp/TestNode.node.ts',
|
|
24
|
-
code: `
|
|
25
|
-
export class NotANode {
|
|
26
|
-
description = {
|
|
27
|
-
properties: [
|
|
28
|
-
{ options: [{ name: '* All', value: 'all' }] },
|
|
29
|
-
],
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
`,
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
name: 'non .node.ts file is ignored',
|
|
36
|
-
filename: '/tmp/helper.ts',
|
|
37
|
-
code: `
|
|
38
|
-
export class TestNode {
|
|
39
|
-
description = {
|
|
40
|
-
properties: [
|
|
41
|
-
{ options: [{ name: '* All', value: 'all' }] },
|
|
42
|
-
],
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
`,
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
name: 'option names without asterisk',
|
|
49
|
-
filename: '/tmp/TestNode.node.ts',
|
|
50
|
-
code: createNodeCode(`
|
|
51
|
-
displayName: 'Test Node',
|
|
52
|
-
name: 'testNode',
|
|
53
|
-
properties: [
|
|
54
|
-
{
|
|
55
|
-
displayName: 'Operation',
|
|
56
|
-
name: 'operation',
|
|
57
|
-
type: 'options',
|
|
58
|
-
options: [
|
|
59
|
-
{ name: 'Create', value: 'create' },
|
|
60
|
-
{ name: '[All]', value: 'all' },
|
|
61
|
-
],
|
|
62
|
-
default: 'create',
|
|
63
|
-
},
|
|
64
|
-
],
|
|
65
|
-
`),
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
name: 'asterisk in displayName outside options is ignored',
|
|
69
|
-
filename: '/tmp/TestNode.node.ts',
|
|
70
|
-
code: createNodeCode(`
|
|
71
|
-
displayName: 'Test * Node',
|
|
72
|
-
name: 'testNode',
|
|
73
|
-
properties: [
|
|
74
|
-
{ displayName: 'Field *', name: 'field', type: 'string', default: '' },
|
|
75
|
-
],
|
|
76
|
-
`),
|
|
77
|
-
},
|
|
78
|
-
],
|
|
79
|
-
invalid: [
|
|
80
|
-
{
|
|
81
|
-
name: 'asterisk in option name within options array',
|
|
82
|
-
filename: '/tmp/TestNode.node.ts',
|
|
83
|
-
code: createNodeCode(`
|
|
84
|
-
displayName: 'Test Node',
|
|
85
|
-
name: 'testNode',
|
|
86
|
-
properties: [
|
|
87
|
-
{
|
|
88
|
-
displayName: 'Resource',
|
|
89
|
-
name: 'resource',
|
|
90
|
-
type: 'options',
|
|
91
|
-
options: [
|
|
92
|
-
{ name: '* All', value: 'all' },
|
|
93
|
-
{ name: 'User', value: 'user' },
|
|
94
|
-
],
|
|
95
|
-
default: 'all',
|
|
96
|
-
},
|
|
97
|
-
],
|
|
98
|
-
`),
|
|
99
|
-
errors: [
|
|
100
|
-
{
|
|
101
|
-
messageId: 'asteriskInOptionName',
|
|
102
|
-
data: { name: '* All' },
|
|
103
|
-
suggestions: [
|
|
104
|
-
{
|
|
105
|
-
messageId: 'replaceAsterisk',
|
|
106
|
-
output: createNodeCode(`
|
|
107
|
-
displayName: 'Test Node',
|
|
108
|
-
name: 'testNode',
|
|
109
|
-
properties: [
|
|
110
|
-
{
|
|
111
|
-
displayName: 'Resource',
|
|
112
|
-
name: 'resource',
|
|
113
|
-
type: 'options',
|
|
114
|
-
options: [
|
|
115
|
-
{ name: '[All] All', value: 'all' },
|
|
116
|
-
{ name: 'User', value: 'user' },
|
|
117
|
-
],
|
|
118
|
-
default: 'all',
|
|
119
|
-
},
|
|
120
|
-
],
|
|
121
|
-
`),
|
|
122
|
-
},
|
|
123
|
-
],
|
|
124
|
-
},
|
|
125
|
-
],
|
|
126
|
-
},
|
|
127
|
-
{
|
|
128
|
-
name: 'asterisk in nested collection options',
|
|
129
|
-
filename: '/tmp/TestNode.node.ts',
|
|
130
|
-
code: createNodeCode(`
|
|
131
|
-
displayName: 'Test Node',
|
|
132
|
-
name: 'testNode',
|
|
133
|
-
properties: [
|
|
134
|
-
{
|
|
135
|
-
displayName: 'Filters',
|
|
136
|
-
name: 'filters',
|
|
137
|
-
type: 'collection',
|
|
138
|
-
default: {},
|
|
139
|
-
options: [
|
|
140
|
-
{
|
|
141
|
-
displayName: 'Status',
|
|
142
|
-
name: 'status',
|
|
143
|
-
type: 'options',
|
|
144
|
-
options: [{ name: '*', value: 'any' }],
|
|
145
|
-
default: 'any',
|
|
146
|
-
},
|
|
147
|
-
],
|
|
148
|
-
},
|
|
149
|
-
],
|
|
150
|
-
`),
|
|
151
|
-
errors: [
|
|
152
|
-
{
|
|
153
|
-
messageId: 'asteriskInOptionName',
|
|
154
|
-
data: { name: '*' },
|
|
155
|
-
suggestions: [
|
|
156
|
-
{
|
|
157
|
-
messageId: 'replaceAsterisk',
|
|
158
|
-
output: createNodeCode(`
|
|
159
|
-
displayName: 'Test Node',
|
|
160
|
-
name: 'testNode',
|
|
161
|
-
properties: [
|
|
162
|
-
{
|
|
163
|
-
displayName: 'Filters',
|
|
164
|
-
name: 'filters',
|
|
165
|
-
type: 'collection',
|
|
166
|
-
default: {},
|
|
167
|
-
options: [
|
|
168
|
-
{
|
|
169
|
-
displayName: 'Status',
|
|
170
|
-
name: 'status',
|
|
171
|
-
type: 'options',
|
|
172
|
-
options: [{ name: '[All]', value: 'any' }],
|
|
173
|
-
default: 'any',
|
|
174
|
-
},
|
|
175
|
-
],
|
|
176
|
-
},
|
|
177
|
-
],
|
|
178
|
-
`),
|
|
179
|
-
},
|
|
180
|
-
],
|
|
181
|
-
},
|
|
182
|
-
],
|
|
183
|
-
},
|
|
184
|
-
],
|
|
185
|
-
});
|