All skills
n8n-io avatar

/create-community-node-lint-rule

@b4d898e official
by n8n - Workflow Automationn8n-io/n8n206k stars
60,960

Create new ESLint rules for the @n8n/eslint-plugin-community-nodes package. Use when adding a lint rule, creating a community node lint, or working on eslint-plugin-community-nodes. Guides rule implementation, tests, docs, and plugin registration.

  • 2 files
  • 9.9 KB
  • Updated 5 months ago
  • GitHub

Use this Skill: https://skilld.dev/gh/n8n-io/n8n/create-community-node-lint-rule

This session only. Nothing lands on disk.

SKILL.md

≈70 tokens always: the name and description. ≈1.5k when used: this file. ≈922 more on demand in 1 file.

Create Community Node Lint Rule

Guide for adding new ESLint rules to packages/@n8n/eslint-plugin-community-nodes/.

All paths below are relative to packages/@n8n/eslint-plugin-community-nodes/.

Step 1: Understand the Rule

Before writing code, clarify:

  • What does the rule detect? (missing property, wrong pattern, bad value)
  • Where does it apply? (.node.ts files, credential classes, both)
  • Severity: error (must fix) or warn (should fix)?
  • Fixable? Can it be auto-fixed safely, or only suggest?
  • Scope: Both recommended configs, or exclude from recommendedWithoutN8nCloudSupport?

Step 2: Implement the Rule

Create src/rules/<rule-name>.ts:

import { AST_NODE_TYPES } from '@typescript-eslint/utils';

import {
  isNodeTypeClass,       // or isCredentialTypeClass
  findClassProperty,
  findObjectProperty,
  createRule,
} from '../utils/index.js';

export const YourRuleNameRule = createRule({
  name: 'rule-name',
  meta: {
    type: 'problem',  // or 'suggestion'
    docs: {
      description: 'One-line description of what the rule enforces',
    },
    messages: {
      messageId: 'Human-readable message. Use {{placeholder}} for dynamic data.',
    },
    fixable: 'code',     // omit if not auto-fixable
    hasSuggestions: true, // omit if no suggestions
    schema: [],           // add options schema if configurable
  },
  defaultOptions: [],
  create(context) {
    return {
      ClassDeclaration(node) {
        if (!isNodeTypeClass(node)) return;

        const descriptionProperty = findClassProperty(node, 'description');
        if (!descriptionProperty) return;

        const descriptionValue = descriptionProperty.value;
        if (descriptionValue?.type !== AST_NODE_TYPES.ObjectExpression) return;

        // Rule logic here — use findObjectProperty(), getLiteralValue(), etc.

        context.report({
          node: targetNode,
          messageId: 'messageId',
          data: { /* template vars */ },
          fix(fixer) {
            return fixer.replaceText(targetNode, 'replacement');
          },
        });
      },
    };
  },
});

Naming: Export as PascalCaseRule (e.g. MissingPairedItemRule). The name field is kebab-case.

Available AST helpers — see reference.md for the full catalog of ast-utils and file-utils exports.

Step 3: Write Tests

Create src/rules/<rule-name>.test.ts:

import { RuleTester } from '@typescript-eslint/rule-tester';

import { YourRuleNameRule } from './rule-name.js';

const ruleTester = new RuleTester();

// Helper to generate test code — keeps test cases readable
function createNodeCode(/* parameterize the varying parts */): string {
  return `
import type { INodeType, INodeTypeDescription } from 'n8n-workflow';

export class TestNode implements INodeType {
  description: INodeTypeDescription = {
    displayName: 'Test Node',
    name: 'testNode',
    group: ['input'],
    version: 1,
    description: 'A test node',
    defaults: { name: 'Test Node' },
    inputs: [],
    outputs: [],
    properties: [],
  };
}`;
}

ruleTester.run('rule-name', YourRuleNameRule, {
  valid: [
    { name: 'class that does not implement INodeType', code: '...' },
    { name: 'node with correct pattern', code: createNodeCode(/* correct */) },
  ],
  invalid: [
    {
      name: 'descriptive case name',
      code: createNodeCode(/* incorrect */),
      errors: [{ messageId: 'messageId', data: { /* expected template vars */ } }],
      output: createNodeCode(/* expected after fix */),  // or `output: null` if no fix
    },
  ],
});

