@n8n/eslint-plugin-community-nodes 0.17.0 → 0.19.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 (56) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/README.md +38 -35
  3. package/dist/plugin.d.ts +12 -0
  4. package/dist/plugin.d.ts.map +1 -1
  5. package/dist/plugin.js +4 -0
  6. package/dist/plugin.js.map +1 -1
  7. package/dist/rules/cred-class-name-field-conventions.d.ts +2 -0
  8. package/dist/rules/cred-class-name-field-conventions.d.ts.map +1 -0
  9. package/dist/rules/cred-class-name-field-conventions.js +89 -0
  10. package/dist/rules/cred-class-name-field-conventions.js.map +1 -0
  11. package/dist/rules/credential-password-field.js +1 -1
  12. package/dist/rules/index.d.ts +3 -1
  13. package/dist/rules/index.d.ts.map +1 -1
  14. package/dist/rules/index.js +4 -0
  15. package/dist/rules/index.js.map +1 -1
  16. package/dist/rules/no-credential-reuse.js +1 -1
  17. package/dist/rules/no-restricted-globals.js +1 -1
  18. package/dist/rules/no-template-placeholders.d.ts.map +1 -1
  19. package/dist/rules/no-template-placeholders.js +3 -1
  20. package/dist/rules/no-template-placeholders.js.map +1 -1
  21. package/dist/rules/node-usable-as-tool.js +1 -1
  22. package/dist/rules/package-name-convention.d.ts +1 -1
  23. package/dist/rules/package-name-convention.d.ts.map +1 -1
  24. package/dist/rules/package-name-convention.js +19 -0
  25. package/dist/rules/package-name-convention.js.map +1 -1
  26. package/dist/rules/require-node-api-error.d.ts +2 -1
  27. package/dist/rules/require-node-api-error.d.ts.map +1 -1
  28. package/dist/rules/require-node-api-error.js +2 -3
  29. package/dist/rules/require-node-api-error.js.map +1 -1
  30. package/dist/rules/valid-credential-references.js +1 -1
  31. package/dist/rules/valid-description.d.ts +2 -0
  32. package/dist/rules/valid-description.d.ts.map +1 -0
  33. package/dist/rules/valid-description.js +48 -0
  34. package/dist/rules/valid-description.js.map +1 -0
  35. package/docs/rules/cred-class-name-field-conventions.md +38 -0
  36. package/docs/rules/no-builder-hint-leakage.md +38 -0
  37. package/docs/rules/package-name-convention.md +14 -0
  38. package/docs/rules/valid-description.md +37 -0
  39. package/package.json +5 -4
  40. package/src/plugin.ts +4 -0
  41. package/src/rules/cred-class-name-field-conventions.test.ts +121 -0
  42. package/src/rules/cred-class-name-field-conventions.ts +102 -0
  43. package/src/rules/credential-password-field.ts +1 -1
  44. package/src/rules/index.ts +4 -0
  45. package/src/rules/no-credential-reuse.ts +1 -1
  46. package/src/rules/no-restricted-globals.ts +1 -1
  47. package/src/rules/no-template-placeholders.test.ts +18 -0
  48. package/src/rules/no-template-placeholders.ts +3 -1
  49. package/src/rules/node-usable-as-tool.ts +1 -1
  50. package/src/rules/package-name-convention.test.ts +32 -5
  51. package/src/rules/package-name-convention.ts +23 -0
  52. package/src/rules/require-node-api-error.ts +4 -3
  53. package/src/rules/valid-credential-references.ts +1 -1
  54. package/src/rules/valid-description.test.ts +56 -0
  55. package/src/rules/valid-description.ts +57 -0
  56. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"package-name-convention.js","sourceRoot":"","sources":["../../src/rules/package-name-convention.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"package-name-convention.js","sourceRoot":"","sources":["../../src/rules/package-name-convention.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,3 @@
1
- export declare const RequireNodeApiErrorRule: import("@typescript-eslint/utils/ts-eslint").RuleModule<"useNodeApiError" | "useNodeApiErrorInsteadOfGeneric", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
1
+ import { TSESLint } from '@typescript-eslint/utils';
2
+ export declare const RequireNodeApiErrorRule: TSESLint.RuleModule<"useNodeApiError" | "useNodeApiErrorInsteadOfGeneric", [], unknown, TSESLint.RuleListener>;
2
3
  //# sourceMappingURL=require-node-api-error.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"require-node-api-error.d.ts","sourceRoot":"","sources":["../../src/rules/require-node-api-error.ts"],"names":[],"mappings":"AA4BA,eAAO,MAAM,uBAAuB,wLA6DlC,CAAC"}
1
+ {"version":3,"file":"require-node-api-error.d.ts","sourceRoot":"","sources":["../../src/rules/require-node-api-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,QAAQ,EAAiB,MAAM,0BAA0B,CAAC;AA2BnF,eAAO,MAAM,uBAAuB,gHA+DlC,CAAC"}
@@ -1,5 +1,4 @@
1
- import { DefinitionType } from '@typescript-eslint/scope-manager';
2
- import { AST_NODE_TYPES } from '@typescript-eslint/utils';
1
+ import { AST_NODE_TYPES, TSESLint } from '@typescript-eslint/utils';
3
2
  import { isFileType } from '../utils/index.js';
4
3
  import { createRule } from '../utils/rule-creator.js';
5
4
  const ALLOWED_ERROR_CLASSES = new Set(['NodeApiError', 'NodeOperationError']);
@@ -56,7 +55,7 @@ export const RequireNodeApiErrorRule = createRule({
56
55
  if (argument.type === AST_NODE_TYPES.Identifier) {
57
56
  const scope = context.sourceCode.getScope(node);
58
57
  const ref = scope.references.find((r) => r.identifier === argument);
59
- const isCatchParam = ref?.resolved?.defs.some((def) => def.type === DefinitionType.CatchClause) ?? false;
58
+ const isCatchParam = ref?.resolved?.defs.some((def) => def.type === TSESLint.Scope.DefinitionType.CatchClause) ?? false;
60
59
  if (isCatchParam) {
61
60
  context.report({ node, messageId: 'useNodeApiError' });
62
61
  }
@@ -1 +1 @@
1
- {"version":3,"file":"require-node-api-error.js","sourceRoot":"","sources":["../../src/rules/require-node-api-error.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"require-node-api-error.js","sourceRoot":"","sources":["../../src/rules/require-node-api-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAiB,MAAM,0BAA0B,CAAC;AAEnF,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC,CAAC,cAAc,EAAE,oBAAoB,CAAC,CAAC,CAAC;AAE9E,SAAS,kBAAkB,CAAC,QAA6B;IACxD,IAAI,QAAQ,CAAC,IAAI,KAAK,cAAc,CAAC,aAAa,EAAE,CAAC;QACpD,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,cAAc,CAAC,UAAU,EAAE,CAAC;YACxD,OAAO,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC;QAC7B,CAAC;IACF,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,SAAS,mBAAmB,CAAC,IAAmB;IAC/C,IAAI,OAAO,GAA8B,IAAI,CAAC,MAAM,CAAC;IACrD,OAAO,OAAO,EAAE,CAAC;QAChB,IAAI,OAAO,CAAC,IAAI,KAAK,cAAc,CAAC,WAAW,EAAE,CAAC;YACjD,OAAO,IAAI,CAAC;QACb,CAAC;QACD,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAC1B,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,MAAM,CAAC,MAAM,uBAAuB,GAAG,UAAU,CAAC;IACjD,IAAI,EAAE,wBAAwB;IAC9B,IAAI,EAAE;QACL,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACL,WAAW,EACV,iFAAiF;gBACjF,6CAA6C;SAC9C;QACD,QAAQ,EAAE;YACT,eAAe,EACd,gFAAgF;gBAChF,wEAAwE;YACzE,+BAA+B,EAC9B,4EAA4E;gBAC5E,wEAAwE;SACzE;QACD,MAAM,EAAE,EAAE;KACV;IACD,cAAc,EAAE,EAAE;IAClB,MAAM,CAAC,OAAO;QACb,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QAC5D,MAAM,YAAY,GACjB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC;YAChC,CAAC,UAAU;YACX,CAAC,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC;QAElD,IAAI,CAAC,UAAU,IAAI,CAAC,YAAY,EAAE,CAAC;YAClC,OAAO,EAAE,CAAC;QACX,CAAC;QAED,OAAO;YACN,cAAc,CAAC,IAAI;gBAClB,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC;oBAAE,OAAO;gBACvC,IAAI,CAAC,IAAI,CAAC,QAAQ;oBAAE,OAAO;gBAE3B,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;gBAE1B,IAAI,QAAQ,CAAC,IAAI,KAAK,cAAc,CAAC,UAAU,EAAE,CAAC;oBACjD,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;oBAChD,MAAM,GAAG,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC;oBACpE,MAAM,YAAY,GACjB,GAAG,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,CACvB,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,WAAW,CAC/D,IAAI,KAAK,CAAC;oBAEZ,IAAI,YAAY,EAAE,CAAC;wBAClB,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAAC,CAAC;oBACxD,CAAC;oBACD,OAAO;gBACR,CAAC;gBAED,MAAM,UAAU,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;gBAChD,IAAI,UAAU,KAAK,IAAI,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;oBACnE,OAAO,CAAC,MAAM,CAAC;wBACd,IAAI;wBACJ,SAAS,EAAE,iCAAiC;wBAC5C,IAAI,EAAE,EAAE,UAAU,EAAE,UAAU,EAAE;qBAChC,CAAC,CAAC;gBACJ,CAAC;YACF,CAAC;SACD,CAAC;IACH,CAAC;CACD,CAAC,CAAC"}
@@ -1,4 +1,4 @@
1
- import { TSESTree } from '@typescript-eslint/types';
1
+ import { TSESTree } from '@typescript-eslint/utils';
2
2
  import { isNodeTypeClass, findClassProperty, findArrayLiteralProperty, extractCredentialNameFromArray, findPackageJson, readPackageJsonCredentials, isFileType, findSimilarStrings, createRule, } from '../utils/index.js';
3
3
  export const ValidCredentialReferencesRule = createRule({
4
4
  name: 'valid-credential-references',
@@ -0,0 +1,2 @@
1
+ export declare const ValidDescriptionRule: import("@typescript-eslint/utils/ts-eslint").RuleModule<"missingDescription" | "emptyDescription", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
2
+ //# sourceMappingURL=valid-description.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"valid-description.d.ts","sourceRoot":"","sources":["../../src/rules/valid-description.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,oBAAoB,4KAmD/B,CAAC"}
@@ -0,0 +1,48 @@
1
+ import { AST_NODE_TYPES } from '@typescript-eslint/utils';
2
+ import { createRule, findJsonProperty } from '../utils/index.js';
3
+ export const ValidDescriptionRule = createRule({
4
+ name: 'valid-description',
5
+ meta: {
6
+ type: 'problem',
7
+ docs: {
8
+ description: 'Require a non-empty "description" field in community node package.json',
9
+ },
10
+ messages: {
11
+ missingDescription: 'The package.json must have a "description" field describing what the community node package does.',
12
+ emptyDescription: 'The "description" field must be a non-empty string. Replace the placeholder with a real description of your community node package.',
13
+ },
14
+ schema: [],
15
+ },
16
+ defaultOptions: [],
17
+ create(context) {
18
+ if (!context.filename.endsWith('package.json')) {
19
+ return {};
20
+ }
21
+ return {
22
+ ObjectExpression(node) {
23
+ if (node.parent?.type !== AST_NODE_TYPES.ExpressionStatement) {
24
+ return;
25
+ }
26
+ const descriptionProp = findJsonProperty(node, 'description');
27
+ if (!descriptionProp) {
28
+ context.report({
29
+ node,
30
+ messageId: 'missingDescription',
31
+ });
32
+ return;
33
+ }
34
+ if (descriptionProp.value.type !== AST_NODE_TYPES.Literal) {
35
+ return;
36
+ }
37
+ const value = descriptionProp.value.value;
38
+ if (typeof value !== 'string' || value === '') {
39
+ context.report({
40
+ node: descriptionProp,
41
+ messageId: 'emptyDescription',
42
+ });
43
+ }
44
+ },
45
+ };
46
+ },
47
+ });
48
+ //# sourceMappingURL=valid-description.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"valid-description.js","sourceRoot":"","sources":["../../src/rules/valid-description.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1D,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEjE,MAAM,CAAC,MAAM,oBAAoB,GAAG,UAAU,CAAC;IAC9C,IAAI,EAAE,mBAAmB;IACzB,IAAI,EAAE;QACL,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACL,WAAW,EAAE,wEAAwE;SACrF;QACD,QAAQ,EAAE;YACT,kBAAkB,EACjB,mGAAmG;YACpG,gBAAgB,EACf,qIAAqI;SACtI;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,eAAe,GAAG,gBAAgB,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;gBAE9D,IAAI,CAAC,eAAe,EAAE,CAAC;oBACtB,OAAO,CAAC,MAAM,CAAC;wBACd,IAAI;wBACJ,SAAS,EAAE,oBAAoB;qBAC/B,CAAC,CAAC;oBACH,OAAO;gBACR,CAAC;gBAED,IAAI,eAAe,CAAC,KAAK,CAAC,IAAI,KAAK,cAAc,CAAC,OAAO,EAAE,CAAC;oBAC3D,OAAO;gBACR,CAAC;gBAED,MAAM,KAAK,GAAG,eAAe,CAAC,KAAK,CAAC,KAAK,CAAC;gBAC1C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;oBAC/C,OAAO,CAAC,MAAM,CAAC;wBACd,IAAI,EAAE,eAAe;wBACrB,SAAS,EAAE,kBAAkB;qBAC7B,CAAC,CAAC;gBACJ,CAAC;YACF,CAAC;SACD,CAAC;IACH,CAAC;CACD,CAAC,CAAC"}
@@ -0,0 +1,38 @@
1
+ # Credential `name` field must end with `Api` and start with a lowercase letter (`@n8n/community-nodes/cred-class-name-field-conventions`)
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
+ The `name` field of a credential class (those implementing `ICredentialType` in `*.credentials.ts` files) is the internal identifier referenced by nodes and stored in the credential registry. n8n convention requires this identifier to:
12
+
13
+ - End with `Api` (e.g. `githubApi`), so credentials are easily recognisable across the codebase.
14
+ - Start with a lowercase letter (camelCase), since it is an identifier value rather than a class name.
15
+
16
+ Both checks are automatically fixable.
17
+
18
+ ## Examples
19
+
20
+ ### ❌ Incorrect
21
+
22
+ ```typescript
23
+ export class GithubApi implements ICredentialType {
24
+ name = 'Github';
25
+ displayName = 'GitHub API';
26
+ properties: INodeProperties[] = [];
27
+ }
28
+ ```
29
+
30
+ ### ✅ Correct
31
+
32
+ ```typescript
33
+ export class GithubApi implements ICredentialType {
34
+ name = 'githubApi';
35
+ displayName = 'GitHub API';
36
+ properties: INodeProperties[] = [];
37
+ }
38
+ ```
@@ -0,0 +1,38 @@
1
+ # Disallow wire-format expression syntax (={{...}}) and NodeConnectionType string literals in builderHint texts and AI-builder prompts. Use expr() and SDK-canonical references instead (`@n8n/community-nodes/no-builder-hint-leakage`)
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
+ `builderHint` texts and AI-builder prompts are authored as human-facing guidance, but they sometimes leak n8n's internal wire format:
10
+
11
+ - Raw expression syntax such as `={{ $json.foo }}`, which only makes sense inside the execution engine.
12
+ - `NodeConnectionType` string literals such as `ai_languageModel` or `ai_tool`, which are structured connection identifiers rather than prose.
13
+
14
+ When these leak into hints and prompts, they confuse the builder experience and the AI assistant. Use the `expr()` SDK helper for expressions and the SDK-canonical reference helpers (e.g. `languageModel()`, `tool()`, `memory()`) instead.
15
+
16
+ ## Options
17
+
18
+ This rule accepts an options object:
19
+
20
+ - `scope` (`'builderHint' | 'all'`, default `'builderHint'`) — `builderHint` only scans string values inside `builderHint` property values. `all` scans every string in the file, intended for AI-builder prompt files.
21
+
22
+ ## Examples
23
+
24
+ ### ❌ Incorrect
25
+
26
+ ```typescript
27
+ const node = {
28
+ builderHint: 'Set the model with ={{ $json.model }} and connect an ai_languageModel.',
29
+ };
30
+ ```
31
+
32
+ ### ✅ Correct
33
+
34
+ ```typescript
35
+ const node = {
36
+ builderHint: 'Set the model with expr($json.model) and connect a languageModel().',
37
+ };
38
+ ```
@@ -10,6 +10,8 @@
10
10
 
11
11
  Validates that your package name follows the correct n8n community node naming convention. Package names must start with `n8n-nodes-` and can optionally be scoped.
12
12
 
13
+ The rule also requires a `name` field to be present and rejects the default placeholder (`n8n-nodes-<...>`) that ships with the node starter template, so packages are not published with a missing or unfilled name.
14
+
13
15
  ## Examples
14
16
 
15
17
  ### ❌ Incorrect
@@ -32,6 +34,18 @@ Validates that your package name follows the correct n8n community node naming c
32
34
  }
33
35
  ```
34
36
 
37
+ ```json
38
+ {
39
+ "name": "n8n-nodes-<...>"
40
+ }
41
+ ```
42
+
43
+ ```json
44
+ {
45
+ "version": "1.0.0"
46
+ }
47
+ ```
48
+
35
49
  ### ✅ Correct
36
50
 
37
51
  ```json
@@ -0,0 +1,37 @@
1
+ # Require a non-empty "description" field in community node package.json (`@n8n/community-nodes/valid-description`)
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
+ The `description` field in `package.json` is what users see when discovering your community node package on npm and inside n8n. It must be present and non-empty so that users can understand what the package does before installing it.
10
+
11
+ The `n8n-nodes-starter` template ships with `"description": ""`, which is a common oversight. This rule catches both a missing `description` key and the unchanged placeholder.
12
+
13
+ ## Examples
14
+
15
+ ### ❌ Incorrect
16
+
17
+ ```json
18
+ {
19
+ "name": "n8n-nodes-acme"
20
+ }
21
+ ```
22
+
23
+ ```json
24
+ {
25
+ "name": "n8n-nodes-acme",
26
+ "description": ""
27
+ }
28
+ ```
29
+
30
+ ### ✅ Correct
31
+
32
+ ```json
33
+ {
34
+ "name": "n8n-nodes-acme",
35
+ "description": "n8n community nodes for the Acme Corp API"
36
+ }
37
+ ```
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.17.0",
4
+ "version": "0.19.0",
5
5
  "main": "./dist/plugin.js",
6
6
  "types": "./dist/plugin.d.ts",
7
7
  "exports": {
@@ -11,6 +11,7 @@
11
11
  }
12
12
  },
13
13
  "dependencies": {
14
+ "@typescript-eslint/typescript-estree": "^8.35.0",
14
15
  "@typescript-eslint/utils": "^8.35.0",
15
16
  "fastest-levenshtein": "1.0.16"
16
17
  },
@@ -22,9 +23,9 @@
22
23
  "rimraf": "6.0.1",
23
24
  "typescript": "6.0.2",
24
25
  "vitest": "^4.1.1",
25
- "@n8n/vitest-config": "1.11.0",
26
26
  "@n8n/typescript-config": "1.4.0",
27
- "n8n-workflow": "2.22.0"
27
+ "@n8n/vitest-config": "1.13.0",
28
+ "n8n-workflow": "2.24.0"
28
29
  },
29
30
  "peerDependencies": {
30
31
  "eslint": "9.29.0",
@@ -59,7 +60,7 @@
59
60
  "dev": "pnpm watch",
60
61
  "format": "biome format --write .",
61
62
  "format:check": "biome ci .",
62
- "lint": "eslint src",
63
+ "lint": "eslint src && pnpm lint:docs",
63
64
  "lint:fix": "eslint src --fix",
64
65
  "lint:docs": "pnpm build && eslint-doc-generator --check",
65
66
  "test": "vitest run",
package/src/plugin.ts CHANGED
@@ -40,6 +40,7 @@ const configs = {
40
40
  '@n8n/community-nodes/resource-operation-pattern': 'warn',
41
41
  '@n8n/community-nodes/credential-documentation-url': 'error',
42
42
  '@n8n/community-nodes/cred-class-field-icon-missing': 'error',
43
+ '@n8n/community-nodes/cred-class-name-field-conventions': 'error',
43
44
  '@n8n/community-nodes/cred-class-name-suffix': 'error',
44
45
  '@n8n/community-nodes/cred-class-oauth2-naming': 'error',
45
46
  '@n8n/community-nodes/node-connection-type-literal': 'error',
@@ -51,6 +52,7 @@ const configs = {
51
52
  '@n8n/community-nodes/require-node-api-error': 'error',
52
53
  '@n8n/community-nodes/require-node-description-fields': 'error',
53
54
  '@n8n/community-nodes/valid-credential-references': 'error',
55
+ '@n8n/community-nodes/valid-description': 'error',
54
56
  '@n8n/community-nodes/valid-peer-dependencies': 'error',
55
57
  '@n8n/community-nodes/webhook-lifecycle-complete': 'error',
56
58
  },
@@ -79,6 +81,7 @@ const configs = {
79
81
  '@n8n/community-nodes/credential-documentation-url': 'error',
80
82
  '@n8n/community-nodes/resource-operation-pattern': 'warn',
81
83
  '@n8n/community-nodes/cred-class-field-icon-missing': 'error',
84
+ '@n8n/community-nodes/cred-class-name-field-conventions': 'error',
82
85
  '@n8n/community-nodes/cred-class-name-suffix': 'error',
83
86
  '@n8n/community-nodes/cred-class-oauth2-naming': 'error',
84
87
  '@n8n/community-nodes/node-connection-type-literal': 'error',
@@ -90,6 +93,7 @@ const configs = {
90
93
  '@n8n/community-nodes/require-node-api-error': 'error',
91
94
  '@n8n/community-nodes/require-node-description-fields': 'error',
92
95
  '@n8n/community-nodes/valid-credential-references': 'error',
96
+ '@n8n/community-nodes/valid-description': 'error',
93
97
  '@n8n/community-nodes/valid-peer-dependencies': 'error',
94
98
  '@n8n/community-nodes/webhook-lifecycle-complete': 'error',
95
99
  },
@@ -0,0 +1,121 @@
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
+ });
@@ -0,0 +1,102 @@
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,4 +1,4 @@
1
- import { TSESTree } from '@typescript-eslint/types';
1
+ import { TSESTree } from '@typescript-eslint/utils';
2
2
  import type { ReportFixFunction } from '@typescript-eslint/utils/ts-eslint';
3
3
 
4
4
  import {
@@ -2,6 +2,7 @@ import type { AnyRuleModule } from '@typescript-eslint/utils/ts-eslint';
2
2
 
3
3
  import { AiNodePackageJsonRule } from './ai-node-package-json.js';
4
4
  import { CredClassFieldIconMissingRule } from './cred-class-field-icon-missing.js';
5
+ import { CredClassNameFieldConventionsRule } from './cred-class-name-field-conventions.js';
5
6
  import { CredClassNameSuffixRule } from './cred-class-name-suffix.js';
6
7
  import { CredClassOAuth2NamingRule } from './cred-class-oauth2-naming.js';
7
8
  import { CredentialDocumentationUrlRule } from './credential-documentation-url.js';
@@ -32,6 +33,7 @@ import { RequireNodeApiErrorRule } from './require-node-api-error.js';
32
33
  import { RequireNodeDescriptionFieldsRule } from './require-node-description-fields.js';
33
34
  import { ResourceOperationPatternRule } from './resource-operation-pattern.js';
34
35
  import { ValidCredentialReferencesRule } from './valid-credential-references.js';
36
+ import { ValidDescriptionRule } from './valid-description.js';
35
37
  import { ValidPeerDependenciesRule } from './valid-peer-dependencies.js';
36
38
  import { WebhookLifecycleCompleteRule } from './webhook-lifecycle-complete.js';
37
39
 
@@ -56,6 +58,7 @@ export const rules = {
56
58
  'credential-documentation-url': CredentialDocumentationUrlRule,
57
59
  'node-class-description-icon-missing': NodeClassDescriptionIconMissingRule,
58
60
  'cred-class-field-icon-missing': CredClassFieldIconMissingRule,
61
+ 'cred-class-name-field-conventions': CredClassNameFieldConventionsRule,
59
62
  'cred-class-name-suffix': CredClassNameSuffixRule,
60
63
  'cred-class-oauth2-naming': CredClassOAuth2NamingRule,
61
64
  'node-connection-type-literal': NodeConnectionTypeLiteralRule,
@@ -68,6 +71,7 @@ export const rules = {
68
71
  'require-node-api-error': RequireNodeApiErrorRule,
69
72
  'require-node-description-fields': RequireNodeDescriptionFieldsRule,
70
73
  'valid-credential-references': ValidCredentialReferencesRule,
74
+ 'valid-description': ValidDescriptionRule,
71
75
  'valid-peer-dependencies': ValidPeerDependenciesRule,
72
76
  'webhook-lifecycle-complete': WebhookLifecycleCompleteRule,
73
77
  } satisfies Record<string, AnyRuleModule>;
@@ -1,4 +1,4 @@
1
- import { TSESTree } from '@typescript-eslint/types';
1
+ import { TSESTree } from '@typescript-eslint/utils';
2
2
  import type { ReportSuggestionArray } from '@typescript-eslint/utils/ts-eslint';
3
3
 
4
4
  import {
@@ -1,4 +1,4 @@
1
- import { TSESTree } from '@typescript-eslint/types';
1
+ import { TSESTree } from '@typescript-eslint/utils';
2
2
  import type { TSESLint } from '@typescript-eslint/utils';
3
3
 
4
4
  import { createRule } from '../utils/index.js';
@@ -37,6 +37,24 @@ ruleTester.run('no-template-placeholders', NoTemplatePlaceholdersRule, {
37
37
  filename: 'package.json',
38
38
  code: '{ "name": "n8n-nodes-example", "private": false, "engines": { "node": ">=18" } }',
39
39
  },
40
+ {
41
+ name: 'npm person string with email in author is not flagged',
42
+ filename: 'package.json',
43
+ code: '{ "author": "Jane Doe <jane@example.com>" }',
44
+ },
45
+ {
46
+ name: 'npm person string with email and url in author is not flagged',
47
+ filename: 'package.json',
48
+ code: '{ "author": "Jane Doe <jane@example.com> (https://example.com)" }',
49
+ },
50
+ {
51
+ name: 'npm person strings in contributors and maintainers are not flagged',
52
+ filename: 'package.json',
53
+ code: `{
54
+ "contributors": ["Jane Doe <jane@example.com> (https://example.com)"],
55
+ "maintainers": ["John Roe <john.roe+n8n@example.org>"]
56
+ }`,
57
+ },
40
58
  ],
41
59
  invalid: [
42
60
  {
@@ -3,7 +3,9 @@ import { AST_NODE_TYPES } from '@typescript-eslint/utils';
3
3
 
4
4
  import { createRule } from '../utils/index.js';
5
5
 
6
- const ANGLE_PLACEHOLDER = /<[^<>\n]+?>/;
6
+ // Excludes `@` so that emails inside npm "person" strings
7
+ // (e.g. `"author": "Jane Doe <jane@example.com>"`) are not flagged.
8
+ const ANGLE_PLACEHOLDER = /<[^<>\n@]+?>/;
7
9
  const MUSTACHE_PLACEHOLDER = /\{\{[^{}\n]+?\}\}/;
8
10
 
9
11
  function findPlaceholder(value: string): { pattern: string; type: 'angle' | 'mustache' } | null {