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-sfcc-a11y

v2.2.4

Published

SFCC adapter for eslint-plugin-html-a11y — adds ISML sanitizer and XML content-asset processor

Readme

eslint-plugin-sfcc-a11y

npm License: MIT Node.js ESLint

eslint-plugin-html-a11y adapter for enabling WCAG accessibility linting on Salesforce Commerce Cloud (SFCC) projects: adds the ISML sanitizer and XML content-asset processor so the same 25 WCAG rules work on .isml templates and XML content asset libraries.

Supports ESLint v8 and v9. The setup differs between the two, so please see Setup below.

Introduction

TODO

Installation

Requires Node.js ≥ 18, ESLint ≥ 8, and @html-eslint/parser ≥ 0.23:

npm install --save-dev eslint-plugin-sfcc-a11y @html-eslint/parser eslint

Setup

ESLint v9 — eslint.config.js (recommended)

import sfccA11y from 'eslint-plugin-sfcc-a11y';

export default [...sfccA11y.configs['flat/recommended']];

The flat/recommended config is self-contained: it wires up the ISML sanitizer, the XML processor, and @html-eslint/parser for the virtual files they produce. No additional configuration is needed.

ESLint v8 — .eslintrc.json

{
  "extends": ["plugin:sfcc-a11y/recommended-error"],
  "overrides": [
    { "files": ["**/*.isml"], "processor": "sfcc-a11y/isml-sanitizer" },
    { "files": ["**/__sanitized.html"], "parser": "@html-eslint/parser" }
  ]
}

The two overrides entries are required. ESLint v8 does not propagate processor or parser configuration from a shared config accessed via extends, so they must be declared in the consuming project's config. The extends entry registers the plugin, applies the ISML sentinel settings, and enables all 25 rules.

Why the difference? ESLint v9's flat config lets each array entry declare its own files, processor, and languageOptions.parser, making plugin configs fully self-contained. ESLint v8's legacy system merges only plugins, rules, settings, and env from shared configs, whilst overrides (which is where processor and parser are set) are not applied from extends.

Changing rule severity

// eslint.config.js (ESLint v9)
import sfccA11y from 'eslint-plugin-sfcc-a11y';

export default [
  ...sfccA11y.configs['flat/recommended'],
  {
    rules: {
      'sfcc-a11y/img-alt': 'error',   // or 2
      'sfcc-a11y/button-name': 'warn',  // or 1
      'sfcc-a11y/html-has-lang': 'off',  // or 0
    },
  },
];
// .eslintrc.json (ESLint v8)
{
  "extends": ["plugin:sfcc-a11y/recommended-error"],
  "overrides": [
    { "files": ["**/*.isml"], "processor": "sfcc-a11y/isml-sanitizer" },
    { "files": ["**/__sanitized.html"], "parser": "@html-eslint/parser" },
    {
      "files": ["**/__sanitized.html"],
      "rules": {
        "sfcc-a11y/img-alt": "error",  // or 2
        "sfcc-a11y/html-has-lang": "off"  // or 0
      }
    }
  ]
}

For available rules and their options, see the html-a11y rule catalogue.

All 25 rule names are listed under Rules below.

Available configs

| Config | ESLint | Severity | Rules | |---|---|---|---| | flat/recommended | v9 | warn | All 25 (Level A + AA) | | flat/recommended-a | v9 | warn | Level A only | | flat/recommended-error | v9 | error | All 25 (Level A + AA) | | recommended | v8 | warn | All 25 (Level A + AA) | | recommended-a | v8 | warn | Level A only | | recommended-error | v8 | error | All 25 (Level A + AA) |

ESLint v9

With flat config, linting ISML files from the command line just works out of the box:

eslint "cartridges/**/*.isml"

The flat/recommended config is self-contained and requires no extra flags.

ESLint v8 — three traps to avoid

Running eslint against ISML files from a script in an ESLint v8 project has several non-obvious failure modes that silently produce wrong results.

Trap 1 — eslint --ext .isml cartridges (silent zero violations)