Test guidelines:

  • Always test that non-INodeType classes are skipped (valid case)
  • Test both the error message and the fixed output for fixable rules
  • For rules with options, test each option combination
  • For rules using filesystem, mock with vi.mock('../utils/file-utils.js')
  • For suggestion-only rules, use errors: [{ messageId, suggestions: [...] }]

Step 4: Register the Rule

4a. Add to src/rules/index.ts

import { YourRuleNameRule } from './rule-name.js';

// Add to the rules object:
export const rules = {
  // ... existing rules
  'rule-name': YourRuleNameRule,
} satisfies Record<string, AnyRuleModule>;

4b. Add to src/plugin.ts configs

Add to both config objects (unless the rule depends on n8n cloud features):

'@n8n/community-nodes/rule-name': 'error',  // or 'warn'
  • Use error for rules that catch bugs or required patterns
  • Use warn for style/convention rules (like options-sorted-alphabetically)
  • If the rule uses no-restricted-globals or no-restricted-imports patterns, only add to recommended (not recommendedWithoutN8nCloudSupport)

Step 5: Write Documentation

Create docs/rules/<rule-name>.md:

# Description of what the rule does (`@n8n/community-nodes/rule-name`)

<!-- end auto-generated rule header -->

## Rule Details

Explain why this rule exists and what problem it prevents.

## Examples

### Incorrect

\`\`\`typescript
// code that triggers the rule
\`\`\`

### Correct

\`\`\`typescript
// code that passes the rule
\`\`\`

The header above <!-- end auto-generated rule header --> will be regenerated by pnpm build:docs. Write a reasonable first version — it gets overwritten.

Step 6: Verify

Run from packages/@n8n/eslint-plugin-community-nodes/:

pushd packages/@n8n/eslint-plugin-community-nodes
pnpm test <rule-name>.test.ts   # tests pass
pnpm typecheck                   # types are clean
pnpm build                       # compiles
pnpm build:docs                  # regenerates doc headers and README table
pnpm lint:docs                   # docs match schema
popd

Checklist

  • Rule file: src/rules/<rule-name>.ts
  • Test file: src/rules/<rule-name>.test.ts
  • Registered in src/rules/index.ts
  • Added to configs in src/plugin.ts
  • Doc file: docs/rules/<rule-name>.md
  • README table updated via pnpm build:docs
  • All verification commands pass

Source: SKILL.md on GitHub

No third-party reports yet.

Signed by skilld at b4d898e. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub yesterday.

Activeupdated 5 months ago
  • TypeScript
  • eslint
  • eslint-plugin
  • n8n
  • community-nodes
  • ast
  • linting
  • rule-creation

README badge

README badge for n8n-io/n8n/create-community-node-lint-rule

Implement new ESLint rules for the n8n community node linter plugin, including rule logic, tests, documentation, and plugin registration. Used when extending @n8n/eslint-plugin-community-nodes to enforce coding patterns in n8n node implementations.

Generated from the current SKILL.md.

Does this skill work with TypeScript ESLint rules?
Yes. The skill guides creating ESLint rules for the @n8n/eslint-plugin-community-nodes package using @typescript-eslint/utils and TypeScript AST utilities.
Can I make a rule that auto-fixes violations?
Yes. Rules can be marked `fixable: 'code'` in the meta object, and the `fix()` callback in `context.report()` provides the fixer methods.
What scope do community node lint rules typically target?
Rules apply to `.node.ts` files and credential classes that implement INodeType or ICredentialType. The skill provides AST helpers like `isNodeTypeClass()` and `isCredentialTypeClass()` to filter the right targets.
Do I need to add my rule to both ESLint configs?
Usually yes—add to both `recommended` and `recommendedWithoutN8nCloudSupport` unless your rule depends on n8n cloud features, in which case add only to `recommended`.
What testing framework does this skill use?
The skill uses @typescript-eslint/rule-tester for unit tests and includes patterns for mocking file utilities and testing both valid and invalid code cases.

Generated from the current SKILL.md. These answers refresh after source changes.