Full Code of github/eslint-plugin-github for AI

main 88014ae2fe53 cached
128 files
204.1 KB
55.8k tokens
65 symbols
1 requests
Download .txt
Showing preview only (233K chars total). Download the full file or copy to clipboard to get everything.
Repository: github/eslint-plugin-github
Branch: main
Commit: 88014ae2fe53
Files: 128
Total size: 204.1 KB

Directory structure:
gitextract_h6p_l7um/

├── .devcontainer/
│   ├── Dockerfile
│   └── devcontainer.json
├── .eslint-doc-generatorrc.js
├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── nodejs.yml
│       └── publish.yml
├── .gitignore
├── CODEOWNERS
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── bin/
│   └── eslint-ignore-errors.js
├── docs/
│   └── rules/
│       ├── a11y-aria-label-is-well-formatted.md
│       ├── a11y-no-generic-link-text.md
│       ├── a11y-no-title-attribute.md
│       ├── a11y-no-visually-hidden-interactive-element.md
│       ├── a11y-role-supports-aria-props.md
│       ├── a11y-svg-has-accessible-name.md
│       ├── array-foreach.md
│       ├── async-currenttarget.md
│       ├── async-preventdefault.md
│       ├── authenticity-token.md
│       ├── filenames-match-regex.md
│       ├── get-attribute.md
│       ├── js-class-name.md
│       ├── no-blur.md
│       ├── no-d-none.md
│       ├── no-dataset.md
│       ├── no-dynamic-script-tag.md
│       ├── no-implicit-buggy-globals.md
│       ├── no-inner-html.md
│       ├── no-innerText.md
│       ├── no-then.md
│       ├── no-useless-passive.md
│       ├── prefer-observers.md
│       ├── require-passive-events.md
│       └── unescaped-html-literal.md
├── eslint.config.js
├── lib/
│   ├── configs/
│   │   ├── browser.js
│   │   ├── flat/
│   │   │   ├── browser.js
│   │   │   ├── internal.js
│   │   │   ├── react.js
│   │   │   ├── recommended.js
│   │   │   └── typescript.js
│   │   ├── internal.js
│   │   ├── react.js
│   │   ├── recommended.js
│   │   └── typescript.js
│   ├── formatters/
│   │   └── stylish-fixes.js
│   ├── index.js
│   ├── plugin.js
│   ├── rules/
│   │   ├── a11y-aria-label-is-well-formatted.js
│   │   ├── a11y-no-generic-link-text.js
│   │   ├── a11y-no-title-attribute.js
│   │   ├── a11y-no-visually-hidden-interactive-element.js
│   │   ├── a11y-role-supports-aria-props.js
│   │   ├── a11y-svg-has-accessible-name.js
│   │   ├── array-foreach.js
│   │   ├── async-currenttarget.js
│   │   ├── async-preventdefault.js
│   │   ├── authenticity-token.js
│   │   ├── filenames-match-regex.js
│   │   ├── get-attribute.js
│   │   ├── js-class-name.js
│   │   ├── no-blur.js
│   │   ├── no-d-none.js
│   │   ├── no-dataset.js
│   │   ├── no-dynamic-script-tag.js
│   │   ├── no-implicit-buggy-globals.js
│   │   ├── no-inner-html.js
│   │   ├── no-innerText.js
│   │   ├── no-then.js
│   │   ├── no-useless-passive.js
│   │   ├── prefer-observers.js
│   │   ├── require-passive-events.js
│   │   └── unescaped-html-literal.js
│   ├── url.js
│   └── utils/
│       ├── commonjs-json-wrappers.cjs
│       ├── get-element-type.js
│       ├── get-exported-name.js
│       ├── get-role.js
│       ├── is-ignored-filename.js
│       ├── object-map.js
│       └── parse-filename.js
├── package.json
├── test-examples/
│   ├── flat/
│   │   ├── eslint.config.mjs
│   │   ├── package.json
│   │   └── src/
│   │       ├── forEachTest.js
│   │       ├── getAttribute.js
│   │       ├── jsx.tsx
│   │       ├── noBlur.js
│   │       └── thisTypescriptTest.ts
│   └── legacy/
│       ├── .eslintrc.cjs
│       ├── package.json
│       └── src/
│           ├── forEachTest.js
│           ├── getAttribute.js
│           ├── jsx.tsx
│           ├── noBlur.js
│           └── thisTypescriptTest.ts
└── tests/
    ├── a11y-aria-label-is-well-formatted.js
    ├── a11y-no-generic-link-text.js
    ├── a11y-no-title-attribute.js
    ├── a11y-no-visually-hidden-interactive-element.js
    ├── a11y-role-supports-aria-props.js
    ├── a11y-svg-has-accessible-name.js
    ├── array-foreach.js
    ├── async-currenttarget.js
    ├── async-preventdefault.js
    ├── authenticity-token.js
    ├── check-rules.js
    ├── get-attribute.js
    ├── js-class-name.js
    ├── no-blur.js
    ├── no-d-none.js
    ├── no-dataset.js
    ├── no-dynamic-script-tag.js
    ├── no-implicit-buggy-globals.js
    ├── no-inner-html.js
    ├── no-innerText.js
    ├── no-then.js
    ├── no-useless-passive.js
    ├── prefer-observers.js
    ├── require-passive-events.js
    ├── unescaped-html-literal.js
    └── utils/
        ├── get-element-type.mjs
        ├── get-role.mjs
        ├── mocks.js
        └── object-map.mjs

================================================
FILE CONTENTS
================================================

================================================
FILE: .devcontainer/Dockerfile
================================================
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.222.0/containers/javascript-node/.devcontainer/base.Dockerfile

# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 16, 14, 12, 16-bullseye, 14-bullseye, 12-bullseye, 16-buster, 14-buster, 12-buster
ARG VARIANT="16"
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:${VARIANT}

# [Optional] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
#     && apt-get -y install --no-install-recommends <your-package-list-here>

# [Optional] Uncomment if you want to install an additional version of node using nvm
# ARG EXTRA_NODE_VERSION=10
# RUN su node -c "source/usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}"

# [Optional] Uncomment if you want to install more global node modules
# RUN su node -c "npm install -g <your-package-list-here>" 


================================================
FILE: .devcontainer/devcontainer.json
================================================
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.222.0/containers/javascript-node
{
  "name": "Node.js",
  "build": {
    "dockerfile": "Dockerfile",
    // Update 'VARIANT' to pick a Node version: 16, 14, 12.
    // Append -bullseye or -buster to pin to an OS version.
    // Use -bullseye variants on local arm64/Apple Silicon.
    "args": {"VARIANT": "22"}
  },

  // Set *default* container specific settings.json values on container create.
  "settings": {},

  // Add the IDs of extensions you want installed when the container is created.
  "extensions": ["dbaeumer.vscode-eslint"],

  // Use 'forwardPorts' to make a list of ports inside the container available locally.
  // "forwardPorts": [],

  // Use 'postCreateCommand' to run commands after the container is created.
  // "postCreateCommand": "yarn install",

  // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
  "remoteUser": "node",
  "features": {
    "git": "latest"
  }
}


================================================
FILE: .eslint-doc-generatorrc.js
================================================
/** @type {import('eslint-doc-generator').GenerateOptions} */
export default {
  configEmoji: [
    ['browser', '🔍'],
    ['internal', '🔐'],
    ['react', '⚛️'],
  ],
  ruleDocSectionInclude: ['Rule Details', 'Version'],
}


================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  - package-ecosystem: npm
    directory: '/'
    schedule:
      interval: weekly
    open-pull-requests-limit: 99
    groups:
      all-dependencies:
        patterns:
          - "*" 
  - package-ecosystem: github-actions
    directory: '/'
    schedule:
      interval: weekly
    open-pull-requests-limit: 99


================================================
FILE: .github/workflows/nodejs.yml
================================================
name: Node CI

on:
  push:
    branches-ignore:
      - 'dependabot/**'
  pull_request:

jobs:
  build:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [20, 22]

    steps:
      - uses: actions/checkout@v6
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v6
        with:
          node-version: ${{ matrix.node-version }}
          cache: npm
      - name: Install
        run: npm ci
      - name: Test
        run: npm test


================================================
FILE: .github/workflows/publish.yml
================================================
name: Publish

on:
  release:
    types: [created]

permissions:
  contents: read
  id-token: write

jobs:
  publish-npm:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-node@v6
        with:
          node-version: 22
          registry-url: https://registry.npmjs.org/
          cache: npm
      - run: npm ci
      - run: npm test
      - run: npm version ${TAG_NAME} --git-tag-version=false
        env:
          TAG_NAME: ${{ github.event.release.tag_name }}
      - run: npm whoami; npm --ignore-scripts publish --provenance
        env:
          NODE_AUTH_TOKEN: ${{secrets.npm_token}}


================================================
FILE: .gitignore
================================================
node_modules/
npm-debug.log
yarn.lock


================================================
FILE: CODEOWNERS
================================================
* @github/web-systems-reviewers


================================================
FILE: CONTRIBUTING.md
================================================
## Publishing this package

