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-html-attributes

v1.0.6

Published

ESLint plugin to enforce required attributes on HTML elements in JSX/React.

Readme

eslint-plugin-html-attributess

ESLint plugin to enforce required attributes on HTML elements in JSX/React with configurable auto-fix strategies.


🎮 Want to USE this, not change it?DEMO

🔧 Want to CHANGE this because you use it?.dev/


Features

  • ✅ Configurable for any HTML tag (button, input, div, etc.)
  • ✅ Configurable attribute name (data-testid, data-qa, etc.)
  • ✅ Multiple auto-fix strategies:
    • uuid: Deterministic UUID generation based on element content and location
    • llm: LLM-powered semantic ID generation via HTTP API
    • custom: Language-agnostic custom generator scripts (Python, Bash, Node.js, etc.)
  • ✅ Pattern validation with regex
  • ✅ Supports JSX and template literals

Installation

npm install --save-dev eslint-plugin-html-attributes

Usage

ESLint Flat Config (recommended)

import htmlAttributePlugin from 'eslint-plugin-html-attributes';

export default [
  {
    plugins: {
      'html-attributes': htmlAttributePlugin,
    },
    rules: {
      'html-attribute/require-attribute': ['error', {
        htmlTag: 'button',
        attributeName: 'data-testid',
        fix: 'uuid'
      }],
    },
  },
];

Legacy .eslintrc

{
  "plugins": ["html-attributes"],
  "rules": {
    "html-attribute/html-attribute": ["error", {
      "htmlTag": "button",
      "attributeName": "data-testid",
      "fix": "uuid"
    }]
  }
}

Configuration Options

htmlTag (string)

The HTML tag to enforce the attribute on.

Default: "button"

Examples: "button", "input", "div", "a"

attributeName (string)

The attribute name to require.

Default: "data-testid"

Examples: "data-testid", "data-qa", "data-cy"

pattern (string, optional)

A regex pattern that the attribute value must match. Useful for enforcing naming conventions.

Default: undefined (no pattern validation)

Examples:

  • "^[a-z0-9]+(-[a-z0-9]+)*$" - Kebab-case (lowercase letters, numbers, and dashes)
  • "^input-[a-z0-9-]+$" - Must start with "input-"
  • "^[A-Z][a-zA-Z0-9]*$" - PascalCase
  • "^[a-z_]+$" - Snake_case (lowercase letters and underscores)

Usage:

{
  "html-attribute/html-attribute": ["error", {
    "htmlTag": "input",
    "attributeName": "data-testid",
    "pattern": "^input-[a-z0-9-]+$"  // All inputs must start with "input-"
  }]
}

Note: UUID auto-fix generates standard UUIDs which may not match custom patterns. For pattern-compliant IDs, use custom fix strategy.

fix (enum: 'uuid' | 'llm' | 'custom')

The ID generation strategy for auto-fix.

Default: "uuid"

uuid - Deterministic UUID Generation

Generates a consistent UUID based on the element's content and location in the file.

{
  "html-attribute/require-attribute": ["error", {
    "htmlTag": "button",
    "fix": "uuid"
  }]
}

llm - LLM-Generated Semantic IDs

Uses an HTTP service (such as an LLM API) to generate semantic IDs based on element context.

Configuration:

{
  "html-attribute/require-attribute": ["error", {
    "htmlTag": "button",
    "fix": "llm",
    "llmConfig": {
      "url": "https://your-llm-api.com/generate",
      "apiKey": "your-api-key",
      "method": "POST",
      "requestBody": {
        "prompt": "Generate a test ID for: {{elementText}}"
      },
      "responseKey": "id"
    }
  }]
}

Environment Variables (via .env file):

Create a .env file in your project root:

# .env
UUID_GENERATOR_API_URL=https://api.openai.com/v1/chat/completions
UUID_GENERATOR_API_URL_API_KEY=sk-proj-your-api-key-here

Then run ESLint with dotenv:

# Install dotenv
npm install --save-dev dotenv

# Run ESLint with environment variables loaded
node -r dotenv/config node_modules/.bin/eslint --fix

# Or add to your package.json scripts:
# "lint:fix": "node -r dotenv/config eslint --fix"

Environment Variables:

  • UUID_GENERATOR_API_URL - LLM API endpoint URL
  • UUID_GENERATOR_API_URL_API_KEY - API key for authentication

Note: See .env.example for a template.

custom - Custom Generator Script

Executes a language-agnostic script that generates custom IDs. The script receives the element text and location as command-line arguments and should print a single line to stdout.

Example Python script (my-generator.py):

#!/usr/bin/env python3
import sys
import hashlib

element_text = sys.argv[1]
location = sys.argv[2]

# Generate custom ID based on element text
id_value = hashlib.md5(element_text.encode()).hexdigest()[:8]
print(f"custom-{id_value}")

Example Bash script (my-generator.sh):

#!/bin/bash
ELEMENT_TEXT="$1"
LOCATION="$2"

# Generate custom ID
echo "btn-$(echo -n "$ELEMENT_TEXT" | md5sum | cut -c1-8)"

Example Node.js script (my-generator.js):

#!/usr/bin/env node
const [,, elementText, location] = process.argv;
const crypto = require('crypto');

// Generate custom ID
const hash = crypto.createHash('md5').update(elementText).digest('hex').slice(0, 8);
console.log(`custom-${hash}`);

Configuration:

{
  "html-attribute/require-attribute": ["error", {
    "htmlTag": "button",
    "fix": "custom",
    "customGeneratorPath": "./scripts/my-generator.py"
  }]
}

