npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

eslint-plugin-apex

v0.1.1

Published

ESLint plugin for Salesforce Apex — ports PMD Apex rules to the ESLint ecosystem

Readme

eslint-plugin-apex

ESLint plugin for Salesforce Apex — inspired by apex-pmd, this plugin ports 57 ESLint rules that align with the PMD Apex rule set into the ESLint ecosystem so that teams working with Salesforce can use a single linting tool for both Apex and Lightning Web Components (LWC) side by side.

If you already use eslint-plugin-lwc for your LWC code, adding eslint-plugin-apex brings your Apex codebase into the same workflow — one eslint.config.js, one npm run lint command, one CI step.

The plugin is backed by the ANTLR4-based @apexdevtools/apex-parser for accurate, whitespace-aware parsing rather than regex heuristics.

Status: Early release. Parser coverage is solid for classes and triggers; advanced patterns (anonymous Apex, complex generics) are progressively improved.

Features

  • 57 rules across 7 categories (Best Practices, Code Style, Design, Documentation, Error Prone, Performance, Security), each with a Markdown page under docs/rules/
  • Custom ANTLR4-backed parser — no regex heuristics
  • ESLint flat config API (v9+)
  • Four ready-made shared configs: recommended, strict, security, performance
  • PMD converter — paste a PMD XML rule file and get an ESLint config snippet in the browser

Installation

npm install --save-dev eslint-plugin-apex

Requires ESLint v9+ and Node.js v18+.

Quick Start

// eslint.config.js
import apex from 'eslint-plugin-apex';

export default [apex.configs.recommended];

Available Configs

| Config | Severity | Description | | -------------------------- | ----------------------------------------------------- | ------------------------------ | | apex.configs.recommended | errors for problem rules, warnings for suggestion | Recommended set of rules | | apex.configs.strict | error for all | Every rule enabled as an error | | apex.configs.security | error | Security rules only | | apex.configs.performance | error | Performance rules only |

All configs automatically target **/*.cls, **/*.trigger, and **/*.apex files.

Manual Rule Configuration

// eslint.config.js
import apex from 'eslint-plugin-apex';

export default [
  {
    files: ['**/*.cls', '**/*.trigger', '**/*.apex'],
    plugins: { apex },
    languageOptions: apex.configs.recommended.languageOptions,
    rules: {
      'apex/security-no-soql-injection': 'error',
      'apex/perf-no-dml-in-loop': 'error',
      'apex/best-test-has-asserts': 'warn',
      // disable a rule
      'apex/doc-require-apexdoc': 'off',
    },
  },
];

Rule Reference

Default reflects apex.configs.recommended: problem + recommended: trueerror; suggestion + recommended: truewarn; recommended: falseoff.

Best Practices

| Rule | Description | PMD equivalent | Default | | --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | ------- | | apex/best-debug-use-logging-level | System.debug() calls should specify a LoggingLevel argument | DebugsShouldUseLoggingLevel | warn | | apex/best-no-future-annotation | Prefer Queueable over @Future — the future annotation has significant limitations | AvoidFutureAnnotation | off | | apex/best-no-global-modifier | Avoid 'global' class modifier — it permanently locks the public API in managed packages | AvoidGlobalModifier | warn | | apex/best-no-logic-in-trigger | Avoid placing business logic directly in triggers — delegate to handler classes | AvoidLogicInTrigger | warn | | apex/best-no-unused-local-variable | Detects local variables that are declared but never read | UnusedLocalVariable | off | | apex/best-queueable-needs-finalizer | Queueable classes should attach a Finalizer for error recovery | QueueableWithoutFinalizer (+ legacy QueueableShouldAttachFinalizer) | off | | apex/best-test-assertions-have-message | System.assert() calls should include a message parameter for clarity | ApexAssertionsShouldIncludeMessage | warn | | apex/best-test-has-asserts | Apex unit test classes should include at least one assertion | ApexUnitTestClassShouldHaveAsserts | warn | | apex/best-test-has-run-as | Test classes should include at least one System.runAs() call | ApexUnitTestClassShouldHaveRunAs | warn | | apex/best-test-method-annotation | Use @IsTest annotation instead of the deprecated 'testMethod' keyword | ApexUnitTestMethodShouldHaveIsTestAnnotation | warn | | apex/best-test-no-see-all-data | Avoid @isTest(seeAllData=true) as it exposes real org data to test modifications | ApexUnitTestShouldNotUseSeeAllDataTrue | error |

Code Style