Publishing this package to npm is done via a [GitHub action](https://github.com/github/eslint-plugin-github/blob/main/.github/workflows/publish.yml) which triggers when a new GitHub Release is created.

To publish to npm, create a release, give it an appropriate [Semantic Versioning](https://semver.org/) tag and fill out the release description. Once you publish the release, the GitHub action will be triggered and it will publish to npm.


================================================
FILE: LICENSE
================================================
Copyright (c) 2016 GitHub, Inc.

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: README.md
================================================
# eslint-plugin-github

## Installation

```sh
npm install --save-dev eslint eslint-plugin-github
```

## Setup

### Legacy Configuration (`.eslintrc`)

Add `github` to your list of plugins in your ESLint config.

JSON ESLint config example:

```json
{
  "plugins": ["github"]
}
```

Extend the configs you wish to use.

JSON ESLint config example:

```json
{
  "extends": ["plugin:github/recommended"]
}
```

### Flat Configuration (`eslint-config.js`)

Import the `eslint-plugin-github`, and extend any of the configurations using `getFlatConfigs()` as needed like so:

```js
import github from 'eslint-plugin-github'

export default [
  github.getFlatConfigs().browser,
  github.getFlatConfigs().recommended,
  github.getFlatConfigs().react,
  ...github.getFlatConfigs().typescript,
  {
    files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],
    ignores: ['eslint.config.mjs'],
    rules: {
      'github/array-foreach': 'error',
      'github/async-preventdefault': 'warn',
      'github/no-then': 'error',
      'github/no-blur': 'error',
    },
  },
]
```

> [!NOTE]
> If you configured the `filenames/match-regex` rule, please note we have adapted the match regex rule into `eslint-plugin-github` as the original `eslint-filenames-plugin` is no longer maintained and needed a flat config support update. 
> 
> Please update the name to `github/filenames-match-regex`, and note, the default rule is kebab case or camelCase with one hump. For custom configuration, such as matching for camelCase regex, here's an example:
>
> `'github/filenames-match-regex': ['error', '^([a-z0-9]+)([A-Z][a-z0-9]+)*$'],`

The available configs are:

- `internal`
  - Rules useful for github applications.
- `browser`
  - Useful rules when shipping your app to the browser.
- `react`
  - Recommended rules for React applications.
- `recommended`
  - Recommended rules for every application.
- `typescript`
  - Useful rules when writing TypeScript.

### Component mapping (Experimental)

_Note: This is experimental and subject to change._

The `react` config includes rules which target specific HTML elements. You may provide a mapping of custom components to an HTML element in your `eslintrc` configuration to increase linter coverage.

By default, these eslint rules will check the "as" prop for underlying element changes. If your repo uses a different prop name for polymorphic components provide the prop name in your `eslintrc` configuration under `polymorphicPropName`.

```json
{
  "settings": {
    "github": {
      "polymorphicPropName": "asChild",
      "components": {
        "Box": "p",
        "Link": "a"
      }
    }
  }
}
```

This config will be interpreted in the following way:

- All `<Box>` elements will be treated as a `p` element type.
- `<Link>` without a defined `as` prop will be treated as a `a`.
- `<Link as='button'>` will be treated as a `button` element type.

### Rules

<!-- begin auto-generated rules list -->

💼 Configurations enabled in.\
🔍 Set in the `browser` configuration.\
🔐 Set in the `internal` configuration.\
⚛️ Set in the `react` configuration.\
✅ Set in the `recommended` configuration.\
🔧 Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).\
❌ Deprecated.

| Name                                                                                                     | Description                                                                                                              | 💼 | 🔧 | ❌  |
| :------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------- | :- | :- | :- |
| [a11y-aria-label-is-well-formatted](docs/rules/a11y-aria-label-is-well-formatted.md)                     | enforce [aria-label] text to be formatted as you would visual text.                                                      | ⚛️ |    |    |
| [a11y-no-generic-link-text](docs/rules/a11y-no-generic-link-text.md)                                     | disallow generic link text                                                                                               |    |    | ❌  |
| [a11y-no-title-attribute](docs/rules/a11y-no-title-attribute.md)                                         | disallow using the title attribute                                                                                       | ⚛️ |    |    |
| [a11y-no-visually-hidden-interactive-element](docs/rules/a11y-no-visually-hidden-interactive-element.md) | enforce that interactive elements are not visually hidden                                                                | ⚛️ |    |    |
| [a11y-role-supports-aria-props](docs/rules/a11y-role-supports-aria-props.md)                             | enforce that elements with explicit or implicit roles defined contain only `aria-*` properties supported by that `role`. | ⚛️ |    |    |
| [a11y-svg-has-accessible-name](docs/rules/a11y-svg-has-accessible-name.md)                               | require SVGs to have an accessible name                                                                                  | ⚛️ |    |    |
| [array-foreach](docs/rules/array-foreach.md)                                                             | enforce `for..of` loops over `Array.forEach`                                                                             | ✅  |    |    |
| [async-currenttarget](docs/rules/async-currenttarget.md)                                                 | disallow `event.currentTarget` calls inside of async functions                                                           | 🔍 |    |    |
| [async-preventdefault](docs/rules/async-preventdefault.md)                                               | disallow `event.preventDefault` calls inside of async functions                                                          | 🔍 |    |    |
| [authenticity-token](docs/rules/authenticity-token.md)                                                   | disallow usage of CSRF tokens in JavaScript                                                                              | 🔐 |    |    |
| [filenames-match-regex](docs/rules/filenames-match-regex.md)                                             | require filenames to match a regex naming convention                                                                     |    |    |    |
| [get-attribute](docs/rules/get-attribute.md)                                                             | disallow wrong usage of attribute names                                                                                  | 🔍 | 🔧 |    |
| [js-class-name](docs/rules/js-class-name.md)                                                             | enforce a naming convention for js- prefixed classes                                                                     | 🔐 |    |    |
| [no-blur](docs/rules/no-blur.md)                                                                         | disallow usage of `Element.prototype.blur()`                                                                             | 🔍 |    |    |
| [no-d-none](docs/rules/no-d-none.md)                                                                     | disallow usage the `d-none` CSS class                                                                                    | 🔐 |    |    |
| [no-dataset](docs/rules/no-dataset.md)                                                                   | enforce usage of `Element.prototype.getAttribute` instead of `Element.prototype.datalist`                                | 🔍 |    |    |
| [no-dynamic-script-tag](docs/rules/no-dynamic-script-tag.md)                                             | disallow creating dynamic script tags                                                                                    | ✅  |    |    |
| [no-implicit-buggy-globals](docs/rules/no-implicit-buggy-globals.md)                                     | disallow implicit global variables                                                                                       | ✅  |    |    |
| [no-inner-html](docs/rules/no-inner-html.md)                                                             | disallow `Element.prototype.innerHTML` in favor of `Element.prototype.textContent`                                       | 🔍 |    |    |
| [no-innerText](docs/rules/no-innerText.md)                                                               | disallow `Element.prototype.innerText` in favor of `Element.prototype.textContent`                                       | 🔍 | 🔧 |    |
| [no-then](docs/rules/no-then.md)                                                                         | enforce using `async/await` syntax over Promises                                                                         | ✅  |    |    |
| [no-useless-passive](docs/rules/no-useless-passive.md)                                                   | disallow marking a event handler as passive when it has no effect                                                        | 🔍 | 🔧 |    |
| [prefer-observers](docs/rules/prefer-observers.md)                                                       | disallow poorly performing event listeners                                                                               | 🔍 |    |    |
| [require-passive-events](docs/rules/require-passive-events.md)                                           | enforce marking high frequency event handlers as passive                                                                 | 🔍 |    |    |
| [unescaped-html-literal](docs/rules/unescaped-html-literal.md)                                           | disallow unescaped HTML literals                                                                                         | 🔍 |    |    |

<!-- end auto-generated rules list -->


================================================
FILE: bin/eslint-ignore-errors.js
================================================
#!/usr/bin/env node
// Disables eslint rules in a JavaScript file with next-line comments. This is
// useful when introducing a new rule that causes many failures. The comments
// can be fixed and removed at while updating the file later.
//
// Usage:
//
//  eslint-ignore-errors app/assets/javascripts/something.js

const fs = require('fs')
const execFile = require('child_process').execFile

execFile('eslint', ['--format', 'json', process.argv[2]], (error, stdout) => {
  for (const result of JSON.parse(stdout)) {
    const filename = result.filePath
    const jsLines = fs.readFileSync(filename, 'utf8').split('\n')
    const offensesByLine = {}
    let addedLines = 0

    // Produces {47: ['github/no-d-none', 'github/no-blur'], 83: ['github/no-blur']}
    for (const message of result.messages) {
      if (offensesByLine[message.line]) {
        offensesByLine[message.line].push(message.ruleId)
      } else {
        offensesByLine[message.line] = [message.ruleId]
      }
    }

    for (const line of Object.keys(offensesByLine)) {
      const lineIndex = line - 1 + addedLines
      const previousLine = jsLines[lineIndex - 1]
      const ruleIds = offensesByLine[line].join(', ')
      if (isDisableComment(previousLine)) {
        jsLines[lineIndex - 1] = previousLine.replace(/\s?\*\/$/, `, ${ruleIds} */`)
      } else {
        const leftPad = ' '.repeat(jsLines[lineIndex].match(/^\s*/g)[0].length)
        jsLines.splice(lineIndex, 0, `${leftPad}/* eslint-disable-next-line ${ruleIds} */`)
      }
      addedLines += 1
    }

    if (result.messages.length !== 0) {
      fs.writeFileSync(filename, jsLines.join('\n'), 'utf8')
    }
  }
})

function isDisableComment(line) {
  return line.match(/\/\* eslint-disable-next-line .+\*\//)
}


================================================
FILE: docs/rules/a11y-aria-label-is-well-formatted.md
================================================
# Enforce [aria-label] text to be formatted as you would visual text (`github/a11y-aria-label-is-well-formatted`)

💼 This rule is enabled in the ⚛️ `react` config.

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

## Rule Details

`[aria-label]` content should be formatted in the same way you would visual text. Please use sentence case.

Do not connect the words like you would an ID. An `aria-label` is not an ID, and should be formatted as human-friendly text.

## Resources

- [Using aria-label](https://www.w3.org/WAI/tutorials/forms/labels/#using-aria-label)

## Examples

### **Incorrect** code for this rule 👎

```html
<a href="..." aria-label="learn more"></a>
```

```html
<a href="..." aria-label="go-to-link"></a>
```

### **Correct** code for this rule 👍

```html
<a href="..." aria-label="Learn more"></a>
```

```html
<a href="..." aria-label="Homepage"></a>
```

## Version


================================================
FILE: docs/rules/a11y-no-generic-link-text.md
================================================
# Disallow generic link text (`github/a11y-no-generic-link-text`)

❌ This rule is deprecated. It was replaced by `jsx-a11y/anchor-ambiguous-text`.

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

## Rule Details

Avoid setting generic link text like, "Click here", "Read more", and "Learn more" which do not make sense when read out of context.

Screen reader users often tab through links on a page to quickly find content without needing to listen to the full page. When link text is too generic, it becomes difficult to quickly identify the destination of the link. While it is possible to provide a more specific link text by setting the `aria-label`, this results in divergence between the label and the text and is not an ideal, future-proof solution.

Additionally, generic link text can also problematic for heavy zoom users where the link context is out of view.

Ensure that your link text is descriptive and the purpose of the link is clear even when read out of context of surrounding text.
Learn more about how to write descriptive link text at [Access Guide: Write descriptive link text](https://www.accessguide.io/guide/descriptive-link-text)

### Use of ARIA attributes

If you _must_ use ARIA to replace the visible link text, include the visible text at the beginning.

For example, on a pricing plans page, the following are good:

- Visible text: `Learn more`
- Accessible label: `Learn more about GitHub pricing plans`

Accessible ✅

```html
<a href="..." aria-label="Learn more about GitHub pricing plans">Learn more</a>
```

Inaccessible 🚫

```html
<a href="..." aria-label="GitHub pricing plans">Learn more</a>
```

Including the visible text in the ARIA label satisfies [SC 2.5.3: Label in Name](https://www.w3.org/WAI/WCAG21/Understanding/label-in-name.html).

#### False negatives

Caution: because of the restrictions of static code analysis, we may not catch all violations.

Please perform browser tests and spot checks:

- when `aria-label` is set dynamically
- when using `aria-labelledby`

## Resources

- [Primer: Links](https://primer.style/design/accessibility/links)
- [Understanding Success Criterion 2.4.4: Link Purpose (In Context)](https://www.w3.org/WAI/WCAG21/Understanding/link-purpose-in-context.html)
- [WebAim: Links and Hypertext](https://webaim.org/techniques/hypertext/)
- [Deque: Use link text that make sense when read out of context](https://dequeuniversity.com/tips/link-text)

## Examples

### **Incorrect** code for this rule 👎

```jsx
<a href="github.com/about">Learn more</a>
```

```jsx
<a href="github.com/about">Read more</a>
```

```jsx
<a href="github.com/about" aria-label="Why dogs are awesome">
  Read more
</a>
```

```jsx
<a href="github.com/about" aria-describedby="element123">
  Read more
</a>
```

### **Correct** code for this rule 👍

```jsx
<a href="github.com/about">Learn more about GitHub</a>
```

```jsx
<a href="github.com/new">Create a new repository</a>
```

## Version


================================================
FILE: docs/rules/a11y-no-title-attribute.md
================================================
# Disallow using the title attribute (`github/a11y-no-title-attribute`)

💼 This rule is enabled in the ⚛️ `react` config.

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

The title attribute is strongly discouraged. The only exception is on an `<iframe>` element. It is hardly useful and cannot be accessed by multiple groups of users including keyboard-only users and mobile users.

The `title` attribute is commonly set on links, matching the link text. This is redundant and unnecessary so it can be simply be removed.

If you are considering the `title` attribute to provide supplementary description, consider whether the text in question can be persisted in the design. Alternatively, if it's important to display supplementary text that is hidden by default, consider using an accessible tooltip implementation that uses the aria-labelledby or aria-describedby semantics. Even so, proceed with caution: tooltips should only be used on interactive elements like links or buttons. See [Tooltip alternatives](https://primer.style/design/guides/accessibility/tooltip-alternatives) for more accessible alternatives.

### Should I use the title attribute to provide an accessible name for an <svg>?

Use a <title> element instead of the title attribute, or an aria-label.

## Rule Details

👎 Examples of **incorrect** code for this rule:

```jsx
<a src="https://www.github.com" title="A home for all developers">
  GitHub
</a>
```

```jsx
<a href="/" title="github.com">
  GitHub
</a>
```

```jsx
<span src="https://www.github.com" title="supercalifragilisticexpialidocious">
  supercali...
</span>
```

👍 Examples of **correct** code for this rule:

```jsx
<iframe src="https://www.github.com" title="Github"></iframe>
```

## Version


================================================
FILE: docs/rules/a11y-no-visually-hidden-interactive-element.md
================================================
# Enforce that interactive elements are not visually hidden (`github/a11y-no-visually-hidden-interactive-element`)

💼 This rule is enabled in the ⚛️ `react` config.

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

## Rule Details

This rule guards against visually hiding interactive elements. If a sighted keyboard user navigates to an interactive element that is visually hidden they might become confused and assume that keyboard focus has been lost.

Note: we are not guarding against visually hidden `input` elements at this time. Some visually hidden inputs might cause a false positive (e.g. some file inputs).

### Why do we visually hide content?

Visually hiding content can be useful when you want to provide information specifically to screen reader users or other assistive technology users while keeping content hidden from sighted users.

Applying the following css will visually hide content while still making it accessible to screen reader users.

```css
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
```

👎 Examples of **incorrect** code for this rule:

```jsx
<button className="visually-hidden">Submit</button>
```

```jsx
<VisuallyHidden>
  <button>Submit</button>
</VisuallyHidden>
```

```jsx
<VisuallyHidden as="button">Submit</VisuallyHidden>
```

👍 Examples of **correct** code for this rule:

```jsx
<h2 className="visually-hidden">Welcome to GitHub</h2>
```

```jsx
<VisuallyHidden>
  <h2>Welcome to GitHub</h2>
</VisuallyHidden>
```

```jsx
<VisuallyHidden as="h2">Welcome to GitHub</VisuallyHidden>
```

## Options

- className - A css className that visually hides content. Defaults to `sr-only`.
- componentName - A react component name that visually hides content. Defaults to `VisuallyHidden`.

```json
{
  "a11y-no-visually-hidden-interactive-element": [
    "error",
    {
      "className": "visually-hidden",
      "componentName": "VisuallyHidden"
    }
  ]
}
```

## Version


================================================
FILE: docs/rules/a11y-role-supports-aria-props.md
================================================
# Enforce that elements with explicit or implicit roles defined contain only `aria-*` properties supported by that `role` (`github/a11y-role-supports-aria-props`)

💼 This rule is enabled in the ⚛️ `react` config.

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

## Rule Details

This rule enforces that elements with explicit or implicit roles defined contain only `aria-*` properties supported by that `role`.

For example, this rule aims to discourage common misuse of the `aria-label` and `aria-labelledby` attribute. `aria-label` and `aria-labelledby` support is only guaranteed on interactive elements like `button` or `a`, or on static elements like `div` and `span` with a permitted `role`. This rule will allow `aria-label` and `aria-labelledby` usage on `div` and `span` elements if it set to a role other than the ones listed in [WSC: a list of ARIA roles which cannot be named](https://w3c.github.io/aria/#namefromprohibited). This rule will never permit usage of `aria-label` and `aria-labelledby` on `h1`, `h2`, `h3`, `h4`, `h5`, `h6`, `strong`, `i`, `p`, `b`, or `code`.

### "Help! I'm trying to set a tooltip on a static element and this rule flagged it!"

Please do not use tooltips on static elements. It is a highly discouraged, inaccessible pattern.
See [Primer: Tooltip alternatives](https://primer.style/design/accessibility/tooltip-alternatives) for what to do instead.

### Resources

- [w3c/aria Consider prohibiting author naming certain roles #833](https://github.com/w3c/aria/issues/833)
- [Not so short note on aria-label usage - Big Table Edition](https://html5accessibility.com/stuff/2020/11/07/not-so-short-note-on-aria-label-usage-big-table-edition/)
- [Your tooltips are bogus](https://heydonworks.com/article/your-tooltips-are-bogus/)
- [Primer: Tooltip alternatives](https://primer.style/design/accessibility/tooltip-alternatives)

### Disclaimer

There are conflicting resources and opinions on what elements should support these naming attributes. For now, this rule will operate under a relatively simple heuristic aimed to minimize false positives. This may have room for future improvements. Learn more at [W3C Name Calcluation](https://w3c.github.io/aria/#namecalculation).

### **Incorrect** code for this rule 👎

```erb
<span class="tooltipped" aria-label="This is a tooltip">I am some text.</span>
```

```erb
<span aria-label="This is some content that will completely override the button content">Please be careful of the following.</span>
```

```erb
<div aria-labelledby="heading1">Goodbye</div>
```

```erb
<h1 aria-label="This will override the page title completely">Page title</h1>
```

### **Correct** code for this rule 👍

```erb
<button aria-label="Close">
  <svg src="closeIcon"></svg>
</button>
```

```erb
<button aria-label="Bold" aria-describedby="tooltip1">
  <svg src="boldIcon"></svg>
</button>
<p id="tooltip1" class="tooltip">Add bold text or turn selection into bold text</p>
```

```erb
<span>Hello</span>
```

```erb
<div>Goodbye</div>
```

```erb
<h1>Page title</h1>
```

```erb
<div role="dialog" aria-labelledby="dialogHeading">
  <h1 id="dialogHeading">Heading</h1>
</div>
```

## Version


================================================
FILE: docs/rules/a11y-svg-has-accessible-name.md
================================================
# Require SVGs to have an accessible name (`github/a11y-svg-has-accessible-name`)

💼 This rule is enabled in the ⚛️ `react` config.

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

## Rule Details

An `<svg>` must have an accessible name. Set `aria-label` or `aria-labelledby`, or nest a `<title>` element as the first child of the `<svg>` element.

However, if the `<svg>` is purely decorative, hide it with `aria-hidden="true"` or `role="presentation"`.

## Resources

- [Accessible SVGs](https://css-tricks.com/accessible-svgs/)

## Examples

### **Incorrect** code for this rule 👎

```html
<svg height='100' width='100'>
  <circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>
</svg>
```

```html
<svg height='100' width='100' title='Circle with a black outline and red fill'>
  <circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>
</svg>
```

```html
<svg height='100' width='100'>
  <circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>
  <title>Circle with a black outline and red fill</title>
</svg>
```

### **Correct** code for this rule 👍

```html
<svg height='100' width='100'>
  <title>Circle with a black outline and red fill</title>
  <circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>
</svg>
```

```html
<svg aria-label='Circle with a black outline and red fill' height='100' width='100'>
  <circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>
</svg>
```

```html
<svg aria-labelledby='circle_text' height='100' width='100'>
  <circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>
</svg>
```

```html
<svg aria-hidden='true' height='100' width='100'>
  <circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>
</svg>
```

```html
<svg role='presentation' height='100' width='100'>
  <circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>
</svg>
```

## Version


================================================
FILE: docs/rules/array-foreach.md
================================================
# Enforce `for..of` loops over `Array.forEach` (`github/array-foreach`)

💼 This rule is enabled in the ✅ `recommended` config.

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

Prefer `for...of` statement instead of `Array.forEach`.

## Rule Details

Here's a summary of why `forEach` is disallowed, and why we prefer `for...of` for almost any use-case of `forEach`:

- Allowing `forEach` encourages **layering of "bad practices"**, such as using `Array.from()` (which is less performant than using `for...of`).
- When more requirements are added on, `forEach` typically gets **chained** with other methods like `filter` or `map`, causing multiple iterations over the same Array. Encouraging `for` loops discourages chaining and encourages single-iteration logic (e.g. using a `continue` instead of `filter`).
- `for` loops are considered "more readable" and have **clearer intent**.
- `for...of` loops offer the **most flexibility** for iteration (especially vs `Array.from`).

Typically developers will reach for a `forEach` when they want to iterate over a set of items. However not all "iterables" have access to Array methods. So a developer might convert their iterable to an Array by using `Array.from(iter).forEach()`. This code has introduced performance problems, where a `for...of` loop would be more performant.

`forEach` does not do anything special with the Array - it does not create a new array or does not aid in encapsulation (except for introducing a new lexical scope within the callback, which isn't a benefit considering we use `let`/`const`). We don't disallow `map`/`filter`/`reduce` because they have a tangible effect - they create a new array - which would take _more_ code and be _less_ readable to do with a `for...of` loop, the exception being as more requirements are added, and we start chaining array methods together...

Often when using a method like `forEach` - when coming back to add new code, let's say to filter certain elements from the Array before operating on them, a developer is implicitly encouraged to use Array's method chaining to achieve this result. For example if we wanted to filter out bad apples from an Array of Apples, if the code already uses `forEach`, then its a simple addition to add `filter()`:

```diff
 apples
+  .filter(apple => !apple.bad)
    .forEach(polishApple)
```

The problem we now have is that we're iterating multiple times over the items in a collection. Using `forEach` to begin with is what encouraged the chaining, if this were a `for` loop then the equivalent behavior would be to use 2 `for` loops, which a developer is far less likely to write, so the `for` loop instead encourages an imperative style `continue`, keeping within a single set of iterations:

```diff
 for(const apple of apples) {
+   if (apple.bad) continue
   polishApple(apple)
 }
```

Chaining isn't always necessarily bad. Chaining can advertise a series of transformations that are independent from one another, and therefore aid readability. Additionally, sometimes the "goto-style" behavior of `continue` in for loops can hamper readability. For small Arrays, performance is not going to be of concern, but caution should be applied where there is a potentially unbounded Array (such as iterating over a fetched users list) as performance can easily become a bottleneck when unchecked.

The `forEach` method passes more than just the current item it is iterating over. The signature of the `forEach` callback method is `(cur: T, i: Number, all: []T) => void` and it can _additionally_ override the `receiver` (`this` value), meaning that often the _intent_ of what the callback does is hidden. To put this another way, there is _no way_ to know what the following code operates on without reading the implementation: `forEach(polishApple)`.

The `for` loop avoids this issue. Calls are explicit within the `for` loop, as they are not passed around. For example:

```js
for (const apple of apples) {
  polishApple(apple)
}
```

We know this code can only possibly mutate `apple`, as the return value is discarded, there is no `receiver` (`this` value) as `.call()` is not used, and it cannot operate on the whole array of `apples` because it is not passed as an argument. In this respect, we can establish what the intent of `polishApple(apple)` is far more than `forEach(polishApple)`. It is too easy for `forEach` to obscure the intent.

While `forEach` provides a set of arguments to the callback, it is still overall _less flexible_ than a `for` loop. A `for` loop can conditionally call the callback, can pass additional arguments to the callback (which would otherwise need to be hoisted or curried), can opt to change the `receiver` (`this` value) or not pass any `receiver` at all. This extra flexibility is the reason we almost always prefer to use `for` loops over any of the Array iteration methods.

A good example of how `for` loops provide flexibility, where `forEach` constrains it, is to see how an iteration would be refactored to handle async work. Consider the following...

```js
apples.forEach(polishApple)
// vs...
for (const apple of apples) {
  polishApple(apple)
}
```

If `polishApple` needed to do some serial async work, then we'd need to refactor the iteration steps to accommodate for this async work, by `await`ing each call to `polishApple`. We cannot simply pass an `async` function to `forEach`, as it does not understand async functions, instead we'd have to turn the `forEach` into a `reduce` and combine that with a `Promise` returning function. For example:

```diff
- apples.forEach(polishApple)
+ await apples.reduce((cur, next) => cur.then(() => polishApple(next)), Promise.resolve())
```

Compare this to the `for` loop, which has a much simpler path to refactoring:

```diff
 for (const apple of apples) {
-  polishApple(apple)
+  await polishApple(apple)
 }
```

See also https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of

👎 Examples of **incorrect** code for this rule:

```js
els.forEach(el => {
  el
})
```

👍 Examples of **correct** code for this rule:

```js
for (const el of els) {
  el
}
```

Use [`entries()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/entries) to get access to the index:

```js
for (const [i, el] of els.entries()) {
  el.name = `Element ${i}`
}
```

## Version

4.3.2


================================================
FILE: docs/rules/async-currenttarget.md
================================================
# Disallow `event.currentTarget` calls inside of async functions (`github/async-currenttarget`)

💼 This rule is enabled in the 🔍 `browser` config.

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

## Rule Details

Accessing `event.currentTarget` inside an `async function()` will likely be `null` as `currentTarget` is mutated as the event is propagated.

1.  A `click` event is dispatched
2.  The handler is invoked once with the expected `currentTarget`
3.  An `await` defers the execution
4.  The event dispatch continues, `event.currentTarget` is modified to point to the current target of another event handler and nulled out at the end of the dispatch
5.  The async function resumes
6.  `event.currentTarget` is now `null`

If you're using `async`, you'll need to synchronously create a reference to `currentTarget` before any async activity.

👎 Examples of **incorrect** code for this rule:

```js
document.addEventListener('click', async function (event) {
  // event.currentTarget will be an HTMLElement
  const url = event.currentTarget.getAttribute('data-url')
  const data = await fetch(url)

  // But now, event.currentTarget will be null
  const text = event.currentTarget.getAttribute('data-text')
  // ...
})
```

👍 Examples of **correct** code for this rule:

```js
document.addEventListener('click', function (event) {
  const currentTarget = event.currentTarget
  const url = currentTarget.getAttribute('data-url')

  // call async IIFE
  ;(async function () {
    const data = await fetch(url)

    const text = currentTarget.getAttribute('data-text')
    // ...
  })()
})
```

Alternatively, extract a function to create an element reference.

```js
document.addEventListener('click', function (event) {
  fetchData(event.currentTarget)
})

async function fetchData(el) {
  const url = el.getAttribute('data-url')
  const data = await fetch(url)
  const text = el.getAttribute('data-text')
  // ...
}
```

## Version

4.3.2


================================================
FILE: docs/rules/async-preventdefault.md
================================================
# Disallow `event.preventDefault` calls inside of async functions (`github/async-preventdefault`)

💼 This rule is enabled in the 🔍 `browser` config.

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

Using `event.preventDefault()` inside an `async function()` won't likely work as you'd expect because synchronous nature of event dispatch.

## Rule Details

1.  A `click` event is dispatched
2.  This handler is scheduled but not ran immediately because its marked async.
3.  The event dispatch completes and nothing has called `preventDefault()` _yet_ and the default click behavior occurs.
4.  The async function is scheduled and runs.
5.  Calling `preventDefault()` is now a no-op as the synchronous event dispatch has already completed.

If you're using `async`, you likely need to wait on a promise in the event handler. In this case you can split the event handler in two parts, one synchronous and asynchronous.

👎 Examples of **incorrect** code for this rule:

```js
document.addEventListener('click', async function (event) {
  const data = await fetch()

  event.preventDefault()
})
```

👍 Examples of **correct** code for this rule:

```js
document.addEventListener('click', function (event) {
  // preventDefault in a regular function
  event.preventDefault()

  // call async helper function
  loadData(event.target)
})

async function loadData(el) {
  const data = await fetch()
  // ...
}
```

This could also be done with an async IIFE.

```js
document.addEventListener('click', function (event) {
  // preventDefault in a regular function
  event.preventDefault()

  // call async IIFE
  ;(async function () {
    const data = await fetch()
    // ...
  })()
})
```

## Version

4.3.2


================================================
FILE: docs/rules/authenticity-token.md
================================================
# Disallow usage of CSRF tokens in JavaScript (`github/authenticity-token`)

💼 This rule is enabled in the 🔐 `internal` config.

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

## Rule Details

The Rails `form_tag` helper creates a `<form>` element with a `<input name="authenticity_token">` child element. The authenticity-token input tag contains a [Cross-Site Request Forgery (CSRF)](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29) token that is verified by the Rails app when the form is submitted.

An attacker who is able to steal a user's CSRF token can perform a CSRF attack against that user. To reduce this risk, GitHub uses per-form CSRF tokens. This means that a form's method and action are embedded in that form's CSRF token. When the form is submitted, the Rails application verifies that the request's path and method match those of the CSRF token: A stolen token for the `POST /preview` endpoint will not be accepted for the `DELETE /github/github` endpoint.

Requests initiated by JavaScript using XHR or Fetch still need to include a CSRF token. Prior to our use of per-form tokens, a common pattern for getting a valid CSRF token to include in a request was

Unless the JavaScript's request is for the same method/action as the form from which it takes the CSRF token, this CSRF token will _not_ be accepted by the Rails application.

The preferred way to make an HTTP request with JavaScript is to use the [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) API to serialize the input elements of a form:

👎 Examples of **incorrect** code for this rule:

```js
const csrfToken = this.closest('form').elements['authenticity_token'].value
```

👍 Examples of **correct** code for this rule:

```erb
<%= form_tag "/my/endpoint" do %>
  <input type="hidden" name="my_field" value="my value">
  <button class="js-my-button">Click Me!</button>
<% end %>
```

```js
on('click', '.js-my-button', function (e) {
  const form = this.closest('form')

  fetch(form.action, {
    method: form.method,
    body: new FormData(form),
  }).then(function () {
    alert('Success!')
  })

  e.preventDefault()
})
```

An alternate, but less preferred approach is to include the a signed CSRF url in a data-attribute:

```erb
<button class="js-my-button" data-url="<%= encode_authenticity_token_path(path) %>">Click Me!</button>
```

```js
on('click', '.js-my-button', function (e) {
  csrfRequest(this.getAttribute('data-url'), {
    method: 'PUT',
    body: data,
  }).then(function () {
    alert('Success!')
  })

  e.preventDefault()
})
```

## Version

4.3.2


================================================
FILE: docs/rules/filenames-match-regex.md
================================================
# Require filenames to match a regex naming convention (`github/filenames-match-regex`)

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

## Rule Details

Rule to ensure that filenames match a convention, with a default of kebab case or camelCase with one hump for flat config.

👎 Examples of **incorrect** filename for this default rule:

- `fileNameRule.js`

👍 Examples of **correct** code for this rule:

- `fileName.js`
- `file-name.js`

## Options

regex - Regex to match the filename structure. Defaults to kebab case or camelCase with one hump.

Default:

```json
{
  "filenames-match-regex": [
    "error"
  ]
}
```

If you want to add custom regex such as matching all camelCase, add the regex as a string. For example, for camelCase it would look like:

```json
{
  "filenames-match-regex": [
    "error",
    "^([a-z0-9]+)([A-Z][a-z0-9]+)*$"
  ]
}
```

## Version

4.3.2


================================================
FILE: docs/rules/get-attribute.md
================================================
# Disallow wrong usage of attribute names (`github/get-attribute`)

💼 This rule is enabled in the 🔍 `browser` config.

🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).

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

## Rule Details

As HTML attributes are case insensitive, prefer using lowercase.

👎 Examples of **incorrect** code for this rule:

```js
el.getAttribute('autoComplete')
```

```js
el.getAttribute('dataFoo')
```

👍 Examples of **correct** code for this rule:

```js
el.getAttribute('autocomplete')
```

```js
el.getAttribute('data-foo')
```

## Version

4.3.2


================================================
FILE: docs/rules/js-class-name.md
================================================
# Enforce a naming convention for js- prefixed classes (`github/js-class-name`)

💼 This rule is enabled in the 🔐 `internal` config.

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

JavaScript should only query and handle events for `js-` prefixed class names.

## Rule Details

The key benefit is that these symbols can be easily searched for.

Looking at HTML, you can find the implementation of any event handler targeting the element. And from any JS file, you can find all the templates using the event handler.

Since its easy for humans to cross reference usage sites and implementation, so can machines. Linters can scan the code base for unused JS event handlers or markup that is still referencing an nonexistent behavior.

In order to trust this system, all `js-` class names MUST be statically written as string literals. This means no dynamically constructed strings by interpolation. For the same reason, `obj.send("can_#{sym}?")` makes you feel bad deep down inside, so should `querySelector("js-" + sym)`.

Typically dynamically constructed `js-` classes are often mixing static symbols and user data together. Like `"js-org-#{org.login}"`. In this case, separating into a `data-` attribute would be a better solution.

```html
<div class="js-org-update" data-org-name="<%= org.login %>"></div>
```

Allows you to select elements by `js-org-update` and still filter by the `data-org-name` attribute if you need to. Both `js-org-update` and `data-org-name` are clearly static symbols that are easy to search for.

`js-` classes must start with `js-` (obviously) and only contain lowercase letters and numbers separated by `-`s. The ESLint [`github/js-class-name`](https://github.com/github/eslint-plugin-github/blob/master/lib/rules/js-class-name.js) rule enforces this style.

[@defunkt's original proposal from 2010](https://web.archive.org/web/20180902223055/http://ozmm.org/posts/slightly_obtrusive_javascript.html).

👎 Examples of **incorrect** code for this rule:

```js
const el = document.querySelector('.js-Foo')
```

👍 Examples of **correct** code for this rule:

```js
const el = document.querySelector('.js-foo')
```

## Version

4.3.2


================================================
FILE: docs/rules/no-blur.md
================================================
# Disallow usage of `Element.prototype.blur()` (`github/no-blur`)

💼 This rule is enabled in the 🔍 `browser` config.

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

Do not use `element.blur()`. Blurring an element causes the focus position to be reset causing accessibility issues when using keyboard or voice navigation. Instead, restore focus by calling `element.focus()` on a prior element.

## Rule Details

- [Use of `blur()` is discouraged by WHATWG HTML spec](https://html.spec.whatwg.org/multipage/interaction.html#dom-blur)

👎 Examples of **incorrect** code for this rule:

```js
menu.addEventListener('close', () => {
  input.blur()
})
```

👍 Examples of **correct** code for this rule:

```js
menu.addEventListener('open', () => {
  const previouslyFocusedElement = document.activeElement

  input.focus()

  menu.addEventListener('close', () => {
    previouslyFocusedElement.focus()
  })
})
```

## Version

4.3.2


================================================
FILE: docs/rules/no-d-none.md
================================================
# Disallow usage the `d-none` CSS class (`github/no-d-none`)

💼 This rule is enabled in the 🔐 `internal` config.

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

## Rule Details

Ideally JavaScript behaviors should not rely on Primer CSS when the `hidden` property can be used.

👎 Examples of **incorrect** code for this rule:

```js
div.classList.add('d-none')
```

👍 Examples of **correct** code for this rule:

```js
div.hidden = false
```

## Version

4.3.2


================================================
FILE: docs/rules/no-dataset.md
================================================
# Enforce usage of `Element.prototype.getAttribute` instead of `Element.prototype.datalist` (`github/no-dataset`)

💼 This rule is enabled in the 🔍 `browser` config.

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

## Rule Details

Due to [camel-case transformations](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset#Name_conversion), using dataset is not easily greppable. Instead, use `el.getAttribute('data-what-ever')`.

👎 Examples of **incorrect** code for this rule:

```js
el.dataset.coolThing
```

👍 Examples of **correct** code for this rule:

```js
el.getAttribute('data-cool-thing')
```

## Version

4.3.2


================================================
FILE: docs/rules/no-dynamic-script-tag.md
================================================
# Disallow creating dynamic script tags (`github/no-dynamic-script-tag`)

💼 This rule is enabled in the ✅ `recommended` config.

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

## Rule Details

Creating dynamic script tags bypasses a lot of security measures - like SRIs - and pose a potential threat to your application.
Instead of creating a `script` tag in the client, provide all necessary `script` tags in the page's HTML.

👎 Examples of **incorrect** code for this rule:

```js
document.createElement('script')
document.getElementById('some-id').type = 'text/javascript'
```

👍 Examples of **correct** code for this rule:

```html
<!-- index.html -->
<script src="/index.js" type="text/javascript">
```

## Version

4.3.2


================================================
FILE: docs/rules/no-implicit-buggy-globals.md
================================================
# Disallow implicit global variables (`github/no-implicit-buggy-globals`)

💼 This rule is enabled in the ✅ `recommended` config.

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

## Rule Details

👎 Examples of **incorrect** code for this rule:

```js
var foo = 1
```

👍 Examples of **correct** code for this rule:

```js
;(function () {
  const foo = 1
})()
```

## Version

4.3.2


================================================
FILE: docs/rules/no-inner-html.md
================================================
# Disallow `Element.prototype.innerHTML` in favor of `Element.prototype.textContent` (`github/no-inner-html`)

💼 This rule is enabled in the 🔍 `browser` config.

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

## Rule Details

Using `innerHTML` poses a potential security risk. Prefer using `textContent` to set text to an element.

[Related security notification](https://github.com/github/paste-markdown/security/advisories/GHSA-gpfj-4j6g-c4w9)

It may be reasonable to disable this rule in testing setups that use known, trusted input and carry little security risk.

👎 Examples of **incorrect** code for this rule:

```js
function setContent(element, content) {
  element.innerHTML = content
}
```

👍 Examples of **correct** code for this rule:

```js
function setContent(element, content) {
  element.textContent = content
}
```

## Version

4.3.2


================================================
FILE: docs/rules/no-innerText.md
================================================
# Disallow `Element.prototype.innerText` in favor of `Element.prototype.textContent` (`github/no-innerText`)

💼 This rule is enabled in the 🔍 `browser` config.

🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).

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

## Rule Details

👎 Examples of **incorrect** code for this rule:

```js
const el = document.createElement('div')
el.innerText = 'foo'
```

👍 Examples of **correct** code for this rule:

```js
const el = document.createElement('div')
el.textContent = 'foo'
```

## Version

4.3.2


================================================
FILE: docs/rules/no-then.md
================================================
# Enforce using `async/await` syntax over Promises (`github/no-then`)

💼 This rule is enabled in the ✅ `recommended` config.

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

## Rule Details

Yes, you should use promises, but prefer `async`/`await` syntax instead of `Promise.then()` callback chaining.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

👎 Examples of **incorrect** code for this rule:

```js
function countData(url) {
  return downloadData(url).then(data => {
    return data.length
  })
}
```

```js
function getProcessedData(url) {
  return downloadData(url).catch(e => {
    console.log('Error occurred!', e)
    return null;
  })
}
```

👍 Examples of **correct** code for this rule:

```js
async function countProcessedData(url) {
  const data = await downloadData(url);
  return data.length
}
```

```js
async function getProcessedData(url) {
  try {
    return await downloadData(url)
  } catch (e) {
    console.log('Error occurred!', e);
    return null;
  }
}
```

## Version

4.3.2


================================================
FILE: docs/rules/no-useless-passive.md
================================================
# Disallow marking a event handler as passive when it has no effect (`github/no-useless-passive`)

💼 This rule is enabled in the 🔍 `browser` config.

🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).

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

This rule disallows setting `passive: true` for events on which it will have no effect.

Events where `passive: true` has an effect are: `touchstart`, `touchmove`, `touchenter`, `touchend`, `touchleave`, `wheel`, and `mousewheel`.

## Rule Details

Adding `passive: true` informs the browser that this event will not be calling `preventDefault` and as such is safe to asynchronously dispatch, freeing up the main thread for lag-free operation. However many events are not cancel-able and as such setting `passive: true` will have no effect on the browser.

It is safe to leave the option set, but this may have a negative effect on code authors, as they might believe setting `passive: true` has a positive effect on their operations, leading to a false-confidence in code with `passive: true`. As such, removing the option where it has no effect demonstrates to the code author that this code will need to avoid expensive operations as this might have a detrimental affect on UI performance.

https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Improving_scrolling_performance_with_passive_listeners

👎 Examples of **incorrect** code for this rule:

```js
// bad (passive has no effect here)
window.addEventListener(
  'scroll',
  () => {
    console.log('Scroll event fired!')
  },
  {passive: true},
)
```

👍 Examples of **correct** code for this rule:

```js
// good
window.addEventListener('scroll', () => {
  console.log('Scroll event fired!')
})
```

## Version

4.3.2


================================================
FILE: docs/rules/prefer-observers.md
================================================
# Disallow poorly performing event listeners (`github/prefer-observers`)

💼 This rule is enabled in the 🔍 `browser` config.

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

Some events, such as `scroll` and `resize` have traditionally caused performance issues on web pages, as they are high frequency events, firing many times per second as the user interacts with the page viewport.

## Rule Details

Typically `scroll` events are used to determine if an element is intersecting a viewport, which can result in expensive operations such as layout calculations, which has a detrimental affect on UI performance. Recent developments in web standards have introduced the `IntersectionObserver`, which is a more performant mechanism for determining if an element is intersecting the viewport.

Similarly, `resize` events are typically used to determine if the viewport is smaller or larger than a certain size, similar to CSS media break points. Similar to the `IntersectionObserver` the `ResizeObserver` also exists as a more performant mechanism for observing changes to the viewport size.

```js
// bad, low-performing code to determine if the element is less than 500px large
window.addEventListener('resize', () => {
  element.classList.toggle('size-small', element.getBoundingClientRect().width < 500)
})

// good - more performant, only fires when the actual elements change size
const observer = new ResizeObserver(entries => {
  for (const {target, contentRect} of entries) {
    target.classList.toggle('size-small', contentRect.width < 500)
  }
})
observer.observe(element)
```

https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
https://developer.mozilla.org/en-US/docs/Web/API/Resize_Observer_API

👎 Examples of **incorrect** code for this rule:

```js
// bad, expensive, error-prone code to determine if the element is in the viewport;
window.addEventListener('scroll', () => {
  const isIntersecting = checkIfElementIntersects(
    element.getBoundingClientRect(),
    window.innerHeight,
    document.clientHeight,
  )
  element.classList.toggle('intersecting', isIntersecting)
})
```

👍 Examples of **correct** code for this rule:

```js
// good - more performant and less error-prone
const observer = new IntersectionObserver(entries => {
  for (const {target, isIntersecting} of entries) {
    target.classList.toggle(target, isIntersecting)
  }
})
observer.observe(element)
```

## Version

4.3.2


================================================
FILE: docs/rules/require-passive-events.md
================================================
# Enforce marking high frequency event handlers as passive (`github/require-passive-events`)

💼 This rule is enabled in the 🔍 `browser` config.

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

This rule enforces adding `passive: true` to high frequency event listeners (`touchstart`, `touchmove`, `touchenter`, `touchend`, `touchleave`, `wheel`, `mousewheel`).

## Rule Details

Adding these events listeners can block the main thread as it waits to find out if the callbacks call `preventDefault`. This can cause large amounts UI lag, which will be noticeable for users.

Adding `passive: true` informs the browser that this event will not be calling `preventDefault` and as such is safe to asynchronously dispatch, freeing up the main thread for lag-free operation.

See also: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Improving_scrolling_performance_with_passive_listeners

👎 Examples of **incorrect** code for this rule:

```js
// bad
window.addEventListener('touchstart', () => {
  /* ... */
})
```

👍 Examples of **correct** code for this rule:

```js
// good
window.addEventListener(
  'touchstart',
  () => {
    /* ... */
  },
  {passive: true},
)
```

## Version

4.3.2


================================================
FILE: docs/rules/unescaped-html-literal.md
================================================
# Disallow unescaped HTML literals (`github/unescaped-html-literal`)

💼 This rule is enabled in the 🔍 `browser` config.

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

## Rule Details

Constructing raw HTML with string literals is error prone and may lead to security issues.

Instead use [`lit-html`](https://github.com/Polymer/lit-html)'s `html` tagged template literal to safely construct HTML literal strings. Alternatively, you can implement your own `html` tagged template literal function, or use document builder APIs like `document.createElement`.

👎 Examples of **incorrect** code for this rule:

```js
const title = `<h1>Hello ${name}!</h1>`
```

👍 Examples of **correct** code for this rule:

```js
// good
const title = html`<h1>Hello ${name}!</h1>`
```

```js
// also good
const title = document.createElement('h1')
title.textContent = `Hello ${name}!`
```

## Version

4.3.2


================================================
FILE: eslint.config.js
================================================
import globals from 'globals'
import eslintPlugin from 'eslint-plugin-eslint-plugin'
import importPlugin from 'eslint-plugin-import'
import i18nTextPlugin from 'eslint-plugin-i18n-text'
import recommendedGitHub from './lib/configs/flat/recommended.js'
import {fixupPluginRules} from '@eslint/compat'

export default [
  recommendedGitHub,
  {
    files: ['lib/rules/**/*.js'],
    ...eslintPlugin.configs['flat/all'],
  },
  {
    ignores: ['test-examples/**'],
  },
  {
    languageOptions: {
      ecmaVersion: 13,
      globals: {
        ...globals.es6,
        ...globals.node,
      },
    },
    plugins: {
      eslintPlugin,
      import: importPlugin,
      'i18n-text': fixupPluginRules(i18nTextPlugin),
    },
    rules: {
      'import/extensions': 'off',
      'import/no-commonjs': 'off',
      'github/filenames-match-regex': 'off',
      'i18n-text/no-en': 'off',
      'eslint-plugin/prefer-placeholders': 'off',
      'eslint-plugin/test-case-shorthand-strings': 'off',
      'eslint-plugin/require-meta-docs-url': 'off',
      'eslint-plugin/require-meta-default-options': 'off',
    },
  },
]


================================================
FILE: lib/configs/browser.js
================================================
export default {
  env: {
    browser: true,
  },
  plugins: ['github', 'escompat'],
  extends: ['plugin:escompat/recommended'],
  rules: {
    'escompat/no-dynamic-imports': 'off',
    'github/async-currenttarget': 'error',
    'github/async-preventdefault': 'error',
    'github/get-attribute': 'error',
    'github/no-blur': 'error',
    'github/no-dataset': 'error',
    'github/no-innerText': 'error',
    'github/no-inner-html': 'error',
    'github/unescaped-html-literal': 'error',
    'github/no-useless-passive': 'error',
    'github/require-passive-events': 'error',
    'github/prefer-observers': 'error',
    'import/no-nodejs-modules': 'error',
    'no-restricted-syntax': [
      'error',
      {
        selector: "NewExpression[callee.name='URL'][arguments.length=1]",
        message: 'Please pass in `window.location.origin` as the 2nd argument to `new URL()`',
      },
    ],
  },
}


================================================
FILE: lib/configs/flat/browser.js
================================================
import globals from 'globals'
import github from '../../plugin.js'
import importPlugin from 'eslint-plugin-import'
import escompat from 'eslint-plugin-escompat'
import {fixupPluginRules} from '@eslint/compat'

export default {
  ...escompat.configs['flat/recommended'],
  languageOptions: {
    globals: {
      ...globals.browser,
    },
  },
  plugins: {import: importPlugin, escompat, github: fixupPluginRules(github)},
  rules: {
    'escompat/no-dynamic-imports': 'off',
    'github/async-currenttarget': 'error',
    'github/async-preventdefault': 'error',
    'github/get-attribute': 'error',
    'github/no-blur': 'error',
    'github/no-dataset': 'error',
    'github/no-innerText': 'error',
    'github/no-inner-html': 'error',
    'github/unescaped-html-literal': 'error',
    'github/no-useless-passive': 'error',
    'github/require-passive-events': 'error',
    'github/prefer-observers': 'error',
    'import/no-nodejs-modules': 'error',
    'no-restricted-syntax': [
      'error',
      {
        selector: "NewExpression[callee.name='URL'][arguments.length=1]",
        message: 'Please pass in `window.location.origin` as the 2nd argument to `new URL()`',
      },
    ],
  },
}


================================================
FILE: lib/configs/flat/internal.js
================================================
import github from '../../plugin.js'
import {fixupPluginRules} from '@eslint/compat'

export default {
  plugins: {github: fixupPluginRules(github)},
  rules: {
    'github/authenticity-token': 'error',
    'github/js-class-name': 'error',
    'github/no-d-none': 'error',
  },
}


================================================
FILE: lib/configs/flat/react.js
================================================
import github from '../../plugin.js'
import jsxA11yPlugin from 'eslint-plugin-jsx-a11y'
import {fixupPluginRules} from '@eslint/compat'

export default {
  ...jsxA11yPlugin.flatConfigs.recommended,
  languageOptions: {
    sourceType: 'module',
    parserOptions: {
      ecmaFeatures: {
        jsx: true,
      },
    },
  },
  plugins: {github: fixupPluginRules(github), 'jsx-a11y': jsxA11yPlugin},
  rules: {
    'jsx-a11y/role-supports-aria-props': 'off', // Override with github/a11y-role-supports-aria-props until https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/issues/910 is resolved
    'github/a11y-aria-label-is-well-formatted': 'error',
    'github/a11y-no-visually-hidden-interactive-element': 'error',
    'github/a11y-no-title-attribute': 'error',
    'github/a11y-svg-has-accessible-name': 'error',
    'github/a11y-role-supports-aria-props': 'error',
    'jsx-a11y/no-aria-hidden-on-focusable': 'error',
    'jsx-a11y/no-autofocus': 'off',
    'jsx-a11y/anchor-ambiguous-text': [
      'error',
      {
        words: ['this', 'more', 'read here', 'read more'],
      },
    ],
    'jsx-a11y/no-interactive-element-to-noninteractive-role': [
      'error',
      {
        tr: ['none', 'presentation'],
        td: ['cell'], // TODO: Remove once https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/pull/937#issuecomment-1638128318 is addressed.
        canvas: ['img'],
      },
    ],
    'jsx-a11y/no-redundant-roles': [
      'error',
      {
        nav: ['navigation'], // default in eslint-plugin-jsx-a11y
        tbody: ['rowgroup'],
        thead: ['rowgroup'],
      },
    ],
  },
}


================================================
FILE: lib/configs/flat/recommended.js
================================================
import globals from 'globals'
import github from '../../plugin.js'
import prettierPlugin from 'eslint-plugin-prettier'
import eslintComments from 'eslint-plugin-eslint-comments'
import importPlugin from 'eslint-plugin-import'
import i18nTextPlugin from 'eslint-plugin-i18n-text'
import noOnlyTestsPlugin from 'eslint-plugin-no-only-tests'
import {fixupPluginRules} from '@eslint/compat'

export default {
  languageOptions: {
    ecmaVersion: 6,
    sourceType: 'module',
    globals: {
      ...globals.es6,
    },
  },
  plugins: {
    prettier: prettierPlugin,
    'eslint-comments': eslintComments,
    import: importPlugin,
    'i18n-text': fixupPluginRules(i18nTextPlugin),
    'no-only-tests': noOnlyTestsPlugin,
    github: fixupPluginRules(github),
  },
  rules: {
    'constructor-super': 'error',
    'eslint-comments/disable-enable-pair': 'off',
    'eslint-comments/no-aggregating-enable': 'off',
    'eslint-comments/no-duplicate-disable': 'error',
    'eslint-comments/no-unlimited-disable': 'error',
    'eslint-comments/no-unused-disable': 'error',
    'eslint-comments/no-unused-enable': 'error',
    'eslint-comments/no-use': ['error', {allow: ['eslint', 'eslint-disable-next-line', 'eslint-env', 'globals']}],
    'github/filenames-match-regex': 'error',
    'func-style': ['error', 'declaration', {allowArrowFunctions: true}],
    'github/array-foreach': 'error',
    'github/no-implicit-buggy-globals': 'error',
    'github/no-then': 'error',
    'github/no-dynamic-script-tag': 'error',
    'i18n-text/no-en': ['error'],
    'import/default': 'error',
    'import/export': 'error',
    'import/extensions': 'error',
    'import/first': 'error',
    'import/named': 'error',
    'import/namespace': 'error',
    'import/no-absolute-path': 'error',
    'import/no-amd': 'error',
    'import/no-anonymous-default-export': [
      'error',
      {
        allowAnonymousClass: false,
        allowAnonymousFunction: false,
        allowArray: true,
        allowArrowFunction: false,
        allowLiteral: true,
        allowObject: true,
      },
    ],
    'import/no-commonjs': 'error',
    'import/no-deprecated': 'error',
    'import/no-duplicates': 'error',
    'import/no-dynamic-require': 'error',
    'import/no-extraneous-dependencies': [0, {devDependencies: false}],
    'import/no-mutable-exports': 'error',
    'import/no-named-as-default': 'error',
    'import/no-named-as-default-member': 'error',
    'import/no-namespace': 'error',
    'import/no-unresolved': 'error',
    'import/no-webpack-loader-syntax': 'error',
    'no-case-declarations': 'error',
    'no-class-assign': 'error',
    'no-compare-neg-zero': 'error',
    'no-cond-assign': 'error',
    'no-console': 'error',
    'no-const-assign': 'error',
    'no-constant-condition': 'error',
    'no-control-regex': 'error',
    'no-debugger': 'error',
    'no-delete-var': 'error',
    'no-dupe-args': 'error',
    'no-dupe-class-members': 'error',
    'no-dupe-keys': 'error',
    'no-duplicate-case': 'error',
    'no-empty': 'error',
    'no-empty-character-class': 'error',
    'no-empty-pattern': 'error',
    'no-ex-assign': 'error',
    'no-extra-boolean-cast': 'error',
    'no-fallthrough': 'error',
    'no-func-assign': 'error',
    'no-global-assign': 'error',
    'no-implicit-globals': 'error',
    'no-implied-eval': 'error',
    'no-inner-declarations': 'error',
    'no-invalid-regexp': 'error',
    'no-invalid-this': 'error',
    'no-irregular-whitespace': 'error',
    'no-new-symbol': 'error',
    'no-obj-calls': 'error',
    'no-octal': 'error',
    'no-only-tests/no-only-tests': [
      'error',
      {
        block: ['describe', 'it', 'context', 'test', 'tape', 'fixture', 'serial', 'suite'],
      },
    ],
    'no-redeclare': 'error',
    'no-regex-spaces': 'error',
    'no-return-assign': 'error',
    'no-self-assign': 'error',
    'no-sequences': ['error'],
    'no-shadow': 'error',
    'no-sparse-arrays': 'error',
    'no-this-before-super': 'error',
    'no-throw-literal': 'error',
    'no-undef': 'error',
    'no-unreachable': 'error',
    'no-unsafe-finally': 'error',
    'no-unsafe-negation': 'error',
    'no-unused-labels': 'error',
    'no-unused-vars': 'error',
    'no-useless-concat': 'error',
    'no-useless-escape': 'error',
    'no-var': 'error',
    'object-shorthand': ['error', 'always', {avoidQuotes: true}],
    'one-var': ['error', 'never'],
    'prefer-const': 'error',
    'prefer-promise-reject-errors': 'error',
    'prefer-rest-params': 'error',
    'prefer-spread': 'error',
    'prefer-template': 'error',
    'prettier/prettier': 'error',
    'require-yield': 'error',
    'use-isnan': 'error',
    'valid-typeof': 'error',
    camelcase: ['error', {properties: 'always'}],
    eqeqeq: ['error', 'smart'],
  },
  settings: {
    'import/resolver': {
      node: {
        extensions: ['.js', '.ts'],
      },
    },
  },
}


================================================
FILE: lib/configs/flat/typescript.js
================================================
// eslint-disable-next-line import/no-unresolved
import tseslint from 'typescript-eslint'
import escompat from 'eslint-plugin-escompat'

export default tseslint.config(...tseslint.configs.recommended, ...escompat.configs['flat/typescript-2020'], {
  languageOptions: {
    parser: tseslint.parser,
  },
  plugins: {'@typescript-eslint': tseslint.plugin, escompat},
  rules: {
    camelcase: 'off',
    'no-unused-vars': 'off',
    'no-shadow': 'off',
    'no-invalid-this': 'off',
    '@typescript-eslint/no-invalid-this': ['error'],
    '@typescript-eslint/no-shadow': ['error'],
    '@typescript-eslint/interface-name-prefix': 'off',
    '@typescript-eslint/array-type': ['error', {default: 'array-simple'}],
    '@typescript-eslint/no-use-before-define': 'off',
    '@typescript-eslint/explicit-member-accessibility': 'off',
    '@typescript-eslint/explicit-function-return-type': 'off',
    '@typescript-eslint/no-non-null-assertion': 'off',
    '@typescript-eslint/no-unused-vars': 'error',
    '@typescript-eslint/explicit-module-boundary-types': 'off',
  },
})


================================================
FILE: lib/configs/internal.js
================================================
export default {
  plugins: ['github'],
  rules: {
    'github/authenticity-token': 'error',
    'github/js-class-name': 'error',
    'github/no-d-none': 'error',
  },
}


================================================
FILE: lib/configs/react.js
================================================
export default {
  parserOptions: {
    sourceType: 'module',
    ecmaFeatures: {
      jsx: true,
    },
  },
  plugins: ['github', 'jsx-a11y'],
  extends: ['plugin:jsx-a11y/recommended'],
  rules: {
    'jsx-a11y/role-supports-aria-props': 'off', // Override with github/a11y-role-supports-aria-props until https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/issues/910 is resolved
    'github/a11y-aria-label-is-well-formatted': 'error',
    'github/a11y-no-visually-hidden-interactive-element': 'error',
    'github/a11y-no-title-attribute': 'error',
    'github/a11y-svg-has-accessible-name': 'error',
    'github/a11y-role-supports-aria-props': 'error',
    'jsx-a11y/no-aria-hidden-on-focusable': 'error',
    'jsx-a11y/no-autofocus': 'off',
    'jsx-a11y/anchor-ambiguous-text': [
      'error',
      {
        words: ['this', 'more', 'read here', 'read more'],
      },
    ],
    'jsx-a11y/no-interactive-element-to-noninteractive-role': [
      'error',
      {
        tr: ['none', 'presentation'],
        td: ['cell'], // TODO: Remove once https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/pull/937#issuecomment-1638128318 is addressed.
        canvas: ['img'],
      },
    ],
    'jsx-a11y/no-redundant-roles': [
      'error',
      {
        nav: ['navigation'], // default in eslint-plugin-jsx-a11y
        tbody: ['rowgroup'],
        thead: ['rowgroup'],
        ul: ['list'], // In webkit, setting list-style-type: none results in semantics being removed. Need explicit role.
      },
    ],
  },
}


================================================
FILE: lib/configs/recommended.js
================================================
export default {
  parserOptions: {
    ecmaFeatures: {
      ecmaVersion: 6,
    },
    sourceType: 'module',
  },
  env: {
    es6: true,
  },
  plugins: ['github', 'prettier', 'eslint-comments', 'import', 'filenames', 'i18n-text', 'no-only-tests'],
  rules: {
    'constructor-super': 'error',
    'eslint-comments/disable-enable-pair': 'off',
    'eslint-comments/no-aggregating-enable': 'off',
    'eslint-comments/no-duplicate-disable': 'error',
    'eslint-comments/no-unlimited-disable': 'error',
    'eslint-comments/no-unused-disable': 'error',
    'eslint-comments/no-unused-enable': 'error',
    'eslint-comments/no-use': ['error', {allow: ['eslint', 'eslint-disable-next-line', 'eslint-env', 'globals']}],
    'filenames/match-regex': ['error', '^[a-z0-9-]+(.[a-z0-9-]+)?$'],
    'func-style': ['error', 'declaration', {allowArrowFunctions: true}],
    'github/array-foreach': 'error',
    'github/no-implicit-buggy-globals': 'error',
    'github/no-then': 'error',
    'github/no-dynamic-script-tag': 'error',
    'i18n-text/no-en': ['error'],
    'import/default': 'error',
    'import/export': 'error',
    'import/extensions': 'error',
    'import/first': 'error',
    'import/named': 'error',
    'import/namespace': 'error',
    'import/no-absolute-path': 'error',
    'import/no-amd': 'error',
    'import/no-anonymous-default-export': [
      'error',
      {
        allowAnonymousClass: false,
        allowAnonymousFunction: false,
        allowArray: true,
        allowArrowFunction: false,
        allowLiteral: true,
        allowObject: true,
      },
    ],
    'import/no-commonjs': 'error',
    'import/no-deprecated': 'error',
    'import/no-duplicates': 'error',
    'import/no-dynamic-require': 'error',
    'import/no-extraneous-dependencies': [0, {devDependencies: false}],
    'import/no-mutable-exports': 'error',
    'import/no-named-as-default': 'error',
    'import/no-named-as-default-member': 'error',
    'import/no-namespace': 'error',
    'import/no-unresolved': 'error',
    'import/no-webpack-loader-syntax': 'error',
    'no-case-declarations': 'error',
    'no-class-assign': 'error',
    'no-compare-neg-zero': 'error',
    'no-cond-assign': 'error',
    'no-console': 'error',
    'no-const-assign': 'error',
    'no-constant-condition': 'error',
    'no-control-regex': 'error',
    'no-debugger': 'error',
    'no-delete-var': 'error',
    'no-dupe-args': 'error',
    'no-dupe-class-members': 'error',
    'no-dupe-keys': 'error',
    'no-duplicate-case': 'error',
    'no-empty': 'error',
    'no-empty-character-class': 'error',
    'no-empty-pattern': 'error',
    'no-ex-assign': 'error',
    'no-extra-boolean-cast': 'error',
    'no-fallthrough': 'error',
    'no-func-assign': 'error',
    'no-global-assign': 'error',
    'no-implicit-globals': 'error',
    'no-implied-eval': 'error',
    'no-inner-declarations': 'error',
    'no-invalid-regexp': 'error',
    'no-invalid-this': 'error',
    'no-irregular-whitespace': 'error',
    'no-new-symbol': 'error',
    'no-obj-calls': 'error',
    'no-octal': 'error',
    'no-only-tests/no-only-tests': [
      'error',
      {
        block: ['describe', 'it', 'context', 'test', 'tape', 'fixture', 'serial', 'suite'],
      },
    ],
    'no-redeclare': 'error',
    'no-regex-spaces': 'error',
    'no-return-assign': 'error',
    'no-self-assign': 'error',
    'no-sequences': ['error'],
    'no-shadow': 'error',
    'no-sparse-arrays': 'error',
    'no-this-before-super': 'error',
    'no-throw-literal': 'error',
    'no-undef': 'error',
    'no-unreachable': 'error',
    'no-unsafe-finally': 'error',
    'no-unsafe-negation': 'error',
    'no-unused-labels': 'error',
    'no-unused-vars': 'error',
    'no-useless-concat': 'error',
    'no-useless-escape': 'error',
    'no-var': 'error',
    'object-shorthand': ['error', 'always', {avoidQuotes: true}],
    'one-var': ['error', 'never'],
    'prefer-const': 'error',
    'prefer-promise-reject-errors': 'error',
    'prefer-rest-params': 'error',
    'prefer-spread': 'error',
    'prefer-template': 'error',
    'prettier/prettier': 'error',
    'require-yield': 'error',
    'use-isnan': 'error',
    'valid-typeof': 'error',
    camelcase: ['error', {properties: 'always'}],
    eqeqeq: ['error', 'smart'],
  },
  settings: {
    'import/resolver': {
      node: {
        extensions: ['.js', '.ts'],
      },
    },
  },
}


================================================
FILE: lib/configs/typescript.js
================================================
export default {
  extends: ['plugin:@typescript-eslint/recommended', 'prettier', 'plugin:escompat/typescript-2020'],
  parser: '@typescript-eslint/parser',
  plugins: ['@typescript-eslint', 'escompat', 'github'],
  rules: {
    camelcase: 'off',
    'no-unused-vars': 'off',
    'no-shadow': 'off',
    'no-invalid-this': 'off',
    '@typescript-eslint/no-invalid-this': ['error'],
    '@typescript-eslint/no-shadow': ['error'],
    '@typescript-eslint/interface-name-prefix': 'off',
    '@typescript-eslint/array-type': ['error', {default: 'array-simple'}],
    '@typescript-eslint/no-use-before-define': 'off',
    '@typescript-eslint/explicit-member-accessibility': 'off',
    '@typescript-eslint/explicit-function-return-type': 'off',
    '@typescript-eslint/no-non-null-assertion': 'off',
    '@typescript-eslint/no-unused-vars': 'error',
    '@typescript-eslint/explicit-module-boundary-types': 'off',
  },
}


================================================
FILE: lib/formatters/stylish-fixes.js
================================================
import childProcess from 'node:child_process'
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import SourceCodeFixer from 'eslint/lib/linter/source-code-fixer.js'
import getRuleURI from 'eslint-rule-documentation'

export default function stylishFixes(results) {
  let output = '\n'
  let errors = 0
  let warnings = 0
  const rootPath = process.cwd()

  for (const result of results) {
    const messages = result.messages

    if (messages.length === 0) {
      continue
    }

    errors += result.errorCount
    warnings += result.warningCount

    const relativePath = path.relative(rootPath, result.filePath)

    output += `${relativePath}\n`

    for (const message of messages) {
      output += `${message.line}:${message.column} ${message.ruleId || ''}`
      if (message.ruleId) {
        const ruleURI = getRuleURI(message.ruleId)
        if (ruleURI.found) {
          output += `  (${ruleURI.url})`
        }
      }
      output += `\n\t${message.message}\n`
    }

    if (messages.some(msg => msg.fix)) {
      const fixResult = SourceCodeFixer.applyFixes(result.source, messages)
      output += `\n\n$ eslint --fix ${relativePath}\n`
      output += diff(result.source, fixResult.output)
    }

    output += '\n\n'
  }

  const total = errors + warnings

  if (total > 0) {
    output += [
      '\u2716 ',
      total,
      pluralize(' problem', total),
      ' (',
      errors,
      pluralize(' error', errors),
      ', ',
      warnings,
      pluralize(' warning', warnings),
      ')\n',
    ].join('')
  }

  return total > 0 ? output : ''
}

function pluralize(word, count) {
  return count === 1 ? word : `${word}s`
}

function diff(a, b) {
  const aPath = path.join(os.tmpdir(), 'a.js')
  const bPath = path.join(os.tmpdir(), 'p.js')
  fs.writeFileSync(aPath, a, {encoding: 'utf8'})
  fs.writeFileSync(bPath, b, {encoding: 'utf8'})
  const result = childProcess.spawnSync('diff', ['-U5', aPath, bPath], {encoding: 'utf8'})
  return result.stdout.split('\n').slice(2).join('\n')
}


================================================
FILE: lib/index.js
================================================
import github from './plugin.js'
import flatBrowserConfig from './configs/flat/browser.js'
import flatInternalConfig from './configs/flat/internal.js'
import flatRecommendedConfig from './configs/flat/recommended.js'
import flatTypescriptConfig from './configs/flat/typescript.js'
import flatReactConfig from './configs/flat/react.js'
import browserConfig from './configs/browser.js'
import internalConfig from './configs/internal.js'
import recommendedConfig from './configs/recommended.js'
import typescriptConfig from './configs/typescript.js'
import reactConfig from './configs/react.js'

const getFlatConfig = () => ({
  browser: flatBrowserConfig,
  internal: flatInternalConfig,
  recommended: flatRecommendedConfig,
  typescript: flatTypescriptConfig,
  react: flatReactConfig,
})

export default {
  rules: github.rules,
  configs: {
    browser: browserConfig,
    internal: internalConfig,
    recommended: recommendedConfig,
    typescript: typescriptConfig,
    react: reactConfig,
  },
  getFlatConfigs: getFlatConfig,
}


================================================
FILE: lib/plugin.js
================================================
import {packageJson} from './utils/commonjs-json-wrappers.cjs'
import a11yNoVisuallyHiddenInteractiveElement from './rules/a11y-no-visually-hidden-interactive-element.js'
import a11yNoGenericLinkText from './rules/a11y-no-generic-link-text.js'
import a11yNoTitleAttribute from './rules/a11y-no-title-attribute.js'
import a11yAriaLabelIsWellFormatted from './rules/a11y-aria-label-is-well-formatted.js'
import a11yRoleSupportsAriaProps from './rules/a11y-role-supports-aria-props.js'
import a11ySvgHasAccessibleName from './rules/a11y-svg-has-accessible-name.js'
import arrayForeach from './rules/array-foreach.js'
import asyncCurrenttarget from './rules/async-currenttarget.js'
import asyncPreventdefault from './rules/async-preventdefault.js'
import authenticityToken from './rules/authenticity-token.js'
import filenamesMatchRegex from './rules/filenames-match-regex.js'
import getAttribute from './rules/get-attribute.js'
import jsClassName from './rules/js-class-name.js'
import noBlur from './rules/no-blur.js'
import noDNone from './rules/no-d-none.js'
import noDataset from './rules/no-dataset.js'
import noImplicitBuggyGlobals from './rules/no-implicit-buggy-globals.js'
import noInnerHTML from './rules/no-inner-html.js'
import noInnerText from './rules/no-innerText.js'
import noDynamicScriptTag from './rules/no-dynamic-script-tag.js'
import noThen from './rules/no-then.js'
import noUselessPassive from './rules/no-useless-passive.js'
import preferObservers from './rules/prefer-observers.js'
import requirePassiveEvents from './rules/require-passive-events.js'
import unescapedHtmlLiteral from './rules/unescaped-html-literal.js'

const {name, version} = packageJson

export default {
  meta: {name, version},
  rules: {
    'a11y-no-visually-hidden-interactive-element': a11yNoVisuallyHiddenInteractiveElement,
    'a11y-no-generic-link-text': a11yNoGenericLinkText,
    'a11y-no-title-attribute': a11yNoTitleAttribute,
    'a11y-aria-label-is-well-formatted': a11yAriaLabelIsWellFormatted,
    'a11y-role-supports-aria-props': a11yRoleSupportsAriaProps,
    'a11y-svg-has-accessible-name': a11ySvgHasAccessibleName,
    'array-foreach': arrayForeach,
    'async-currenttarget': asyncCurrenttarget,
    'async-preventdefault': asyncPreventdefault,
    'authenticity-token': authenticityToken,
    'filenames-match-regex': filenamesMatchRegex,
    'get-attribute': getAttribute,
    'js-class-name': jsClassName,
    'no-blur': noBlur,
    'no-d-none': noDNone,
    'no-dataset': noDataset,
    'no-implicit-buggy-globals': noImplicitBuggyGlobals,
    'no-inner-html': noInnerHTML,
    'no-innerText': noInnerText,
    'no-dynamic-script-tag': noDynamicScriptTag,
    'no-then': noThen,
    'no-useless-passive': noUselessPassive,
    'prefer-observers': preferObservers,
    'require-passive-events': requirePassiveEvents,
    'unescaped-html-literal': unescapedHtmlLiteral,
  },
}


================================================
FILE: lib/rules/a11y-aria-label-is-well-formatted.js
================================================
import jsxAstUtils from 'jsx-ast-utils'
import url from '../url.js'

const {getProp} = jsxAstUtils

export default {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce [aria-label] text to be formatted as you would visual text.',
      url: url(import.meta.url),
      recommended: false,
    },
    schema: [],
    messages: {
      formatting: '[aria-label] text should be formatted the same as you would visual text. Use sentence case.',
    },
  },

  create(context) {
    return {
      JSXOpeningElement: node => {
        const prop = getProp(node.attributes, 'aria-label')
        if (!prop) return

        const propValue = prop.value
        if (propValue.type !== 'Literal') return

        const ariaLabel = propValue.value
        if (ariaLabel.match(/^[a-z]+.*$/)) {
          context.report({
            node,
            messageId: 'formatting',
          })
        }
      },
    }
  },
}


================================================
FILE: lib/rules/a11y-no-generic-link-text.js
================================================
import jsxAstUtils from 'jsx-ast-utils'
import {getElementType} from '../utils/get-element-type.js'
import url from '../url.js'

const {getProp, getPropValue} = jsxAstUtils
const bannedLinkText = ['read more', 'here', 'click here', 'learn more', 'more']

/* Downcase and strip extra whitespaces and punctuation */
const stripAndDowncaseText = text => {
  return text
    .toLowerCase()
    .replace(/[.,/#!$%^&*;:{}=\-_`~()]/g, '')
    .replace(/\s{2,}/g, ' ')
    .trim()
}

export default {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow generic link text',
      url: url(import.meta.url),
      recommended: false,
    },
    deprecated: {
      message: 'It was replaced by `jsx-a11y/anchor-ambiguous-text`.',
      replacedBy: [
        {
          rule: {
            name: 'jsx-a11y/anchor-ambiguous-text',
          },
        },
      ],
    },
    replacedBy: ['jsx-a11y/anchor-ambiguous-text'],
    schema: [],
    messages: {
      avoidGenericLinkText:
        'Avoid setting generic link text like `Here`, `Click here`, `Read more`. Make sure that your link text is both descriptive and concise.',
      ariaLabelDescriptive: 'When using ARIA to set a more descriptive text, it must fully contain the visible label.',
    },
  },

  create(context) {
    return {
      JSXOpeningElement: node => {
        const elementType = getElementType(context, node)

        if (elementType !== 'a') return
        if (getProp(node.attributes, 'aria-labelledby')) return

        let cleanTextContent // text content we can reliably fetch

        const parent = node.parent
        let jsxTextNode
        if (parent.children && parent.children.length > 0 && parent.children[0].type === 'JSXText') {
          jsxTextNode = parent.children[0]
          cleanTextContent = stripAndDowncaseText(parent.children[0].value)
        }

        const ariaLabel = getPropValue(getProp(node.attributes, 'aria-label'))
        const cleanAriaLabelValue = ariaLabel && stripAndDowncaseText(ariaLabel)

        if (ariaLabel) {
          if (bannedLinkText.includes(cleanAriaLabelValue)) {
            context.report({
              node,
              messageId: 'avoidGenericLinkText',
            })
          }
          if (cleanTextContent && !cleanAriaLabelValue.includes(cleanTextContent)) {
            context.report({
              node,
              messageId: 'ariaLabelDescriptive',
            })
          }
        } else {
          if (cleanTextContent) {
            if (!bannedLinkText.includes(cleanTextContent)) return
            context.report({
              node: jsxTextNode,
              messageId: 'avoidGenericLinkText',
            })
          }
        }
      },
    }
  },
}


================================================
FILE: lib/rules/a11y-no-title-attribute.js
================================================
import jsxAstUtils from 'jsx-ast-utils'
import {getElementType} from '../utils/get-element-type.js'
import url from '../url.js'

const {getProp, getPropValue} = jsxAstUtils
const SEMANTIC_ELEMENTS = [
  'a',
  'button',
  'summary',
  'select',
  'option',
  'textarea',
  'input',
  'span',
  'div',
  'p',
  'h1',
  'h2',
  'h3',
  'h4',
  'h5',
  'h6',
  'details',
  'summary',
  'dialog',
  'tr',
  'th',
  'td',
  'label',
]

const ifSemanticElement = (context, node) => {
  const elementType = getElementType(context, node.openingElement, true)

  for (const semanticElement of SEMANTIC_ELEMENTS) {
    if (elementType === semanticElement) {
      return true
    }
  }
  return false
}

export default {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow using the title attribute',
      url: url(import.meta.url),
      recommended: false,
    },
    schema: [],
    messages: {
      titleAttribute: 'The title attribute is not accessible and should never be used unless for an `<iframe>`.',
    },
  },

  create(context) {
    return {
      JSXElement: node => {
        const elementType = getElementType(context, node.openingElement, true)
        if (elementType !== `iframe` && ifSemanticElement(context, node)) {
          const titleProp = getPropValue(getProp(node.openingElement.attributes, `title`))
          if (titleProp) {
            context.report({
              node,
              messageId: 'titleAttribute',
            })
          }
        }
      },
    }
  },
}


================================================
FILE: lib/rules/a11y-no-visually-hidden-interactive-element.js
================================================
import jsxAstUtils from 'jsx-ast-utils'
import {getElementType} from '../utils/get-element-type.js'
import {generateObjSchema} from 'eslint-plugin-jsx-a11y/lib/util/schemas.js'
import url from '../url.js'

const {getProp, getLiteralPropValue} = jsxAstUtils
const defaultClassName = 'sr-only'
const defaultcomponentName = 'VisuallyHidden'

const schema = generateObjSchema({
  className: {type: 'string'},
  componentName: {type: 'string'},
})

/** Note: we are not including input elements at this time
 * because a visually hidden input field might cause a false positive.
 * (e.g. fileUpload https://github.com/primer/react/pull/3492)
 */
const INTERACTIVE_ELEMENTS = ['a', 'button', 'summary', 'select', 'option', 'textarea']

const checkIfInteractiveElement = (context, node) => {
  const elementType = getElementType(context, node.openingElement)

  for (const interactiveElement of INTERACTIVE_ELEMENTS) {
    if (elementType === interactiveElement) {
      return true
    }
  }
  return false
}

// if the node is visually hidden recursively check if it has interactive children
const checkIfVisuallyHiddenAndInteractive = (context, options, node, isParentVisuallyHidden) => {
  const {className, componentName} = options
  if (node.type === 'JSXElement') {
    const classes = getLiteralPropValue(getProp(node.openingElement.attributes, 'className'))
    const isVisuallyHiddenElement = node.openingElement.name.name === componentName
    let hasSROnlyClass = false
    if (classes != null) {
      hasSROnlyClass = classes.includes(className)
    }
    let isHidden = false
    if (hasSROnlyClass || isVisuallyHiddenElement || !!isParentVisuallyHidden) {
      if (checkIfInteractiveElement(context, node)) {
        return true
      }
      isHidden = true
    }
    if (node.children && node.children.length > 0) {
      return (
        typeof node.children?.find(child =>
          checkIfVisuallyHiddenAndInteractive(context, options, child, !!isParentVisuallyHidden || isHidden),
        ) !== 'undefined'
      )
    }
  }
  return false
}

export default {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce that interactive elements are not visually hidden',
      url: url(import.meta.url),
      recommended: false,
    },
    schema: [schema],
    messages: {
      avoid:
        'Avoid visually hidding interactive elements. Visually hiding interactive elements can be confusing to sighted keyboard users as it appears their focus has been lost when they navigate to the hidden element.',
    },
  },

  create(context) {
    const {options} = context
    const config = options[0] || {}
    const className = config.className || defaultClassName
    const componentName = config.componentName || defaultcomponentName

    return {
      JSXElement: node => {
        if (checkIfVisuallyHiddenAndInteractive(context, {className, componentName}, node, false)) {
          context.report({
            node,
            messageId: 'avoid',
          })
        }
      },
    }
  },
}


================================================
FILE: lib/rules/a11y-role-supports-aria-props.js
================================================
// @ts-check
import {aria, roles} from 'aria-query'
import jsxAstUtils from 'jsx-ast-utils'
import {getRole} from '../utils/get-role.js'
import url from '../url.js'

const {getPropValue, propName} = jsxAstUtils

export default {
  meta: {
    type: 'problem',
    docs: {
      description:
        'enforce that elements with explicit or implicit roles defined contain only `aria-*` properties supported by that `role`.',
      url: url(import.meta.url),
      recommended: false,
    },
    schema: [],
    messages: {
      notSupported: 'The attribute {{attr}} is not supported by the role {{role}}.',
    },
  },

  create(context) {
    return {
      JSXOpeningElement(node) {
        // Get the element’s explicit or implicit role
        const role = getRole(context, node)

        // Return early if role could not be determined
        if (!role) return

        // Get allowed ARIA attributes:
        // - From the role itself
        let allowedProps = Object.keys(roles.get(role)?.props || {})
        // - From parent roles
        for (const parentRole of roles.get(role)?.superClass.flat() ?? []) {
          allowedProps = allowedProps.concat(Object.keys(roles.get(parentRole)?.props || {}))
        }
        // Dedupe, for performance
        allowedProps = Array.from(new Set(allowedProps))

        // Get prohibited ARIA attributes:
        // - From the role itself
        let prohibitedProps = roles.get(role)?.prohibitedProps || []
        // - From parent roles
        for (const parentRole of roles.get(role)?.superClass.flat() ?? []) {
          prohibitedProps = prohibitedProps.concat(roles.get(parentRole)?.prohibitedProps || [])
        }
        // - From comparing allowed vs all ARIA attributes
        prohibitedProps = prohibitedProps.concat(aria.keys().filter(x => !allowedProps.includes(x)))
        // Dedupe, for performance
        prohibitedProps = Array.from(new Set(prohibitedProps))

        for (const prop of node.attributes) {
          // Return early if prohibited ARIA attribute is set to an ignorable value
          if (getPropValue(prop) == null || prop.type === 'JSXSpreadAttribute') return

          if (prohibitedProps?.includes(propName(prop))) {
            context.report({
              node,
              messageId: 'notSupported',
              data: {
                attr: propName(prop),
                role,
              },
            })
          }
        }
      },
    }
  },
}


================================================
FILE: lib/rules/a11y-svg-has-accessible-name.js
================================================
import jsxAstUtils from 'jsx-ast-utils'
import {getElementType} from '../utils/get-element-type.js'
import url from '../url.js'

const {hasProp} = jsxAstUtils

export default {
  meta: {
    type: 'problem',
    docs: {
      description: 'require SVGs to have an accessible name',
      url: url(import.meta.url),
      recommended: false,
    },
    schema: [],
    messages: {
      accessibleName:
        '`<svg>` must have an accessible name. Set `aria-label` or `aria-labelledby`, or nest a `<title>` element. However, if the `<svg>` is purely decorative, hide it with `aria-hidden="true"` or `role="presentation"`.',
    },
  },

  create(context) {
    return {
      JSXOpeningElement: node => {
        const elementType = getElementType(context, node)
        if (elementType !== 'svg') return

        // Check if there is a nested title element that is the first non-whitespace child of the `<svg>`
        const childrenWithoutWhitespace = node.parent.children?.filter(({type, value}) =>
          type === 'JSXText' ? value.trim() !== '' : type !== 'JSXText',
        )

        const hasNestedTitleAsFirstChild =
          childrenWithoutWhitespace?.[0]?.type === 'JSXElement' &&
          childrenWithoutWhitespace?.[0]?.openingElement?.name?.name === 'title'

        // Check if `aria-label` or `aria-labelledby` is set
        const hasAccessibleName = hasProp(node.attributes, 'aria-label') || hasProp(node.attributes, 'aria-labelledby')

        // Check if SVG is decorative
        const isDecorative =
          hasProp(node.attributes, 'role', 'presentation') || hasProp(node.attributes, 'aria-hidden', 'true')

        if (elementType === 'svg' && !hasAccessibleName && !isDecorative && !hasNestedTitleAsFirstChild) {
          context.report({
            node,
            messageId: 'accessibleName',
          })
        }
      },
    }
  },
}


================================================
FILE: lib/rules/array-foreach.js
================================================
import url from '../url.js'

export default {
  meta: {
    type: 'suggestion',
    docs: {
      description: 'enforce `for..of` loops over `Array.forEach`',
      url: url(import.meta.url),
      recommended: true,
    },
    schema: [],
    messages: {
      preferForOf: 'Prefer for...of instead of Array.forEach',
    },
  },

  create(context) {
    return {
      CallExpression(node) {
        if (node.callee.property && node.callee.property.name === 'forEach') {
          context.report({node, messageId: 'preferForOf'})
        }
      },
    }
  },
}


================================================
FILE: lib/rules/async-currenttarget.js
================================================
import url from '../url.js'

export default {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow `event.currentTarget` calls inside of async functions',
      url: url(import.meta.url),
      recommended: false,
    },
    schema: [],
    messages: {
      asyncCurrentTarget: 'event.currentTarget inside an async function is error prone',
    },
  },

  create(context) {
    const scopeDidWait = new WeakSet()
    const sourceCode = context.sourceCode ?? context.getSourceCode()

    return {
      AwaitExpression(node) {
        scopeDidWait.add(sourceCode.getScope ? sourceCode.getScope(node) : context.getScope())
      },
      MemberExpression(node) {
        if (node.property && node.property.name === 'currentTarget') {
          let scope = sourceCode.getScope ? sourceCode.getScope(node) : context.getScope()
          while (scope) {
            if (scopeDidWait.has(scope)) {
              context.report({
                node,
                messageId: 'asyncCurrentTarget',
              })
              break
            }
            scope = scope.upper
          }
        }
      },
    }
  },
}


================================================
FILE: lib/rules/async-preventdefault.js
================================================
import url from '../url.js'

export default {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow `event.preventDefault` calls inside of async functions',
      url: url(import.meta.url),
      recommended: false,
    },
    schema: [],
    messages: {
      eventPreventDefault: 'event.preventDefault() inside an async function is error prone',
    },
  },

  create(context) {
    const scopeDidWait = new WeakSet()
    const sourceCode = context.sourceCode ?? context.getSourceCode()

    return {
      AwaitExpression(node) {
        scopeDidWait.add(sourceCode.getScope ? sourceCode.getScope(node) : context.getScope())
      },
      CallExpression(node) {
        if (node.callee.property && node.callee.property.name === 'preventDefault') {
          let scope = sourceCode.getScope ? sourceCode.getScope(node) : context.getScope()
          while (scope) {
            if (scopeDidWait.has(scope)) {
              context.report({
                node,
                messageId: 'eventPreventDefault',
              })
              break
            }
            scope = scope.upper
          }
        }
      },
    }
  },
}


================================================
FILE: lib/rules/authenticity-token.js
================================================
import url from '../url.js'

export default {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow usage of CSRF tokens in JavaScript',
      url: url(import.meta.url),
      recommended: false,
    },
    schema: [],
    messages: {
      authenticityTokenUsage:
        'Form CSRF tokens (authenticity tokens) should not be created in JavaScript and their values should not be used directly for XHR requests.',
    },
  },

  create(context) {
    function checkAuthenticityTokenUsage(node, str) {
      if (str.includes('authenticity_token')) {
        context.report({
          node,
          messageId: 'authenticityTokenUsage',
        })
      }
    }

    return {
      Literal(node) {
        if (typeof node.value === 'string') {
          checkAuthenticityTokenUsage(node, node.value)
        }
      },
    }
  },
}


================================================
FILE: lib/rules/filenames-match-regex.js
================================================
// This is adapted from https://github.com/selaux/eslint-plugin-filenames since it's no longer actively maintained
// and needed a fix for eslint v9
import path from 'node:path'
import parseFilename from '../utils/parse-filename.js'
import getExportedName from '../utils/get-exported-name.js'
import isIgnoredFilename from '../utils/is-ignored-filename.js'
import url from '../url.js'

export default {
  meta: {
    type: 'problem',
    docs: {
      description: 'require filenames to match a regex naming convention',
      url: url(import.meta.url),
      recommended: true,
    },
    schema: {
      type: 'array',
      minItems: 0,
      maxItems: 1,
      items: [
        {
          type: 'string',
        },
      ],
    },
    messages: {
      regex: "Filename '{{name}}' does not match the regex naming convention.",
    },
  },

  create(context) {
    // GitHub's default is kebab case or one hump camel case
    const defaultRegexp = /^[a-z0-9-]+(.[a-z0-9-]+)?$/
    const conventionRegexp = context.options[0] ? new RegExp(context.options[0]) : defaultRegexp
    const ignoreExporting = context.options[1] ? context.options[1] : false

    return {
      Program(node) {
        const filename = context.filename ?? context.getFilename()
        const absoluteFilename = path.resolve(filename)
        const parsed = parseFilename(absoluteFilename)
        const shouldIgnore = isIgnoredFilename(filename)
        const isExporting = Boolean(getExportedName(node))
        const matchesRegex = conventionRegexp.test(parsed.name)

        if (shouldIgnore) return
        if (ignoreExporting && isExporting) return
        if (!matchesRegex) {
          context.report({
            node,
            messageId: 'regex',
            data: {
              name: parsed.base,
            },
          })
        }
      },
    }
  },
}


================================================
FILE: lib/rules/get-attribute.js
================================================
import {svgElementAttributes} from '../utils/commonjs-json-wrappers.cjs'
import url from '../url.js'

const attributeCalls = /^(get|has|set|remove)Attribute$/
const validAttributeName = /^[a-z][a-z0-9-]*$/

// these are common SVG attributes that *must* have the correct case to work
const camelCaseAttributes = Object.values(svgElementAttributes)
  .reduce((all, elementAttrs) => all.concat(elementAttrs), [])
  .filter(name => !validAttributeName.test(name))

const validSVGAttributeSet = new Set(camelCaseAttributes)

// lowercase variants of camelCase SVG attributes are probably an error
const invalidSVGAttributeSet = new Set(camelCaseAttributes.map(name => name.toLowerCase()))

function isValidAttribute(name) {
  return validSVGAttributeSet.has(name) || (validAttributeName.test(name) && !invalidSVGAttributeSet.has(name))
}

export default {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow wrong usage of attribute names',
      url: url(import.meta.url),
      recommended: false,
    },
    fixable: 'code',
    schema: [],
    messages: {
      invalidAttribute: 'Attributes should be lowercase and hyphen separated, or part of the SVG whitelist.',
    },
  },
  create(context) {
    return {
      CallExpression(node) {
        if (!node.callee.property) return

        const calleeName = node.callee.property.name
        if (!attributeCalls.test(calleeName)) return

        const attributeNameNode = node.arguments[0]
        if (!attributeNameNode) return

        if (!isValidAttribute(attributeNameNode.value)) {
          context.report({
            node: attributeNameNode,
            messageId: 'invalidAttribute',
            fix(fixer) {
              return fixer.replaceText(attributeNameNode, `'${attributeNameNode.value.toLowerCase()}'`)
            },
          })
        }
      },
    }
  },
}


================================================
FILE: lib/rules/js-class-name.js
================================================
import url from '../url.js'

export default {
  meta: {
    type: 'suggestion',
    docs: {
      description: 'enforce a naming convention for js- prefixed classes',
      url: url(import.meta.url),
      recommended: false,
    },
    schema: [],
    messages: {
      jsClassName: 'js- class names should be lowercase and only contain dashes.',
      jsClassNameStatic: 'js- class names should be statically defined.',
    },
  },

  create(context) {
    const allJsClassNameRegexp = /\bjs-[_a-zA-Z0-9-]*/g
    const validJsClassNameRegexp = /^js(-[a-z0-9]+)+$/g
    const endWithJsClassNameRegexp = /\bjs-[_a-zA-Z0-9-]*$/g

    function checkStringFormat(node, str) {
      const matches = str.match(allJsClassNameRegexp) || []
      for (const match of matches) {
        if (!match.match(validJsClassNameRegexp)) {
          context.report({node, messageId: 'jsClassName'})
        }
      }
    }

    function checkStringEndsWithJSClassName(node, str) {
      if (str.match(endWithJsClassNameRegexp)) {
        context.report({node, messageId: 'jsClassNameStatic'})
      }
    }

    return {
      Literal(node) {
        if (typeof node.value === 'string') {
          checkStringFormat(node, node.value)

          if (
            node.parent &&
            node.parent.type === 'BinaryExpression' &&
            node.parent.operator === '+' &&
            node.parent.left.value
          ) {
            checkStringEndsWithJSClassName(node.parent.left, node.parent.left.value)
          }
        }
      },
      TemplateLiteral(node) {
        for (const quasi of node.quasis) {
          checkStringFormat(quasi, quasi.value.raw)

          if (quasi.tail === false) {
            checkStringEndsWithJSClassName(quasi, quasi.value.raw)
          }
        }
      },
    }
  },
}


================================================
FILE: lib/rules/no-blur.js
================================================
import url from '../url.js'

export default {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow usage of `Element.prototype.blur()`',
      url: url(import.meta.url),
      recommended: false,
    },
    schema: [],
    messages: {
      noBlur: 'Do not use element.blur(), instead restore the focus of a previous element.',
    },
  },
  create(context) {
    return {
      CallExpression(node) {
        if (node.callee.property && node.callee.property.name === 'blur') {
          context.report({node, messageId: 'noBlur'})
        }
      },
    }
  },
}