ESLint v8 has an internal filterCodeBlock function that decides which virtual files produced by a processor are eligible for linting. When --ext .isml is passed, filterCodeBlock applies the same extension regex to code blocks and the virtual __sanitized.html file (extension .html) silently fails that test. ESLint calls the sanitizer, receives the sanitized HTML block, then immediately discards it. Zero violations, no error, no warning.

Trap 2 — eslint cartridges without --ext (crash)

Without --ext, filterCodeBlock falls back to matching overrides[].files and the virtual .html files are correctly accepted. But all rules from the project config also apply to those virtual files. JS-targeting rules such as spaced-comment (from airbnb-base) crash when they encounter the HTML AST produced by @html-eslint/parser, because HTML comment nodes have a different structure from JavaScript comments.

The solution — isolated config with a glob pattern

  1. Create .eslintrc-a11y.json in the project root:
{
  "extends": ["plugin:sfcc-a11y/recommended-error"],
  "overrides": [
    { "files": ["**/*.isml"], "processor": "sfcc-a11y/isml-sanitizer" },
    { "files": ["**/__sanitized.html"], "parser": "@html-eslint/parser" }
  ]
}
  1. Run the linter with:
eslint --no-eslintrc --config .eslintrc-a11y.json "cartridges/**/*.isml"

Why each part is necessary:

  • --no-eslintrc completely ignores the project's .eslintrc.json and everything it extends (airbnb, sonar, prettier…). Only sfcc-a11y rules run, so nothing crashes on the HTML AST.
  • --config .eslintrc-a11y.json loads the minimal accessibility-only config.
  • passing a directory (e.g. cartridges) without --ext causes ESLint to also enumerate .js files by default, which fail to parse under this minimal config. A glob only matches ISML files.
  • No --ext flag keeps filterCodeBlock in overrides-matching mode. The moment --ext .isml is added, the virtual .html code blocks are silently dropped (Trap 1).

Why --no-eslintrc is the way to go

ESLint's --no-eslintrc --config <file> pattern was designed for exactly this use case: running a sandboxed lint pass with a specific set of rules, independent of the project's base config. This is the same pattern used by Create React App, Next.js's built-in ESLint integration, and other tools that ship their own ruleset.

The underlying complexity (processor virtual files, filterCodeBlock) is an ESLint v8 architectural limitation that the ESLint team addressed in v9. The ESLint v9 migration guide explicitly lists "processors configured in shared configs accessed via extends were not applied" as a known v8 limitation and it is one of the motivating reasons flat config was introduced.

ISML support

The ISML sanitizer (sfcc-a11y/isml-sanitizer) is an ESLint processor that translates ISML-specific syntax into HTML before the parser runs.

What the sanitizer does

| Input | Output | |---|---| | ${...} expressions | __ISML_EXPR__ sentinel | | <is*> tags (<isif>, <isloop>, <isinclude>, etc.) | stripped (replaced with whitespace) | | Content-emitting tags (<isprint>, <ispicture>, <iscontentasset>) | __ISML_CONTENT__ sentinel |

The two sentinel strings are registered in the plugin's settings so that rules treat them as non-empty runtime values rather than flagging them as missing alt text, empty button labels, and so on.

Virtual file name

The sanitizer produces one code block per ISML file, named <originalPath>/__sanitized.html. ESLint uses this virtual name to resolve config for the block, which is why the override { "files": ["**/__sanitized.html"], "parser": "@html-eslint/parser" } is required in ESLint v8. Line numbers in reported violations are automatically remapped back to the original .isml file.

XML content-asset support

TODO

Rules

All 25 rules are re-exported from eslint-plugin-html-a11y under the sfcc-a11y/ prefix.

Known limitation — false positives from cross-file associations

SFCC projects frequently split <label> and <input> elements across separate <isinclude> files. ESLint processes each file independently, so the label rule will report a false positive when the label tag <label for="x"> is in a different file than the related input <input id="x">, which is frequently the case in SFCC projects (e.g., a shared header include).

Suppress with an inline disable comment when needed:

<%-- eslint-disable-next-line sfcc-a11y/label --%>
<input type="email" id="email">

License

MIT © Nicola Carkaxhija