| Rule | Description | PMD equivalent | Default | | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | apex/style-annotation-naming | Annotation names should use PascalCase | AnnotationsNamingConventions | warn | | apex/style-braces-for-for | Require braces around for loop bodies | ForLoopsMustUseBraces | warn | | apex/style-braces-for-if | Require braces around if/else statement bodies | IfStmtsMustUseBraces / IfElseStmtsMustUseBraces | warn | | apex/style-braces-for-while | Require braces around while loop bodies | WhileLoopsMustUseBraces | warn | | apex/style-fields-at-start | Field declarations should appear before method declarations | FieldDeclarationsShouldBeAtStart | warn | | apex/style-naming-conventions | Enforce configurable naming conventions for Apex declarations | ClassNamingConventions / MethodNamingConventions / FieldNamingConventions / LocalVariableNamingConventions / FormalParameterNamingConventions / PropertyNamingConventions | warn | | apex/style-one-declaration-per-line | Declare only one variable per statement | OneDeclarationPerLine | warn |

Design

| Rule | Description | PMD equivalent | Default | | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | -------------------------------------------------- | ------- | | apex/design-cognitive-complexity | Limit cognitive complexity of methods and classes | CognitiveComplexity | warn | | apex/design-cyclomatic-complexity | Limit cyclomatic complexity of methods and classes | CyclomaticComplexity / StdCyclomaticComplexity | warn | | apex/design-excessive-parameters | Flag methods with too many parameters | ExcessiveParameterList | warn | | apex/design-excessive-public-count | Flag classes with too many public methods or attributes | ExcessivePublicCount | warn | | apex/design-ncss-method-count | Limit the number of non-commenting source statements per method and class | NcssMethodCount / NcssCount / NcssTypeCount | warn | | apex/design-no-boolean-parameters | Avoid boolean parameters in public and global methods | AvoidBooleanMethodParameters | warn | | apex/design-no-deep-nesting | Avoid deeply nested if statements | AvoidDeeplyNestedIfStmts | warn | | apex/design-no-unused-method | Detect private methods that are never called within the class | UnusedMethod | off | | apex/design-too-many-fields | Flag classes with too many fields | TooManyFields | warn |

Documentation

| Rule | Description | PMD equivalent | Default | | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------ | -------------- | ------- | | apex/doc-require-apexdoc | Require ApexDoc comments on public and global classes, methods, and properties | ApexDoc | off |

Error Prone

| Rule | Description | PMD equivalent | Default | | --------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ------- | | apex/error-aura-enabled-getter-public | @AuraEnabled property getters must be public or global | InaccessibleAuraEnabledGetter (older docs sometimes mis-labeled this as AuraEnabledWithoutCatchBlock) | error | | apex/error-no-csrf-in-constructor | Disallow DML operations in constructors or class initializers | ApexCSRF | error | | apex/error-no-direct-trigger-map-access | Avoid direct index access to Trigger.new or Trigger.old — iterate instead | AvoidDirectAccessTriggerMap | error | | apex/error-no-empty-catch | Disallow empty catch blocks | EmptyCatchBlock | error | | apex/error-no-empty-if | Disallow empty if statement bodies | EmptyIfStmt | error | | apex/error-no-empty-try | Disallow empty try or finally blocks | EmptyTryOrFinallyBlock | error | | apex/error-no-empty-while | Disallow empty while loop bodies | EmptyWhileStmt | error | | apex/error-no-hardcoded-id | Avoid hardcoding Salesforce record IDs — they differ between environments | AvoidHardcodingId | error | | apex/error-no-method-name-as-class | Non-constructor methods should not share the name of the enclosing class | MethodWithSameNameAsEnclosingClass | error | | apex/error-no-nonexistent-annotation | Avoid annotations that do not exist in Apex | AvoidNonExistentAnnotations (+ legacy NonExistentCustomSettingOrMetadata) | error | | apex/error-no-stateful-db-result | Avoid storing Database result types as instance variables in Database.Stateful batch classes | AvoidStatefulDatabaseResult | off | | apex/error-no-type-shadow-namespace | Avoid declaring types with the same name as System or Schema namespace types | TypeShadowsBuiltInNamespace (+ legacy AvoidShadowingField) | off | | apex/error-override-both-equals-hashcode | If overriding equals(), also override hashCode(), and vice versa | OverrideBothEqualsAndHashcode | error | | apex/error-test-methods-in-test-class | @IsTest methods must reside in @IsTest annotated classes | TestMethodsMustBeInTestClasses | error |

Performance