================================================
FILE: lib/rules/no-d-none.js
================================================
import url from '../url.js'

export default {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow usage the `d-none` CSS class',
      url: url(import.meta.url),
      recommended: false,
    },
    schema: [],
    messages: {
      preferHidden: 'Prefer hidden property to d-none class',
    },
  },
  create(context) {
    return {
      CallExpression(node) {
        if (
          node.callee.type === 'MemberExpression' &&
          node.callee.object.property &&
          node.callee.object.property.name === 'classList'
        ) {
          const invalidArgument = node.arguments.some(arg => {
            return arg.type === 'Literal' && arg.value === 'd-none'
          })
          if (invalidArgument) {
            context.report({
              node,
              messageId: 'preferHidden',
            })
          }
        }
      },
    }
  },
}


================================================
FILE: lib/rules/no-dataset.js
================================================
import url from '../url.js'

export default {
  meta: {
    type: 'problem',
    docs: {
      description: 'enforce usage of `Element.prototype.getAttribute` instead of `Element.prototype.datalist`',
      url: url(import.meta.url),
      recommended: false,
    },
    schema: [],
    messages: {
      useGetAttribute: "Use getAttribute('data-your-attribute') instead of dataset",
    },
  },

  create(context) {
    return {
      MemberExpression(node) {
        if (node.property && node.property.name === 'dataset') {
          context.report({node, messageId: 'useGetAttribute'})
        }
      },
    }
  },
}