Important:

  • Make sure your script is executable (chmod +x script-name)
  • Custom scripts can access environment variables from .env when ESLint is run with node -r dotenv/config
  • See DEMO/scripts/llm-generator.sh for an example of using LLM APIs in custom scripts

Error Handling & Robustness:

Custom generator scripts should implement graceful error handling. When an API call fails (non-200 status), the script should:

  1. Log the error to stderr for visibility
  2. Generate a fallback ID to ensure the fix never fails
  3. Return a valid ID to stdout

Example error handling in bash:

# Make API request
RESPONSE=$(curl -s -w "\n%{http_code}" "$API_URL" ...)
HTTP_CODE=$(echo "$RESPONSE" | tail -n 1)

# Check status
if [ "$HTTP_CODE" != "200" ]; then
    echo "Error: API returned status $HTTP_CODE" >&2
    # Generate fallback ID
    echo "fallback-$(echo -n "$ELEMENT_TEXT" | md5sum | cut -c1-8)"
    exit 0
fi

# Extract and return API response
echo "$GENERATED_ID"

Common HTTP error codes to handle:

  • 403 - Invalid API key or insufficient permissions
  • 429 - Rate limit exceeded
  • 500 - Server error
  • 503 - Service unavailable

The plugin ensures ESLint auto-fix always succeeds by falling back to UUID generation if the custom script fails.

customGeneratorPath (string)

Path to an executable script (required when fix is "custom"). The script will be called with two arguments:

  1. elementText - The full JSX/HTML text of the element
  2. location - The file path and line number (format: path/to/file.tsx:123)

The script should print a single line to stdout containing the generated ID.

llmConfig (object)

Configuration for LLM-based ID generation (used when fix is "llm").

Properties:

  • url (string) - LLM API endpoint URL
  • apiKey (string, optional) - API key for authentication
  • headers (object, optional) - Additional HTTP headers
  • method (string, optional) - HTTP method: GET, POST, or PUT (default: POST)
  • requestBody (object, optional) - Request body template with placeholders:
    • {{elementText}} - Replaced with element JSX/HTML
    • {{location}} - Replaced with file location
  • responseKey (string, optional) - JSON path to extract ID from response (default: "id")

Multiple HTML Tags

You can check multiple HTML tags with different settings in the same file by passing an array of configurations:

Simple Array Configuration (Recommended)

import htmlAttributePlugin from 'eslint-plugin-html-attributes';

export default [
  {
    files: ['src/**/*.tsx'],
    plugins: { 'html-attributes': htmlAttributePlugin },
    rules: {
      'html-attribute/require-attribute': ['error', [
        // Check all buttons with kebab-case pattern
        {
          htmlTag: 'button',
          attributeName: 'data-testid',
          pattern: '^[a-z0-9]+(-[a-z0-9]+)*$',
          fix: 'uuid'
        },
        // Check all inputs with "input-" prefix
        {
          htmlTag: 'input',
          attributeName: 'data-testid',
          pattern: '^input-[a-z0-9-]+$',
          fix: 'uuid'
        },
        // Check all selects with "select-" prefix
        {
          htmlTag: 'select',
          attributeName: 'data-testid',
          pattern: '^select-[a-z0-9-]+$',
          fix: 'uuid'
        },
      ]],
    },
  },
];

Advanced: Different Fix Strategies per Tag

You can use different auto-fix strategies for different tags:

export default [
  {
    files: ['src/**/*.tsx'],
    plugins: { 'html-attributes': htmlAttributePlugin },
    rules: {
      'html-attribute/require-attribute': ['error', [
        // Buttons use custom semantic ID generator
        {
          htmlTag: 'button',
          fix: 'custom',
          customGeneratorPath: './scripts/semantic-id-generator.py'
        },
        // Inputs use standard UUID
        {
          htmlTag: 'input',
          fix: 'uuid'
        },
        // Links use custom attribute name
        {
          htmlTag: 'a',
          attributeName: 'data-qa',
          fix: 'uuid'
        },
      ]],
    },
  },
];

Alternative: Separate Config Blocks

If you need different file patterns for different tags, you can still use separate config blocks:

export default [
  // Check buttons in all component files
  {
    files: ['src/components/**/*.tsx'],
    plugins: { 'html-attributes': htmlAttributePlugin },
    rules: {
      'html-attribute/require-attribute': ['error', {
        htmlTag: 'button',
        attributeName: 'data-testid',
        pattern: '^[a-z0-9]+(-[a-z0-9]+)*$',
        fix: 'uuid'
      }],
    },
  },
  // Check inputs only in form files
  {
    files: ['src/forms/**/*.tsx'],
    plugins: { 'html-attributes': htmlAttributePlugin },
    rules: {
      'html-attribute/require-attribute': ['error', {
        htmlTag: 'input',
        attributeName: 'data-testid',
        pattern: '^input-[a-z0-9-]+$',
        fix: 'uuid'
      }],
    },
  },
];

Rules

html-attribute/require-attribute

Enforces that specified HTML elements have the required attribute.

❌ Incorrect

<button onClick={handleClick}>Click me</button>
<button type="submit">Submit</button>

✅ Correct

<button data-testid="a1b2c3d4-..." onClick={handleClick}>Click me</button>
<button data-testid="e5f6g7h8-..." type="submit">Submit</button>

Development

To develop the plugin:

cd eslint-plugin-html-attributes
yarn install
yarn build

To try the plugin:

cd DEMO
yarn install
yarn lint

License

MIT