| Rule | Description | PMD equivalent | Default | | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------- | ------- | | apex/perf-no-debug-statements | Remove or gate System.debug() calls in production code | AvoidDebugStatements | off | | apex/perf-no-dml-in-loop | Avoid DML operations, SOQL queries, and governor-limited calls inside loops | OperationWithLimitsInLoop (+ legacy AvoidDmlStatementsInLoops) | error | | apex/perf-no-eager-describe | Avoid calling Schema.describe*() methods inside loops — cache results instead | AvoidEagerDescribes (distinct from EagerlyLoadedDescribeSObjectResult) | error | | apex/perf-no-high-cost-in-loop | Avoid high-cost Apex calls inside loops | OperationWithHighCostInLoop (+ legacy AvoidSoqlInLoops / AvoidHighCostInLoopWithoutBulkification) | error | | apex/perf-no-non-restrictive-query | SOQL queries should include a WHERE clause to limit results | AvoidNonRestrictiveQueries (+ legacy WherelessSOQLQuery) | error |

Security

| Rule | Description | PMD equivalent | Default | | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | --------------------------- | ------- | | apex/security-crud-violation | DML operations and SOQL queries should include CRUD/FLS permission checks | ApexCRUDViolation | warn | | apex/security-no-dangerous-methods | Flag potentially dangerous Apex method calls | ApexDangerousMethods | warn | | apex/security-no-hardcoded-crypto | Avoid hardcoded cryptographic keys or IVs | ApexBadCrypto | error | | apex/security-no-insecure-endpoint | HTTP callout endpoints must use HTTPS | ApexInsecureEndpoint | error | | apex/security-no-open-redirect | Avoid constructing PageReference from user-controlled input | ApexOpenRedirect | error | | apex/security-no-soql-injection | Avoid SOQL injection — use bind variables or String.escapeSingleQuotes() | ApexSOQLInjection | error | | apex/security-no-xss-false-escape | Disabling HTML escaping (escape=false) can introduce XSS vulnerabilities | ApexXSSFromEscapeFalse | error | | apex/security-no-xss-from-url | URL parameter values must be sanitized before output to prevent XSS | ApexXSSFromURLParam | error | | apex/security-sharing-violations | Classes that access data should explicitly declare 'with sharing' or 'without sharing' | ApexSharingViolations | warn | | apex/security-use-named-credentials | Use Named Credentials instead of hardcoding authentication details in HTTP requests | ApexSuggestUsingNamedCred | warn |

PMD Converter

The PMD Converter is a browser-based tool that converts PMD Apex XML rule files into:

  1. An ESLint flat-config snippet with the correct rule IDs and severities
  2. A rule implementation skeleton (when the PMD rule is not yet in this plugin)

No data is sent anywhere — conversion happens entirely in the browser.

PMD rules in the reference mirror without an ESLint twin (yet)

The offline PMD rule list this project tracks includes ExcessiveClassLength, EmptyStatementBlock, EagerlyLoadedDescribeSObjectResult, and NcssConstructorCount. They are intentionally not mapped to an apex/* rule today; the converter emits a custom-… skeleton for them. See the per-rule notes for perf-no-eager-describe versus EagerlyLoadedDescribeSObjectResult, and design-ncss-method-count versus per-constructor NCSS.

Architecture

eslint-plugin-apex/
├── src/
│   ├── node-types.js       ← custom AST node type constants + VISITOR_KEYS
│   ├── ast-builder.js      ← ANTLR4 parse tree → custom ESLint AST
│   ├── apex-parser.js      ← parseForESLint() adapter
│   ├── index.js            ← plugin entry point, rule registry, shared configs
│   └── rules/
│       ├── best-practices/ ← 11 rules
│       ├── code-style/     ← 7 rules
│       ├── design/         ← 9 rules
│       ├── documentation/  ← 1 rule
│       ├── error-prone/    ← 14 rules
│       ├── performance/    ← 5 rules
│       └── security/       ← 10 rules
├── tests/
│   ├── parser.test.js      ← custom parser tests (node:test)
│   ├── rules.test.js       ← RuleTester tests for all rules
│   └── docs-coverage.test.js
└── docs/
    ├── index.html          ← GitHub Pages PMD converter
    └── rules/              ← one Markdown page per registered rule
        ├── best-practices/
        ├── code-style/
        ├── design/
        ├── documentation/
        ├── error-prone/
        ├── performance/
        └── security/

The parser uses @apexdevtools/apex-parser, an ANTLR4-based Apex parser, and wraps its concrete syntax tree into a flat, traversable AST with custom node types.

Contributing

Contributions welcome — especially:

  • Improved AST coverage for edge-case Apex constructs
  • More comprehensive rule implementations
  • Additional test cases

License

MIT — see LICENSE.