================================================
FILE: lib/rules/no-dynamic-script-tag.js
================================================
import url from '../url.js'

export default {
  meta: {
    type: 'suggestion',
    docs: {
      description: 'disallow creating dynamic script tags',
      url: url(import.meta.url),
      recommended: true,
    },
    schema: [],
    messages: {
      noDynamicScriptTag: "Don't create dynamic script tags, add them in the server template instead.",
    },
  },

  create(context) {
    return {
      'CallExpression[callee.property.name="createElement"][arguments.length > 0]': function (node) {
        if (node.arguments[0].value !== 'script') return

        context.report({
          node: node.arguments[0],
          messageId: 'noDynamicScriptTag',
        })
      },
      'AssignmentExpression[left.property.name="type"][right.value="text/javascript"]': function (node) {
        context.report({
          node: node.right,
          messageId: 'noDynamicScriptTag',
        })
      },
    }
  },
}


================================================
FILE: lib/rules/no-implicit-buggy-globals.js
================================================
import url from '../url.js'

export default {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow implicit global variables',
      url: url(import.meta.url),
      recommended: true,
    },
    schema: [],
    messages: {
      implicitGlobalVariable: 'Implicit global variable, assign as global property instead.',
    },
  },

  create(context) {
    const sourceCode = context.sourceCode ?? context.getSourceCode()
    return {
      Program(node) {
        const scope = sourceCode.getScope(node) ? sourceCode.getScope(node) : context.getScope()

        for (const variable of scope.variables) {
          if (variable.writeable) {
            return
          }

          for (const def of variable.defs) {
            if (
              def.type === 'FunctionName' ||
              def.type === 'ClassName' ||
              (def.type === 'Variable' && def.parent.kind === 'const') ||
              (def.type === 'Variable' && def.parent.kind === 'let')
            ) {
              context.report({
                node: def.node,
                messageId: 'implicitGlobalVariable',
              })
            }
          }
        }
      },
    }
  },
}


================================================
FILE: lib/rules/no-inner-html.js
================================================
import url from '../url.js'

export default {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow `Element.prototype.innerHTML` in favor of `Element.prototype.textContent`',
      url: url(import.meta.url),
      recommended: false,
    },
    schema: [],
    messages: {
      noInnerHTML: 'Using innerHTML poses a potential security risk and should not be used. Prefer using textContent.',
    },
  },

  create(context) {
    return {
      'MemberExpression[property.name=innerHTML]': function (node) {
        context.report({
          node: node.property,
          messageId: 'noInnerHTML',
        })
      },
    }
  },
}


================================================
FILE: lib/rules/no-innerText.js
================================================
import url from '../url.js'

export default {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow `Element.prototype.innerText` in favor of `Element.prototype.textContent`',
      url: url(import.meta.url),
      recommended: false,
    },
    fixable: 'code',
    schema: [],
    messages: {
      preferTextContent: 'Prefer textContent to innerText',
    },
  },

  create(context) {
    return {
      MemberExpression(node) {
        // If the member expression is part of a call expression like `.innerText()` then it is not the same
        // as the `Element.innerText` property, and should not trigger a warning
        if (node.parent.type === 'CallExpression') return

        if (node.property && node.property.name === 'innerText') {
          context.report({
            meta: {
              fixable: 'code',
            },
            node: node.property,
            messageId: 'preferTextContent',
            fix(fixer) {
              return fixer.replaceText(node.property, 'textContent')
            },
          })
        }
      },
    }
  },
}


================================================
FILE: lib/rules/no-then.js
================================================
import url from '../url.js'

export default {
  meta: {
    type: 'suggestion',
    docs: {
      description: 'enforce using `async/await` syntax over Promises',
      url: url(import.meta.url),
      recommended: true,
    },
    schema: [],
    messages: {
      preferAsyncAwait: 'Prefer async/await to Promise.{{method}}()',
    },
  },

  create(context) {
    return {
      MemberExpression(node) {
        if (node.property && node.property.name === 'then') {
          context.report({
            node: node.property,
            messageId: 'preferAsyncAwait',
            data: {method: 'then'},
          })
        } else if (node.property && node.property.name === 'catch') {
          context.report({
            node: node.property,
            messageId: 'preferAsyncAwait',
            data: {method: 'catch'},
          })
        }
      },
    }
  },
}


================================================
FILE: lib/rules/no-useless-passive.js
================================================
import url from '../url.js'

const passiveEventListenerNames = new Set([
  'touchstart',
  'touchmove',
  'touchenter',
  'touchend',
  'touchleave',
  'wheel',
  'mousewheel',
])

const propIsPassiveTrue = prop => prop.key && prop.key.name === 'passive' && prop.value && prop.value.value === true

export default {
  meta: {
    type: 'suggestion',
    docs: {
      description: 'disallow marking a event handler as passive when it has no effect',
      url: url(import.meta.url),
      recommended: false,
    },
    fixable: 'code',
    schema: [],
    messages: {
      passive: '"{{name}}" event listener is not cancellable and so `passive: true` does nothing.',
    },
  },

  create(context) {
    return {
      ['CallExpression[callee.property.name="addEventListener"]']: function (node) {
        const [name, listener, options] = node.arguments
        if (name.type !== 'Literal') return
        if (passiveEventListenerNames.has(name.value)) return
        if (options && options.type === 'ObjectExpression') {
          const i = options.properties.findIndex(propIsPassiveTrue)
          if (i === -1) return
          const passiveProp = options.properties[i]
          const l = options.properties.length
          const source = context.sourceCode ?? context.getSourceCode()
          context.report({
            node: passiveProp,
            messageId: 'passive',
            data: {name: name.value},
            fix(fixer) {
              const removals = []
              if (l === 1) {
                removals.push(options)
                removals.push(...source.getTokensBetween(listener, options))
              } else {
                removals.push(passiveProp)
                if (i > 0) {
                  removals.push(...source.getTokensBetween(options.properties[i - 1], passiveProp))
                } else {
                  removals.push(...source.getTokensBetween(passiveProp, options.properties[i + 1]))
                }
              }
              return removals.map(t => fixer.remove(t))
            },
          })
        }
      },
    }
  },
}


