new-rule

star 1.2k

Implement a new SonarJS rule from scratch. Use when creating a new rule, scaffolding rule files, or understanding the full rule implementation workflow.

SonarSource By SonarSource schedule Updated 3/26/2026

name: new-rule description: Implement a new SonarJS rule from scratch. Use when creating a new rule, scaffolding rule files, or understanding the full rule implementation workflow. disable-model-invocation: true

Overview

New rules follow the pattern: RSPEC description → scaffold → implement → test → ruling.

Step 1: Scaffold the Rule

npm run new-rule

This interactive script generates in packages/analysis/src/jsts/rules/SXXXX/:

  • index.ts — rule export
  • rule.ts — ESLint rule implementation (skeleton)
  • cb.fixture.js — empty comment-based test fixture
  • cb.test.js — test launcher

It also auto-generates (not tracked by git):

  • Java check class SXXXX.java
  • Updates rules/rules.ts and rules/plugin-rules.ts
  • Updates AllRules.java

Step 2: Configure the Java Check Class

In the generated Java class, verify:

  • @JavaScriptRule and/or @TypeScriptRule annotations match target languages
  • If rule has options, override configurations() method (see /rule-options skill)
  • If rule targets test files, extend TestFileCheck instead of Check

Step 3: Implement the Rule

File Structure

File Purpose
rule.ts ESLint rule implementation
meta.ts Manual metadata: implementation, eslintId, schema, re-exports fields
config.ts Option definitions with fields array (if rule has options)
generated-meta.ts Auto-generated from RSPEC — do not edit

Rule Template

import { generateMeta } from '../helpers/index.js';
import { meta } from './meta.js';

const messages = {
  errorKey: 'Error message to display',
};

export const rule: Rule.RuleModule = {
  meta: generateMeta(meta, { messages }),
  create(context: Rule.RuleContext) {
    return {
      Identifier(node: estree.Identifier) {
        if (/* violation detected */) {
          context.report({ messageId: 'errorKey', node });
        }
      },
    };
  },
};

Be Conservative

Never report when uncertain. False positives are worse than missed detections.

const services = context.sourceCode.parserServices;
if (!isRequiredParserServices(services)) {
  return; // No type info — don't report
}

When in doubt: skip.

Step 4: Check Shared Helpers

Before writing any utility code, check packages/analysis/src/jsts/rules/helpers/:

File Contains
ast.ts isFunctionNode, isIdentifier, hasTypePredicateReturn, AST traversal
module.ts isESModule, getImportDeclarations, getFullyQualifiedName
package-jsons/dependencies.ts getDependencies, getReactVersion
index.ts Re-exports all helpers — check here first

If a new utility would benefit multiple rules, add it to the appropriate helper file.

Step 5: Generate Metadata

After setting up meta.ts and optionally config.ts:

npm run generate-meta

This creates/updates generated-meta.ts with defaultOptions, sonarKey, scope, languages.

Step 6: Write Tests

See /test-rule skill for full testing documentation.

Quick start — write cb.fixture.js:

someCleanCode(); // no issue raised

someFaultyCode(); // Noncompliant {{message}}
//  ^^^^^^^^^^

Run:

npx tsx --test packages/analysis/src/jsts/rules/S1234/**/*.test.ts

Step 7: Run Ruling

See /ruling skill. Required before merging new or modified rules.

Rule Implementation Patterns

Wrapping an ESLint Rule (decorated)

// meta.ts
export const implementation = 'decorated';
export const eslintId = 'no-magic-numbers';
export const externalRules = [
  { externalPlugin: 'typescript-eslint', externalRule: 'no-magic-numbers' },
];
export * from './config.js';

Original Rule

// meta.ts
export const implementation = 'original';
export const eslintId = 'function-name';
export * from './config.js';
import type { JSONSchema4 } from '@typescript-eslint/utils/json-schema';
export const schema = {
  type: 'array',
  items: [{ type: 'object', properties: { format: { type: 'string' } } }],
} as const satisfies JSONSchema4;

RSPEC Tags

When creating the RSPEC PR:

  • Tag type-dependent if the rule uses TypeScript type information
  • Add dependencies field if rule requires a specific import (e.g., 'react', 'jest')
  • Add compatibleLanguages: ['js', 'ts'] as appropriate

References

Install via CLI
npx skills add https://github.com/SonarSource/SonarJS --skill new-rule
Repository Details
star Stars 1,243
call_split Forks 193
navigation Branch main
article Path SKILL.md
More from Creator