================================================
FILE: lib/rules/prefer-observers.js
================================================
import url from '../url.js'

const observerMap = {
  scroll: 'IntersectionObserver',
  resize: 'ResizeObserver',
}
export default {
  meta: {
    type: 'suggestion',
    docs: {
      description: 'disallow poorly performing event listeners',
      url: url(import.meta.url),
      recommended: false,
    },
    schema: [],
    messages: {
      avoid: 'Avoid using "{{name}}" event listener. Consider using {{observer}} instead',
    },
  },

  create(context) {
    return {
      ['CallExpression[callee.property.name="addEventListener"]']: function (node) {
        const [name] = node.arguments
        if (name.type !== 'Literal') return
        if (!(name.value in observerMap)) return
        context.report({
          node,
          messageId: 'avoid',
          data: {name: name.value, observer: observerMap[name.value]},
        })
      },
    }
  },
}


================================================
FILE: lib/rules/require-passive-events.js
================================================
import url from '../url.js'

const passiveEventListenerNames = new Set([
  'touchstart',
  'touchmove',
  'touchenter',
  'touchend',
  'touchleave',
  'wheel',
  'mousewheel',
])

const propIsPassiveTrue = prop => prop.key && prop.key.name === 'passive' && prop.value && prop.value.value === true

export default {
  meta: {
    type: 'suggestion',
    docs: {
      description: 'enforce marking high frequency event handlers as passive',
      url: url(import.meta.url),
      recommended: false,
    },
    schema: [],
    messages: {
      passive: 'High Frequency Events like "{{name}}" should be `passive: true`',
    },
  },

  create(context) {
    return {
      ['CallExpression[callee.property.name="addEventListener"]']: function (node) {
        const [name, listener, options] = node.arguments
        if (!listener) return
        if (name.type !== 'Literal') return
        if (!passiveEventListenerNames.has(name.value)) return
        if (options && options.type === 'ObjectExpression' && options.properties.some(propIsPassiveTrue)) return
        context.report({node, messageId: 'passive', data: {name: name.value}})
      },
    }
  },
}


================================================
FILE: lib/rules/unescaped-html-literal.js
================================================
import url from '../url.js'

export default {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow unescaped HTML literals',
      url: url(import.meta.url),
      recommended: false,
    },
    schema: [],
    messages: {
      unescapedHtmlLiteral: 'Unescaped HTML literal. Use html`` tag template literal for secure escaping.',
    },
  },

  create(context) {
    const htmlOpenTag = /^\s*<[a-zA-Z]/

    return {
      Literal(node) {
        if (!htmlOpenTag.test(node.value)) return

        context.report({
          node,
          messageId: 'unescapedHtmlLiteral',
        })
      },
      TemplateLiteral(node) {
        if (!htmlOpenTag.test(node.quasis[0].value.raw)) return

        if (!node.parent.tag || node.parent.tag.name !== 'html') {
          context.report({
            node,
            messageId: 'unescapedHtmlLiteral',
          })
        }
      },
    }
  },
}


================================================
FILE: lib/url.js
================================================
import {packageJson} from './utils/commonjs-json-wrappers.cjs'
import path from 'node:path'
import {fileURLToPath} from 'node:url'

const {homepage, version} = packageJson

const getUrl = id => {
  const url = new URL(homepage)
  const rule = path.basename(fileURLToPath(id), '.js')
  url.hash = ''
  url.pathname += `/blob/v${version}/docs/rules/${rule}.md`
  return url.toString()
}

export default getUrl


================================================
FILE: lib/utils/commonjs-json-wrappers.cjs
================================================
const svgElementAttributes = require('svg-element-attributes')
const packageJson = require('../../package.json')

module.exports.svgElementAttributes = svgElementAttributes
module.exports.packageJson = packageJson


================================================
FILE: lib/utils/get-element-type.js
================================================
import jsxAstUtils from 'jsx-ast-utils'

const {elementType, getProp, getLiteralPropValue} = jsxAstUtils

/*
Allows custom component to be mapped to an element type.
When a default is set, all instances of the component will be mapped to the default.
If a prop determines the type, it can be specified with `props`.

For now, we only support the mapping of one prop type to an element type, rather than combinations of props.
*/
export function getElementType(context, node, lazyElementCheck = false) {
  const {settings} = context

  if (lazyElementCheck) {
    return elementType(node)
  }

  // check if the node contains a polymorphic prop
  const polymorphicPropName = settings?.github?.polymorphicPropName ?? 'as'

  const prop = getProp(node.attributes, polymorphicPropName)
  const literalPropValue = getLiteralPropValue(getProp(node.attributes, polymorphicPropName))
  let checkConditionalMap = true

  // If the prop is not a literal and we cannot determine it, don't fall back to the conditional map value, if it exists
  if (prop && !literalPropValue) {
    checkConditionalMap = false
  }
  const rawElement = getLiteralPropValue(getProp(node.attributes, polymorphicPropName)) ?? elementType(node)

  // if a component configuration does not exists, return the raw element
  if (!settings?.github?.components?.[rawElement]) return rawElement

  // check if the default component is also defined in the configuration
  return checkConditionalMap ? settings.github.components[rawElement] : rawElement
}


================================================
FILE: lib/utils/get-exported-name.js
================================================
function getNodeName(node, options) {
  const op = options || []

  if (node.type === 'Identifier') {
    return node.name
  }

  if (node.id && node.id.type === 'Identifier') {
    return node.id.name
  }

  if (op[2] && node.type === 'CallExpression' && node.callee.type === 'Identifier') {
    return node.callee.name
  }
}

export default function getExportedName(programNode, options) {
  for (let i = 0; i < programNode.body.length; i += 1) {
    const node = programNode.body[i]

    if (node.type === 'ExportDefaultDeclaration') {
      return getNodeName(node.declaration, options)
    }

    if (
      node.type === 'ExpressionStatement' &&
      node.expression.type === 'AssignmentExpression' &&
      node.expression.left.type === 'MemberExpression' &&
      node.expression.left.object.type === 'Identifier' &&
      node.expression.left.object.name === 'module' &&
      node.expression.left.property.type === 'Identifier' &&
      node.expression.left.property.name === 'exports'
    ) {
      return getNodeName(node.expression.right, options)
    }
  }
}


================================================
FILE: lib/utils/get-role.js
================================================
import jsxAstUtils from 'jsx-ast-utils'
import {elementRoles} from 'aria-query'
import {getElementType} from './get-element-type.js'
import ObjectMap from './object-map.js'

const {getProp, getLiteralPropValue} = jsxAstUtils
const elementRolesMap = cleanElementRolesMap()

/*
  Returns an element roles map which uses `aria-query`'s elementRoles as the foundation.
  We additionally clean the data so we're able to fetch a role using a key we construct based on the node we're looking at.
  In a few scenarios, we stray from the roles returned by `aria-query` and hard code the mapping.
*/
function cleanElementRolesMap() {
  const rolesMap = new ObjectMap()

  for (const [key, value] of elementRoles.entries()) {
    // - Remove empty `attributes` key
    if (!key.attributes || key.attributes?.length === 0) {
      delete key.attributes
    }
    rolesMap.set(key, value)
  }
  // Remove insufficiently-disambiguated `menuitem` entry
  rolesMap.delete({name: 'menuitem'})
  // Disambiguate `menuitem` and `menu` roles by `type`
  rolesMap.set({name: 'menuitem', attributes: [{name: 'type', value: 'command'}]}, ['menuitem'])
  rolesMap.set({name: 'menuitem', attributes: [{name: 'type', value: 'radio'}]}, ['menuitemradio'])
  rolesMap.set({name: 'menuitem', attributes: [{name: 'type', value: 'toolbar'}]}, ['toolbar'])
  rolesMap.set({name: 'menu', attributes: [{name: 'type', value: 'toolbar'}]}, ['toolbar'])

  /* These have constraints defined in aria-query's `elementRoles` which depend on knowledge of ancestor roles which we cant accurately determine in a linter context.
     However, we benefit more from assuming the role, than assuming it's generic or undefined so we opt to hard code the mapping */
  rolesMap.set({name: 'aside'}, ['complementary']) // `aside` still maps to `complementary` in https://www.w3.org/TR/html-aria/#docconformance.
  rolesMap.set({name: 'li'}, ['listitem']) // `li` can be generic if it's not within a list but we would never want to render `li` outside of a list.

  return rolesMap
}

/*
  Determine role of an element, based on its name and attributes.
  We construct a key and look up the element's role in `elementRolesMap`.
  If there is no match, we return undefined.
*/
export function getRole(context, node) {
  // Early return if role is explicitly set
  const explicitRole = getLiteralPropValue(getProp(node.attributes, 'role'))
  if (explicitRole) {
    return explicitRole
  } else if (getProp(node.attributes, 'role')) {
    // If role is set to anything other than a literal prop
    return undefined
  }

  // Assemble a key for looking-up the element’s role in the `elementRolesMap`
  // - Get the element’s name
  const key = {name: getElementType(context, node)}

  for (const prop of [
    'aria-label',
    'aria-labelledby',
    'alt',
    'type',
    'size',
    'role',
    'href',
    'multiple',
    'scope',
    'name',
  ]) {
    if ((prop === 'aria-labelledby' || prop === 'aria-label') && !['section', 'form'].includes(key.name)) continue
    if (prop === 'name' && key.name !== 'form') continue
    if (prop === 'href' && key.name !== 'a' && key.name !== 'area') continue
    if (prop === 'alt' && key.name !== 'img') continue

    const propOnNode = getProp(node.attributes, prop)

    if (!('attributes' in key)) {
      key.attributes = []
    }
    // Disambiguate "undefined" props
    if (propOnNode === undefined && prop === 'alt' && key.name === 'img') {
      key.attributes.push({name: prop, constraints: ['undefined']})
      continue
    }

    const value = getLiteralPropValue(propOnNode)
    if (propOnNode) {
      if (
        prop === 'href' ||
        prop === 'aria-labelledby' ||
        prop === 'aria-label' ||
        prop === 'name' ||
        (prop === 'alt' && value !== '')
      ) {
        key.attributes.push({name: prop, constraints: ['set']})
      } else if (value || (value === '' && prop === 'alt')) {
        key.attributes.push({name: prop, value})
      }
    }
  }

  // - Remove empty `attributes` key
  if (!key.attributes || key.attributes?.length === 0) {
    delete key.attributes
  }

  // Get the element’s implicit role
  return elementRolesMap.get(key)?.[0]
}


================================================
FILE: lib/utils/is-ignored-filename.js
================================================
const ignoredFilenames = ['<text>', '<input>']

export default function isIgnoredFilename(filename) {
  return ignoredFilenames.indexOf(filename) !== -1
}


================================================
FILE: lib/utils/object-map.js
================================================
// @ts-check
import {isDeepStrictEqual} from 'node:util'

/**
 * ObjectMap extends Map, but determines key equality using Node.js’ `util.isDeepStrictEqual` rather than using [SameValueZero](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#key_equality). This makes using objects as keys a bit simpler.
 */
export default class ObjectMap extends Map {
  #data

  constructor(iterable = []) {
    super()
    this.#data = iterable
  }

  clear() {
    this.#data = []
  }

  delete(key) {
    if (!this.has(key)) {
      return false
    }
    this.#data = this.#data.filter(([existingKey]) => !isDeepStrictEqual(existingKey, key))
    return true
  }

  entries() {
    return this.#data[Symbol.iterator]()
  }

  forEach(cb) {
    for (const [key, value] of this.#data) {
      cb(value, key, this.#data)
    }
  }

  get(key) {
    return this.#data.find(([existingKey]) => isDeepStrictEqual(existingKey, key))?.[1]
  }

  has(key) {
    return this.#data.findIndex(([existingKey]) => isDeepStrictEqual(existingKey, key)) !== -1
  }

  keys() {
    return this.#data.map(([key]) => key)[Symbol.iterator]()
  }

  set(key, value) {
    this.delete(key)
    this.#data.push([key, value])
    return this
  }

  values() {
    return this.#data.map(([, value]) => value)[Symbol.iterator]()
  }
}


================================================
FILE: lib/utils/parse-filename.js
================================================
import path from 'node:path'

export default function parseFilename(filename) {
  const ext = path.extname(filename)

  return {
    dir: path.dirname(filename),
    base: path.basename(filename),
    ext,
    name: path.basename(filename, ext),
  }
}


================================================
FILE: package.json
================================================
{
  "name": "eslint-plugin-github",
  "version": "0.0.0-dev",
  "type": "module",
  "description": "An opinionated collection of ESLint shared configs and rules used by GitHub.",
  "main": "lib/index.js",
  "entries": [
    "lib/formatters/stylish-fixes.js"
  ],
  "bin": {
    "eslint-ignore-errors": "bin/eslint-ignore-errors.js"
  },
  "scripts": {
    "eslint-check": "eslint-config-prettier .eslintrc.js",
    "lint": "npm-run-all \"lint:*\"",
    "lint:eslint-docs": "npm run update:eslint-docs -- --check",
    "lint:js": "eslint .",
    "pretest": "mkdir -p node_modules/ && ln -fs $(pwd) node_modules/",
    "test": "npm run eslint-check && npm run lint && mocha tests/**/*.js tests/**/*.mjs",
    "update:eslint-docs": "eslint-doc-generator",
    "test-examples:legacy": "cd test-examples/legacy && npm install && npm run lint",
    "test-examples:flat": "cd test-examples/flat && npm install && npm run lint"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/github/eslint-plugin-github.git"
  },
  "author": "GitHub, Inc.",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/github/eslint-plugin-github/issues"
  },
  "homepage": "https://github.com/github/eslint-plugin-github#readme",
  "dependencies": {
    "@eslint/compat": "^2.0.0",
    "@eslint/eslintrc": "^3.1.0",
    "@eslint/js": "^9.14.0",
    "@github/browserslist-config": "^1.0.0",
    "@typescript-eslint/eslint-plugin": "^8.0.0",
    "@typescript-eslint/parser": "^8.0.0",
    "aria-query": "^5.3.0",
    "eslint-config-prettier": ">=8.0.0",
    "eslint-plugin-escompat": "^3.11.3",
    "eslint-plugin-eslint-comments": "^3.2.0",
    "eslint-plugin-filenames": "^1.3.2",
    "eslint-plugin-i18n-text": "^1.0.1",
    "eslint-plugin-import": "^2.31.0",
    "eslint-plugin-jsx-a11y": "^6.10.2",
    "eslint-plugin-no-only-tests": "^3.0.0",
    "eslint-plugin-prettier": "^5.2.1",
    "eslint-rule-documentation": ">=1.0.0",
    "globals": "^16.0.0",
    "jsx-ast-utils": "^3.3.2",
    "prettier": "^3.0.0",
    "svg-element-attributes": "^1.3.1",
    "typescript": "^5.7.3",
    "typescript-eslint": "^8.14.0"
  },
  "prettier": "@github/prettier-config",
  "browserslist": "extends @github/browserslist-config",
  "peerDependencies": {
    "eslint": "^8 || ^9"
  },
  "files": [
    "bin/*",
    "lib/*"
  ],
  "devDependencies": {
    "@github/prettier-config": "0.0.6",
    "chai": "^6.0.1",
    "eslint": "^9.14.0",
    "eslint-doc-generator": "^2.2.2",
    "eslint-plugin-eslint-plugin": "^7.0.0",
    "mocha": "^11.0.1",
    "npm-run-all": "^4.1.5"
  }
}


================================================
FILE: test-examples/flat/eslint.config.mjs
================================================
import github from 'eslint-plugin-github'

export default [
  github.getFlatConfigs().recommended,
  github.getFlatConfigs().browser,
  github.getFlatConfigs().react,
  ...github.getFlatConfigs().typescript,
  {
    files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],
    ignores: ['eslint.config.mjs'],
    rules: {
      'github/array-foreach': 'error',
      'github/no-then': 'error',
      'github/no-blur': 'error',
      'github/async-preventdefault': 'error',
      'github/async-currenttarget': 'error',
      'github/no-useless-passive': 'error',
      'github/filenames-match-regex': 'error',
    },
  },
]


================================================
FILE: test-examples/flat/package.json
================================================
{
  "name": "flat",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "lint": "cross-env ESLINT_USE_FLAT_CONFIG=true eslint src --report-unused-disable-directives"
  },
  "devDependencies": {
    "@eslint/js": "^9.5.0",
    "@types/node": "^20.14.5",
    "cross-env": "^7.0.3",
    "eslint": "^9.14.0",
    "eslint-plugin-github": "file:../..",
    "typescript": "^5.6.3",
    "typescript-eslint": "^8.12.2"
  }
}


================================================
FILE: test-examples/flat/src/forEachTest.js
================================================
const testing = [
  [{name: 'github'}, ['test1']],
  [{name: 'mona'}, ['test2']],
]
const objectMap = new ObjectMap(testing)
const values = []
const keys = []

objectMap.forEach((value, key) => {
  values.push(value)
  keys.push(key)
})

================================================
FILE: test-examples/flat/src/getAttribute.js
================================================
function foobar(el) {
  el.getAttribute('autoComplete')
}

const title = document.createElement('h1')
title.textContent = `${title}!`

foobar(title)

document.addEventListener('click', async function (event) {
  const data = await fetch()

  event.preventDefault()
})

window.addEventListener(
  'scroll',
  () => {
    console.log('Scroll event fired!')
  },
  {passive: true},
)

document.addEventListener('click', async function (event) {
  // event.currentTarget will be an HTMLElement
  const url = event.currentTarget.getAttribute('data-url')
  const data = await fetch(url)

  // But now, event.currentTarget will be null
  const text = event.currentTarget.getAttribute('data-text')
  // ...
})


================================================
FILE: test-examples/flat/src/jsx.tsx
================================================
const Components = () => {
  return (
    <a aria-label="learn more" src="https://www.github.com" title="A home for all developers">
      GitHub
    </a>
  )
}

================================================
FILE: test-examples/flat/src/noBlur.js
================================================
function focusInput() {
  const textField = document.getElementById("sampleText")

  textField.focus()

  setTimeout(() => {
    textField.blur()
  }, 3000)
}

================================================
FILE: test-examples/flat/src/thisTypescriptTest.ts
================================================
class Safe {
  contents: string;

  constructor(contents: string) {
    this.contents = contents;
  }

  printContents() {
    console.log(this.contents);
  }
}

var foo = function() {
  this.a = 0;
  baz(() => this);
};


================================================
FILE: test-examples/legacy/.eslintrc.cjs
================================================
module.exports = {
  root: true,
  env: { es2022: true },
  extends: [
    'eslint:recommended',
    'plugin:github/browser',
    'plugin:github/recommended',
    'plugin:github/react',
    'plugin:github/typescript',
  ],
  settings: {},
  ignorePatterns: ['.eslintrc.cjs'],
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
  },
  plugins: ['github'],
  rules: {
    'no-unused-vars': 'off',
    'github/no-blur': 'error',
    'github/no-innerText': 'warn',
  },
};

================================================
FILE: test-examples/legacy/package.json
================================================
{
  "name": "legacy",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "lint": "cross-env ESLINT_USE_FLAT_CONFIG=false eslint src --ext js,jsx,ts,tsx --report-unused-disable-directives"
  },
  "devDependencies": {
    "@types/node": "^20.14.5",
    "@typescript-eslint/parser": "^8.0.0",
    "cross-env": "^7.0.3",
    "eslint": "^8.57.0",
    "eslint-plugin-github": "file:../..",
    "typescript": "^5.6.3",
    "typescript-eslint": "^8.12.2"
  }
}

================================================
FILE: test-examples/legacy/src/forEachTest.js
================================================
const testing = [
  [{name: 'github'}, ['test1']],
  [{name: 'mona'}, ['test2']],
]
const objectMap = new ObjectMap(testing)
const values = []
const keys = []

objectMap.forEach((value, key) => {
  values.push(value)
  keys.push(key)
})

================================================
FILE: test-examples/legacy/src/getAttribute.js
================================================
function foobar(el) {
  el.getAttribute('autoComplete')
}

const title = document.createElement('h1')
title.textContent = `${title}!`

foobar(title)


================================================
FILE: test-examples/legacy/src/jsx.tsx
================================================
const Components = () => {
  return (
    <a aria-label="learn more" src="https://www.github.com" title="A home for all developers">
      GitHub
    </a>
  )
}

================================================
FILE: test-examples/legacy/src/noBlur.js
================================================
function focusInput() {
  const textField = document.getElementById("sampleText")

  textField.focus()

  setTimeout(() => {
    textField.blur()
  }, 3000)
}

================================================
FILE: test-examples/legacy/src/thisTypescriptTest.ts
================================================
class Safe {
  contents: string;

  constructor(contents: string) {
    this.contents = contents;
  }

  printContents() {
    console.log(this.contents);
  }
}

var foo = function() {
  this.a = 0;
  baz(() => this);
};

================================================
FILE: tests/a11y-aria-label-is-well-formatted.js
================================================
import rule from '../lib/rules/a11y-aria-label-is-well-formatted.js'
import {RuleTester} from 'eslint'

const ruleTester = new RuleTester({
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
    ecmaFeatures: {
      jsx: true,
    },
  },
})

const errorMessage = '[aria-label] text should be formatted the same as you would visual text. Use sentence case.'

ruleTester.run('a11y-aria-label-is-well-formatted', rule, {
  valid: [
    {code: "<a aria-labelledby='someId' href='#'>Read more</a>;"},
    {code: "<a aria-label={someName} href='#'>Read more</a>;"},
    {code: "<a aria-label='This is a label'></a>;"},
    {code: "<a aria-label='Valid'></a>;"},
    {code: "<a aria-label='VALID'></a>;"},
    {code: '<Link aria-label="Valid" href="#">Read more</Link>'},
  ],
  invalid: [
    {code: "<a aria-label='close modal'></a>;", errors: [{message: errorMessage}]},
    {code: "<a aria-label='submit'></a>;", errors: [{message: errorMessage}]},
    {code: "<a aria-label='submit.yml'></a>;", errors: [{message: errorMessage}]},
    {code: "<a aria-label='this-is-not-an-id'></a>;", errors: [{message: errorMessage}]},
  ],
})


================================================
FILE: tests/a11y-no-generic-link-text.js
================================================
import rule from '../lib/rules/a11y-no-generic-link-text.js'
import {RuleTester} from 'eslint'

const ruleTester = new RuleTester({
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
    ecmaFeatures: {
      jsx: true,
    },
  },
})

const errorMessage =
  'Avoid setting generic link text like `Here`, `Click here`, `Read more`. Make sure that your link text is both descriptive and concise.'

ruleTester.run('a11y-no-generic-link-text', rule, {
  valid: [
    {code: "<a href='#'>GitHub Home</a>;"},
    {code: "<Box><a href='#'>GitHub Home</a></Box>;"},
    {code: "<a aria-label='Read more about our project' href='#'>Read more</a>;"},
    {code: "<a aria-labelledby='someId' href='#'>Read more</a>;"},
    {code: '<summary>Read more</summary>;'},
    {
      code: '<Link as="button" href="#">Read more</Link>',
      settings: {
        github: {
          components: {
            Link: 'a',
          },
        },
      },
    },
  ],
  invalid: [
    {
      code: '<ButtonLink href="#">Read more</ButtonLink>',
      errors: [{message: errorMessage}],
      settings: {
        github: {
          components: {
            ButtonLink: 'a',
          },
        },
      },
    },
    {
      code: '<Link href="#">Read more</Link>',
      errors: [{message: errorMessage}],
      settings: {
        github: {
          components: {
            Link: 'a',
          },
        },
      },
    },
    {
      code: '<Test as="a" href="#">Read more</Test>',
      errors: [{message: errorMessage}],
    },
    {
      code: "<Box><a href='#'>Click here</a></Box>;",
      errors: [{message: errorMessage}],
    },
    {code: '<a>Click here*</a>;', errors: [{message: errorMessage}]},
    {code: '<a>Learn more.</a>;', errors: [{message: errorMessage}]},
    {code: "<a aria-label='read more!!!'></a>;", errors: [{message: errorMessage}]},
    {code: "<a aria-label='click here.'></a>;", errors: [{message: errorMessage}]},
    {code: "<a aria-label='Here'></a>;", errors: [{message: errorMessage}]},
    {
      code: "<a aria-label='Does not include visible label'>Read more!</a>;",
      errors: [
        {
          message: 'When using ARIA to set a more descriptive text, it must fully contain the visible label.',
        },
      ],
    },
  ],
})


================================================
FILE: tests/a11y-no-title-attribute.js
================================================
import rule from '../lib/rules/a11y-no-title-attribute.js'
import {RuleTester} from 'eslint'

const ruleTester = new RuleTester({
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
    ecmaFeatures: {
      jsx: true,
    },
  },
})

const errorMessage = 'The title attribute is not accessible and should never be used unless for an `<iframe>`.'

ruleTester.run('a11y-no-title-attribute', rule, {
  valid: [
    {code: '<button>Submit</button>'},
    {code: '<iframe title="an allowed title">GitHub</iframe>'},
    {code: '<span>some information</span>'},
    {code: '<a href="github.com">GitHub</a>'},
    {
      code: '<Component title="some title">Submit</Component>',
      settings: {
        github: {
          components: {
            Component: 'iframe',
          },
        },
      },
    },
    {
      // Note: we are only checking semantic elements. We cannot make assumptions about how a React Components is using the title prop.
      code: '<Link title="some title">Submit</Link>',
      settings: {
        github: {
          components: {
            Link: 'a',
          },
        },
      },
    },
    {
      // Note: we are only checking semantic elements. We cannot make assumptions about how a React Components is using the title prop.
      code: '<Link as="a" title="some title">Submit</Link>',
    },
  ],
  invalid: [
    {code: '<a title="some title" href="github.com">GitHub</a>', errors: [{message: errorMessage}]},
    {code: '<span><button title="some title">submit</button></span>', errors: [{message: errorMessage}]},
  ],
})


================================================
FILE: tests/a11y-no-visually-hidden-interactive-element.js
================================================
import rule from '../lib/rules/a11y-no-visually-hidden-interactive-element.js'
import {RuleTester} from 'eslint'

const ruleTester = new RuleTester({
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
    ecmaFeatures: {
      jsx: true,
    },
  },
})

const errorMessage =
  'Avoid visually hidding interactive elements. Visually hiding interactive elements can be confusing to sighted keyboard users as it appears their focus has been lost when they navigate to the hidden element.'

ruleTester.run('a11y-no-visually-hidden-interactive-element', rule, {
  valid: [
    {code: '<VisuallyHidden as="h2">Submit</VisuallyHidden>'},
    {code: "<div className='sr-only'>Text</div>;"},
    {code: '<VisuallyHidden><div>Text</div></VisuallyHidden>'},
    {code: "<div className='other visually-hidden'>Text</div>;"},
    {code: "<span className='sr-only'>Text</span>;"},
    {code: "<button className='other'>Submit</button>"},
    {code: "<input className='sr-only' />"},
    {
      code: "<Foo className={({isOn}) => { return isOn || isOnProps ? `${className} selected`.trim() : className ?? ''}}/>",
    },
    {code: "<a className='other show-on-focus'>skip to main content</a>"},
    {code: '<button>Submit</button>'},
    {
      code: "<button className='sr-only'>Submit</button>",
      options: [
        {
          className: 'visually-hidden',
        },
      ],
    },
    {
      code: "<VisuallyHidden as='button'>Submit</VisuallyHidden>",
      options: [
        {
          componentName: 'Hidden',
        },
      ],
      errors: [{message: errorMessage}],
    },
    {
      code: "<VisuallyHidden as='button'>Submit</VisuallyHidden>",
      settings: {
        github: {
          polymorphicPropName: 'html',
        },
      },
    },
  ],
  invalid: [
    {code: '<VisuallyHidden as="button">Submit</VisuallyHidden>', errors: [{message: errorMessage}]},
    {code: '<VisuallyHidden><button>Submit</button></VisuallyHidden>', errors: [{message: errorMessage}]},
    {
      code: '<VisuallyHidden><button class="sr-only">Submit</button></VisuallyHidden>',
      errors: [{message: errorMessage}],
    },
    {code: "<button className='sr-only'>Submit</button>", errors: [{message: errorMessage}]},
    {code: '<VisuallyHidden><div><button>Submit</button></div></VisuallyHidden>', errors: [{message: errorMessage}]},
    {code: "<a className='other sr-only' href='github.com'>GitHub</a>", errors: [{message: errorMessage}]},
    {code: "<summary className='sr-only'>Toggle open</summary>", errors: [{message: errorMessage}]},
    {code: "<textarea className='sr-only' />", errors: [{message: errorMessage}]},
    {code: "<select className='sr-only' />", errors: [{message: errorMessage}]},
    {code: "<option className='sr-only' />", errors: [{message: errorMessage}]},
    {code: "<a className='sr-only'>Read more</a>", errors: [{message: errorMessage}]},
    {
      code: "<button className='visually-hidden'>Submit</button>",
      options: [
        {
          className: 'visually-hidden',
        },
      ],
      errors: [{message: errorMessage}],
    },
    {
      code: "<Hidden as='button'>Submit</Hidden>",
      options: [
        {
          componentName: 'Hidden',
        },
      ],
      errors: [{message: errorMessage}],
    },
    {
      code: "<VisuallyHidden html='button'>Submit</VisuallyHidden>",
      errors: [{message: errorMessage}],
      settings: {
        github: {
          polymorphicPropName: 'html',
        },
      },
    },
  ],
})


================================================
FILE: tests/a11y-role-supports-aria-props.js
================================================
// @ts-check

// Tests in this file were adapted from https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/__tests__/src/rules/role-supports-aria-props-test.js, which was authored by Ethan Cohen and is distributed under the MIT license as follows:
//
// The MIT License (MIT) Copyright (c) 2016 Ethan Cohen
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

import rule from '../lib/rules/a11y-role-supports-aria-props.js'
import {RuleTester} from 'eslint'

const ruleTester = new RuleTester({
  languageOptions: {
    parserOptions: {
      ecmaVersion: 'latest',
      sourceType: 'module',
      ecmaFeatures: {
        jsx: true,
      },
    },
  },
})

function getErrorMessage(attribute, role) {
  return `The attribute ${attribute} is not supported by the role ${role}.`
}

ruleTester.run('a11y-role-supports-aria-props', rule, {
  valid: [
    {
      code: `
      <div
        id={id}
        role={
          sectionHasHeader && rowIndex.row === 0 ? 'presentation' : 'option'
        }
        aria-label={this.props.ariaLabel}
      >
        {children}
      </div>`,
    },
    {code: '<Foo bar />'},
    {code: '<div />'},
    {code: '<div id="main" />'},
    {code: '<div role />'},
    {code: '<div role="presentation" {...props} />'},
    {code: '<Foo.Bar baz={true} />'},
    {code: '<Foo as="a" href={url} aria-label={`Issue #${title}`} />'},
    {code: '<Link href="#" aria-checked />'},
    // Don't try to evaluate expression
    {code: '<Box aria-labelledby="some-id" role={role} />'},
    {code: '<Box aria-labelledby="some-id" as={isNavigationOpen ? "div" : "nav"} />'},
    // IMPLICIT ROLE TESTS
    // A TESTS - implicit role is `link`
    {code: '<a href="#" aria-expanded />'},
    {code: '<a href="#" aria-atomic />'},
    {code: '<a href="#" aria-busy />'},
    {code: '<a href="#" aria-controls />'},
    {code: '<a href="#" aria-current />'},
    {code: '<a href="#" aria-describedby />'},
    {code: '<a href="#" aria-disabled />'},
    {code: '<a href="#" aria-dropeffect />'},
    {code: '<a href="#" aria-flowto />'},
    {code: '<a href="#" aria-haspopup />'},
    {code: '<a href="#" aria-grabbed />'},
    {code: '<a href="#" aria-hidden />'},
    {code: '<a href="#" aria-label />'},
    {code: '<a href="#" aria-labelledby />'},
    {code: '<a href="#" aria-live />'},
    {code: '<a href="#" aria-owns />'},
    {code: '<a href="#" aria-relevant />'},

    // AREA TESTS - implicit role is `link`
    {code: '<area href="#" aria-expanded />'},
    {code: '<area href="#" aria-atomic />'},
    {code: '<area href="#" aria-busy />'},
    {code: '<area href="#" aria-controls />'},
    {code: '<area href="#" aria-describedby />'},
    {code: '<area href="#" aria-disabled />'},
    {code: '<area href="#" aria-dropeffect />'},
    {code: '<area href="#" aria-flowto />'},
    {code: '<area href="#" aria-grabbed />'},
    {code: '<area href="#" aria-haspopup />'},
    {code: '<area href="#" aria-hidden />'},
    {code: '<area href="#" aria-label />'},
    {code: '<area href="#" aria-labelledby />'},
    {code: '<area href="#" aria-live />'},
    {code: '<area href="#" aria-owns />'},
    {code: '<area href="#" aria-relevant />'},

    // this will have role of `img`
    {code: '<img alt="foobar" aria-busy />'},

    // MENU TESTS - implicit role is `toolbar` when `type="toolbar"`
    {code: '<menu type="toolbar" aria-activedescendant />'},
    {code: '<menu type="toolbar" aria-atomic />'},
    {code: '<menu type="toolbar" aria-busy />'},
    {code: '<menu type="toolbar" aria-controls />'},
    {code: '<menu type="toolbar" aria-describedby />'},
    {code: '<menu type="toolbar" aria-disabled />'},
    {code: '<menu type="toolbar" aria-dropeffect />'},
    {code: '<menu type="toolbar" aria-flowto />'},
    {code: '<menu type="toolbar" aria-grabbed />'},
    {code: '<menu type="toolbar" aria-hidden />'},
    {code: '<menu type="toolbar" aria-label />'},
    {code: '<menu type="toolbar" aria-labelledby />'},
    {code: '<menu type="toolbar" aria-live />'},
    {code: '<menu type="toolbar" aria-owns />'},
    {code: '<menu type="toolbar" aria-relevant />'},

    // MENUITEM TESTS
    // when `type="command`, the implicit role is `menuitem`
    {code: '<menuitem type="command" aria-atomic />'},
    {code: '<menuitem type="command" aria-busy />'},
    {code: '<menuitem type="command" aria-controls />'},
    {code: '<menuitem type="command" aria-describedby />'},
    {code: '<menuitem type="command" aria-disabled />'},
    {code: '<menuitem type="command" aria-dropeffect />'},
    {code: '<menuitem type="command" aria-flowto />'},
    {code: '<menuitem type="command" aria-grabbed />'},
    {code: '<menuitem type="command" aria-haspopup />'},
    {code: '<menuitem type="command" aria-hidden />'},
    {code: '<menuitem type="command" aria-label />'},
    {code: '<menuitem type="command" aria-labelledby />'},
    {code: '<menuitem type="command" aria-live />'},
    {code: '<menuitem type="command" aria-owns />'},
    {code: '<menuitem type="command" aria-relevant />'},
    // when `type="checkbox`, the implicit role is `menuitemcheckbox`
    {code: '<menuitem type="checkbox" aria-checked />'},
    {code: '<menuitem type="checkbox" aria-atomic />'},
    {code: '<menuitem type="checkbox" aria-busy />'},
    {code: '<menuitem type="checkbox" aria-controls />'},
    {code: '<menuitem type="checkbox" aria-describedby />'},
    {code: '<menuitem type="checkbox" aria-disabled />'},
    {code: '<menuitem type="checkbox" aria-dropeffect />'},
    {code: '<menuitem type="checkbox" aria-flowto />'},
    {code: '<menuitem type="checkbox" aria-grabbed />'},
    {code: '<menuitem type="checkbox" aria-haspopup />'},
    {code: '<menuitem type="checkbox" aria-hidden />'},
    {code: '<menuitem type="checkbox" aria-invalid />'},
    {code: '<menuitem type="checkbox" aria-label />'},
    {code: '<menuitem type="checkbox" aria-labelledby />'},
    {code: '<menuitem type="checkbox" aria-live />'},
    {code: '<menuitem type="checkbox" aria-owns />'},
    {code: '<menuitem type="checkbox" aria-relevant />'},
    // when `type="radio`, the implicit role is `menuitemradio`
    {code: '<menuitem type="radio" aria-checked />'},
    {code: '<menuitem type="radio" aria-atomic />'},
    {code: '<menuitem type="radio" aria-busy />'},
    {code: '<menuitem type="radio" aria-controls />'},
    {code: '<menuitem type="radio" aria-describedby />'},
    {code: '<menuitem type="radio" aria-disabled />'},
    {code: '<menuitem type="radio" aria-dropeffect />'},
    {code: '<menuitem type="radio" aria-flowto />'},
    {code: '<menuitem type="radio" aria-grabbed />'},
    {code: '<menuitem type="radio" aria-haspopup />'},
    {code: '<menuitem type="radio" aria-hidden />'},
    {code: '<menuitem type="radio" aria-invalid />'},
    {code: '<menuitem type="radio" aria-label />'},
    {code: '<menuitem type="radio" aria-labelledby />'},
    {code: '<menuitem type="radio" aria-live />'},
    {code: '<menuitem type="radio" aria-owns />'},
    {code: '<menuitem type="radio" aria-relevant />'},
    {code: '<menuitem type="radio" aria-posinset />'},
    {code: '<menuitem type="radio" aria-setsize />'},

    // these will have global
    {code: '<menuitem aria-checked />'},
    {code: '<menuitem type="foo" aria-checked />'},

    // INPUT TESTS
    // when `type="button"`, the implicit role is `button`
    {code: '<input type="button" aria-expanded />'},
    {code: '<input type="button" aria-pressed />'},
    {code: '<input type="button" aria-atomic />'},
    {code: '<input type="button" aria-busy />'},
    {code: '<input type="button" aria-controls />'},
    {code: '<input type="button" aria-describedby />'},
    {code: '<input type="button" aria-disabled />'},
    {code: '<input type="button" aria-dropeffect />'},
    {code: '<input type="button" aria-flowto />'},
    {code: '<input type="button" aria-grabbed />'},
    {code: '<input type="button" aria-haspopup />'},
    {code: '<input type="button" aria-hidden />'},
    {code: '<input type="button" aria-label />'},
    {code: '<input type="button" aria-labelledby />'},
    {code: '<input type="button" aria-live />'},
    {code: '<input type="button" aria-owns />'},
    {code: '<input type="button" aria-relevant />'},
    // when `type="image"`, the implicit role is `button`
    {code: '<input type="image" aria-expanded />'},
    {code: '<input type="image" aria-pressed />'},
    {code: '<input type="image" aria-atomic />'},
    {code: '<input type="image" aria-busy />'},
    {code: '<input type="image" aria-controls />'},
    {code: '<input type="image" aria-describedby />'},
    {code: '<input type="image" aria-disabled />'},
    {code: '<input type="image" aria-dropeffect />'},
    {code: '<input type="image" aria-flowto />'},
    {code: '<input type="image" aria-grabbed />'},
    {code: '<input type="image" aria-haspopup />'},
    {code: '<input type="image" aria-hidden />'},
    {code: '<input type="image" aria-label />'},
    {code: '<input type="image" aria-labelledby />'},
    {code: '<input type="image" aria-live />'},
    {code: '<input type="image" aria-owns />'},
    {code: '<input type="image" aria-relevant />'},
    // when `type="reset"`, the implicit role is `button`
    {code: '<input type="reset" aria-expanded />'},
    {code: '<input type="reset" aria-pressed />'},
    {code: '<input type="reset" aria-atomic />'},
    {code: '<input type="reset" aria-busy />'},
    {code: '<input type="reset" aria-controls />'},
    {code: '<input type="reset" aria-describedby />'},
    {code: '<input type="reset" aria-disabled />'},
    {code: '<input type="reset" aria-dropeffect />'},
    {code: '<input type="reset" aria-flowto />'},
    {code: '<input type="reset" aria-grabbed />'},
    {code: '<input type="reset" aria-haspopup />'},
    {code: '<input type="reset" aria-hidden />'},
    {code: '<input type="reset" aria-label />'},
    {code: '<input type="reset" aria-labelledby />'},
    {code: '<input type="reset" aria-live />'},
    {code: '<input type="reset" aria-owns />'},
    {code: '<input type="reset" aria-relevant />'},
    // when `type="submit"`, the implicit role is `button`
    {code: '<input type="submit" aria-expanded />'},
    {code: '<input type="submit" aria-pressed />'},
    {code: '<input type="submit" aria-atomic />'},
    {code: '<input type="submit" aria-busy />'},
    {code: '<input type="submit" aria-controls />'},
    {code: '<input type="submit" aria-describedby />'},
    {code: '<input type="submit" aria-disabled />'},
    {code: '<input type="submit" aria-dropeffect />'},
    {code: '<input type="submit" aria-flowto />'},
    {code: '<input type="submit" aria-grabbed />'},
    {code: '<input type="submit" aria-haspopup />'},
    {code: '<input type="submit" aria-hidden />'},
    {code: '<input type="submit" aria-label />'},
    {code: '<input type="submit" aria-labelledby />'},
    {code: '<input type="submit" aria-live />'},
    {code: '<input type="submit" aria-owns />'},
    {code: '<input type="submit" aria-relevant />'},
    // when `type="checkbox"`, the implicit role is `checkbox`
    {code: '<input type="checkbox" aria-atomic />'},
    {code: '<input type="checkbox" aria-busy />'},
    {code: '<input type="checkbox" aria-checked />'},
    {code: '<input type="checkbox" aria-controls />'},
    {code: '<input type="checkbox" aria-describedby />'},
    {code: '<input type="checkbox" aria-disabled />'},
    {code: '<input type="checkbox" aria-dropeffect />'},
    {code: '<input type="checkbox" aria-flowto />'},
    {code: '<input type="checkbox" aria-grabbed />'},
    {code: '<input type="checkbox" aria-hidden />'},
    {code: '<input type="checkbox" aria-invalid />'},
    {code: '<input type="checkbox" aria-label />'},
    {code: '<input type="checkbox" aria-labelledby />'},
    {code: '<input type="checkbox" aria-live />'},
    {code: '<input type="checkbox" aria-owns />'},
    {code: '<input type="checkbox" aria-relevant />'},
    // when `type="radio"`, the implicit role is `radio`
    {code: '<input type="radio" aria-atomic />'},
    {code: '<input type="radio" aria-busy />'},
    {code: '<input type="radio" aria-checked />'},
    {code: '<input type="radio" aria-controls />'},
    {code: '<input type="radio" aria-describedby />'},
    {code: '<input type="radio" aria-disabled />'},
    {code: '<input type="radio" aria-dropeffect />'},
    {code: '<input type="radio" aria-flowto />'},
    {code: '<input type="radio" aria-grabbed />'},
    {code: '<input type="radio" aria-hidden />'},
    {code: '<input type="radio" aria-label />'},
    {code: '<input type="radio" aria-labelledby />'},
    {code: '<input type="radio" aria-live />'},
    {code: '<input type="radio" aria-owns />'},
    {code: '<input type="radio" aria-relevant />'},
    {code: '<input type="radio" aria-posinset />'},
    {code: '<input type="radio" aria-setsize />'},
    // when `type="range"`, the implicit role is `slider`
    {code: '<input type="range" aria-valuemax />'},
    {code: '<input type="range" aria-valuemin />'},
    {code: '<input type="range" aria-valuenow />'},
    {code: '<input type="range" aria-orientation />'},
    {code: '<input type="range" aria-atomic />'},
    {code: '<input type="range" aria-busy />'},
    {code: '<input type="range" aria-controls />'},
    {code: '<input type="range" aria-describedby />'},
    {code: '<input type="range" aria-disabled />'},
    {code: '<input type="range" aria-dropeffect />'},
    {code: '<input type="range" aria-flowto />'},
    {code: '<input type="range" aria-grabbed />'},
    {code: '<input type="range" aria-haspopup />'},
    {code: '<input type="range" aria-hidden />'},
    {code: '<input type="range" aria-invalid />'},
    {code: '<input type="range" aria-label />'},
    {code: '<input type="range" aria-labelledby />'},
    {code: '<input type="range" aria-live />'},
    {code: '<input type="range" aria-owns />'},
    {code: '<input type="range" aria-relevant />'},
    {code: '<input type="range" aria-valuetext />'},

    // these will have role of `textbox`,
    {code: '<input type="email" aria-disabled />'},
    {code: '<input type="password" aria-disabled />'},
    {code: '<input type="search" aria-disabled />'},
    {code: '<input type="tel" aria-disabled />'},
    {code: '<input type="url" aria-disabled />'},
    {code: '<input aria-disabled />'},

    // Allow null/undefined values regardless of role
    {code: '<h2 role="presentation" aria-level={null} />'},
    {code: '<h2 role="presentation" aria-level={undefined} />'},

    // OTHER TESTS
    {code: '<button aria-pressed />'},
    {code: '<form aria-hidden />'},
    {code: '<h1 aria-hidden />'},
    {code: '<h2 aria-hidden />'},
    {code: '<h3 aria-hidden />'},
    {code: '<h4 aria-hidden />'},
    {code: '<h5 aria-hidden />'},
    {code: '<h6 aria-hidden />'},
    {code: '<hr aria-hidden />'},
    {code: '<li aria-current />'},
    {code: '<meter aria-atomic />'},
    {code: '<option aria-atomic />'},
    {code: '<progress aria-atomic />'},
    {code: '<textarea aria-hidden />'},
    {code: '<select aria-expanded />'},
    {code: '<datalist aria-expanded />'},
    {code: '<div role="heading" aria-level />'},
    {code: '<div role="heading" aria-level="1" />'},
    {code: '<link href="#" aria-expanded />'}, // link maps to nothing
  ],

  invalid: [
    // implicit basic checks
    {
      code: '<area aria-checked />',
      errors: [getErrorMessage('aria-checked', 'generic')],
    },
    {
      code: '<a aria-checked />',
      errors: [getErrorMessage('aria-checked', 'generic')],
    },
    {
      code: '<a href="#" aria-checked />',
      errors: [getErrorMessage('aria-checked', 'link')],
    },
    {
      code: '<area href="#" aria-checked />',
      errors: [getErrorMessage('aria-checked', 'link')],
    },
    {
      code: '<img alt="foobar" aria-checked />',
      errors: [getErrorMessage('aria-checked', 'img')],
    },
    {
      code: '<menu type="toolbar" aria-checked />',
      errors: [getErrorMessage('aria-checked', 'toolbar')],
    },
    {
      code: '<aside aria-checked />',
      errors: [getErrorMessage('aria-checked', 'complementary')],
    },
    {
      code: '<ul aria-expanded />',
      errors: [getErrorMessage('aria-expanded', 'list')],
    },
    {
      code: '<details aria-expanded />',
      errors: [getErrorMessage('aria-expanded', 'group')],
    },
    {
      code: '<dialog aria-expanded />',
      errors: [getErrorMessage('aria-expanded', 'dialog')],
    },
    {
      code: '<aside aria-expanded />',
      errors: [getErrorMessage('aria-expanded', 'complementary')],
    },
    {
      code: '<article aria-expanded />',
      errors: [getErrorMessage('aria-expanded', 'article')],
    },
    {
      code: '<body aria-expanded />',
      errors: [getErrorMessage('aria-expanded', 'generic')],
    },
    {
      code: '<li aria-expanded />',
      errors: [getErrorMessage('aria-expanded', 'listitem')],
    },
    {
      code: '<nav aria-expanded />',
      errors: [getErrorMessage('aria-expanded', 'navigation')],
    },
    {
      code: '<ol aria-expanded />',
      errors: [getErrorMessage('aria-expanded', 'list')],
    },
    {
      code: '<output aria-expanded />',
      errors: [getErrorMessage('aria-expanded', 'status')],
    },
    {
      code: '<section aria-expanded />',
      errors: [getErrorMessage('aria-expanded', 'generic')],
    },
    {
      code: '<section aria-label="something" aria-expanded />',
      errors: [getErrorMessage('aria-expanded', 'region')],
    },
    {
      code: '<tbody aria-expanded />',
      errors: [getErrorMessage('aria-expanded', 'rowgroup')],
    },
    {
      code: '<tfoot aria-expanded />',
      errors: [getErrorMessage('aria-expanded', 'rowgroup')],
    },
    {
      code: '<thead aria-expanded />',
      errors: [getErrorMessage('aria-expanded', 'rowgroup')],
    },
    {
      code: '<input type="radio" aria-invalid />',
      errors: [getErrorMessage('aria-invalid', 'radio')],
    },
    {
      code: '<input type="radio" aria-selected />',
      errors: [getErrorMessage('aria-selected', 'radio')],
    },
    {
      code: '<input type="radio" aria-haspopup />',
      errors: [getErrorMessage('aria-haspopup', 'radio')],
    },
    {
      code: '<input type="checkbox" aria-haspopup />',
      errors: [getErrorMessage('aria-haspopup', 'checkbox')],
    },
    {
      code: '<input type="reset" aria-invalid />',
      errors: [getErrorMessage('aria-invalid', 'button')],
    },
    {
      code: '<input type="submit" aria-invalid />',
      errors: [getErrorMessage('aria-invalid', 'button')],
    },
    {
      code: '<input type="image" aria-invalid />',
      errors: [getErrorMessage('aria-invalid', 'button')],
    },
    {
      code: '<input type="button" aria-invalid />',
      errors: [getErrorMessage('aria-invalid', 'button')],
    },
    {
      code: '<menuitem type="command" aria-invalid />',
      errors: [getErrorMessage('aria-invalid', 'menuitem')],
    },
    {
      code: '<menuitem type="radio" aria-selected />',
      errors: [getErrorMessage('aria-selected', 'menuitemradio')],
    },
    {
      code: '<menu type="toolbar" aria-haspopup />',
      errors: [getErrorMessage('aria-haspopup', 'toolbar')],
    },
    {
      code: '<menu type="toolbar" aria-invalid />',
      errors: [getErrorMessage('aria-invalid', 'toolbar')],
    },
    {
      code: '<menu type="toolbar" aria-expanded />',
      errors: [getErrorMessage('aria-expanded', 'toolbar')],
    },
    {
      code: '<area href="#" aria-invalid />',
      errors: [getErrorMessage('aria-invalid', 'link')],
    },
    {
      code: '<a href="#" aria-invalid />',
      errors: [getErrorMessage('aria-invalid', 'link')],
    },
    {
      code: '<span aria-label />',
      errors: [getErrorMessage('aria-label', 'generic')],
    },
    {
      code: '<span aria-labelledby />',
      errors: [getErrorMessage('aria-labelledby', 'generic')],
    },
    {
      code: '<div aria-labelledby />',
      errors: [getErrorMessage('aria-labelledby', 'generic')],
    },
    // Determines role from literal `as` prop.
    {
      code: '<Box as="span" aria-labelledby />',
      errors: [getErrorMessage('aria-labelledby', 'generic')],
    },
    {
      code: '<p role="generic" aria-label />',
      errors: [getErrorMessage('aria-label', 'generic')],
    },
  ],
})


================================================
FILE: tests/a11y-svg-has-accessible-name.js
================================================
import rule from '../lib/rules/a11y-svg-has-accessible-name.js'
import {RuleTester} from 'eslint'

const ruleTester = new RuleTester({
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
    ecmaFeatures: {
      jsx: true,
    },
  },
})

const errorMessage =
  '`<svg>` must have an accessible name. Set `aria-label` or `aria-labelledby`, or nest a `<title>` element. However, if the `<svg>` is purely decorative, hide it with `aria-hidden="true"` or `role="presentation"`.'

ruleTester.run('a11y-svg-has-accessible-name', rule, {
  valid: [
    {
      code: "<svg height='100' width='100'><title>Circle with a black outline and red fill</title><circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/></svg>",
    },
    {
      code: `<svg height='100' width='100'>
              <title>Circle with a black outline and red fill</title>
              <circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>
            </svg>`,
    },
    {
      code: "<svg aria-label='Circle with a black outline and red fill' height='100' width='100'><circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/></svg>",
    },
    {
      code: "<svg aria-labelledby='circle_text' height='100' width='100'><circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/></svg>",
    },
    {
      code: "<svg aria-hidden='true' height='100' width='100'><circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/></svg>",
    },
    {
      code: "<svg role='presentation' height='100' width='100'><circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/></svg>",
    },
  ],
  invalid: [
    {
      code: "<svg height='100' width='100'><circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/></svg>",
      errors: [{message: errorMessage}],
    },
    {
      code: "<svg height='100' width='100' title='Circle with a black outline and red fill'><circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/></svg>",
      errors: [{message: errorMessage}],
    },
    {
      code: "<svg height='100' width='100'><circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/><title>Circle with a black outline and red fill</title></svg>",
      errors: [{message: errorMessage}],
    },
  ],
})


================================================
FILE: tests/array-foreach.js
================================================
import rule from '../lib/rules/array-foreach.js'
import {RuleTester} from 'eslint'

const ruleTester = new RuleTester()

ruleTester.run('array-foreach', rule, {
  valid: [
    {
      code: 'for (const el of els) { el }',
      parserOptions: {ecmaVersion: 6},
    },
    {
      code: 'els.map(el => el)',
      parserOptions: {ecmaVersion: 6},
    },
    {code: 'forEach()'},
  ],
  invalid: [
    {
      code: 'els.forEach(el => el)',
      parserOptions: {ecmaVersion: 6},
      errors: [
        {
          message: 'Prefer for...of instead of Array.forEach',
          type: 'CallExpression',
        },
      ],
    },
  ],
})


================================================
FILE: tests/async-currenttarget.js
================================================
import rule from '../lib/rules/async-currenttarget.js'
import {RuleTester} from 'eslint'

const ruleTester = new RuleTester()

ruleTester.run('async-currenttarget', rule, {
  valid: [
    {
      code: 'document.addEventListener(function(event) { event.currentTarget })',
    },
    {
      code: 'document.addEventListener(async function(event) { event.currentTarget; await delay() })',
      parserOptions: {ecmaVersion: 2017},
    },
    {
      code: 'document.addEventListener(async function(event) { const currentTarget = event.currentTarget; await delay(); foo(() => currentTarget) })',
      parserOptions: {ecmaVersion: 2017},
    },
  ],
  invalid: [
    {
      code: 'document.addEventListener(async function(event) { await delay(); event.currentTarget })',
      parserOptions: {ecmaVersion: 2017},
      errors: [
        {
          message: 'event.currentTarget inside an async function is error prone',
          type: 'MemberExpression',
        },
      ],
    },
    {
      code: 'document.addEventListener(async function(event) { await delay(); foo(() => e.currentTarget) })',
      parserOptions: {ecmaVersion: 2017},
      errors: [
        {
          message: 'event.currentTarget inside an async function is error prone',
          type: 'MemberExpression',
        },
      ],
    },
  ],
})


================================================
FILE: tests/async-preventdefault.js
================================================
import rule from '../lib/rules/async-preventdefault.js'
import {RuleTester} from 'eslint'

const ruleTester = new RuleTester()

ruleTester.run('async-preventdefault', rule, {
  valid: [
    {
      code: 'document.addEventListener(function(event) { event.preventDefault() })',
    },
    {
      code: 'document.addEventListener(function(event) { event.target })',
    },
    {
      code: 'document.addEventListener(async function(event) { event.preventDefault() })',
      parserOptions: {ecmaVersion: 2017},
    },
  ],
  invalid: [
    {
      code: 'document.addEventListener(async function(event) { await delay(); event.preventDefault() })',
      parserOptions: {ecmaVersion: 2017},
      errors: [
        {
          message: 'event.preventDefault() inside an async function is error prone',
          type: 'CallExpression',
        },
      ],
    },
    {
      code: 'document.addEventListener(async function(event) { await delay(); foo(() => event.preventDefault()) })',
      parserOptions: {ecmaVersion: 2017},
      errors: [
        {
          message: 'event.preventDefault() inside an async function is error prone',
          type: 'CallExpression',
        },
      ],
    },
  ],
})


================================================
FILE: tests/authenticity-token.js
================================================
import rule from '../lib/rules/authenticity-token.js'
import {RuleTester} from 'eslint'

const ruleTester = new RuleTester()

ruleTester.run('authenticity-token', rule, {
  valid: [],
  invalid: [
    {
      code: "document.querySelector('form').elements['authenticity_token'].value",
      errors: [
        {
          message:
            'Form CSRF tokens (authenticity tokens) should not be created in JavaScript and their values should not be used directly for XHR requests.',
          type: 'Literal',
        },
      ],
    },
    {
      code: "document.querySelector('input[name=authenticity_token]').value",
      errors: [
        {
          message:
            'Form CSRF tokens (authenticity tokens) should not be created in JavaScript and their values should not be used directly for XHR requests.',
          type: 'Literal',
        },
      ],
    },
    {
      code: "(new FormData()).append('authenticity_token', 'asdf')",
      errors: [
        {
          message:
            'Form CSRF tokens (authenticity tokens) should not be created in JavaScript and their values should not be used directly for XHR requests.',
          type: 'Literal',
        },
      ],
    },
  ],
})


================================================
FILE: tests/check-rules.js
================================================
/* globals describe, it*/
import config from '../lib/index.js'
import fs from 'node:fs'
import assert from 'node:assert'
import path from 'node:path'

describe('smoke tests', () => {
  it('ensure all rules in lib/rules are included in index', () => {
    const exportedRules = new Set(Object.keys(config.rules))
    const files = new Set(fs.readdirSync('./lib/rules').map(f => path.basename(f, path.extname(f))))
    assert.deepEqual(files, exportedRules)
  })

  it('exports every config in lib/config as .configs', () => {
    const exportedConfigs = new Set(Object.keys(config.configs))
    const files = new Set(fs.readdirSync('./lib/configs').map(f => path.basename(f, path.extname(f))))
    assert.deepEqual(files, exportedConfigs)
  })

  it('exports valid rules in each config', () => {
    const exportedRules = new Set(Object.keys(config.rules))
    for (const flavour in config.configs) {
      for (const rule in config.configs[flavour].rules) {
        if (rule.startsWith('github/')) {
          assert(exportedRules.has(rule.replace(/^github\//, '')), `rule ${rule} is not a valid rule`)
        }
      }
    }
  })
})


================================================
FILE: tests/get-attribute.js
================================================
import rule from '../lib/rules/get-attribute.js'
import {RuleTester} from 'eslint'

const ruleTester = new RuleTester()

ruleTester.run('get-attribute', rule, {
  valid: [
    {code: "el.getAttribute('src')"},
    {code: "el.hasAttribute('src')"},
    {code: "el.setAttribute('src', 'https://github.com/')"},
    {code: "el.removeAttribute('src')"},
    {code: "el.getAttribute('data-foo')"},
    {code: "el.hasAttribute('data-foo')"},
    {code: "el.setAttribute('data-foo', 'bar')"},
    {code: "el.removeAttribute('data-foo')"},
    {code: "el.getAttribute('data-foo1')"},
    // some SVG attributes must preserve case
    {code: "el.getAttribute('preserveAspectRatio')"},
    {code: "el.getAttribute('viewBox')"},
  ],
  invalid: [
    {
      code: "el.getAttribute('SRC')",
      output: "el.getAttribute('src')",
      errors: [
        {
          message: 'Attributes should be lowercase and hyphen separated, or part of the SVG whitelist.',
          type: 'Literal',
        },
      ],
    },
    {
      code: "el.hasAttribute('SRC')",
      output: "el.hasAttribute('src')",
      errors: [
        {
          message: 'Attributes should be lowercase and hyphen separated, or part of the SVG whitelist.',
          type: 'Literal',
        },
      ],
    },
    {
      code: "el.getAttribute('onClick')",
      output: "el.getAttribute('onclick')",
      errors: [
        {
          message: 'Attributes should be lowercase and hyphen separated, or part of the SVG whitelist.',
          type: 'Literal',
        },
      ],
    },
    {
      code: "el.getAttribute('viewbox')",
      output: null,
      errors: [
        {
          message: 'Attributes should be lowercase and hyphen separated, or part of the SVG whitelist.',
          type: 'Literal',
        },
      ],
    },
    {
      code: "el.getAttribute('preserveaspectratio')",
      output: null,
      errors: [
        {
          message: 'Attributes should be lowercase and hyphen separated, or part of the SVG whitelist.',
          type: 'Literal',
        },
      ],
    },
  ],
})


================================================
FILE: tests/js-class-name.js
================================================
import rule from '../lib/rules/js-class-name.js'
import {RuleTester} from 'eslint'

const ruleTester = new RuleTester()

ruleTester.run('js-class-name', rule, {
  valid: [
    {code: "document.querySelector('body')"},
    {code: "document.querySelector('#js-foo')"},
    {code: "document.querySelector('.js-foo')"},
    {code: "document.querySelector('.js-foo > .js-bar')"},
    {code: 'document.querySelector(".js-foo")'},
    {code: "document.querySelector('.js-foo ' + ' > .js-bar')"},
    {code: "document.querySelector('.js-foo ' + ' > ' + '.js-bar')"},
    {
      code: 'document.querySelector(`.js-foo`)',
      parserOptions: {ecmaVersion: 6},
    },
    {code: "'random textjs-XXX'"},
  ],
  invalid: [
    {
      code: "document.querySelector('.js-Foo')",
      errors: [
        {
          message: 'js- class names should be lowercase and only contain dashes.',
          type: 'Literal',
        },
      ],
    },
    {
      code: "document.querySelector('#js-Foo')",
      errors: [
        {
          message: 'js- class names should be lowercase and only contain dashes.',
          type: 'Literal',
        },
      ],
    },
    {
      code: "document.querySelector('.js-foo > .js-Bar')",
      errors: [
        {
          message: 'js- class names should be lowercase and only contain dashes.',
          type: 'Literal',
        },
      ],
    },
    {
      code: "document.querySelector('.js-' + foo)",
      errors: [
        {
          message: 'js- class names should be lowercase and only contain dashes.',
          type: 'Literal',
        },
        {
          message: 'js- class names should be statically defined.',
          type: 'Literal',
        },
      ],
    },
    {
      code: "document.querySelector('.js-foo-' + foo)",
      errors: [
        {
          message: 'js- class names should be lowercase and only contain dashes.',
          type: 'Literal',
        },
        {
          message: 'js- class names should be statically defined.',
          type: 'Literal',
        },
      ],
    },
    {
      code: "document.querySelector('.js-foo' + idx)",
      errors: [
        {
          message: 'js- class names should be statically defined.',
          type: 'Literal',
        },
      ],
    },
    {
      code: 'document.querySelector(`.js-${foo}`)',
      parserOptions: {ecmaVersion: 6},
      errors: [
        {
          message: 'js- class names should be lowercase and only contain dashes.',
          type: 'TemplateElement',
        },
        {
          message: 'js- class names should be statically defined.',
          type: 'TemplateElement',
        },
      ],
    },
    {
      code: 'document.querySelector(`.js-foo-${foo}`)',
      parserOptions: {ecmaVersion: 6},
      errors: [
        {
          message: 'js- class names should be lowercase and only contain dashes.',
          type: 'TemplateElement',
        },
        {
          message: 'js- class names should be statically defined.',
          type: 'TemplateElement',
        },
      ],
    },
    {
      code: 'document.querySelector(`.js-foo${idx}`)',
      parserOptions: {ecmaVersion: 6},
      errors: [
        {
          message: 'js- class names should be statically defined.',
          type: 'TemplateElement',
        },
      ],
    },
  ],
})


================================================
FILE: tests/no-blur.js
================================================
import rule from '../lib/rules/no-blur.js'
import {RuleTester} from 'eslint'

const ruleTester = new RuleTester()

ruleTester.run('no-blur', rule, {
  valid: [{code: 'target.focus()'}],
  invalid: [
    {
      code: 'el.blur()',
      errors: [
        {
          message: 'Do not use element.blur(), instead restore the focus of a previous element.',
        },
      ],
    },
  ],
})


================================================
FILE: tests/no-d-none.js
================================================
import rule from '../lib/rules/no-d-none.js'
import {RuleTester} from 'eslint'

const ruleTester = new RuleTester()

ruleTester.run('no-d-none', rule, {
  valid: [
    {
      code: 'el.classList.add("dnone")',
    },
    {
      code: 'el.classList.toggle("responsive-d-none")',
    },
    {
      code: '[].pop()',
    },
  ],
  invalid: [
    {
      code: 'el.classList.add("d-none")',
      errors: [
        {
          message: 'Prefer hidden property to d-none class',
          type: 'CallExpression',
        },
      ],
    },
    {
      code: 'el.classList.add("another-class", "d-none")',
      errors: [
        {
          message: 'Prefer hidden property to d-none class',
          type: 'CallExpression',
        },
      ],
    },
  ],
})


================================================
FILE: tests/no-dataset.js
================================================
import rule from '../lib/rules/no-dataset.js'
import {RuleTester} from 'eslint'

const ruleTester = new RuleTester()

ruleTester.run('no-dataset', rule, {
  valid: [
    {code: "el.getAttribute('data-cool-thing')"},
    {code: "var dataset = 'this is cool'"},
    {code: 'function dataset() { }'},
  ],
  invalid: [
    {
      code: 'el.dataset.coolThing',
      errors: [
        {
          message: "Use getAttribute('data-your-attribute') instead of dataset.",
        },
      ],
    },
    {
      code: "el.dataset['cool-thing']",
      errors: [
        {
          message: "Use getAttribute('data-your-attribute') instead of dataset.",
        },
      ],
    },
  ],
})


================================================
FILE: tests/no-dynamic-script-tag.js
================================================
import rule from '../lib/rules/no-dynamic-script-tag.js'
import {RuleTester} from 'eslint'

const ruleTester = new RuleTester()

ruleTester.run('no-dynamic-script-tag', rule, {
  valid: [
    {
      code: 'document.createElement("div")',
    },
    {
      code: 'document.createElement("span")',
    },
    {
      code: 'document.createElement("span").type = "foo"',
    },
  ],
  invalid: [
    {
      code: 'document.createElement("script")',
      errors: [
        {
          message: "Don't create dynamic script tags, add them in the server template instead.",
          type: 'Literal',
        },
      ],
    },
    {
      code: 'document.createElement("span").type = "text/javascript"',
      errors: [
        {
          message: "Don't create dynamic script tags, add them in the server template instead.",
          type: 'Literal',
        },
      ],
    },
  ],
})


================================================
FILE: tests/no-implicit-buggy-globals.js
================================================
import rule from '../lib/rules/no-implicit-buggy-globals.js'
import {RuleTester} from 'eslint'

const ruleTester = new RuleTester()

ruleTester.run('no-implicit-buggy-globals', rule, {
  valid: [
    {
      code: '(function() { var foo = 1; })();',
    },
    {
      code: '(function() { let foo = 1; })();',
      parserOptions: {ecmaVersion: 6},
    },
    {
      code: '(function() { const foo = 1; })();',
      parserOptions: {ecmaVersion: 6},
    },
    {
      code: 'var foo = 1;',
      parserOptions: {sourceType: 'module', ecmaVersion: 2015},
    },
    {
      code: 'let foo = 1;',
      parserOptions: {sourceType: 'module', ecmaVersion: 2015},
    },
    {
      code: 'const foo = 1;',
      parserOptions: {sourceType: 'module', ecmaVersion: 2015},
    },
  ],
  invalid: [
    {
      code: 'const foo = 1;',
      parserOptions: {ecmaVersion: 6},
      errors: [
        {
          message: 'Implicit global variable, assign as global property instead.',
          type: 'VariableDeclarator',
        },
      ],
    },
    {
      code: 'let foo = 1;',
      parserOptions: {ecmaVersion: 6},
      errors: [
        {
          message: 'Implicit global variable, assign as global property instead.',
          type: 'VariableDeclarator',
        },
      ],
    },
    {
      code: 'let foo = function() {};',
      parserOptions: {ecmaVersion: 6},
      errors: [
        {
          message: 'Implicit global variable, assign as global property instead.',
          type: 'VariableDeclarator',
        },
      ],
    },
    {
      code: 'const foo = function() {};',
      parserOptions: {ecmaVersion: 6},
      errors: [
        {
          message: 'Implicit global variable, assign as global property instead.',
          type: 'VariableDeclarator',
        },
      ],
    },
    {
      code: 'class Foo {}',
      parserOptions: {ecmaVersion: 6},
      errors: [
        {
          message: 'Implicit global variable, assign as global property instead.',
          type: 'ClassDeclaration',
        },
      ],
    },
  ],
})


================================================
FILE: tests/no-inner-html.js
================================================
import rule from '../lib/rules/no-inner-html.js'
import {RuleTester} from 'eslint'

const ruleTester = new RuleTester()

ruleTester.run('no-inner-html', rule, {
  valid: [
    {
      code: 'document.createElement("js-flash-text").textContent = ""',
    },
    {
      code: 'document.createElement("js-flash-text").textContent = "foo"',
    },
  ],
  invalid: [
    {
      code: 'document.createElement("js-flash-text").innerHTML = "foo"',
      errors: [
        {
          message: 'Using innerHTML poses a potential security risk and should not be used. Prefer using textContent.',
          type: 'Identifier',
        },
      ],
    },
    {
      code: 'document.querySelector("js-flash-text").innerHTML = "<div>code</div>"',
      errors: [
        {
          message: 'Using innerHTML poses a potential security risk and should not be used. Prefer using textContent.',
          type: 'Identifier',
        },
      ],
    },
    {
      code: 'document.querySelector("js-flash-text").innerHTML = ""',
      errors: [
        {
          message: 'Using innerHTML poses a potential security risk and should not be used. Prefer using textContent.',
          type: 'Identifier',
        },
      ],
    },
  ],
})


================================================
FILE: tests/no-innerText.js
================================================
import rule from '../lib/rules/no-innerText.js'
import {RuleTester} from 'eslint'

const ruleTester = new RuleTester()

ruleTester.run('no-innerText', rule, {
  valid: [
    {
      code: 'document.createElement("js-flash-text").textContent = "foo"',
    },
    {
      code: 'document.querySelector("js-flash-text").textContent = "bar"',
    },
    {
      // This is unrelated to the `HTMLElement.innerText` property, and should not trigger a warning
      code: 'var text = element.textContent()',
    },
  ],
  invalid: [
    {
      code: 'document.createElement("js-flash-text").innerText = "foo"',
      output: 'document.createElement("js-flash-text").textContent = "foo"',
      errors: [
        {
          message: 'Prefer textContent to innerText',
          type: 'Identifier',
        },
      ],
    },
    {
      code: 'document.querySelector("js-flash-text").innerText = "bar"',
      output: 'document.querySelector("js-flash-text").textContent = "bar"',
      errors: [
        {
          message: 'Prefer textContent to innerText',
          type: 'Identifier',
        },
      ],
    },
  ],
})


================================================
FILE: tests/no-then.js
================================================
import rule from '../lib/rules/no-then.js'
import {RuleTester} from 'eslint'

const ruleTester = new RuleTester()

ruleTester.run('no-then', rule, {
  valid: [
    {
      code: '(async function() { const data = await read(); console.log(data) })()',
      parserOptions: {ecmaVersion: 2017},
    },
    {
      code: '(async function() { try { await read() } catch(error) { console.error(error) } })()',
      parserOptions: {ecmaVersion: 2017},
    },
  ],
  invalid: [
    {
      code: '(function() { read().then(data => console.log(data)) })()',
      parserOptions: {ecmaVersion: 2017},
      errors: [
        {
          message: 'Prefer async/await to Promise.then()',
          type: 'Identifier',
        },
      ],
    },
    {
      code: '(function() { read().catch(error => console.error(error)) })()',
      parserOptions: {ecmaVersion: 2017},
      errors: [
        {
          message: 'Prefer async/await to Promise.catch()',
          type: 'Identifier',
        },
      ],
    },
  ],
})


================================================
FILE: tests/no-useless-passive.js
================================================
import rule from '../lib/rules/no-useless-passive.js'
import {RuleTester} from 'eslint'

const ruleTester = new RuleTester()

ruleTester.run('no-useless-passive', rule, {
  valid: [
    {
      code: 'document.addEventListener("scroll", function(event) {})',
    },
    {
      code: 'document.addEventListener("resize", function(event) {})',
    },
    {
      cod
Download .txt
gitextract_h6p_l7um/

├── .devcontainer/
│   ├── Dockerfile
│   └── devcontainer.json
├── .eslint-doc-generatorrc.js
├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── nodejs.yml
│       └── publish.yml
├── .gitignore
├── CODEOWNERS
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── bin/
│   └── eslint-ignore-errors.js
├── docs/
│   └── rules/
│       ├── a11y-aria-label-is-well-formatted.md
│       ├── a11y-no-generic-link-text.md
│       ├── a11y-no-title-attribute.md
│       ├── a11y-no-visually-hidden-interactive-element.md
│       ├── a11y-role-supports-aria-props.md
│       ├── a11y-svg-has-accessible-name.md
│       ├── array-foreach.md
│       ├── async-currenttarget.md
│       ├── async-preventdefault.md
│       ├── authenticity-token.md
│       ├── filenames-match-regex.md
│       ├── get-attribute.md
│       ├── js-class-name.md
│       ├── no-blur.md
│       ├── no-d-none.md
│       ├── no-dataset.md
│       ├── no-dynamic-script-tag.md
│       ├── no-implicit-buggy-globals.md
│       ├── no-inner-html.md
│       ├── no-innerText.md
│       ├── no-then.md
│       ├── no-useless-passive.md
│       ├── prefer-observers.md
│       ├── require-passive-events.md
│       └── unescaped-html-literal.md
├── eslint.config.js
├── lib/
│   ├── configs/
│   │   ├── browser.js
│   │   ├── flat/
│   │   │   ├── browser.js
│   │   │   ├── internal.js
│   │   │   ├── react.js
│   │   │   ├── recommended.js
│   │   │   └── typescript.js
│   │   ├── internal.js
│   │   ├── react.js
│   │   ├── recommended.js
│   │   └── typescript.js
│   ├── formatters/
│   │   └── stylish-fixes.js
│   ├── index.js
│   ├── plugin.js
│   ├── rules/
│   │   ├── a11y-aria-label-is-well-formatted.js
│   │   ├── a11y-no-generic-link-text.js
│   │   ├── a11y-no-title-attribute.js
│   │   ├── a11y-no-visually-hidden-interactive-element.js
│   │   ├── a11y-role-supports-aria-props.js
│   │   ├── a11y-svg-has-accessible-name.js
│   │   ├── array-foreach.js
│   │   ├── async-currenttarget.js
│   │   ├── async-preventdefault.js
│   │   ├── authenticity-token.js
│   │   ├── filenames-match-regex.js
│   │   ├── get-attribute.js
│   │   ├── js-class-name.js
│   │   ├── no-blur.js
│   │   ├── no-d-none.js
│   │   ├── no-dataset.js
│   │   ├── no-dynamic-script-tag.js
│   │   ├── no-implicit-buggy-globals.js
│   │   ├── no-inner-html.js
│   │   ├── no-innerText.js
│   │   ├── no-then.js
│   │   ├── no-useless-passive.js
│   │   ├── prefer-observers.js
│   │   ├── require-passive-events.js
│   │   └── unescaped-html-literal.js
│   ├── url.js
│   └── utils/
│       ├── commonjs-json-wrappers.cjs
│       ├── get-element-type.js
│       ├── get-exported-name.js
│       ├── get-role.js
│       ├── is-ignored-filename.js
│       ├── object-map.js
│       └── parse-filename.js
├── package.json
├── test-examples/
│   ├── flat/
│   │   ├── eslint.config.mjs
│   │   ├── package.json
│   │   └── src/
│   │       ├── forEachTest.js
│   │       ├── getAttribute.js
│   │       ├── jsx.tsx
│   │       ├── noBlur.js
│   │       └── thisTypescriptTest.ts
│   └── legacy/
│       ├── .eslintrc.cjs
│       ├── package.json
│       └── src/
│           ├── forEachTest.js
│           ├── getAttribute.js
│           ├── jsx.tsx
│           ├── noBlur.js
│           └── thisTypescriptTest.ts
└── tests/
    ├── a11y-aria-label-is-well-formatted.js
    ├── a11y-no-generic-link-text.js
    ├── a11y-no-title-attribute.js
    ├── a11y-no-visually-hidden-interactive-element.js
    ├── a11y-role-supports-aria-props.js
    ├── a11y-svg-has-accessible-name.js
    ├── array-foreach.js
    ├── async-currenttarget.js
    ├── async-preventdefault.js
    ├── authenticity-token.js
    ├── check-rules.js
    ├── get-attribute.js
    ├── js-class-name.js
    ├── no-blur.js
    ├── no-d-none.js
    ├── no-dataset.js
    ├── no-dynamic-script-tag.js
    ├── no-implicit-buggy-globals.js
    ├── no-inner-html.js
    ├── no-innerText.js
    ├── no-then.js
    ├── no-useless-passive.js
    ├── prefer-observers.js
    ├── require-passive-events.js
    ├── unescaped-html-literal.js
    └── utils/
        ├── get-element-type.mjs
        ├── get-role.mjs
        ├── mocks.js
        └── object-map.mjs
Download .txt
SYMBOL INDEX (65 symbols across 42 files)

FILE: bin/eslint-ignore-errors.js
  function isDisableComment (line 48) | function isDisableComment(line) {

FILE: lib/formatters/stylish-fixes.js
  function stylishFixes (line 8) | function stylishFixes(results) {
  function pluralize (line 68) | function pluralize(word, count) {
  function diff (line 72) | function diff(a, b) {

FILE: lib/rules/a11y-aria-label-is-well-formatted.js
  method create (line 20) | create(context) {

FILE: lib/rules/a11y-no-generic-link-text.js
  method create (line 44) | create(context) {

FILE: lib/rules/a11y-no-title-attribute.js
  constant SEMANTIC_ELEMENTS (line 6) | const SEMANTIC_ELEMENTS = [
  method create (line 57) | create(context) {

FILE: lib/rules/a11y-no-visually-hidden-interactive-element.js
  constant INTERACTIVE_ELEMENTS (line 19) | const INTERACTIVE_ELEMENTS = ['a', 'button', 'summary', 'select', 'optio...
  method create (line 75) | create(context) {

FILE: lib/rules/a11y-role-supports-aria-props.js
  method create (line 24) | create(context) {

FILE: lib/rules/a11y-svg-has-accessible-name.js
  method create (line 22) | create(context) {

FILE: lib/rules/array-foreach.js
  method create (line 17) | create(context) {

FILE: lib/rules/async-currenttarget.js
  method create (line 17) | create(context) {

FILE: lib/rules/async-preventdefault.js
  method create (line 17) | create(context) {

FILE: lib/rules/authenticity-token.js
  method create (line 18) | create(context) {

FILE: lib/rules/filenames-match-regex.js
  method create (line 32) | create(context) {

FILE: lib/rules/get-attribute.js
  function isValidAttribute (line 17) | function isValidAttribute(name) {
  method create (line 35) | create(context) {

FILE: lib/rules/js-class-name.js
  method create (line 18) | create(context) {

FILE: lib/rules/no-blur.js
  method create (line 16) | create(context) {

FILE: lib/rules/no-d-none.js
  method create (line 16) | create(context) {

FILE: lib/rules/no-dataset.js
  method create (line 17) | create(context) {

FILE: lib/rules/no-dynamic-script-tag.js
  method create (line 17) | create(context) {

FILE: lib/rules/no-implicit-buggy-globals.js
  method create (line 17) | create(context) {

FILE: lib/rules/no-inner-html.js
  method create (line 17) | create(context) {

FILE: lib/rules/no-innerText.js
  method create (line 18) | create(context) {

FILE: lib/rules/no-then.js
  method create (line 17) | create(context) {

FILE: lib/rules/no-useless-passive.js
  method create (line 30) | create(context) {

FILE: lib/rules/prefer-observers.js
  method create (line 21) | create(context) {

FILE: lib/rules/require-passive-events.js
  method create (line 29) | create(context) {

FILE: lib/rules/unescaped-html-literal.js
  method create (line 17) | create(context) {

FILE: lib/utils/get-element-type.js
  function getElementType (line 12) | function getElementType(context, node, lazyElementCheck = false) {

FILE: lib/utils/get-exported-name.js
  function getNodeName (line 1) | function getNodeName(node, options) {
  function getExportedName (line 17) | function getExportedName(programNode, options) {

FILE: lib/utils/get-role.js
  function cleanElementRolesMap (line 14) | function cleanElementRolesMap() {
  function getRole (line 45) | function getRole(context, node) {

FILE: lib/utils/is-ignored-filename.js
  function isIgnoredFilename (line 3) | function isIgnoredFilename(filename) {

FILE: lib/utils/object-map.js
  class ObjectMap (line 7) | class ObjectMap extends Map {
    method constructor (line 10) | constructor(iterable = []) {
    method clear (line 15) | clear() {
    method delete (line 19) | delete(key) {
    method entries (line 27) | entries() {
    method forEach (line 31) | forEach(cb) {
    method get (line 37) | get(key) {
    method has (line 41) | has(key) {
    method keys (line 45) | keys() {
    method set (line 49) | set(key, value) {
    method values (line 55) | values() {

FILE: lib/utils/parse-filename.js
  function parseFilename (line 3) | function parseFilename(filename) {

FILE: test-examples/flat/src/getAttribute.js
  function foobar (line 1) | function foobar(el) {

FILE: test-examples/flat/src/noBlur.js
  function focusInput (line 1) | function focusInput() {

FILE: test-examples/flat/src/thisTypescriptTest.ts
  class Safe (line 1) | class Safe {
    method constructor (line 4) | constructor(contents: string) {
    method printContents (line 8) | printContents() {

FILE: test-examples/legacy/src/getAttribute.js
  function foobar (line 1) | function foobar(el) {

FILE: test-examples/legacy/src/noBlur.js
  function focusInput (line 1) | function focusInput() {

FILE: test-examples/legacy/src/thisTypescriptTest.ts
  class Safe (line 1) | class Safe {
    method constructor (line 4) | constructor(contents: string) {
    method printContents (line 8) | printContents() {

FILE: tests/a11y-role-supports-aria-props.js
  function getErrorMessage (line 28) | function getErrorMessage(attribute, role) {

FILE: tests/utils/get-element-type.mjs
  function mockSetting (line 6) | function mockSetting(componentSetting = {}) {

FILE: tests/utils/mocks.js
  function mockJSXAttribute (line 1) | function mockJSXAttribute(prop, propValue) {
  function mockJSXConditionalAttribute (line 19) | function mockJSXConditionalAttribute(prop, expression, consequentValue, ...
  function mockJSXOpeningElement (line 47) | function mockJSXOpeningElement(tagName, attributes = []) {
Condensed preview — 128 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (225K chars).
[
  {
    "path": ".devcontainer/Dockerfile",
    "chars": 957,
    "preview": "# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.222.0/containers/javascript-no"
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "chars": 1110,
    "preview": "// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:\n// https://github.co"
  },
  {
    "path": ".eslint-doc-generatorrc.js",
    "chars": 223,
    "preview": "/** @type {import('eslint-doc-generator').GenerateOptions} */\nexport default {\n  configEmoji: [\n    ['browser', '🔍'],\n  "
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 334,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: npm\n    directory: '/'\n    schedule:\n      interval: weekly\n    open-pull-req"
  },
  {
    "path": ".github/workflows/nodejs.yml",
    "chars": 495,
    "preview": "name: Node CI\n\non:\n  push:\n    branches-ignore:\n      - 'dependabot/**'\n  pull_request:\n\njobs:\n  build:\n    runs-on: ubu"
  },
  {
    "path": ".github/workflows/publish.yml",
    "chars": 647,
    "preview": "name: Publish\n\non:\n  release:\n    types: [created]\n\npermissions:\n  contents: read\n  id-token: write\n\njobs:\n  publish-npm"
  },
  {
    "path": ".gitignore",
    "chars": 38,
    "preview": "node_modules/\nnpm-debug.log\nyarn.lock\n"
  },
  {
    "path": "CODEOWNERS",
    "chars": 32,
    "preview": "* @github/web-systems-reviewers\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 470,
    "preview": "## Publishing this package\n\nPublishing this package to npm is done via a [GitHub action](https://github.com/github/eslin"
  },
  {
    "path": "LICENSE",
    "chars": 1056,
    "preview": "Copyright (c) 2016 GitHub, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this so"
  },
  {
    "path": "README.md",
    "chars": 9954,
    "preview": "# eslint-plugin-github\n\n## Installation\n\n```sh\nnpm install --save-dev eslint eslint-plugin-github\n```\n\n## Setup\n\n### Leg"
  },
  {
    "path": "bin/eslint-ignore-errors.js",
    "chars": 1759,
    "preview": "#!/usr/bin/env node\n// Disables eslint rules in a JavaScript file with next-line comments. This is\n// useful when introd"
  },
  {
    "path": "docs/rules/a11y-aria-label-is-well-formatted.md",
    "chars": 883,
    "preview": "# Enforce [aria-label] text to be formatted as you would visual text (`github/a11y-aria-label-is-well-formatted`)\n\n💼 Thi"
  },
  {
    "path": "docs/rules/a11y-no-generic-link-text.md",
    "chars": 2944,
    "preview": "# Disallow generic link text (`github/a11y-no-generic-link-text`)\n\n❌ This rule is deprecated. It was replaced by `jsx-a1"
  },
  {
    "path": "docs/rules/a11y-no-title-attribute.md",
    "chars": 1730,
    "preview": "# Disallow using the title attribute (`github/a11y-no-title-attribute`)\n\n💼 This rule is enabled in the ⚛️ `react` config"
  },
  {
    "path": "docs/rules/a11y-no-visually-hidden-interactive-element.md",
    "chars": 1970,
    "preview": "# Enforce that interactive elements are not visually hidden (`github/a11y-no-visually-hidden-interactive-element`)\n\n💼 Th"
  },
  {
    "path": "docs/rules/a11y-role-supports-aria-props.md",
    "chars": 3155,
    "preview": "# Enforce that elements with explicit or implicit roles defined contain only `aria-*` properties supported by that `role"
  },
  {
    "path": "docs/rules/a11y-svg-has-accessible-name.md",
    "chars": 1948,
    "preview": "# Require SVGs to have an accessible name (`github/a11y-svg-has-accessible-name`)\n\n💼 This rule is enabled in the ⚛️ `rea"
  },
  {
    "path": "docs/rules/array-foreach.md",
    "chars": 6370,
    "preview": "# Enforce `for..of` loops over `Array.forEach` (`github/array-foreach`)\n\n💼 This rule is enabled in the ✅ `recommended` c"
  },
  {
    "path": "docs/rules/async-currenttarget.md",
    "chars": 1937,
    "preview": "# Disallow `event.currentTarget` calls inside of async functions (`github/async-currenttarget`)\n\n💼 This rule is enabled "
  },
  {
    "path": "docs/rules/async-preventdefault.md",
    "chars": 1693,
    "preview": "# Disallow `event.preventDefault` calls inside of async functions (`github/async-preventdefault`)\n\n💼 This rule is enable"
  },
  {
    "path": "docs/rules/authenticity-token.md",
    "chars": 2601,
    "preview": "# Disallow usage of CSRF tokens in JavaScript (`github/authenticity-token`)\n\n💼 This rule is enabled in the 🔐 `internal` "
  },
  {
    "path": "docs/rules/filenames-match-regex.md",
    "chars": 874,
    "preview": "# Require filenames to match a regex naming convention (`github/filenames-match-regex`)\n\n<!-- end auto-generated rule he"
  },
  {
    "path": "docs/rules/get-attribute.md",
    "chars": 659,
    "preview": "# Disallow wrong usage of attribute names (`github/get-attribute`)\n\n💼 This rule is enabled in the 🔍 `browser` config.\n\n🔧"
  },
  {
    "path": "docs/rules/js-class-name.md",
    "chars": 2155,
    "preview": "# Enforce a naming convention for js- prefixed classes (`github/js-class-name`)\n\n💼 This rule is enabled in the 🔐 `intern"
  },
  {
    "path": "docs/rules/no-blur.md",
    "chars": 921,
    "preview": "# Disallow usage of `Element.prototype.blur()` (`github/no-blur`)\n\n💼 This rule is enabled in the 🔍 `browser` config.\n\n<!"
  },
  {
    "path": "docs/rules/no-d-none.md",
    "chars": 455,
    "preview": "# Disallow usage the `d-none` CSS class (`github/no-d-none`)\n\n💼 This rule is enabled in the 🔐 `internal` config.\n\n<!-- e"
  },
  {
    "path": "docs/rules/no-dataset.md",
    "chars": 629,
    "preview": "# Enforce usage of `Element.prototype.getAttribute` instead of `Element.prototype.datalist` (`github/no-dataset`)\n\n💼 Thi"
  },
  {
    "path": "docs/rules/no-dynamic-script-tag.md",
    "chars": 721,
    "preview": "# Disallow creating dynamic script tags (`github/no-dynamic-script-tag`)\n\n💼 This rule is enabled in the ✅ `recommended` "
  },
  {
    "path": "docs/rules/no-implicit-buggy-globals.md",
    "chars": 373,
    "preview": "# Disallow implicit global variables (`github/no-implicit-buggy-globals`)\n\n💼 This rule is enabled in the ✅ `recommended`"
  },
  {
    "path": "docs/rules/no-inner-html.md",
    "chars": 846,
    "preview": "# Disallow `Element.prototype.innerHTML` in favor of `Element.prototype.textContent` (`github/no-inner-html`)\n\n💼 This ru"
  },
  {
    "path": "docs/rules/no-innerText.md",
    "chars": 620,
    "preview": "# Disallow `Element.prototype.innerText` in favor of `Element.prototype.textContent` (`github/no-innerText`)\n\n💼 This rul"
  },
  {
    "path": "docs/rules/no-then.md",
    "chars": 1044,
    "preview": "# Enforce using `async/await` syntax over Promises (`github/no-then`)\n\n💼 This rule is enabled in the ✅ `recommended` con"
  },
  {
    "path": "docs/rules/no-useless-passive.md",
    "chars": 1832,
    "preview": "# Disallow marking a event handler as passive when it has no effect (`github/no-useless-passive`)\n\n💼 This rule is enable"
  },
  {
    "path": "docs/rules/prefer-observers.md",
    "chars": 2432,
    "preview": "# Disallow poorly performing event listeners (`github/prefer-observers`)\n\n💼 This rule is enabled in the 🔍 `browser` conf"
  },
  {
    "path": "docs/rules/require-passive-events.md",
    "chars": 1209,
    "preview": "# Enforce marking high frequency event handlers as passive (`github/require-passive-events`)\n\n💼 This rule is enabled in "
  },
  {
    "path": "docs/rules/unescaped-html-literal.md",
    "chars": 884,
    "preview": "# Disallow unescaped HTML literals (`github/unescaped-html-literal`)\n\n💼 This rule is enabled in the 🔍 `browser` config.\n"
  },
  {
    "path": "eslint.config.js",
    "chars": 1114,
    "preview": "import globals from 'globals'\nimport eslintPlugin from 'eslint-plugin-eslint-plugin'\nimport importPlugin from 'eslint-pl"
  },
  {
    "path": "lib/configs/browser.js",
    "chars": 904,
    "preview": "export default {\n  env: {\n    browser: true,\n  },\n  plugins: ['github', 'escompat'],\n  extends: ['plugin:escompat/recomm"
  },
  {
    "path": "lib/configs/flat/browser.js",
    "chars": 1198,
    "preview": "import globals from 'globals'\nimport github from '../../plugin.js'\nimport importPlugin from 'eslint-plugin-import'\nimpor"
  },
  {
    "path": "lib/configs/flat/internal.js",
    "chars": 280,
    "preview": "import github from '../../plugin.js'\nimport {fixupPluginRules} from '@eslint/compat'\n\nexport default {\n  plugins: {githu"
  },
  {
    "path": "lib/configs/flat/react.js",
    "chars": 1616,
    "preview": "import github from '../../plugin.js'\nimport jsxA11yPlugin from 'eslint-plugin-jsx-a11y'\nimport {fixupPluginRules} from '"
  },
  {
    "path": "lib/configs/flat/recommended.js",
    "chars": 4887,
    "preview": "import globals from 'globals'\nimport github from '../../plugin.js'\nimport prettierPlugin from 'eslint-plugin-prettier'\ni"
  },
  {
    "path": "lib/configs/flat/typescript.js",
    "chars": 1068,
    "preview": "// eslint-disable-next-line import/no-unresolved\nimport tseslint from 'typescript-eslint'\nimport escompat from 'eslint-p"
  },
  {
    "path": "lib/configs/internal.js",
    "chars": 170,
    "preview": "export default {\n  plugins: ['github'],\n  rules: {\n    'github/authenticity-token': 'error',\n    'github/js-class-name':"
  },
  {
    "path": "lib/configs/react.js",
    "chars": 1526,
    "preview": "export default {\n  parserOptions: {\n    sourceType: 'module',\n    ecmaFeatures: {\n      jsx: true,\n    },\n  },\n  plugins"
  },
  {
    "path": "lib/configs/recommended.js",
    "chars": 4401,
    "preview": "export default {\n  parserOptions: {\n    ecmaFeatures: {\n      ecmaVersion: 6,\n    },\n    sourceType: 'module',\n  },\n  en"
  },
  {
    "path": "lib/configs/typescript.js",
    "chars": 916,
    "preview": "export default {\n  extends: ['plugin:@typescript-eslint/recommended', 'prettier', 'plugin:escompat/typescript-2020'],\n  "
  },
  {
    "path": "lib/formatters/stylish-fixes.js",
    "chars": 2045,
    "preview": "import childProcess from 'node:child_process'\nimport fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:p"
  },
  {
    "path": "lib/index.js",
    "chars": 1035,
    "preview": "import github from './plugin.js'\nimport flatBrowserConfig from './configs/flat/browser.js'\nimport flatInternalConfig fro"
  },
  {
    "path": "lib/plugin.js",
    "chars": 2896,
    "preview": "import {packageJson} from './utils/commonjs-json-wrappers.cjs'\nimport a11yNoVisuallyHiddenInteractiveElement from './rul"
  },
  {
    "path": "lib/rules/a11y-aria-label-is-well-formatted.js",
    "chars": 930,
    "preview": "import jsxAstUtils from 'jsx-ast-utils'\nimport url from '../url.js'\n\nconst {getProp} = jsxAstUtils\n\nexport default {\n  m"
  },
  {
    "path": "lib/rules/a11y-no-generic-link-text.js",
    "chars": 2733,
    "preview": "import jsxAstUtils from 'jsx-ast-utils'\nimport {getElementType} from '../utils/get-element-type.js'\nimport url from '../"
  },
  {
    "path": "lib/rules/a11y-no-title-attribute.js",
    "chars": 1522,
    "preview": "import jsxAstUtils from 'jsx-ast-utils'\nimport {getElementType} from '../utils/get-element-type.js'\nimport url from '../"
  },
  {
    "path": "lib/rules/a11y-no-visually-hidden-interactive-element.js",
    "chars": 3028,
    "preview": "import jsxAstUtils from 'jsx-ast-utils'\nimport {getElementType} from '../utils/get-element-type.js'\nimport {generateObjS"
  },
  {
    "path": "lib/rules/a11y-role-supports-aria-props.js",
    "chars": 2460,
    "preview": "// @ts-check\nimport {aria, roles} from 'aria-query'\nimport jsxAstUtils from 'jsx-ast-utils'\nimport {getRole} from '../ut"
  },
  {
    "path": "lib/rules/a11y-svg-has-accessible-name.js",
    "chars": 1877,
    "preview": "import jsxAstUtils from 'jsx-ast-utils'\nimport {getElementType} from '../utils/get-element-type.js'\nimport url from '../"
  },
  {
    "path": "lib/rules/array-foreach.js",
    "chars": 564,
    "preview": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'suggestion',\n    docs: {\n      description: 'enforce "
  },
  {
    "path": "lib/rules/async-currenttarget.js",
    "chars": 1141,
    "preview": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'disallow `e"
  },
  {
    "path": "lib/rules/async-preventdefault.js",
    "chars": 1160,
    "preview": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'disallow `e"
  },
  {
    "path": "lib/rules/authenticity-token.js",
    "chars": 850,
    "preview": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'disallow us"
  },
  {
    "path": "lib/rules/filenames-match-regex.js",
    "chars": 1853,
    "preview": "// This is adapted from https://github.com/selaux/eslint-plugin-filenames since it's no longer actively maintained\n// an"
  },
  {
    "path": "lib/rules/get-attribute.js",
    "chars": 1856,
    "preview": "import {svgElementAttributes} from '../utils/commonjs-json-wrappers.cjs'\nimport url from '../url.js'\n\nconst attributeCal"
  },
  {
    "path": "lib/rules/js-class-name.js",
    "chars": 1799,
    "preview": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'suggestion',\n    docs: {\n      description: 'enforce "
  },
  {
    "path": "lib/rules/no-blur.js",
    "chars": 583,
    "preview": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'disallow us"
  },
  {
    "path": "lib/rules/no-d-none.js",
    "chars": 887,
    "preview": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'disallow us"
  },
  {
    "path": "lib/rules/no-dataset.js",
    "chars": 621,
    "preview": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'enforce usa"
  },
  {
    "path": "lib/rules/no-dynamic-script-tag.js",
    "chars": 917,
    "preview": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'suggestion',\n    docs: {\n      description: 'disallow"
  },
  {
    "path": "lib/rules/no-implicit-buggy-globals.js",
    "chars": 1189,
    "preview": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'disallow im"
  },
  {
    "path": "lib/rules/no-inner-html.js",
    "chars": 652,
    "preview": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'disallow `E"
  },
  {
    "path": "lib/rules/no-innerText.js",
    "chars": 1090,
    "preview": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'disallow `E"
  },
  {
    "path": "lib/rules/no-then.js",
    "chars": 876,
    "preview": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'suggestion',\n    docs: {\n      description: 'enforce "
  },
  {
    "path": "lib/rules/no-useless-passive.js",
    "chars": 2097,
    "preview": "import url from '../url.js'\n\nconst passiveEventListenerNames = new Set([\n  'touchstart',\n  'touchmove',\n  'touchenter',\n"
  },
  {
    "path": "lib/rules/prefer-observers.js",
    "chars": 869,
    "preview": "import url from '../url.js'\n\nconst observerMap = {\n  scroll: 'IntersectionObserver',\n  resize: 'ResizeObserver',\n}\nexpor"
  },
  {
    "path": "lib/rules/require-passive-events.js",
    "chars": 1160,
    "preview": "import url from '../url.js'\n\nconst passiveEventListenerNames = new Set([\n  'touchstart',\n  'touchmove',\n  'touchenter',\n"
  },
  {
    "path": "lib/rules/unescaped-html-literal.js",
    "chars": 915,
    "preview": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'disallow un"
  },
  {
    "path": "lib/url.js",
    "chars": 408,
    "preview": "import {packageJson} from './utils/commonjs-json-wrappers.cjs'\nimport path from 'node:path'\nimport {fileURLToPath} from "
  },
  {
    "path": "lib/utils/commonjs-json-wrappers.cjs",
    "chars": 214,
    "preview": "const svgElementAttributes = require('svg-element-attributes')\nconst packageJson = require('../../package.json')\n\nmodule"
  },
  {
    "path": "lib/utils/get-element-type.js",
    "chars": 1514,
    "preview": "import jsxAstUtils from 'jsx-ast-utils'\n\nconst {elementType, getProp, getLiteralPropValue} = jsxAstUtils\n\n/*\nAllows cust"
  },
  {
    "path": "lib/utils/get-exported-name.js",
    "chars": 1074,
    "preview": "function getNodeName(node, options) {\n  const op = options || []\n\n  if (node.type === 'Identifier') {\n    return node.na"
  },
  {
    "path": "lib/utils/get-role.js",
    "chars": 4186,
    "preview": "import jsxAstUtils from 'jsx-ast-utils'\nimport {elementRoles} from 'aria-query'\nimport {getElementType} from './get-elem"
  },
  {
    "path": "lib/utils/is-ignored-filename.js",
    "chars": 155,
    "preview": "const ignoredFilenames = ['<text>', '<input>']\n\nexport default function isIgnoredFilename(filename) {\n  return ignoredFi"
  },
  {
    "path": "lib/utils/object-map.js",
    "chars": 1330,
    "preview": "// @ts-check\nimport {isDeepStrictEqual} from 'node:util'\n\n/**\n * ObjectMap extends Map, but determines key equality usin"
  },
  {
    "path": "lib/utils/parse-filename.js",
    "chars": 252,
    "preview": "import path from 'node:path'\n\nexport default function parseFilename(filename) {\n  const ext = path.extname(filename)\n\n  "
  },
  {
    "path": "package.json",
    "chars": 2576,
    "preview": "{\n  \"name\": \"eslint-plugin-github\",\n  \"version\": \"0.0.0-dev\",\n  \"type\": \"module\",\n  \"description\": \"An opinionated colle"
  },
  {
    "path": "test-examples/flat/eslint.config.mjs",
    "chars": 617,
    "preview": "import github from 'eslint-plugin-github'\n\nexport default [\n  github.getFlatConfigs().recommended,\n  github.getFlatConfi"
  },
  {
    "path": "test-examples/flat/package.json",
    "chars": 427,
    "preview": "{\n  \"name\": \"flat\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"lint\": \"cross-env ESLINT_USE_FLAT_CO"
  },
  {
    "path": "test-examples/flat/src/forEachTest.js",
    "chars": 236,
    "preview": "const testing = [\n  [{name: 'github'}, ['test1']],\n  [{name: 'mona'}, ['test2']],\n]\nconst objectMap = new ObjectMap(test"
  },
  {
    "path": "test-examples/flat/src/getAttribute.js",
    "chars": 702,
    "preview": "function foobar(el) {\n  el.getAttribute('autoComplete')\n}\n\nconst title = document.createElement('h1')\ntitle.textContent "
  },
  {
    "path": "test-examples/flat/src/jsx.tsx",
    "chars": 160,
    "preview": "const Components = () => {\n  return (\n    <a aria-label=\"learn more\" src=\"https://www.github.com\" title=\"A home for all "
  },
  {
    "path": "test-examples/flat/src/noBlur.js",
    "chars": 158,
    "preview": "function focusInput() {\n  const textField = document.getElementById(\"sampleText\")\n\n  textField.focus()\n\n  setTimeout(() "
  },
  {
    "path": "test-examples/flat/src/thisTypescriptTest.ts",
    "chars": 221,
    "preview": "class Safe {\n  contents: string;\n\n  constructor(contents: string) {\n    this.contents = contents;\n  }\n\n  printContents()"
  },
  {
    "path": "test-examples/legacy/.eslintrc.cjs",
    "chars": 528,
    "preview": "module.exports = {\n  root: true,\n  env: { es2022: true },\n  extends: [\n    'eslint:recommended',\n    'plugin:github/brow"
  },
  {
    "path": "test-examples/legacy/package.json",
    "chars": 464,
    "preview": "{\n  \"name\": \"legacy\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"lint\": \"cross-env ESLINT_USE_FLAT_"
  },
  {
    "path": "test-examples/legacy/src/forEachTest.js",
    "chars": 236,
    "preview": "const testing = [\n  [{name: 'github'}, ['test1']],\n  [{name: 'mona'}, ['test2']],\n]\nconst objectMap = new ObjectMap(test"
  },
  {
    "path": "test-examples/legacy/src/getAttribute.js",
    "chars": 149,
    "preview": "function foobar(el) {\n  el.getAttribute('autoComplete')\n}\n\nconst title = document.createElement('h1')\ntitle.textContent "
  },
  {
    "path": "test-examples/legacy/src/jsx.tsx",
    "chars": 160,
    "preview": "const Components = () => {\n  return (\n    <a aria-label=\"learn more\" src=\"https://www.github.com\" title=\"A home for all "
  },
  {
    "path": "test-examples/legacy/src/noBlur.js",
    "chars": 158,
    "preview": "function focusInput() {\n  const textField = document.getElementById(\"sampleText\")\n\n  textField.focus()\n\n  setTimeout(() "
  },
  {
    "path": "test-examples/legacy/src/thisTypescriptTest.ts",
    "chars": 220,
    "preview": "class Safe {\n  contents: string;\n\n  constructor(contents: string) {\n    this.contents = contents;\n  }\n\n  printContents()"
  },
  {
    "path": "tests/a11y-aria-label-is-well-formatted.js",
    "chars": 1150,
    "preview": "import rule from '../lib/rules/a11y-aria-label-is-well-formatted.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester"
  },
  {
    "path": "tests/a11y-no-generic-link-text.js",
    "chars": 2291,
    "preview": "import rule from '../lib/rules/a11y-no-generic-link-text.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new R"
  },
  {
    "path": "tests/a11y-no-title-attribute.js",
    "chars": 1589,
    "preview": "import rule from '../lib/rules/a11y-no-title-attribute.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new Rul"
  },
  {
    "path": "tests/a11y-no-visually-hidden-interactive-element.js",
    "chars": 3522,
    "preview": "import rule from '../lib/rules/a11y-no-visually-hidden-interactive-element.js'\nimport {RuleTester} from 'eslint'\n\nconst "
  },
  {
    "path": "tests/a11y-role-supports-aria-props.js",
    "chars": 21420,
    "preview": "// @ts-check\n\n// Tests in this file were adapted from https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/__t"
  },
  {
    "path": "tests/a11y-svg-has-accessible-name.js",
    "chars": 2353,
    "preview": "import rule from '../lib/rules/a11y-svg-has-accessible-name.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = ne"
  },
  {
    "path": "tests/array-foreach.js",
    "chars": 636,
    "preview": "import rule from '../lib/rules/array-foreach.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n"
  },
  {
    "path": "tests/async-currenttarget.js",
    "chars": 1320,
    "preview": "import rule from '../lib/rules/async-currenttarget.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTes"
  },
  {
    "path": "tests/async-preventdefault.js",
    "chars": 1207,
    "preview": "import rule from '../lib/rules/async-preventdefault.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTe"
  },
  {
    "path": "tests/authenticity-token.js",
    "chars": 1209,
    "preview": "import rule from '../lib/rules/authenticity-token.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTest"
  },
  {
    "path": "tests/check-rules.js",
    "chars": 1135,
    "preview": "/* globals describe, it*/\nimport config from '../lib/index.js'\nimport fs from 'node:fs'\nimport assert from 'node:assert'"
  },
  {
    "path": "tests/get-attribute.js",
    "chars": 2077,
    "preview": "import rule from '../lib/rules/get-attribute.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n"
  },
  {
    "path": "tests/js-class-name.js",
    "chars": 3316,
    "preview": "import rule from '../lib/rules/js-class-name.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n"
  },
  {
    "path": "tests/no-blur.js",
    "chars": 389,
    "preview": "import rule from '../lib/rules/no-blur.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n\nruleT"
  },
  {
    "path": "tests/no-d-none.js",
    "chars": 759,
    "preview": "import rule from '../lib/rules/no-d-none.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n\nrul"
  },
  {
    "path": "tests/no-dataset.js",
    "chars": 682,
    "preview": "import rule from '../lib/rules/no-dataset.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n\nru"
  },
  {
    "path": "tests/no-dynamic-script-tag.js",
    "chars": 888,
    "preview": "import rule from '../lib/rules/no-dynamic-script-tag.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleT"
  },
  {
    "path": "tests/no-implicit-buggy-globals.js",
    "chars": 2063,
    "preview": "import rule from '../lib/rules/no-implicit-buggy-globals.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new R"
  },
  {
    "path": "tests/no-inner-html.js",
    "chars": 1226,
    "preview": "import rule from '../lib/rules/no-inner-html.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n"
  },
  {
    "path": "tests/no-innerText.js",
    "chars": 1120,
    "preview": "import rule from '../lib/rules/no-innerText.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n\n"
  },
  {
    "path": "tests/no-then.js",
    "chars": 1012,
    "preview": "import rule from '../lib/rules/no-then.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n\nruleT"
  },
  {
    "path": "tests/no-useless-passive.js",
    "chars": 1206,
    "preview": "import rule from '../lib/rules/no-useless-passive.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTest"
  },
  {
    "path": "tests/prefer-observers.js",
    "chars": 820,
    "preview": "import rule from '../lib/rules/prefer-observers.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester"
  },
  {
    "path": "tests/require-passive-events.js",
    "chars": 1896,
    "preview": "import rule from '../lib/rules/require-passive-events.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new Rule"
  },
  {
    "path": "tests/unescaped-html-literal.js",
    "chars": 2204,
    "preview": "import rule from '../lib/rules/unescaped-html-literal.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new Rule"
  },
  {
    "path": "tests/utils/get-element-type.mjs",
    "chars": 2394,
    "preview": "import {expect} from 'chai'\nimport {getElementType} from '../../lib/utils/get-element-type.js'\nimport {mockJSXAttribute,"
  },
  {
    "path": "tests/utils/get-role.mjs",
    "chars": 9498,
    "preview": "import {expect} from 'chai'\nimport {getRole} from '../../lib/utils/get-role.js'\nimport {mockJSXAttribute, mockJSXConditi"
  },
  {
    "path": "tests/utils/mocks.js",
    "chars": 1219,
    "preview": "export function mockJSXAttribute(prop, propValue) {\n  return {\n    type: 'JSXAttribute',\n    name: {\n      type: 'JSXIde"
  },
  {
    "path": "tests/utils/object-map.mjs",
    "chars": 6610,
    "preview": "// @ts-check\nimport {expect} from 'chai'\nimport ObjectMap from '../../lib/utils/object-map.js'\nimport {describe, it} fro"
  }
]

About this extraction

This page contains the full source code of the github/eslint-plugin-github GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 128 files (204.1 KB), approximately 55.8k tokens, and a symbol index with 65 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!