[
  {
    "path": ".devcontainer/Dockerfile",
    "content": "# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.222.0/containers/javascript-node/.devcontainer/base.Dockerfile\n\n# [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\nARG VARIANT=\"16\"\nFROM mcr.microsoft.com/vscode/devcontainers/javascript-node:${VARIANT}\n\n# [Optional] Uncomment this section to install additional OS packages.\n# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \\\n#     && apt-get -y install --no-install-recommends <your-package-list-here>\n\n# [Optional] Uncomment if you want to install an additional version of node using nvm\n# ARG EXTRA_NODE_VERSION=10\n# RUN su node -c \"source/usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}\"\n\n# [Optional] Uncomment if you want to install more global node modules\n# RUN su node -c \"npm install -g <your-package-list-here>\" \n"
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "content": "// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:\n// https://github.com/microsoft/vscode-dev-containers/tree/v0.222.0/containers/javascript-node\n{\n  \"name\": \"Node.js\",\n  \"build\": {\n    \"dockerfile\": \"Dockerfile\",\n    // Update 'VARIANT' to pick a Node version: 16, 14, 12.\n    // Append -bullseye or -buster to pin to an OS version.\n    // Use -bullseye variants on local arm64/Apple Silicon.\n    \"args\": {\"VARIANT\": \"22\"}\n  },\n\n  // Set *default* container specific settings.json values on container create.\n  \"settings\": {},\n\n  // Add the IDs of extensions you want installed when the container is created.\n  \"extensions\": [\"dbaeumer.vscode-eslint\"],\n\n  // Use 'forwardPorts' to make a list of ports inside the container available locally.\n  // \"forwardPorts\": [],\n\n  // Use 'postCreateCommand' to run commands after the container is created.\n  // \"postCreateCommand\": \"yarn install\",\n\n  // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.\n  \"remoteUser\": \"node\",\n  \"features\": {\n    \"git\": \"latest\"\n  }\n}\n"
  },
  {
    "path": ".eslint-doc-generatorrc.js",
    "content": "/** @type {import('eslint-doc-generator').GenerateOptions} */\nexport default {\n  configEmoji: [\n    ['browser', '🔍'],\n    ['internal', '🔐'],\n    ['react', '⚛️'],\n  ],\n  ruleDocSectionInclude: ['Rule Details', 'Version'],\n}\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: npm\n    directory: '/'\n    schedule:\n      interval: weekly\n    open-pull-requests-limit: 99\n    groups:\n      all-dependencies:\n        patterns:\n          - \"*\" \n  - package-ecosystem: github-actions\n    directory: '/'\n    schedule:\n      interval: weekly\n    open-pull-requests-limit: 99\n"
  },
  {
    "path": ".github/workflows/nodejs.yml",
    "content": "name: Node CI\n\non:\n  push:\n    branches-ignore:\n      - 'dependabot/**'\n  pull_request:\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    strategy:\n      matrix:\n        node-version: [20, 22]\n\n    steps:\n      - uses: actions/checkout@v6\n      - name: Use Node.js ${{ matrix.node-version }}\n        uses: actions/setup-node@v6\n        with:\n          node-version: ${{ matrix.node-version }}\n          cache: npm\n      - name: Install\n        run: npm ci\n      - name: Test\n        run: npm test\n"
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: Publish\n\non:\n  release:\n    types: [created]\n\npermissions:\n  contents: read\n  id-token: write\n\njobs:\n  publish-npm:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 22\n          registry-url: https://registry.npmjs.org/\n          cache: npm\n      - run: npm ci\n      - run: npm test\n      - run: npm version ${TAG_NAME} --git-tag-version=false\n        env:\n          TAG_NAME: ${{ github.event.release.tag_name }}\n      - run: npm whoami; npm --ignore-scripts publish --provenance\n        env:\n          NODE_AUTH_TOKEN: ${{secrets.npm_token}}\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules/\nnpm-debug.log\nyarn.lock\n"
  },
  {
    "path": "CODEOWNERS",
    "content": "* @github/web-systems-reviewers\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "## Publishing this package\n\nPublishing 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.\n\nTo 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.\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2016 GitHub, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# eslint-plugin-github\n\n## Installation\n\n```sh\nnpm install --save-dev eslint eslint-plugin-github\n```\n\n## Setup\n\n### Legacy Configuration (`.eslintrc`)\n\nAdd `github` to your list of plugins in your ESLint config.\n\nJSON ESLint config example:\n\n```json\n{\n  \"plugins\": [\"github\"]\n}\n```\n\nExtend the configs you wish to use.\n\nJSON ESLint config example:\n\n```json\n{\n  \"extends\": [\"plugin:github/recommended\"]\n}\n```\n\n### Flat Configuration (`eslint-config.js`)\n\nImport the `eslint-plugin-github`, and extend any of the configurations using `getFlatConfigs()` as needed like so:\n\n```js\nimport github from 'eslint-plugin-github'\n\nexport default [\n  github.getFlatConfigs().browser,\n  github.getFlatConfigs().recommended,\n  github.getFlatConfigs().react,\n  ...github.getFlatConfigs().typescript,\n  {\n    files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],\n    ignores: ['eslint.config.mjs'],\n    rules: {\n      'github/array-foreach': 'error',\n      'github/async-preventdefault': 'warn',\n      'github/no-then': 'error',\n      'github/no-blur': 'error',\n    },\n  },\n]\n```\n\n> [!NOTE]\n> 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. \n> \n> 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:\n>\n> `'github/filenames-match-regex': ['error', '^([a-z0-9]+)([A-Z][a-z0-9]+)*$'],`\n\nThe available configs are:\n\n- `internal`\n  - Rules useful for github applications.\n- `browser`\n  - Useful rules when shipping your app to the browser.\n- `react`\n  - Recommended rules for React applications.\n- `recommended`\n  - Recommended rules for every application.\n- `typescript`\n  - Useful rules when writing TypeScript.\n\n### Component mapping (Experimental)\n\n_Note: This is experimental and subject to change._\n\nThe `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.\n\nBy 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`.\n\n```json\n{\n  \"settings\": {\n    \"github\": {\n      \"polymorphicPropName\": \"asChild\",\n      \"components\": {\n        \"Box\": \"p\",\n        \"Link\": \"a\"\n      }\n    }\n  }\n}\n```\n\nThis config will be interpreted in the following way:\n\n- All `<Box>` elements will be treated as a `p` element type.\n- `<Link>` without a defined `as` prop will be treated as a `a`.\n- `<Link as='button'>` will be treated as a `button` element type.\n\n### Rules\n\n<!-- begin auto-generated rules list -->\n\n💼 Configurations enabled in.\\\n🔍 Set in the `browser` configuration.\\\n🔐 Set in the `internal` configuration.\\\n⚛️ Set in the `react` configuration.\\\n✅ Set in the `recommended` configuration.\\\n🔧 Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).\\\n❌ Deprecated.\n\n| Name                                                                                                     | Description                                                                                                              | 💼 | 🔧 | ❌  |\n| :------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------- | :- | :- | :- |\n| [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.                                                      | ⚛️ |    |    |\n| [a11y-no-generic-link-text](docs/rules/a11y-no-generic-link-text.md)                                     | disallow generic link text                                                                                               |    |    | ❌  |\n| [a11y-no-title-attribute](docs/rules/a11y-no-title-attribute.md)                                         | disallow using the title attribute                                                                                       | ⚛️ |    |    |\n| [a11y-no-visually-hidden-interactive-element](docs/rules/a11y-no-visually-hidden-interactive-element.md) | enforce that interactive elements are not visually hidden                                                                | ⚛️ |    |    |\n| [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`. | ⚛️ |    |    |\n| [a11y-svg-has-accessible-name](docs/rules/a11y-svg-has-accessible-name.md)                               | require SVGs to have an accessible name                                                                                  | ⚛️ |    |    |\n| [array-foreach](docs/rules/array-foreach.md)                                                             | enforce `for..of` loops over `Array.forEach`                                                                             | ✅  |    |    |\n| [async-currenttarget](docs/rules/async-currenttarget.md)                                                 | disallow `event.currentTarget` calls inside of async functions                                                           | 🔍 |    |    |\n| [async-preventdefault](docs/rules/async-preventdefault.md)                                               | disallow `event.preventDefault` calls inside of async functions                                                          | 🔍 |    |    |\n| [authenticity-token](docs/rules/authenticity-token.md)                                                   | disallow usage of CSRF tokens in JavaScript                                                                              | 🔐 |    |    |\n| [filenames-match-regex](docs/rules/filenames-match-regex.md)                                             | require filenames to match a regex naming convention                                                                     |    |    |    |\n| [get-attribute](docs/rules/get-attribute.md)                                                             | disallow wrong usage of attribute names                                                                                  | 🔍 | 🔧 |    |\n| [js-class-name](docs/rules/js-class-name.md)                                                             | enforce a naming convention for js- prefixed classes                                                                     | 🔐 |    |    |\n| [no-blur](docs/rules/no-blur.md)                                                                         | disallow usage of `Element.prototype.blur()`                                                                             | 🔍 |    |    |\n| [no-d-none](docs/rules/no-d-none.md)                                                                     | disallow usage the `d-none` CSS class                                                                                    | 🔐 |    |    |\n| [no-dataset](docs/rules/no-dataset.md)                                                                   | enforce usage of `Element.prototype.getAttribute` instead of `Element.prototype.datalist`                                | 🔍 |    |    |\n| [no-dynamic-script-tag](docs/rules/no-dynamic-script-tag.md)                                             | disallow creating dynamic script tags                                                                                    | ✅  |    |    |\n| [no-implicit-buggy-globals](docs/rules/no-implicit-buggy-globals.md)                                     | disallow implicit global variables                                                                                       | ✅  |    |    |\n| [no-inner-html](docs/rules/no-inner-html.md)                                                             | disallow `Element.prototype.innerHTML` in favor of `Element.prototype.textContent`                                       | 🔍 |    |    |\n| [no-innerText](docs/rules/no-innerText.md)                                                               | disallow `Element.prototype.innerText` in favor of `Element.prototype.textContent`                                       | 🔍 | 🔧 |    |\n| [no-then](docs/rules/no-then.md)                                                                         | enforce using `async/await` syntax over Promises                                                                         | ✅  |    |    |\n| [no-useless-passive](docs/rules/no-useless-passive.md)                                                   | disallow marking a event handler as passive when it has no effect                                                        | 🔍 | 🔧 |    |\n| [prefer-observers](docs/rules/prefer-observers.md)                                                       | disallow poorly performing event listeners                                                                               | 🔍 |    |    |\n| [require-passive-events](docs/rules/require-passive-events.md)                                           | enforce marking high frequency event handlers as passive                                                                 | 🔍 |    |    |\n| [unescaped-html-literal](docs/rules/unescaped-html-literal.md)                                           | disallow unescaped HTML literals                                                                                         | 🔍 |    |    |\n\n<!-- end auto-generated rules list -->\n"
  },
  {
    "path": "bin/eslint-ignore-errors.js",
    "content": "#!/usr/bin/env node\n// Disables eslint rules in a JavaScript file with next-line comments. This is\n// useful when introducing a new rule that causes many failures. The comments\n// can be fixed and removed at while updating the file later.\n//\n// Usage:\n//\n//  eslint-ignore-errors app/assets/javascripts/something.js\n\nconst fs = require('fs')\nconst execFile = require('child_process').execFile\n\nexecFile('eslint', ['--format', 'json', process.argv[2]], (error, stdout) => {\n  for (const result of JSON.parse(stdout)) {\n    const filename = result.filePath\n    const jsLines = fs.readFileSync(filename, 'utf8').split('\\n')\n    const offensesByLine = {}\n    let addedLines = 0\n\n    // Produces {47: ['github/no-d-none', 'github/no-blur'], 83: ['github/no-blur']}\n    for (const message of result.messages) {\n      if (offensesByLine[message.line]) {\n        offensesByLine[message.line].push(message.ruleId)\n      } else {\n        offensesByLine[message.line] = [message.ruleId]\n      }\n    }\n\n    for (const line of Object.keys(offensesByLine)) {\n      const lineIndex = line - 1 + addedLines\n      const previousLine = jsLines[lineIndex - 1]\n      const ruleIds = offensesByLine[line].join(', ')\n      if (isDisableComment(previousLine)) {\n        jsLines[lineIndex - 1] = previousLine.replace(/\\s?\\*\\/$/, `, ${ruleIds} */`)\n      } else {\n        const leftPad = ' '.repeat(jsLines[lineIndex].match(/^\\s*/g)[0].length)\n        jsLines.splice(lineIndex, 0, `${leftPad}/* eslint-disable-next-line ${ruleIds} */`)\n      }\n      addedLines += 1\n    }\n\n    if (result.messages.length !== 0) {\n      fs.writeFileSync(filename, jsLines.join('\\n'), 'utf8')\n    }\n  }\n})\n\nfunction isDisableComment(line) {\n  return line.match(/\\/\\* eslint-disable-next-line .+\\*\\//)\n}\n"
  },
  {
    "path": "docs/rules/a11y-aria-label-is-well-formatted.md",
    "content": "# Enforce [aria-label] text to be formatted as you would visual text (`github/a11y-aria-label-is-well-formatted`)\n\n💼 This rule is enabled in the ⚛️ `react` config.\n\n<!-- end auto-generated rule header -->\n\n## Rule Details\n\n`[aria-label]` content should be formatted in the same way you would visual text. Please use sentence case.\n\nDo not connect the words like you would an ID. An `aria-label` is not an ID, and should be formatted as human-friendly text.\n\n## Resources\n\n- [Using aria-label](https://www.w3.org/WAI/tutorials/forms/labels/#using-aria-label)\n\n## Examples\n\n### **Incorrect** code for this rule 👎\n\n```html\n<a href=\"...\" aria-label=\"learn more\"></a>\n```\n\n```html\n<a href=\"...\" aria-label=\"go-to-link\"></a>\n```\n\n### **Correct** code for this rule 👍\n\n```html\n<a href=\"...\" aria-label=\"Learn more\"></a>\n```\n\n```html\n<a href=\"...\" aria-label=\"Homepage\"></a>\n```\n\n## Version\n"
  },
  {
    "path": "docs/rules/a11y-no-generic-link-text.md",
    "content": "# Disallow generic link text (`github/a11y-no-generic-link-text`)\n\n❌ This rule is deprecated. It was replaced by `jsx-a11y/anchor-ambiguous-text`.\n\n<!-- end auto-generated rule header -->\n\n## Rule Details\n\nAvoid setting generic link text like, \"Click here\", \"Read more\", and \"Learn more\" which do not make sense when read out of context.\n\nScreen 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.\n\nAdditionally, generic link text can also problematic for heavy zoom users where the link context is out of view.\n\nEnsure that your link text is descriptive and the purpose of the link is clear even when read out of context of surrounding text.\nLearn more about how to write descriptive link text at [Access Guide: Write descriptive link text](https://www.accessguide.io/guide/descriptive-link-text)\n\n### Use of ARIA attributes\n\nIf you _must_ use ARIA to replace the visible link text, include the visible text at the beginning.\n\nFor example, on a pricing plans page, the following are good:\n\n- Visible text: `Learn more`\n- Accessible label: `Learn more about GitHub pricing plans`\n\nAccessible ✅\n\n```html\n<a href=\"...\" aria-label=\"Learn more about GitHub pricing plans\">Learn more</a>\n```\n\nInaccessible 🚫\n\n```html\n<a href=\"...\" aria-label=\"GitHub pricing plans\">Learn more</a>\n```\n\nIncluding 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).\n\n#### False negatives\n\nCaution: because of the restrictions of static code analysis, we may not catch all violations.\n\nPlease perform browser tests and spot checks:\n\n- when `aria-label` is set dynamically\n- when using `aria-labelledby`\n\n## Resources\n\n- [Primer: Links](https://primer.style/design/accessibility/links)\n- [Understanding Success Criterion 2.4.4: Link Purpose (In Context)](https://www.w3.org/WAI/WCAG21/Understanding/link-purpose-in-context.html)\n- [WebAim: Links and Hypertext](https://webaim.org/techniques/hypertext/)\n- [Deque: Use link text that make sense when read out of context](https://dequeuniversity.com/tips/link-text)\n\n## Examples\n\n### **Incorrect** code for this rule 👎\n\n```jsx\n<a href=\"github.com/about\">Learn more</a>\n```\n\n```jsx\n<a href=\"github.com/about\">Read more</a>\n```\n\n```jsx\n<a href=\"github.com/about\" aria-label=\"Why dogs are awesome\">\n  Read more\n</a>\n```\n\n```jsx\n<a href=\"github.com/about\" aria-describedby=\"element123\">\n  Read more\n</a>\n```\n\n### **Correct** code for this rule 👍\n\n```jsx\n<a href=\"github.com/about\">Learn more about GitHub</a>\n```\n\n```jsx\n<a href=\"github.com/new\">Create a new repository</a>\n```\n\n## Version\n"
  },
  {
    "path": "docs/rules/a11y-no-title-attribute.md",
    "content": "# Disallow using the title attribute (`github/a11y-no-title-attribute`)\n\n💼 This rule is enabled in the ⚛️ `react` config.\n\n<!-- end auto-generated rule header -->\n\nThe 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.\n\nThe `title` attribute is commonly set on links, matching the link text. This is redundant and unnecessary so it can be simply be removed.\n\nIf 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.\n\n### Should I use the title attribute to provide an accessible name for an <svg>?\n\nUse a <title> element instead of the title attribute, or an aria-label.\n\n## Rule Details\n\n👎 Examples of **incorrect** code for this rule:\n\n```jsx\n<a src=\"https://www.github.com\" title=\"A home for all developers\">\n  GitHub\n</a>\n```\n\n```jsx\n<a href=\"/\" title=\"github.com\">\n  GitHub\n</a>\n```\n\n```jsx\n<span src=\"https://www.github.com\" title=\"supercalifragilisticexpialidocious\">\n  supercali...\n</span>\n```\n\n👍 Examples of **correct** code for this rule:\n\n```jsx\n<iframe src=\"https://www.github.com\" title=\"Github\"></iframe>\n```\n\n## Version\n"
  },
  {
    "path": "docs/rules/a11y-no-visually-hidden-interactive-element.md",
    "content": "# Enforce that interactive elements are not visually hidden (`github/a11y-no-visually-hidden-interactive-element`)\n\n💼 This rule is enabled in the ⚛️ `react` config.\n\n<!-- end auto-generated rule header -->\n\n## Rule Details\n\nThis 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.\n\nNote: 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).\n\n### Why do we visually hide content?\n\nVisually 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.\n\nApplying the following css will visually hide content while still making it accessible to screen reader users.\n\n```css\nclip-path: inset(50%);\nheight: 1px;\noverflow: hidden;\nposition: absolute;\nwhite-space: nowrap;\nwidth: 1px;\n```\n\n👎 Examples of **incorrect** code for this rule:\n\n```jsx\n<button className=\"visually-hidden\">Submit</button>\n```\n\n```jsx\n<VisuallyHidden>\n  <button>Submit</button>\n</VisuallyHidden>\n```\n\n```jsx\n<VisuallyHidden as=\"button\">Submit</VisuallyHidden>\n```\n\n👍 Examples of **correct** code for this rule:\n\n```jsx\n<h2 className=\"visually-hidden\">Welcome to GitHub</h2>\n```\n\n```jsx\n<VisuallyHidden>\n  <h2>Welcome to GitHub</h2>\n</VisuallyHidden>\n```\n\n```jsx\n<VisuallyHidden as=\"h2\">Welcome to GitHub</VisuallyHidden>\n```\n\n## Options\n\n- className - A css className that visually hides content. Defaults to `sr-only`.\n- componentName - A react component name that visually hides content. Defaults to `VisuallyHidden`.\n\n```json\n{\n  \"a11y-no-visually-hidden-interactive-element\": [\n    \"error\",\n    {\n      \"className\": \"visually-hidden\",\n      \"componentName\": \"VisuallyHidden\"\n    }\n  ]\n}\n```\n\n## Version\n"
  },
  {
    "path": "docs/rules/a11y-role-supports-aria-props.md",
    "content": "# Enforce that elements with explicit or implicit roles defined contain only `aria-*` properties supported by that `role` (`github/a11y-role-supports-aria-props`)\n\n💼 This rule is enabled in the ⚛️ `react` config.\n\n<!-- end auto-generated rule header -->\n\n## Rule Details\n\nThis rule enforces that elements with explicit or implicit roles defined contain only `aria-*` properties supported by that `role`.\n\nFor 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`.\n\n### \"Help! I'm trying to set a tooltip on a static element and this rule flagged it!\"\n\nPlease do not use tooltips on static elements. It is a highly discouraged, inaccessible pattern.\nSee [Primer: Tooltip alternatives](https://primer.style/design/accessibility/tooltip-alternatives) for what to do instead.\n\n### Resources\n\n- [w3c/aria Consider prohibiting author naming certain roles #833](https://github.com/w3c/aria/issues/833)\n- [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/)\n- [Your tooltips are bogus](https://heydonworks.com/article/your-tooltips-are-bogus/)\n- [Primer: Tooltip alternatives](https://primer.style/design/accessibility/tooltip-alternatives)\n\n### Disclaimer\n\nThere 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).\n\n### **Incorrect** code for this rule 👎\n\n```erb\n<span class=\"tooltipped\" aria-label=\"This is a tooltip\">I am some text.</span>\n```\n\n```erb\n<span aria-label=\"This is some content that will completely override the button content\">Please be careful of the following.</span>\n```\n\n```erb\n<div aria-labelledby=\"heading1\">Goodbye</div>\n```\n\n```erb\n<h1 aria-label=\"This will override the page title completely\">Page title</h1>\n```\n\n### **Correct** code for this rule 👍\n\n```erb\n<button aria-label=\"Close\">\n  <svg src=\"closeIcon\"></svg>\n</button>\n```\n\n```erb\n<button aria-label=\"Bold\" aria-describedby=\"tooltip1\">\n  <svg src=\"boldIcon\"></svg>\n</button>\n<p id=\"tooltip1\" class=\"tooltip\">Add bold text or turn selection into bold text</p>\n```\n\n```erb\n<span>Hello</span>\n```\n\n```erb\n<div>Goodbye</div>\n```\n\n```erb\n<h1>Page title</h1>\n```\n\n```erb\n<div role=\"dialog\" aria-labelledby=\"dialogHeading\">\n  <h1 id=\"dialogHeading\">Heading</h1>\n</div>\n```\n\n## Version\n"
  },
  {
    "path": "docs/rules/a11y-svg-has-accessible-name.md",
    "content": "# Require SVGs to have an accessible name (`github/a11y-svg-has-accessible-name`)\n\n💼 This rule is enabled in the ⚛️ `react` config.\n\n<!-- end auto-generated rule header -->\n\n## Rule Details\n\nAn `<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.\n\nHowever, if the `<svg>` is purely decorative, hide it with `aria-hidden=\"true\"` or `role=\"presentation\"`.\n\n## Resources\n\n- [Accessible SVGs](https://css-tricks.com/accessible-svgs/)\n\n## Examples\n\n### **Incorrect** code for this rule 👎\n\n```html\n<svg height='100' width='100'>\n  <circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>\n</svg>\n```\n\n```html\n<svg height='100' width='100' title='Circle with a black outline and red fill'>\n  <circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>\n</svg>\n```\n\n```html\n<svg height='100' width='100'>\n  <circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>\n  <title>Circle with a black outline and red fill</title>\n</svg>\n```\n\n### **Correct** code for this rule 👍\n\n```html\n<svg height='100' width='100'>\n  <title>Circle with a black outline and red fill</title>\n  <circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>\n</svg>\n```\n\n```html\n<svg aria-label='Circle with a black outline and red fill' height='100' width='100'>\n  <circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>\n</svg>\n```\n\n```html\n<svg aria-labelledby='circle_text' height='100' width='100'>\n  <circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>\n</svg>\n```\n\n```html\n<svg aria-hidden='true' height='100' width='100'>\n  <circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>\n</svg>\n```\n\n```html\n<svg role='presentation' height='100' width='100'>\n  <circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>\n</svg>\n```\n\n## Version\n"
  },
  {
    "path": "docs/rules/array-foreach.md",
    "content": "# Enforce `for..of` loops over `Array.forEach` (`github/array-foreach`)\n\n💼 This rule is enabled in the ✅ `recommended` config.\n\n<!-- end auto-generated rule header -->\n\nPrefer `for...of` statement instead of `Array.forEach`.\n\n## Rule Details\n\nHere's a summary of why `forEach` is disallowed, and why we prefer `for...of` for almost any use-case of `forEach`:\n\n- Allowing `forEach` encourages **layering of \"bad practices\"**, such as using `Array.from()` (which is less performant than using `for...of`).\n- 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`).\n- `for` loops are considered \"more readable\" and have **clearer intent**.\n- `for...of` loops offer the **most flexibility** for iteration (especially vs `Array.from`).\n\nTypically 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.\n\n`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...\n\nOften 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()`:\n\n```diff\n apples\n+  .filter(apple => !apple.bad)\n    .forEach(polishApple)\n```\n\nThe 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:\n\n```diff\n for(const apple of apples) {\n+   if (apple.bad) continue\n   polishApple(apple)\n }\n```\n\nChaining 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.\n\nThe `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)`.\n\nThe `for` loop avoids this issue. Calls are explicit within the `for` loop, as they are not passed around. For example:\n\n```js\nfor (const apple of apples) {\n  polishApple(apple)\n}\n```\n\nWe 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.\n\nWhile `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.\n\nA 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...\n\n```js\napples.forEach(polishApple)\n// vs...\nfor (const apple of apples) {\n  polishApple(apple)\n}\n```\n\nIf `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:\n\n```diff\n- apples.forEach(polishApple)\n+ await apples.reduce((cur, next) => cur.then(() => polishApple(next)), Promise.resolve())\n```\n\nCompare this to the `for` loop, which has a much simpler path to refactoring:\n\n```diff\n for (const apple of apples) {\n-  polishApple(apple)\n+  await polishApple(apple)\n }\n```\n\nSee also https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of\n\n👎 Examples of **incorrect** code for this rule:\n\n```js\nels.forEach(el => {\n  el\n})\n```\n\n👍 Examples of **correct** code for this rule:\n\n```js\nfor (const el of els) {\n  el\n}\n```\n\nUse [`entries()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/entries) to get access to the index:\n\n```js\nfor (const [i, el] of els.entries()) {\n  el.name = `Element ${i}`\n}\n```\n\n## Version\n\n4.3.2\n"
  },
  {
    "path": "docs/rules/async-currenttarget.md",
    "content": "# Disallow `event.currentTarget` calls inside of async functions (`github/async-currenttarget`)\n\n💼 This rule is enabled in the 🔍 `browser` config.\n\n<!-- end auto-generated rule header -->\n\n## Rule Details\n\nAccessing `event.currentTarget` inside an `async function()` will likely be `null` as `currentTarget` is mutated as the event is propagated.\n\n1.  A `click` event is dispatched\n2.  The handler is invoked once with the expected `currentTarget`\n3.  An `await` defers the execution\n4.  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\n5.  The async function resumes\n6.  `event.currentTarget` is now `null`\n\nIf you're using `async`, you'll need to synchronously create a reference to `currentTarget` before any async activity.\n\n👎 Examples of **incorrect** code for this rule:\n\n```js\ndocument.addEventListener('click', async function (event) {\n  // event.currentTarget will be an HTMLElement\n  const url = event.currentTarget.getAttribute('data-url')\n  const data = await fetch(url)\n\n  // But now, event.currentTarget will be null\n  const text = event.currentTarget.getAttribute('data-text')\n  // ...\n})\n```\n\n👍 Examples of **correct** code for this rule:\n\n```js\ndocument.addEventListener('click', function (event) {\n  const currentTarget = event.currentTarget\n  const url = currentTarget.getAttribute('data-url')\n\n  // call async IIFE\n  ;(async function () {\n    const data = await fetch(url)\n\n    const text = currentTarget.getAttribute('data-text')\n    // ...\n  })()\n})\n```\n\nAlternatively, extract a function to create an element reference.\n\n```js\ndocument.addEventListener('click', function (event) {\n  fetchData(event.currentTarget)\n})\n\nasync function fetchData(el) {\n  const url = el.getAttribute('data-url')\n  const data = await fetch(url)\n  const text = el.getAttribute('data-text')\n  // ...\n}\n```\n\n## Version\n\n4.3.2\n"
  },
  {
    "path": "docs/rules/async-preventdefault.md",
    "content": "# Disallow `event.preventDefault` calls inside of async functions (`github/async-preventdefault`)\n\n💼 This rule is enabled in the 🔍 `browser` config.\n\n<!-- end auto-generated rule header -->\n\nUsing `event.preventDefault()` inside an `async function()` won't likely work as you'd expect because synchronous nature of event dispatch.\n\n## Rule Details\n\n1.  A `click` event is dispatched\n2.  This handler is scheduled but not ran immediately because its marked async.\n3.  The event dispatch completes and nothing has called `preventDefault()` _yet_ and the default click behavior occurs.\n4.  The async function is scheduled and runs.\n5.  Calling `preventDefault()` is now a no-op as the synchronous event dispatch has already completed.\n\nIf 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.\n\n👎 Examples of **incorrect** code for this rule:\n\n```js\ndocument.addEventListener('click', async function (event) {\n  const data = await fetch()\n\n  event.preventDefault()\n})\n```\n\n👍 Examples of **correct** code for this rule:\n\n```js\ndocument.addEventListener('click', function (event) {\n  // preventDefault in a regular function\n  event.preventDefault()\n\n  // call async helper function\n  loadData(event.target)\n})\n\nasync function loadData(el) {\n  const data = await fetch()\n  // ...\n}\n```\n\nThis could also be done with an async IIFE.\n\n```js\ndocument.addEventListener('click', function (event) {\n  // preventDefault in a regular function\n  event.preventDefault()\n\n  // call async IIFE\n  ;(async function () {\n    const data = await fetch()\n    // ...\n  })()\n})\n```\n\n## Version\n\n4.3.2\n"
  },
  {
    "path": "docs/rules/authenticity-token.md",
    "content": "# Disallow usage of CSRF tokens in JavaScript (`github/authenticity-token`)\n\n💼 This rule is enabled in the 🔐 `internal` config.\n\n<!-- end auto-generated rule header -->\n\n## Rule Details\n\nThe 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.\n\nAn 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.\n\nRequests 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\n\nUnless 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.\n\nThe 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:\n\n👎 Examples of **incorrect** code for this rule:\n\n```js\nconst csrfToken = this.closest('form').elements['authenticity_token'].value\n```\n\n👍 Examples of **correct** code for this rule:\n\n```erb\n<%= form_tag \"/my/endpoint\" do %>\n  <input type=\"hidden\" name=\"my_field\" value=\"my value\">\n  <button class=\"js-my-button\">Click Me!</button>\n<% end %>\n```\n\n```js\non('click', '.js-my-button', function (e) {\n  const form = this.closest('form')\n\n  fetch(form.action, {\n    method: form.method,\n    body: new FormData(form),\n  }).then(function () {\n    alert('Success!')\n  })\n\n  e.preventDefault()\n})\n```\n\nAn alternate, but less preferred approach is to include the a signed CSRF url in a data-attribute:\n\n```erb\n<button class=\"js-my-button\" data-url=\"<%= encode_authenticity_token_path(path) %>\">Click Me!</button>\n```\n\n```js\non('click', '.js-my-button', function (e) {\n  csrfRequest(this.getAttribute('data-url'), {\n    method: 'PUT',\n    body: data,\n  }).then(function () {\n    alert('Success!')\n  })\n\n  e.preventDefault()\n})\n```\n\n## Version\n\n4.3.2\n"
  },
  {
    "path": "docs/rules/filenames-match-regex.md",
    "content": "# Require filenames to match a regex naming convention (`github/filenames-match-regex`)\n\n<!-- end auto-generated rule header -->\n\n## Rule Details\n\nRule to ensure that filenames match a convention, with a default of kebab case or camelCase with one hump for flat config.\n\n👎 Examples of **incorrect** filename for this default rule:\n\n- `fileNameRule.js`\n\n👍 Examples of **correct** code for this rule:\n\n- `fileName.js`\n- `file-name.js`\n\n## Options\n\nregex - Regex to match the filename structure. Defaults to kebab case or camelCase with one hump.\n\nDefault:\n\n```json\n{\n  \"filenames-match-regex\": [\n    \"error\"\n  ]\n}\n```\n\nIf 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:\n\n```json\n{\n  \"filenames-match-regex\": [\n    \"error\",\n    \"^([a-z0-9]+)([A-Z][a-z0-9]+)*$\"\n  ]\n}\n```\n\n## Version\n\n4.3.2\n"
  },
  {
    "path": "docs/rules/get-attribute.md",
    "content": "# Disallow wrong usage of attribute names (`github/get-attribute`)\n\n💼 This rule is enabled in the 🔍 `browser` config.\n\n🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).\n\n<!-- end auto-generated rule header -->\n\n## Rule Details\n\nAs HTML attributes are case insensitive, prefer using lowercase.\n\n👎 Examples of **incorrect** code for this rule:\n\n```js\nel.getAttribute('autoComplete')\n```\n\n```js\nel.getAttribute('dataFoo')\n```\n\n👍 Examples of **correct** code for this rule:\n\n```js\nel.getAttribute('autocomplete')\n```\n\n```js\nel.getAttribute('data-foo')\n```\n\n## Version\n\n4.3.2\n"
  },
  {
    "path": "docs/rules/js-class-name.md",
    "content": "# Enforce a naming convention for js- prefixed classes (`github/js-class-name`)\n\n💼 This rule is enabled in the 🔐 `internal` config.\n\n<!-- end auto-generated rule header -->\n\nJavaScript should only query and handle events for `js-` prefixed class names.\n\n## Rule Details\n\nThe key benefit is that these symbols can be easily searched for.\n\nLooking 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.\n\nSince 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.\n\nIn 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)`.\n\nTypically 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.\n\n```html\n<div class=\"js-org-update\" data-org-name=\"<%= org.login %>\"></div>\n```\n\nAllows 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.\n\n`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.\n\n[@defunkt's original proposal from 2010](https://web.archive.org/web/20180902223055/http://ozmm.org/posts/slightly_obtrusive_javascript.html).\n\n👎 Examples of **incorrect** code for this rule:\n\n```js\nconst el = document.querySelector('.js-Foo')\n```\n\n👍 Examples of **correct** code for this rule:\n\n```js\nconst el = document.querySelector('.js-foo')\n```\n\n## Version\n\n4.3.2\n"
  },
  {
    "path": "docs/rules/no-blur.md",
    "content": "# Disallow usage of `Element.prototype.blur()` (`github/no-blur`)\n\n💼 This rule is enabled in the 🔍 `browser` config.\n\n<!-- end auto-generated rule header -->\n\nDo 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.\n\n## Rule Details\n\n- [Use of `blur()` is discouraged by WHATWG HTML spec](https://html.spec.whatwg.org/multipage/interaction.html#dom-blur)\n\n👎 Examples of **incorrect** code for this rule:\n\n```js\nmenu.addEventListener('close', () => {\n  input.blur()\n})\n```\n\n👍 Examples of **correct** code for this rule:\n\n```js\nmenu.addEventListener('open', () => {\n  const previouslyFocusedElement = document.activeElement\n\n  input.focus()\n\n  menu.addEventListener('close', () => {\n    previouslyFocusedElement.focus()\n  })\n})\n```\n\n## Version\n\n4.3.2\n"
  },
  {
    "path": "docs/rules/no-d-none.md",
    "content": "# Disallow usage the `d-none` CSS class (`github/no-d-none`)\n\n💼 This rule is enabled in the 🔐 `internal` config.\n\n<!-- end auto-generated rule header -->\n\n## Rule Details\n\nIdeally JavaScript behaviors should not rely on Primer CSS when the `hidden` property can be used.\n\n👎 Examples of **incorrect** code for this rule:\n\n```js\ndiv.classList.add('d-none')\n```\n\n👍 Examples of **correct** code for this rule:\n\n```js\ndiv.hidden = false\n```\n\n## Version\n\n4.3.2\n"
  },
  {
    "path": "docs/rules/no-dataset.md",
    "content": "# Enforce usage of `Element.prototype.getAttribute` instead of `Element.prototype.datalist` (`github/no-dataset`)\n\n💼 This rule is enabled in the 🔍 `browser` config.\n\n<!-- end auto-generated rule header -->\n\n## Rule Details\n\nDue 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')`.\n\n👎 Examples of **incorrect** code for this rule:\n\n```js\nel.dataset.coolThing\n```\n\n👍 Examples of **correct** code for this rule:\n\n```js\nel.getAttribute('data-cool-thing')\n```\n\n## Version\n\n4.3.2\n"
  },
  {
    "path": "docs/rules/no-dynamic-script-tag.md",
    "content": "# Disallow creating dynamic script tags (`github/no-dynamic-script-tag`)\n\n💼 This rule is enabled in the ✅ `recommended` config.\n\n<!-- end auto-generated rule header -->\n\n## Rule Details\n\nCreating dynamic script tags bypasses a lot of security measures - like SRIs - and pose a potential threat to your application.\nInstead of creating a `script` tag in the client, provide all necessary `script` tags in the page's HTML.\n\n👎 Examples of **incorrect** code for this rule:\n\n```js\ndocument.createElement('script')\ndocument.getElementById('some-id').type = 'text/javascript'\n```\n\n👍 Examples of **correct** code for this rule:\n\n```html\n<!-- index.html -->\n<script src=\"/index.js\" type=\"text/javascript\">\n```\n\n## Version\n\n4.3.2\n"
  },
  {
    "path": "docs/rules/no-implicit-buggy-globals.md",
    "content": "# Disallow implicit global variables (`github/no-implicit-buggy-globals`)\n\n💼 This rule is enabled in the ✅ `recommended` config.\n\n<!-- end auto-generated rule header -->\n\n## Rule Details\n\n👎 Examples of **incorrect** code for this rule:\n\n```js\nvar foo = 1\n```\n\n👍 Examples of **correct** code for this rule:\n\n```js\n;(function () {\n  const foo = 1\n})()\n```\n\n## Version\n\n4.3.2\n"
  },
  {
    "path": "docs/rules/no-inner-html.md",
    "content": "# Disallow `Element.prototype.innerHTML` in favor of `Element.prototype.textContent` (`github/no-inner-html`)\n\n💼 This rule is enabled in the 🔍 `browser` config.\n\n<!-- end auto-generated rule header -->\n\n## Rule Details\n\nUsing `innerHTML` poses a potential security risk. Prefer using `textContent` to set text to an element.\n\n[Related security notification](https://github.com/github/paste-markdown/security/advisories/GHSA-gpfj-4j6g-c4w9)\n\nIt may be reasonable to disable this rule in testing setups that use known, trusted input and carry little security risk.\n\n👎 Examples of **incorrect** code for this rule:\n\n```js\nfunction setContent(element, content) {\n  element.innerHTML = content\n}\n```\n\n👍 Examples of **correct** code for this rule:\n\n```js\nfunction setContent(element, content) {\n  element.textContent = content\n}\n```\n\n## Version\n\n4.3.2\n"
  },
  {
    "path": "docs/rules/no-innerText.md",
    "content": "# Disallow `Element.prototype.innerText` in favor of `Element.prototype.textContent` (`github/no-innerText`)\n\n💼 This rule is enabled in the 🔍 `browser` config.\n\n🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).\n\n<!-- end auto-generated rule header -->\n\n## Rule Details\n\n👎 Examples of **incorrect** code for this rule:\n\n```js\nconst el = document.createElement('div')\nel.innerText = 'foo'\n```\n\n👍 Examples of **correct** code for this rule:\n\n```js\nconst el = document.createElement('div')\nel.textContent = 'foo'\n```\n\n## Version\n\n4.3.2\n"
  },
  {
    "path": "docs/rules/no-then.md",
    "content": "# Enforce using `async/await` syntax over Promises (`github/no-then`)\n\n💼 This rule is enabled in the ✅ `recommended` config.\n\n<!-- end auto-generated rule header -->\n\n## Rule Details\n\nYes, you should use promises, but prefer `async`/`await` syntax instead of `Promise.then()` callback chaining.\n\nhttps://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function\n\n👎 Examples of **incorrect** code for this rule:\n\n```js\nfunction countData(url) {\n  return downloadData(url).then(data => {\n    return data.length\n  })\n}\n```\n\n```js\nfunction getProcessedData(url) {\n  return downloadData(url).catch(e => {\n    console.log('Error occurred!', e)\n    return null;\n  })\n}\n```\n\n👍 Examples of **correct** code for this rule:\n\n```js\nasync function countProcessedData(url) {\n  const data = await downloadData(url);\n  return data.length\n}\n```\n\n```js\nasync function getProcessedData(url) {\n  try {\n    return await downloadData(url)\n  } catch (e) {\n    console.log('Error occurred!', e);\n    return null;\n  }\n}\n```\n\n## Version\n\n4.3.2\n"
  },
  {
    "path": "docs/rules/no-useless-passive.md",
    "content": "# Disallow marking a event handler as passive when it has no effect (`github/no-useless-passive`)\n\n💼 This rule is enabled in the 🔍 `browser` config.\n\n🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).\n\n<!-- end auto-generated rule header -->\n\nThis rule disallows setting `passive: true` for events on which it will have no effect.\n\nEvents where `passive: true` has an effect are: `touchstart`, `touchmove`, `touchenter`, `touchend`, `touchleave`, `wheel`, and `mousewheel`.\n\n## Rule Details\n\nAdding `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.\n\nIt 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.\n\nhttps://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Improving_scrolling_performance_with_passive_listeners\n\n👎 Examples of **incorrect** code for this rule:\n\n```js\n// bad (passive has no effect here)\nwindow.addEventListener(\n  'scroll',\n  () => {\n    console.log('Scroll event fired!')\n  },\n  {passive: true},\n)\n```\n\n👍 Examples of **correct** code for this rule:\n\n```js\n// good\nwindow.addEventListener('scroll', () => {\n  console.log('Scroll event fired!')\n})\n```\n\n## Version\n\n4.3.2\n"
  },
  {
    "path": "docs/rules/prefer-observers.md",
    "content": "# Disallow poorly performing event listeners (`github/prefer-observers`)\n\n💼 This rule is enabled in the 🔍 `browser` config.\n\n<!-- end auto-generated rule header -->\n\nSome 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.\n\n## Rule Details\n\nTypically `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.\n\nSimilarly, `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.\n\n```js\n// bad, low-performing code to determine if the element is less than 500px large\nwindow.addEventListener('resize', () => {\n  element.classList.toggle('size-small', element.getBoundingClientRect().width < 500)\n})\n\n// good - more performant, only fires when the actual elements change size\nconst observer = new ResizeObserver(entries => {\n  for (const {target, contentRect} of entries) {\n    target.classList.toggle('size-small', contentRect.width < 500)\n  }\n})\nobserver.observe(element)\n```\n\nhttps://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API\nhttps://developer.mozilla.org/en-US/docs/Web/API/Resize_Observer_API\n\n👎 Examples of **incorrect** code for this rule:\n\n```js\n// bad, expensive, error-prone code to determine if the element is in the viewport;\nwindow.addEventListener('scroll', () => {\n  const isIntersecting = checkIfElementIntersects(\n    element.getBoundingClientRect(),\n    window.innerHeight,\n    document.clientHeight,\n  )\n  element.classList.toggle('intersecting', isIntersecting)\n})\n```\n\n👍 Examples of **correct** code for this rule:\n\n```js\n// good - more performant and less error-prone\nconst observer = new IntersectionObserver(entries => {\n  for (const {target, isIntersecting} of entries) {\n    target.classList.toggle(target, isIntersecting)\n  }\n})\nobserver.observe(element)\n```\n\n## Version\n\n4.3.2\n"
  },
  {
    "path": "docs/rules/require-passive-events.md",
    "content": "# Enforce marking high frequency event handlers as passive (`github/require-passive-events`)\n\n💼 This rule is enabled in the 🔍 `browser` config.\n\n<!-- end auto-generated rule header -->\n\nThis rule enforces adding `passive: true` to high frequency event listeners (`touchstart`, `touchmove`, `touchenter`, `touchend`, `touchleave`, `wheel`, `mousewheel`).\n\n## Rule Details\n\nAdding 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.\n\nAdding `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.\n\nSee also: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Improving_scrolling_performance_with_passive_listeners\n\n👎 Examples of **incorrect** code for this rule:\n\n```js\n// bad\nwindow.addEventListener('touchstart', () => {\n  /* ... */\n})\n```\n\n👍 Examples of **correct** code for this rule:\n\n```js\n// good\nwindow.addEventListener(\n  'touchstart',\n  () => {\n    /* ... */\n  },\n  {passive: true},\n)\n```\n\n## Version\n\n4.3.2\n"
  },
  {
    "path": "docs/rules/unescaped-html-literal.md",
    "content": "# Disallow unescaped HTML literals (`github/unescaped-html-literal`)\n\n💼 This rule is enabled in the 🔍 `browser` config.\n\n<!-- end auto-generated rule header -->\n\n## Rule Details\n\nConstructing raw HTML with string literals is error prone and may lead to security issues.\n\nInstead 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`.\n\n👎 Examples of **incorrect** code for this rule:\n\n```js\nconst title = `<h1>Hello ${name}!</h1>`\n```\n\n👍 Examples of **correct** code for this rule:\n\n```js\n// good\nconst title = html`<h1>Hello ${name}!</h1>`\n```\n\n```js\n// also good\nconst title = document.createElement('h1')\ntitle.textContent = `Hello ${name}!`\n```\n\n## Version\n\n4.3.2\n"
  },
  {
    "path": "eslint.config.js",
    "content": "import globals from 'globals'\nimport eslintPlugin from 'eslint-plugin-eslint-plugin'\nimport importPlugin from 'eslint-plugin-import'\nimport i18nTextPlugin from 'eslint-plugin-i18n-text'\nimport recommendedGitHub from './lib/configs/flat/recommended.js'\nimport {fixupPluginRules} from '@eslint/compat'\n\nexport default [\n  recommendedGitHub,\n  {\n    files: ['lib/rules/**/*.js'],\n    ...eslintPlugin.configs['flat/all'],\n  },\n  {\n    ignores: ['test-examples/**'],\n  },\n  {\n    languageOptions: {\n      ecmaVersion: 13,\n      globals: {\n        ...globals.es6,\n        ...globals.node,\n      },\n    },\n    plugins: {\n      eslintPlugin,\n      import: importPlugin,\n      'i18n-text': fixupPluginRules(i18nTextPlugin),\n    },\n    rules: {\n      'import/extensions': 'off',\n      'import/no-commonjs': 'off',\n      'github/filenames-match-regex': 'off',\n      'i18n-text/no-en': 'off',\n      'eslint-plugin/prefer-placeholders': 'off',\n      'eslint-plugin/test-case-shorthand-strings': 'off',\n      'eslint-plugin/require-meta-docs-url': 'off',\n      'eslint-plugin/require-meta-default-options': 'off',\n    },\n  },\n]\n"
  },
  {
    "path": "lib/configs/browser.js",
    "content": "export default {\n  env: {\n    browser: true,\n  },\n  plugins: ['github', 'escompat'],\n  extends: ['plugin:escompat/recommended'],\n  rules: {\n    'escompat/no-dynamic-imports': 'off',\n    'github/async-currenttarget': 'error',\n    'github/async-preventdefault': 'error',\n    'github/get-attribute': 'error',\n    'github/no-blur': 'error',\n    'github/no-dataset': 'error',\n    'github/no-innerText': 'error',\n    'github/no-inner-html': 'error',\n    'github/unescaped-html-literal': 'error',\n    'github/no-useless-passive': 'error',\n    'github/require-passive-events': 'error',\n    'github/prefer-observers': 'error',\n    'import/no-nodejs-modules': 'error',\n    'no-restricted-syntax': [\n      'error',\n      {\n        selector: \"NewExpression[callee.name='URL'][arguments.length=1]\",\n        message: 'Please pass in `window.location.origin` as the 2nd argument to `new URL()`',\n      },\n    ],\n  },\n}\n"
  },
  {
    "path": "lib/configs/flat/browser.js",
    "content": "import globals from 'globals'\nimport github from '../../plugin.js'\nimport importPlugin from 'eslint-plugin-import'\nimport escompat from 'eslint-plugin-escompat'\nimport {fixupPluginRules} from '@eslint/compat'\n\nexport default {\n  ...escompat.configs['flat/recommended'],\n  languageOptions: {\n    globals: {\n      ...globals.browser,\n    },\n  },\n  plugins: {import: importPlugin, escompat, github: fixupPluginRules(github)},\n  rules: {\n    'escompat/no-dynamic-imports': 'off',\n    'github/async-currenttarget': 'error',\n    'github/async-preventdefault': 'error',\n    'github/get-attribute': 'error',\n    'github/no-blur': 'error',\n    'github/no-dataset': 'error',\n    'github/no-innerText': 'error',\n    'github/no-inner-html': 'error',\n    'github/unescaped-html-literal': 'error',\n    'github/no-useless-passive': 'error',\n    'github/require-passive-events': 'error',\n    'github/prefer-observers': 'error',\n    'import/no-nodejs-modules': 'error',\n    'no-restricted-syntax': [\n      'error',\n      {\n        selector: \"NewExpression[callee.name='URL'][arguments.length=1]\",\n        message: 'Please pass in `window.location.origin` as the 2nd argument to `new URL()`',\n      },\n    ],\n  },\n}\n"
  },
  {
    "path": "lib/configs/flat/internal.js",
    "content": "import github from '../../plugin.js'\nimport {fixupPluginRules} from '@eslint/compat'\n\nexport default {\n  plugins: {github: fixupPluginRules(github)},\n  rules: {\n    'github/authenticity-token': 'error',\n    'github/js-class-name': 'error',\n    'github/no-d-none': 'error',\n  },\n}\n"
  },
  {
    "path": "lib/configs/flat/react.js",
    "content": "import github from '../../plugin.js'\nimport jsxA11yPlugin from 'eslint-plugin-jsx-a11y'\nimport {fixupPluginRules} from '@eslint/compat'\n\nexport default {\n  ...jsxA11yPlugin.flatConfigs.recommended,\n  languageOptions: {\n    sourceType: 'module',\n    parserOptions: {\n      ecmaFeatures: {\n        jsx: true,\n      },\n    },\n  },\n  plugins: {github: fixupPluginRules(github), 'jsx-a11y': jsxA11yPlugin},\n  rules: {\n    '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\n    'github/a11y-aria-label-is-well-formatted': 'error',\n    'github/a11y-no-visually-hidden-interactive-element': 'error',\n    'github/a11y-no-title-attribute': 'error',\n    'github/a11y-svg-has-accessible-name': 'error',\n    'github/a11y-role-supports-aria-props': 'error',\n    'jsx-a11y/no-aria-hidden-on-focusable': 'error',\n    'jsx-a11y/no-autofocus': 'off',\n    'jsx-a11y/anchor-ambiguous-text': [\n      'error',\n      {\n        words: ['this', 'more', 'read here', 'read more'],\n      },\n    ],\n    'jsx-a11y/no-interactive-element-to-noninteractive-role': [\n      'error',\n      {\n        tr: ['none', 'presentation'],\n        td: ['cell'], // TODO: Remove once https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/pull/937#issuecomment-1638128318 is addressed.\n        canvas: ['img'],\n      },\n    ],\n    'jsx-a11y/no-redundant-roles': [\n      'error',\n      {\n        nav: ['navigation'], // default in eslint-plugin-jsx-a11y\n        tbody: ['rowgroup'],\n        thead: ['rowgroup'],\n      },\n    ],\n  },\n}\n"
  },
  {
    "path": "lib/configs/flat/recommended.js",
    "content": "import globals from 'globals'\nimport github from '../../plugin.js'\nimport prettierPlugin from 'eslint-plugin-prettier'\nimport eslintComments from 'eslint-plugin-eslint-comments'\nimport importPlugin from 'eslint-plugin-import'\nimport i18nTextPlugin from 'eslint-plugin-i18n-text'\nimport noOnlyTestsPlugin from 'eslint-plugin-no-only-tests'\nimport {fixupPluginRules} from '@eslint/compat'\n\nexport default {\n  languageOptions: {\n    ecmaVersion: 6,\n    sourceType: 'module',\n    globals: {\n      ...globals.es6,\n    },\n  },\n  plugins: {\n    prettier: prettierPlugin,\n    'eslint-comments': eslintComments,\n    import: importPlugin,\n    'i18n-text': fixupPluginRules(i18nTextPlugin),\n    'no-only-tests': noOnlyTestsPlugin,\n    github: fixupPluginRules(github),\n  },\n  rules: {\n    'constructor-super': 'error',\n    'eslint-comments/disable-enable-pair': 'off',\n    'eslint-comments/no-aggregating-enable': 'off',\n    'eslint-comments/no-duplicate-disable': 'error',\n    'eslint-comments/no-unlimited-disable': 'error',\n    'eslint-comments/no-unused-disable': 'error',\n    'eslint-comments/no-unused-enable': 'error',\n    'eslint-comments/no-use': ['error', {allow: ['eslint', 'eslint-disable-next-line', 'eslint-env', 'globals']}],\n    'github/filenames-match-regex': 'error',\n    'func-style': ['error', 'declaration', {allowArrowFunctions: true}],\n    'github/array-foreach': 'error',\n    'github/no-implicit-buggy-globals': 'error',\n    'github/no-then': 'error',\n    'github/no-dynamic-script-tag': 'error',\n    'i18n-text/no-en': ['error'],\n    'import/default': 'error',\n    'import/export': 'error',\n    'import/extensions': 'error',\n    'import/first': 'error',\n    'import/named': 'error',\n    'import/namespace': 'error',\n    'import/no-absolute-path': 'error',\n    'import/no-amd': 'error',\n    'import/no-anonymous-default-export': [\n      'error',\n      {\n        allowAnonymousClass: false,\n        allowAnonymousFunction: false,\n        allowArray: true,\n        allowArrowFunction: false,\n        allowLiteral: true,\n        allowObject: true,\n      },\n    ],\n    'import/no-commonjs': 'error',\n    'import/no-deprecated': 'error',\n    'import/no-duplicates': 'error',\n    'import/no-dynamic-require': 'error',\n    'import/no-extraneous-dependencies': [0, {devDependencies: false}],\n    'import/no-mutable-exports': 'error',\n    'import/no-named-as-default': 'error',\n    'import/no-named-as-default-member': 'error',\n    'import/no-namespace': 'error',\n    'import/no-unresolved': 'error',\n    'import/no-webpack-loader-syntax': 'error',\n    'no-case-declarations': 'error',\n    'no-class-assign': 'error',\n    'no-compare-neg-zero': 'error',\n    'no-cond-assign': 'error',\n    'no-console': 'error',\n    'no-const-assign': 'error',\n    'no-constant-condition': 'error',\n    'no-control-regex': 'error',\n    'no-debugger': 'error',\n    'no-delete-var': 'error',\n    'no-dupe-args': 'error',\n    'no-dupe-class-members': 'error',\n    'no-dupe-keys': 'error',\n    'no-duplicate-case': 'error',\n    'no-empty': 'error',\n    'no-empty-character-class': 'error',\n    'no-empty-pattern': 'error',\n    'no-ex-assign': 'error',\n    'no-extra-boolean-cast': 'error',\n    'no-fallthrough': 'error',\n    'no-func-assign': 'error',\n    'no-global-assign': 'error',\n    'no-implicit-globals': 'error',\n    'no-implied-eval': 'error',\n    'no-inner-declarations': 'error',\n    'no-invalid-regexp': 'error',\n    'no-invalid-this': 'error',\n    'no-irregular-whitespace': 'error',\n    'no-new-symbol': 'error',\n    'no-obj-calls': 'error',\n    'no-octal': 'error',\n    'no-only-tests/no-only-tests': [\n      'error',\n      {\n        block: ['describe', 'it', 'context', 'test', 'tape', 'fixture', 'serial', 'suite'],\n      },\n    ],\n    'no-redeclare': 'error',\n    'no-regex-spaces': 'error',\n    'no-return-assign': 'error',\n    'no-self-assign': 'error',\n    'no-sequences': ['error'],\n    'no-shadow': 'error',\n    'no-sparse-arrays': 'error',\n    'no-this-before-super': 'error',\n    'no-throw-literal': 'error',\n    'no-undef': 'error',\n    'no-unreachable': 'error',\n    'no-unsafe-finally': 'error',\n    'no-unsafe-negation': 'error',\n    'no-unused-labels': 'error',\n    'no-unused-vars': 'error',\n    'no-useless-concat': 'error',\n    'no-useless-escape': 'error',\n    'no-var': 'error',\n    'object-shorthand': ['error', 'always', {avoidQuotes: true}],\n    'one-var': ['error', 'never'],\n    'prefer-const': 'error',\n    'prefer-promise-reject-errors': 'error',\n    'prefer-rest-params': 'error',\n    'prefer-spread': 'error',\n    'prefer-template': 'error',\n    'prettier/prettier': 'error',\n    'require-yield': 'error',\n    'use-isnan': 'error',\n    'valid-typeof': 'error',\n    camelcase: ['error', {properties: 'always'}],\n    eqeqeq: ['error', 'smart'],\n  },\n  settings: {\n    'import/resolver': {\n      node: {\n        extensions: ['.js', '.ts'],\n      },\n    },\n  },\n}\n"
  },
  {
    "path": "lib/configs/flat/typescript.js",
    "content": "// eslint-disable-next-line import/no-unresolved\nimport tseslint from 'typescript-eslint'\nimport escompat from 'eslint-plugin-escompat'\n\nexport default tseslint.config(...tseslint.configs.recommended, ...escompat.configs['flat/typescript-2020'], {\n  languageOptions: {\n    parser: tseslint.parser,\n  },\n  plugins: {'@typescript-eslint': tseslint.plugin, escompat},\n  rules: {\n    camelcase: 'off',\n    'no-unused-vars': 'off',\n    'no-shadow': 'off',\n    'no-invalid-this': 'off',\n    '@typescript-eslint/no-invalid-this': ['error'],\n    '@typescript-eslint/no-shadow': ['error'],\n    '@typescript-eslint/interface-name-prefix': 'off',\n    '@typescript-eslint/array-type': ['error', {default: 'array-simple'}],\n    '@typescript-eslint/no-use-before-define': 'off',\n    '@typescript-eslint/explicit-member-accessibility': 'off',\n    '@typescript-eslint/explicit-function-return-type': 'off',\n    '@typescript-eslint/no-non-null-assertion': 'off',\n    '@typescript-eslint/no-unused-vars': 'error',\n    '@typescript-eslint/explicit-module-boundary-types': 'off',\n  },\n})\n"
  },
  {
    "path": "lib/configs/internal.js",
    "content": "export default {\n  plugins: ['github'],\n  rules: {\n    'github/authenticity-token': 'error',\n    'github/js-class-name': 'error',\n    'github/no-d-none': 'error',\n  },\n}\n"
  },
  {
    "path": "lib/configs/react.js",
    "content": "export default {\n  parserOptions: {\n    sourceType: 'module',\n    ecmaFeatures: {\n      jsx: true,\n    },\n  },\n  plugins: ['github', 'jsx-a11y'],\n  extends: ['plugin:jsx-a11y/recommended'],\n  rules: {\n    '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\n    'github/a11y-aria-label-is-well-formatted': 'error',\n    'github/a11y-no-visually-hidden-interactive-element': 'error',\n    'github/a11y-no-title-attribute': 'error',\n    'github/a11y-svg-has-accessible-name': 'error',\n    'github/a11y-role-supports-aria-props': 'error',\n    'jsx-a11y/no-aria-hidden-on-focusable': 'error',\n    'jsx-a11y/no-autofocus': 'off',\n    'jsx-a11y/anchor-ambiguous-text': [\n      'error',\n      {\n        words: ['this', 'more', 'read here', 'read more'],\n      },\n    ],\n    'jsx-a11y/no-interactive-element-to-noninteractive-role': [\n      'error',\n      {\n        tr: ['none', 'presentation'],\n        td: ['cell'], // TODO: Remove once https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/pull/937#issuecomment-1638128318 is addressed.\n        canvas: ['img'],\n      },\n    ],\n    'jsx-a11y/no-redundant-roles': [\n      'error',\n      {\n        nav: ['navigation'], // default in eslint-plugin-jsx-a11y\n        tbody: ['rowgroup'],\n        thead: ['rowgroup'],\n        ul: ['list'], // In webkit, setting list-style-type: none results in semantics being removed. Need explicit role.\n      },\n    ],\n  },\n}\n"
  },
  {
    "path": "lib/configs/recommended.js",
    "content": "export default {\n  parserOptions: {\n    ecmaFeatures: {\n      ecmaVersion: 6,\n    },\n    sourceType: 'module',\n  },\n  env: {\n    es6: true,\n  },\n  plugins: ['github', 'prettier', 'eslint-comments', 'import', 'filenames', 'i18n-text', 'no-only-tests'],\n  rules: {\n    'constructor-super': 'error',\n    'eslint-comments/disable-enable-pair': 'off',\n    'eslint-comments/no-aggregating-enable': 'off',\n    'eslint-comments/no-duplicate-disable': 'error',\n    'eslint-comments/no-unlimited-disable': 'error',\n    'eslint-comments/no-unused-disable': 'error',\n    'eslint-comments/no-unused-enable': 'error',\n    'eslint-comments/no-use': ['error', {allow: ['eslint', 'eslint-disable-next-line', 'eslint-env', 'globals']}],\n    'filenames/match-regex': ['error', '^[a-z0-9-]+(.[a-z0-9-]+)?$'],\n    'func-style': ['error', 'declaration', {allowArrowFunctions: true}],\n    'github/array-foreach': 'error',\n    'github/no-implicit-buggy-globals': 'error',\n    'github/no-then': 'error',\n    'github/no-dynamic-script-tag': 'error',\n    'i18n-text/no-en': ['error'],\n    'import/default': 'error',\n    'import/export': 'error',\n    'import/extensions': 'error',\n    'import/first': 'error',\n    'import/named': 'error',\n    'import/namespace': 'error',\n    'import/no-absolute-path': 'error',\n    'import/no-amd': 'error',\n    'import/no-anonymous-default-export': [\n      'error',\n      {\n        allowAnonymousClass: false,\n        allowAnonymousFunction: false,\n        allowArray: true,\n        allowArrowFunction: false,\n        allowLiteral: true,\n        allowObject: true,\n      },\n    ],\n    'import/no-commonjs': 'error',\n    'import/no-deprecated': 'error',\n    'import/no-duplicates': 'error',\n    'import/no-dynamic-require': 'error',\n    'import/no-extraneous-dependencies': [0, {devDependencies: false}],\n    'import/no-mutable-exports': 'error',\n    'import/no-named-as-default': 'error',\n    'import/no-named-as-default-member': 'error',\n    'import/no-namespace': 'error',\n    'import/no-unresolved': 'error',\n    'import/no-webpack-loader-syntax': 'error',\n    'no-case-declarations': 'error',\n    'no-class-assign': 'error',\n    'no-compare-neg-zero': 'error',\n    'no-cond-assign': 'error',\n    'no-console': 'error',\n    'no-const-assign': 'error',\n    'no-constant-condition': 'error',\n    'no-control-regex': 'error',\n    'no-debugger': 'error',\n    'no-delete-var': 'error',\n    'no-dupe-args': 'error',\n    'no-dupe-class-members': 'error',\n    'no-dupe-keys': 'error',\n    'no-duplicate-case': 'error',\n    'no-empty': 'error',\n    'no-empty-character-class': 'error',\n    'no-empty-pattern': 'error',\n    'no-ex-assign': 'error',\n    'no-extra-boolean-cast': 'error',\n    'no-fallthrough': 'error',\n    'no-func-assign': 'error',\n    'no-global-assign': 'error',\n    'no-implicit-globals': 'error',\n    'no-implied-eval': 'error',\n    'no-inner-declarations': 'error',\n    'no-invalid-regexp': 'error',\n    'no-invalid-this': 'error',\n    'no-irregular-whitespace': 'error',\n    'no-new-symbol': 'error',\n    'no-obj-calls': 'error',\n    'no-octal': 'error',\n    'no-only-tests/no-only-tests': [\n      'error',\n      {\n        block: ['describe', 'it', 'context', 'test', 'tape', 'fixture', 'serial', 'suite'],\n      },\n    ],\n    'no-redeclare': 'error',\n    'no-regex-spaces': 'error',\n    'no-return-assign': 'error',\n    'no-self-assign': 'error',\n    'no-sequences': ['error'],\n    'no-shadow': 'error',\n    'no-sparse-arrays': 'error',\n    'no-this-before-super': 'error',\n    'no-throw-literal': 'error',\n    'no-undef': 'error',\n    'no-unreachable': 'error',\n    'no-unsafe-finally': 'error',\n    'no-unsafe-negation': 'error',\n    'no-unused-labels': 'error',\n    'no-unused-vars': 'error',\n    'no-useless-concat': 'error',\n    'no-useless-escape': 'error',\n    'no-var': 'error',\n    'object-shorthand': ['error', 'always', {avoidQuotes: true}],\n    'one-var': ['error', 'never'],\n    'prefer-const': 'error',\n    'prefer-promise-reject-errors': 'error',\n    'prefer-rest-params': 'error',\n    'prefer-spread': 'error',\n    'prefer-template': 'error',\n    'prettier/prettier': 'error',\n    'require-yield': 'error',\n    'use-isnan': 'error',\n    'valid-typeof': 'error',\n    camelcase: ['error', {properties: 'always'}],\n    eqeqeq: ['error', 'smart'],\n  },\n  settings: {\n    'import/resolver': {\n      node: {\n        extensions: ['.js', '.ts'],\n      },\n    },\n  },\n}\n"
  },
  {
    "path": "lib/configs/typescript.js",
    "content": "export default {\n  extends: ['plugin:@typescript-eslint/recommended', 'prettier', 'plugin:escompat/typescript-2020'],\n  parser: '@typescript-eslint/parser',\n  plugins: ['@typescript-eslint', 'escompat', 'github'],\n  rules: {\n    camelcase: 'off',\n    'no-unused-vars': 'off',\n    'no-shadow': 'off',\n    'no-invalid-this': 'off',\n    '@typescript-eslint/no-invalid-this': ['error'],\n    '@typescript-eslint/no-shadow': ['error'],\n    '@typescript-eslint/interface-name-prefix': 'off',\n    '@typescript-eslint/array-type': ['error', {default: 'array-simple'}],\n    '@typescript-eslint/no-use-before-define': 'off',\n    '@typescript-eslint/explicit-member-accessibility': 'off',\n    '@typescript-eslint/explicit-function-return-type': 'off',\n    '@typescript-eslint/no-non-null-assertion': 'off',\n    '@typescript-eslint/no-unused-vars': 'error',\n    '@typescript-eslint/explicit-module-boundary-types': 'off',\n  },\n}\n"
  },
  {
    "path": "lib/formatters/stylish-fixes.js",
    "content": "import childProcess from 'node:child_process'\nimport fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport SourceCodeFixer from 'eslint/lib/linter/source-code-fixer.js'\nimport getRuleURI from 'eslint-rule-documentation'\n\nexport default function stylishFixes(results) {\n  let output = '\\n'\n  let errors = 0\n  let warnings = 0\n  const rootPath = process.cwd()\n\n  for (const result of results) {\n    const messages = result.messages\n\n    if (messages.length === 0) {\n      continue\n    }\n\n    errors += result.errorCount\n    warnings += result.warningCount\n\n    const relativePath = path.relative(rootPath, result.filePath)\n\n    output += `${relativePath}\\n`\n\n    for (const message of messages) {\n      output += `${message.line}:${message.column} ${message.ruleId || ''}`\n      if (message.ruleId) {\n        const ruleURI = getRuleURI(message.ruleId)\n        if (ruleURI.found) {\n          output += `  (${ruleURI.url})`\n        }\n      }\n      output += `\\n\\t${message.message}\\n`\n    }\n\n    if (messages.some(msg => msg.fix)) {\n      const fixResult = SourceCodeFixer.applyFixes(result.source, messages)\n      output += `\\n\\n$ eslint --fix ${relativePath}\\n`\n      output += diff(result.source, fixResult.output)\n    }\n\n    output += '\\n\\n'\n  }\n\n  const total = errors + warnings\n\n  if (total > 0) {\n    output += [\n      '\\u2716 ',\n      total,\n      pluralize(' problem', total),\n      ' (',\n      errors,\n      pluralize(' error', errors),\n      ', ',\n      warnings,\n      pluralize(' warning', warnings),\n      ')\\n',\n    ].join('')\n  }\n\n  return total > 0 ? output : ''\n}\n\nfunction pluralize(word, count) {\n  return count === 1 ? word : `${word}s`\n}\n\nfunction diff(a, b) {\n  const aPath = path.join(os.tmpdir(), 'a.js')\n  const bPath = path.join(os.tmpdir(), 'p.js')\n  fs.writeFileSync(aPath, a, {encoding: 'utf8'})\n  fs.writeFileSync(bPath, b, {encoding: 'utf8'})\n  const result = childProcess.spawnSync('diff', ['-U5', aPath, bPath], {encoding: 'utf8'})\n  return result.stdout.split('\\n').slice(2).join('\\n')\n}\n"
  },
  {
    "path": "lib/index.js",
    "content": "import github from './plugin.js'\nimport flatBrowserConfig from './configs/flat/browser.js'\nimport flatInternalConfig from './configs/flat/internal.js'\nimport flatRecommendedConfig from './configs/flat/recommended.js'\nimport flatTypescriptConfig from './configs/flat/typescript.js'\nimport flatReactConfig from './configs/flat/react.js'\nimport browserConfig from './configs/browser.js'\nimport internalConfig from './configs/internal.js'\nimport recommendedConfig from './configs/recommended.js'\nimport typescriptConfig from './configs/typescript.js'\nimport reactConfig from './configs/react.js'\n\nconst getFlatConfig = () => ({\n  browser: flatBrowserConfig,\n  internal: flatInternalConfig,\n  recommended: flatRecommendedConfig,\n  typescript: flatTypescriptConfig,\n  react: flatReactConfig,\n})\n\nexport default {\n  rules: github.rules,\n  configs: {\n    browser: browserConfig,\n    internal: internalConfig,\n    recommended: recommendedConfig,\n    typescript: typescriptConfig,\n    react: reactConfig,\n  },\n  getFlatConfigs: getFlatConfig,\n}\n"
  },
  {
    "path": "lib/plugin.js",
    "content": "import {packageJson} from './utils/commonjs-json-wrappers.cjs'\nimport a11yNoVisuallyHiddenInteractiveElement from './rules/a11y-no-visually-hidden-interactive-element.js'\nimport a11yNoGenericLinkText from './rules/a11y-no-generic-link-text.js'\nimport a11yNoTitleAttribute from './rules/a11y-no-title-attribute.js'\nimport a11yAriaLabelIsWellFormatted from './rules/a11y-aria-label-is-well-formatted.js'\nimport a11yRoleSupportsAriaProps from './rules/a11y-role-supports-aria-props.js'\nimport a11ySvgHasAccessibleName from './rules/a11y-svg-has-accessible-name.js'\nimport arrayForeach from './rules/array-foreach.js'\nimport asyncCurrenttarget from './rules/async-currenttarget.js'\nimport asyncPreventdefault from './rules/async-preventdefault.js'\nimport authenticityToken from './rules/authenticity-token.js'\nimport filenamesMatchRegex from './rules/filenames-match-regex.js'\nimport getAttribute from './rules/get-attribute.js'\nimport jsClassName from './rules/js-class-name.js'\nimport noBlur from './rules/no-blur.js'\nimport noDNone from './rules/no-d-none.js'\nimport noDataset from './rules/no-dataset.js'\nimport noImplicitBuggyGlobals from './rules/no-implicit-buggy-globals.js'\nimport noInnerHTML from './rules/no-inner-html.js'\nimport noInnerText from './rules/no-innerText.js'\nimport noDynamicScriptTag from './rules/no-dynamic-script-tag.js'\nimport noThen from './rules/no-then.js'\nimport noUselessPassive from './rules/no-useless-passive.js'\nimport preferObservers from './rules/prefer-observers.js'\nimport requirePassiveEvents from './rules/require-passive-events.js'\nimport unescapedHtmlLiteral from './rules/unescaped-html-literal.js'\n\nconst {name, version} = packageJson\n\nexport default {\n  meta: {name, version},\n  rules: {\n    'a11y-no-visually-hidden-interactive-element': a11yNoVisuallyHiddenInteractiveElement,\n    'a11y-no-generic-link-text': a11yNoGenericLinkText,\n    'a11y-no-title-attribute': a11yNoTitleAttribute,\n    'a11y-aria-label-is-well-formatted': a11yAriaLabelIsWellFormatted,\n    'a11y-role-supports-aria-props': a11yRoleSupportsAriaProps,\n    'a11y-svg-has-accessible-name': a11ySvgHasAccessibleName,\n    'array-foreach': arrayForeach,\n    'async-currenttarget': asyncCurrenttarget,\n    'async-preventdefault': asyncPreventdefault,\n    'authenticity-token': authenticityToken,\n    'filenames-match-regex': filenamesMatchRegex,\n    'get-attribute': getAttribute,\n    'js-class-name': jsClassName,\n    'no-blur': noBlur,\n    'no-d-none': noDNone,\n    'no-dataset': noDataset,\n    'no-implicit-buggy-globals': noImplicitBuggyGlobals,\n    'no-inner-html': noInnerHTML,\n    'no-innerText': noInnerText,\n    'no-dynamic-script-tag': noDynamicScriptTag,\n    'no-then': noThen,\n    'no-useless-passive': noUselessPassive,\n    'prefer-observers': preferObservers,\n    'require-passive-events': requirePassiveEvents,\n    'unescaped-html-literal': unescapedHtmlLiteral,\n  },\n}\n"
  },
  {
    "path": "lib/rules/a11y-aria-label-is-well-formatted.js",
    "content": "import jsxAstUtils from 'jsx-ast-utils'\nimport url from '../url.js'\n\nconst {getProp} = jsxAstUtils\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'enforce [aria-label] text to be formatted as you would visual text.',\n      url: url(import.meta.url),\n      recommended: false,\n    },\n    schema: [],\n    messages: {\n      formatting: '[aria-label] text should be formatted the same as you would visual text. Use sentence case.',\n    },\n  },\n\n  create(context) {\n    return {\n      JSXOpeningElement: node => {\n        const prop = getProp(node.attributes, 'aria-label')\n        if (!prop) return\n\n        const propValue = prop.value\n        if (propValue.type !== 'Literal') return\n\n        const ariaLabel = propValue.value\n        if (ariaLabel.match(/^[a-z]+.*$/)) {\n          context.report({\n            node,\n            messageId: 'formatting',\n          })\n        }\n      },\n    }\n  },\n}\n"
  },
  {
    "path": "lib/rules/a11y-no-generic-link-text.js",
    "content": "import jsxAstUtils from 'jsx-ast-utils'\nimport {getElementType} from '../utils/get-element-type.js'\nimport url from '../url.js'\n\nconst {getProp, getPropValue} = jsxAstUtils\nconst bannedLinkText = ['read more', 'here', 'click here', 'learn more', 'more']\n\n/* Downcase and strip extra whitespaces and punctuation */\nconst stripAndDowncaseText = text => {\n  return text\n    .toLowerCase()\n    .replace(/[.,/#!$%^&*;:{}=\\-_`~()]/g, '')\n    .replace(/\\s{2,}/g, ' ')\n    .trim()\n}\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'disallow generic link text',\n      url: url(import.meta.url),\n      recommended: false,\n    },\n    deprecated: {\n      message: 'It was replaced by `jsx-a11y/anchor-ambiguous-text`.',\n      replacedBy: [\n        {\n          rule: {\n            name: 'jsx-a11y/anchor-ambiguous-text',\n          },\n        },\n      ],\n    },\n    replacedBy: ['jsx-a11y/anchor-ambiguous-text'],\n    schema: [],\n    messages: {\n      avoidGenericLinkText:\n        'Avoid setting generic link text like `Here`, `Click here`, `Read more`. Make sure that your link text is both descriptive and concise.',\n      ariaLabelDescriptive: 'When using ARIA to set a more descriptive text, it must fully contain the visible label.',\n    },\n  },\n\n  create(context) {\n    return {\n      JSXOpeningElement: node => {\n        const elementType = getElementType(context, node)\n\n        if (elementType !== 'a') return\n        if (getProp(node.attributes, 'aria-labelledby')) return\n\n        let cleanTextContent // text content we can reliably fetch\n\n        const parent = node.parent\n        let jsxTextNode\n        if (parent.children && parent.children.length > 0 && parent.children[0].type === 'JSXText') {\n          jsxTextNode = parent.children[0]\n          cleanTextContent = stripAndDowncaseText(parent.children[0].value)\n        }\n\n        const ariaLabel = getPropValue(getProp(node.attributes, 'aria-label'))\n        const cleanAriaLabelValue = ariaLabel && stripAndDowncaseText(ariaLabel)\n\n        if (ariaLabel) {\n          if (bannedLinkText.includes(cleanAriaLabelValue)) {\n            context.report({\n              node,\n              messageId: 'avoidGenericLinkText',\n            })\n          }\n          if (cleanTextContent && !cleanAriaLabelValue.includes(cleanTextContent)) {\n            context.report({\n              node,\n              messageId: 'ariaLabelDescriptive',\n            })\n          }\n        } else {\n          if (cleanTextContent) {\n            if (!bannedLinkText.includes(cleanTextContent)) return\n            context.report({\n              node: jsxTextNode,\n              messageId: 'avoidGenericLinkText',\n            })\n          }\n        }\n      },\n    }\n  },\n}\n"
  },
  {
    "path": "lib/rules/a11y-no-title-attribute.js",
    "content": "import jsxAstUtils from 'jsx-ast-utils'\nimport {getElementType} from '../utils/get-element-type.js'\nimport url from '../url.js'\n\nconst {getProp, getPropValue} = jsxAstUtils\nconst SEMANTIC_ELEMENTS = [\n  'a',\n  'button',\n  'summary',\n  'select',\n  'option',\n  'textarea',\n  'input',\n  'span',\n  'div',\n  'p',\n  'h1',\n  'h2',\n  'h3',\n  'h4',\n  'h5',\n  'h6',\n  'details',\n  'summary',\n  'dialog',\n  'tr',\n  'th',\n  'td',\n  'label',\n]\n\nconst ifSemanticElement = (context, node) => {\n  const elementType = getElementType(context, node.openingElement, true)\n\n  for (const semanticElement of SEMANTIC_ELEMENTS) {\n    if (elementType === semanticElement) {\n      return true\n    }\n  }\n  return false\n}\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'disallow using the title attribute',\n      url: url(import.meta.url),\n      recommended: false,\n    },\n    schema: [],\n    messages: {\n      titleAttribute: 'The title attribute is not accessible and should never be used unless for an `<iframe>`.',\n    },\n  },\n\n  create(context) {\n    return {\n      JSXElement: node => {\n        const elementType = getElementType(context, node.openingElement, true)\n        if (elementType !== `iframe` && ifSemanticElement(context, node)) {\n          const titleProp = getPropValue(getProp(node.openingElement.attributes, `title`))\n          if (titleProp) {\n            context.report({\n              node,\n              messageId: 'titleAttribute',\n            })\n          }\n        }\n      },\n    }\n  },\n}\n"
  },
  {
    "path": "lib/rules/a11y-no-visually-hidden-interactive-element.js",
    "content": "import jsxAstUtils from 'jsx-ast-utils'\nimport {getElementType} from '../utils/get-element-type.js'\nimport {generateObjSchema} from 'eslint-plugin-jsx-a11y/lib/util/schemas.js'\nimport url from '../url.js'\n\nconst {getProp, getLiteralPropValue} = jsxAstUtils\nconst defaultClassName = 'sr-only'\nconst defaultcomponentName = 'VisuallyHidden'\n\nconst schema = generateObjSchema({\n  className: {type: 'string'},\n  componentName: {type: 'string'},\n})\n\n/** Note: we are not including input elements at this time\n * because a visually hidden input field might cause a false positive.\n * (e.g. fileUpload https://github.com/primer/react/pull/3492)\n */\nconst INTERACTIVE_ELEMENTS = ['a', 'button', 'summary', 'select', 'option', 'textarea']\n\nconst checkIfInteractiveElement = (context, node) => {\n  const elementType = getElementType(context, node.openingElement)\n\n  for (const interactiveElement of INTERACTIVE_ELEMENTS) {\n    if (elementType === interactiveElement) {\n      return true\n    }\n  }\n  return false\n}\n\n// if the node is visually hidden recursively check if it has interactive children\nconst checkIfVisuallyHiddenAndInteractive = (context, options, node, isParentVisuallyHidden) => {\n  const {className, componentName} = options\n  if (node.type === 'JSXElement') {\n    const classes = getLiteralPropValue(getProp(node.openingElement.attributes, 'className'))\n    const isVisuallyHiddenElement = node.openingElement.name.name === componentName\n    let hasSROnlyClass = false\n    if (classes != null) {\n      hasSROnlyClass = classes.includes(className)\n    }\n    let isHidden = false\n    if (hasSROnlyClass || isVisuallyHiddenElement || !!isParentVisuallyHidden) {\n      if (checkIfInteractiveElement(context, node)) {\n        return true\n      }\n      isHidden = true\n    }\n    if (node.children && node.children.length > 0) {\n      return (\n        typeof node.children?.find(child =>\n          checkIfVisuallyHiddenAndInteractive(context, options, child, !!isParentVisuallyHidden || isHidden),\n        ) !== 'undefined'\n      )\n    }\n  }\n  return false\n}\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'enforce that interactive elements are not visually hidden',\n      url: url(import.meta.url),\n      recommended: false,\n    },\n    schema: [schema],\n    messages: {\n      avoid:\n        '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.',\n    },\n  },\n\n  create(context) {\n    const {options} = context\n    const config = options[0] || {}\n    const className = config.className || defaultClassName\n    const componentName = config.componentName || defaultcomponentName\n\n    return {\n      JSXElement: node => {\n        if (checkIfVisuallyHiddenAndInteractive(context, {className, componentName}, node, false)) {\n          context.report({\n            node,\n            messageId: 'avoid',\n          })\n        }\n      },\n    }\n  },\n}\n"
  },
  {
    "path": "lib/rules/a11y-role-supports-aria-props.js",
    "content": "// @ts-check\nimport {aria, roles} from 'aria-query'\nimport jsxAstUtils from 'jsx-ast-utils'\nimport {getRole} from '../utils/get-role.js'\nimport url from '../url.js'\n\nconst {getPropValue, propName} = jsxAstUtils\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description:\n        'enforce that elements with explicit or implicit roles defined contain only `aria-*` properties supported by that `role`.',\n      url: url(import.meta.url),\n      recommended: false,\n    },\n    schema: [],\n    messages: {\n      notSupported: 'The attribute {{attr}} is not supported by the role {{role}}.',\n    },\n  },\n\n  create(context) {\n    return {\n      JSXOpeningElement(node) {\n        // Get the element’s explicit or implicit role\n        const role = getRole(context, node)\n\n        // Return early if role could not be determined\n        if (!role) return\n\n        // Get allowed ARIA attributes:\n        // - From the role itself\n        let allowedProps = Object.keys(roles.get(role)?.props || {})\n        // - From parent roles\n        for (const parentRole of roles.get(role)?.superClass.flat() ?? []) {\n          allowedProps = allowedProps.concat(Object.keys(roles.get(parentRole)?.props || {}))\n        }\n        // Dedupe, for performance\n        allowedProps = Array.from(new Set(allowedProps))\n\n        // Get prohibited ARIA attributes:\n        // - From the role itself\n        let prohibitedProps = roles.get(role)?.prohibitedProps || []\n        // - From parent roles\n        for (const parentRole of roles.get(role)?.superClass.flat() ?? []) {\n          prohibitedProps = prohibitedProps.concat(roles.get(parentRole)?.prohibitedProps || [])\n        }\n        // - From comparing allowed vs all ARIA attributes\n        prohibitedProps = prohibitedProps.concat(aria.keys().filter(x => !allowedProps.includes(x)))\n        // Dedupe, for performance\n        prohibitedProps = Array.from(new Set(prohibitedProps))\n\n        for (const prop of node.attributes) {\n          // Return early if prohibited ARIA attribute is set to an ignorable value\n          if (getPropValue(prop) == null || prop.type === 'JSXSpreadAttribute') return\n\n          if (prohibitedProps?.includes(propName(prop))) {\n            context.report({\n              node,\n              messageId: 'notSupported',\n              data: {\n                attr: propName(prop),\n                role,\n              },\n            })\n          }\n        }\n      },\n    }\n  },\n}\n"
  },
  {
    "path": "lib/rules/a11y-svg-has-accessible-name.js",
    "content": "import jsxAstUtils from 'jsx-ast-utils'\nimport {getElementType} from '../utils/get-element-type.js'\nimport url from '../url.js'\n\nconst {hasProp} = jsxAstUtils\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'require SVGs to have an accessible name',\n      url: url(import.meta.url),\n      recommended: false,\n    },\n    schema: [],\n    messages: {\n      accessibleName:\n        '`<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\"`.',\n    },\n  },\n\n  create(context) {\n    return {\n      JSXOpeningElement: node => {\n        const elementType = getElementType(context, node)\n        if (elementType !== 'svg') return\n\n        // Check if there is a nested title element that is the first non-whitespace child of the `<svg>`\n        const childrenWithoutWhitespace = node.parent.children?.filter(({type, value}) =>\n          type === 'JSXText' ? value.trim() !== '' : type !== 'JSXText',\n        )\n\n        const hasNestedTitleAsFirstChild =\n          childrenWithoutWhitespace?.[0]?.type === 'JSXElement' &&\n          childrenWithoutWhitespace?.[0]?.openingElement?.name?.name === 'title'\n\n        // Check if `aria-label` or `aria-labelledby` is set\n        const hasAccessibleName = hasProp(node.attributes, 'aria-label') || hasProp(node.attributes, 'aria-labelledby')\n\n        // Check if SVG is decorative\n        const isDecorative =\n          hasProp(node.attributes, 'role', 'presentation') || hasProp(node.attributes, 'aria-hidden', 'true')\n\n        if (elementType === 'svg' && !hasAccessibleName && !isDecorative && !hasNestedTitleAsFirstChild) {\n          context.report({\n            node,\n            messageId: 'accessibleName',\n          })\n        }\n      },\n    }\n  },\n}\n"
  },
  {
    "path": "lib/rules/array-foreach.js",
    "content": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'suggestion',\n    docs: {\n      description: 'enforce `for..of` loops over `Array.forEach`',\n      url: url(import.meta.url),\n      recommended: true,\n    },\n    schema: [],\n    messages: {\n      preferForOf: 'Prefer for...of instead of Array.forEach',\n    },\n  },\n\n  create(context) {\n    return {\n      CallExpression(node) {\n        if (node.callee.property && node.callee.property.name === 'forEach') {\n          context.report({node, messageId: 'preferForOf'})\n        }\n      },\n    }\n  },\n}\n"
  },
  {
    "path": "lib/rules/async-currenttarget.js",
    "content": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'disallow `event.currentTarget` calls inside of async functions',\n      url: url(import.meta.url),\n      recommended: false,\n    },\n    schema: [],\n    messages: {\n      asyncCurrentTarget: 'event.currentTarget inside an async function is error prone',\n    },\n  },\n\n  create(context) {\n    const scopeDidWait = new WeakSet()\n    const sourceCode = context.sourceCode ?? context.getSourceCode()\n\n    return {\n      AwaitExpression(node) {\n        scopeDidWait.add(sourceCode.getScope ? sourceCode.getScope(node) : context.getScope())\n      },\n      MemberExpression(node) {\n        if (node.property && node.property.name === 'currentTarget') {\n          let scope = sourceCode.getScope ? sourceCode.getScope(node) : context.getScope()\n          while (scope) {\n            if (scopeDidWait.has(scope)) {\n              context.report({\n                node,\n                messageId: 'asyncCurrentTarget',\n              })\n              break\n            }\n            scope = scope.upper\n          }\n        }\n      },\n    }\n  },\n}\n"
  },
  {
    "path": "lib/rules/async-preventdefault.js",
    "content": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'disallow `event.preventDefault` calls inside of async functions',\n      url: url(import.meta.url),\n      recommended: false,\n    },\n    schema: [],\n    messages: {\n      eventPreventDefault: 'event.preventDefault() inside an async function is error prone',\n    },\n  },\n\n  create(context) {\n    const scopeDidWait = new WeakSet()\n    const sourceCode = context.sourceCode ?? context.getSourceCode()\n\n    return {\n      AwaitExpression(node) {\n        scopeDidWait.add(sourceCode.getScope ? sourceCode.getScope(node) : context.getScope())\n      },\n      CallExpression(node) {\n        if (node.callee.property && node.callee.property.name === 'preventDefault') {\n          let scope = sourceCode.getScope ? sourceCode.getScope(node) : context.getScope()\n          while (scope) {\n            if (scopeDidWait.has(scope)) {\n              context.report({\n                node,\n                messageId: 'eventPreventDefault',\n              })\n              break\n            }\n            scope = scope.upper\n          }\n        }\n      },\n    }\n  },\n}\n"
  },
  {
    "path": "lib/rules/authenticity-token.js",
    "content": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'disallow usage of CSRF tokens in JavaScript',\n      url: url(import.meta.url),\n      recommended: false,\n    },\n    schema: [],\n    messages: {\n      authenticityTokenUsage:\n        'Form CSRF tokens (authenticity tokens) should not be created in JavaScript and their values should not be used directly for XHR requests.',\n    },\n  },\n\n  create(context) {\n    function checkAuthenticityTokenUsage(node, str) {\n      if (str.includes('authenticity_token')) {\n        context.report({\n          node,\n          messageId: 'authenticityTokenUsage',\n        })\n      }\n    }\n\n    return {\n      Literal(node) {\n        if (typeof node.value === 'string') {\n          checkAuthenticityTokenUsage(node, node.value)\n        }\n      },\n    }\n  },\n}\n"
  },
  {
    "path": "lib/rules/filenames-match-regex.js",
    "content": "// This is adapted from https://github.com/selaux/eslint-plugin-filenames since it's no longer actively maintained\n// and needed a fix for eslint v9\nimport path from 'node:path'\nimport parseFilename from '../utils/parse-filename.js'\nimport getExportedName from '../utils/get-exported-name.js'\nimport isIgnoredFilename from '../utils/is-ignored-filename.js'\nimport url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'require filenames to match a regex naming convention',\n      url: url(import.meta.url),\n      recommended: true,\n    },\n    schema: {\n      type: 'array',\n      minItems: 0,\n      maxItems: 1,\n      items: [\n        {\n          type: 'string',\n        },\n      ],\n    },\n    messages: {\n      regex: \"Filename '{{name}}' does not match the regex naming convention.\",\n    },\n  },\n\n  create(context) {\n    // GitHub's default is kebab case or one hump camel case\n    const defaultRegexp = /^[a-z0-9-]+(.[a-z0-9-]+)?$/\n    const conventionRegexp = context.options[0] ? new RegExp(context.options[0]) : defaultRegexp\n    const ignoreExporting = context.options[1] ? context.options[1] : false\n\n    return {\n      Program(node) {\n        const filename = context.filename ?? context.getFilename()\n        const absoluteFilename = path.resolve(filename)\n        const parsed = parseFilename(absoluteFilename)\n        const shouldIgnore = isIgnoredFilename(filename)\n        const isExporting = Boolean(getExportedName(node))\n        const matchesRegex = conventionRegexp.test(parsed.name)\n\n        if (shouldIgnore) return\n        if (ignoreExporting && isExporting) return\n        if (!matchesRegex) {\n          context.report({\n            node,\n            messageId: 'regex',\n            data: {\n              name: parsed.base,\n            },\n          })\n        }\n      },\n    }\n  },\n}\n"
  },
  {
    "path": "lib/rules/get-attribute.js",
    "content": "import {svgElementAttributes} from '../utils/commonjs-json-wrappers.cjs'\nimport url from '../url.js'\n\nconst attributeCalls = /^(get|has|set|remove)Attribute$/\nconst validAttributeName = /^[a-z][a-z0-9-]*$/\n\n// these are common SVG attributes that *must* have the correct case to work\nconst camelCaseAttributes = Object.values(svgElementAttributes)\n  .reduce((all, elementAttrs) => all.concat(elementAttrs), [])\n  .filter(name => !validAttributeName.test(name))\n\nconst validSVGAttributeSet = new Set(camelCaseAttributes)\n\n// lowercase variants of camelCase SVG attributes are probably an error\nconst invalidSVGAttributeSet = new Set(camelCaseAttributes.map(name => name.toLowerCase()))\n\nfunction isValidAttribute(name) {\n  return validSVGAttributeSet.has(name) || (validAttributeName.test(name) && !invalidSVGAttributeSet.has(name))\n}\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'disallow wrong usage of attribute names',\n      url: url(import.meta.url),\n      recommended: false,\n    },\n    fixable: 'code',\n    schema: [],\n    messages: {\n      invalidAttribute: 'Attributes should be lowercase and hyphen separated, or part of the SVG whitelist.',\n    },\n  },\n  create(context) {\n    return {\n      CallExpression(node) {\n        if (!node.callee.property) return\n\n        const calleeName = node.callee.property.name\n        if (!attributeCalls.test(calleeName)) return\n\n        const attributeNameNode = node.arguments[0]\n        if (!attributeNameNode) return\n\n        if (!isValidAttribute(attributeNameNode.value)) {\n          context.report({\n            node: attributeNameNode,\n            messageId: 'invalidAttribute',\n            fix(fixer) {\n              return fixer.replaceText(attributeNameNode, `'${attributeNameNode.value.toLowerCase()}'`)\n            },\n          })\n        }\n      },\n    }\n  },\n}\n"
  },
  {
    "path": "lib/rules/js-class-name.js",
    "content": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'suggestion',\n    docs: {\n      description: 'enforce a naming convention for js- prefixed classes',\n      url: url(import.meta.url),\n      recommended: false,\n    },\n    schema: [],\n    messages: {\n      jsClassName: 'js- class names should be lowercase and only contain dashes.',\n      jsClassNameStatic: 'js- class names should be statically defined.',\n    },\n  },\n\n  create(context) {\n    const allJsClassNameRegexp = /\\bjs-[_a-zA-Z0-9-]*/g\n    const validJsClassNameRegexp = /^js(-[a-z0-9]+)+$/g\n    const endWithJsClassNameRegexp = /\\bjs-[_a-zA-Z0-9-]*$/g\n\n    function checkStringFormat(node, str) {\n      const matches = str.match(allJsClassNameRegexp) || []\n      for (const match of matches) {\n        if (!match.match(validJsClassNameRegexp)) {\n          context.report({node, messageId: 'jsClassName'})\n        }\n      }\n    }\n\n    function checkStringEndsWithJSClassName(node, str) {\n      if (str.match(endWithJsClassNameRegexp)) {\n        context.report({node, messageId: 'jsClassNameStatic'})\n      }\n    }\n\n    return {\n      Literal(node) {\n        if (typeof node.value === 'string') {\n          checkStringFormat(node, node.value)\n\n          if (\n            node.parent &&\n            node.parent.type === 'BinaryExpression' &&\n            node.parent.operator === '+' &&\n            node.parent.left.value\n          ) {\n            checkStringEndsWithJSClassName(node.parent.left, node.parent.left.value)\n          }\n        }\n      },\n      TemplateLiteral(node) {\n        for (const quasi of node.quasis) {\n          checkStringFormat(quasi, quasi.value.raw)\n\n          if (quasi.tail === false) {\n            checkStringEndsWithJSClassName(quasi, quasi.value.raw)\n          }\n        }\n      },\n    }\n  },\n}\n"
  },
  {
    "path": "lib/rules/no-blur.js",
    "content": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'disallow usage of `Element.prototype.blur()`',\n      url: url(import.meta.url),\n      recommended: false,\n    },\n    schema: [],\n    messages: {\n      noBlur: 'Do not use element.blur(), instead restore the focus of a previous element.',\n    },\n  },\n  create(context) {\n    return {\n      CallExpression(node) {\n        if (node.callee.property && node.callee.property.name === 'blur') {\n          context.report({node, messageId: 'noBlur'})\n        }\n      },\n    }\n  },\n}\n"
  },
  {
    "path": "lib/rules/no-d-none.js",
    "content": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'disallow usage the `d-none` CSS class',\n      url: url(import.meta.url),\n      recommended: false,\n    },\n    schema: [],\n    messages: {\n      preferHidden: 'Prefer hidden property to d-none class',\n    },\n  },\n  create(context) {\n    return {\n      CallExpression(node) {\n        if (\n          node.callee.type === 'MemberExpression' &&\n          node.callee.object.property &&\n          node.callee.object.property.name === 'classList'\n        ) {\n          const invalidArgument = node.arguments.some(arg => {\n            return arg.type === 'Literal' && arg.value === 'd-none'\n          })\n          if (invalidArgument) {\n            context.report({\n              node,\n              messageId: 'preferHidden',\n            })\n          }\n        }\n      },\n    }\n  },\n}\n"
  },
  {
    "path": "lib/rules/no-dataset.js",
    "content": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'enforce usage of `Element.prototype.getAttribute` instead of `Element.prototype.datalist`',\n      url: url(import.meta.url),\n      recommended: false,\n    },\n    schema: [],\n    messages: {\n      useGetAttribute: \"Use getAttribute('data-your-attribute') instead of dataset\",\n    },\n  },\n\n  create(context) {\n    return {\n      MemberExpression(node) {\n        if (node.property && node.property.name === 'dataset') {\n          context.report({node, messageId: 'useGetAttribute'})\n        }\n      },\n    }\n  },\n}\n"
  },
  {
    "path": "lib/rules/no-dynamic-script-tag.js",
    "content": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'suggestion',\n    docs: {\n      description: 'disallow creating dynamic script tags',\n      url: url(import.meta.url),\n      recommended: true,\n    },\n    schema: [],\n    messages: {\n      noDynamicScriptTag: \"Don't create dynamic script tags, add them in the server template instead.\",\n    },\n  },\n\n  create(context) {\n    return {\n      'CallExpression[callee.property.name=\"createElement\"][arguments.length > 0]': function (node) {\n        if (node.arguments[0].value !== 'script') return\n\n        context.report({\n          node: node.arguments[0],\n          messageId: 'noDynamicScriptTag',\n        })\n      },\n      'AssignmentExpression[left.property.name=\"type\"][right.value=\"text/javascript\"]': function (node) {\n        context.report({\n          node: node.right,\n          messageId: 'noDynamicScriptTag',\n        })\n      },\n    }\n  },\n}\n"
  },
  {
    "path": "lib/rules/no-implicit-buggy-globals.js",
    "content": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'disallow implicit global variables',\n      url: url(import.meta.url),\n      recommended: true,\n    },\n    schema: [],\n    messages: {\n      implicitGlobalVariable: 'Implicit global variable, assign as global property instead.',\n    },\n  },\n\n  create(context) {\n    const sourceCode = context.sourceCode ?? context.getSourceCode()\n    return {\n      Program(node) {\n        const scope = sourceCode.getScope(node) ? sourceCode.getScope(node) : context.getScope()\n\n        for (const variable of scope.variables) {\n          if (variable.writeable) {\n            return\n          }\n\n          for (const def of variable.defs) {\n            if (\n              def.type === 'FunctionName' ||\n              def.type === 'ClassName' ||\n              (def.type === 'Variable' && def.parent.kind === 'const') ||\n              (def.type === 'Variable' && def.parent.kind === 'let')\n            ) {\n              context.report({\n                node: def.node,\n                messageId: 'implicitGlobalVariable',\n              })\n            }\n          }\n        }\n      },\n    }\n  },\n}\n"
  },
  {
    "path": "lib/rules/no-inner-html.js",
    "content": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'disallow `Element.prototype.innerHTML` in favor of `Element.prototype.textContent`',\n      url: url(import.meta.url),\n      recommended: false,\n    },\n    schema: [],\n    messages: {\n      noInnerHTML: 'Using innerHTML poses a potential security risk and should not be used. Prefer using textContent.',\n    },\n  },\n\n  create(context) {\n    return {\n      'MemberExpression[property.name=innerHTML]': function (node) {\n        context.report({\n          node: node.property,\n          messageId: 'noInnerHTML',\n        })\n      },\n    }\n  },\n}\n"
  },
  {
    "path": "lib/rules/no-innerText.js",
    "content": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'disallow `Element.prototype.innerText` in favor of `Element.prototype.textContent`',\n      url: url(import.meta.url),\n      recommended: false,\n    },\n    fixable: 'code',\n    schema: [],\n    messages: {\n      preferTextContent: 'Prefer textContent to innerText',\n    },\n  },\n\n  create(context) {\n    return {\n      MemberExpression(node) {\n        // If the member expression is part of a call expression like `.innerText()` then it is not the same\n        // as the `Element.innerText` property, and should not trigger a warning\n        if (node.parent.type === 'CallExpression') return\n\n        if (node.property && node.property.name === 'innerText') {\n          context.report({\n            meta: {\n              fixable: 'code',\n            },\n            node: node.property,\n            messageId: 'preferTextContent',\n            fix(fixer) {\n              return fixer.replaceText(node.property, 'textContent')\n            },\n          })\n        }\n      },\n    }\n  },\n}\n"
  },
  {
    "path": "lib/rules/no-then.js",
    "content": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'suggestion',\n    docs: {\n      description: 'enforce using `async/await` syntax over Promises',\n      url: url(import.meta.url),\n      recommended: true,\n    },\n    schema: [],\n    messages: {\n      preferAsyncAwait: 'Prefer async/await to Promise.{{method}}()',\n    },\n  },\n\n  create(context) {\n    return {\n      MemberExpression(node) {\n        if (node.property && node.property.name === 'then') {\n          context.report({\n            node: node.property,\n            messageId: 'preferAsyncAwait',\n            data: {method: 'then'},\n          })\n        } else if (node.property && node.property.name === 'catch') {\n          context.report({\n            node: node.property,\n            messageId: 'preferAsyncAwait',\n            data: {method: 'catch'},\n          })\n        }\n      },\n    }\n  },\n}\n"
  },
  {
    "path": "lib/rules/no-useless-passive.js",
    "content": "import url from '../url.js'\n\nconst passiveEventListenerNames = new Set([\n  'touchstart',\n  'touchmove',\n  'touchenter',\n  'touchend',\n  'touchleave',\n  'wheel',\n  'mousewheel',\n])\n\nconst propIsPassiveTrue = prop => prop.key && prop.key.name === 'passive' && prop.value && prop.value.value === true\n\nexport default {\n  meta: {\n    type: 'suggestion',\n    docs: {\n      description: 'disallow marking a event handler as passive when it has no effect',\n      url: url(import.meta.url),\n      recommended: false,\n    },\n    fixable: 'code',\n    schema: [],\n    messages: {\n      passive: '\"{{name}}\" event listener is not cancellable and so `passive: true` does nothing.',\n    },\n  },\n\n  create(context) {\n    return {\n      ['CallExpression[callee.property.name=\"addEventListener\"]']: function (node) {\n        const [name, listener, options] = node.arguments\n        if (name.type !== 'Literal') return\n        if (passiveEventListenerNames.has(name.value)) return\n        if (options && options.type === 'ObjectExpression') {\n          const i = options.properties.findIndex(propIsPassiveTrue)\n          if (i === -1) return\n          const passiveProp = options.properties[i]\n          const l = options.properties.length\n          const source = context.sourceCode ?? context.getSourceCode()\n          context.report({\n            node: passiveProp,\n            messageId: 'passive',\n            data: {name: name.value},\n            fix(fixer) {\n              const removals = []\n              if (l === 1) {\n                removals.push(options)\n                removals.push(...source.getTokensBetween(listener, options))\n              } else {\n                removals.push(passiveProp)\n                if (i > 0) {\n                  removals.push(...source.getTokensBetween(options.properties[i - 1], passiveProp))\n                } else {\n                  removals.push(...source.getTokensBetween(passiveProp, options.properties[i + 1]))\n                }\n              }\n              return removals.map(t => fixer.remove(t))\n            },\n          })\n        }\n      },\n    }\n  },\n}\n"
  },
  {
    "path": "lib/rules/prefer-observers.js",
    "content": "import url from '../url.js'\n\nconst observerMap = {\n  scroll: 'IntersectionObserver',\n  resize: 'ResizeObserver',\n}\nexport default {\n  meta: {\n    type: 'suggestion',\n    docs: {\n      description: 'disallow poorly performing event listeners',\n      url: url(import.meta.url),\n      recommended: false,\n    },\n    schema: [],\n    messages: {\n      avoid: 'Avoid using \"{{name}}\" event listener. Consider using {{observer}} instead',\n    },\n  },\n\n  create(context) {\n    return {\n      ['CallExpression[callee.property.name=\"addEventListener\"]']: function (node) {\n        const [name] = node.arguments\n        if (name.type !== 'Literal') return\n        if (!(name.value in observerMap)) return\n        context.report({\n          node,\n          messageId: 'avoid',\n          data: {name: name.value, observer: observerMap[name.value]},\n        })\n      },\n    }\n  },\n}\n"
  },
  {
    "path": "lib/rules/require-passive-events.js",
    "content": "import url from '../url.js'\n\nconst passiveEventListenerNames = new Set([\n  'touchstart',\n  'touchmove',\n  'touchenter',\n  'touchend',\n  'touchleave',\n  'wheel',\n  'mousewheel',\n])\n\nconst propIsPassiveTrue = prop => prop.key && prop.key.name === 'passive' && prop.value && prop.value.value === true\n\nexport default {\n  meta: {\n    type: 'suggestion',\n    docs: {\n      description: 'enforce marking high frequency event handlers as passive',\n      url: url(import.meta.url),\n      recommended: false,\n    },\n    schema: [],\n    messages: {\n      passive: 'High Frequency Events like \"{{name}}\" should be `passive: true`',\n    },\n  },\n\n  create(context) {\n    return {\n      ['CallExpression[callee.property.name=\"addEventListener\"]']: function (node) {\n        const [name, listener, options] = node.arguments\n        if (!listener) return\n        if (name.type !== 'Literal') return\n        if (!passiveEventListenerNames.has(name.value)) return\n        if (options && options.type === 'ObjectExpression' && options.properties.some(propIsPassiveTrue)) return\n        context.report({node, messageId: 'passive', data: {name: name.value}})\n      },\n    }\n  },\n}\n"
  },
  {
    "path": "lib/rules/unescaped-html-literal.js",
    "content": "import url from '../url.js'\n\nexport default {\n  meta: {\n    type: 'problem',\n    docs: {\n      description: 'disallow unescaped HTML literals',\n      url: url(import.meta.url),\n      recommended: false,\n    },\n    schema: [],\n    messages: {\n      unescapedHtmlLiteral: 'Unescaped HTML literal. Use html`` tag template literal for secure escaping.',\n    },\n  },\n\n  create(context) {\n    const htmlOpenTag = /^\\s*<[a-zA-Z]/\n\n    return {\n      Literal(node) {\n        if (!htmlOpenTag.test(node.value)) return\n\n        context.report({\n          node,\n          messageId: 'unescapedHtmlLiteral',\n        })\n      },\n      TemplateLiteral(node) {\n        if (!htmlOpenTag.test(node.quasis[0].value.raw)) return\n\n        if (!node.parent.tag || node.parent.tag.name !== 'html') {\n          context.report({\n            node,\n            messageId: 'unescapedHtmlLiteral',\n          })\n        }\n      },\n    }\n  },\n}\n"
  },
  {
    "path": "lib/url.js",
    "content": "import {packageJson} from './utils/commonjs-json-wrappers.cjs'\nimport path from 'node:path'\nimport {fileURLToPath} from 'node:url'\n\nconst {homepage, version} = packageJson\n\nconst getUrl = id => {\n  const url = new URL(homepage)\n  const rule = path.basename(fileURLToPath(id), '.js')\n  url.hash = ''\n  url.pathname += `/blob/v${version}/docs/rules/${rule}.md`\n  return url.toString()\n}\n\nexport default getUrl\n"
  },
  {
    "path": "lib/utils/commonjs-json-wrappers.cjs",
    "content": "const svgElementAttributes = require('svg-element-attributes')\nconst packageJson = require('../../package.json')\n\nmodule.exports.svgElementAttributes = svgElementAttributes\nmodule.exports.packageJson = packageJson\n"
  },
  {
    "path": "lib/utils/get-element-type.js",
    "content": "import jsxAstUtils from 'jsx-ast-utils'\n\nconst {elementType, getProp, getLiteralPropValue} = jsxAstUtils\n\n/*\nAllows custom component to be mapped to an element type.\nWhen a default is set, all instances of the component will be mapped to the default.\nIf a prop determines the type, it can be specified with `props`.\n\nFor now, we only support the mapping of one prop type to an element type, rather than combinations of props.\n*/\nexport function getElementType(context, node, lazyElementCheck = false) {\n  const {settings} = context\n\n  if (lazyElementCheck) {\n    return elementType(node)\n  }\n\n  // check if the node contains a polymorphic prop\n  const polymorphicPropName = settings?.github?.polymorphicPropName ?? 'as'\n\n  const prop = getProp(node.attributes, polymorphicPropName)\n  const literalPropValue = getLiteralPropValue(getProp(node.attributes, polymorphicPropName))\n  let checkConditionalMap = true\n\n  // If the prop is not a literal and we cannot determine it, don't fall back to the conditional map value, if it exists\n  if (prop && !literalPropValue) {\n    checkConditionalMap = false\n  }\n  const rawElement = getLiteralPropValue(getProp(node.attributes, polymorphicPropName)) ?? elementType(node)\n\n  // if a component configuration does not exists, return the raw element\n  if (!settings?.github?.components?.[rawElement]) return rawElement\n\n  // check if the default component is also defined in the configuration\n  return checkConditionalMap ? settings.github.components[rawElement] : rawElement\n}\n"
  },
  {
    "path": "lib/utils/get-exported-name.js",
    "content": "function getNodeName(node, options) {\n  const op = options || []\n\n  if (node.type === 'Identifier') {\n    return node.name\n  }\n\n  if (node.id && node.id.type === 'Identifier') {\n    return node.id.name\n  }\n\n  if (op[2] && node.type === 'CallExpression' && node.callee.type === 'Identifier') {\n    return node.callee.name\n  }\n}\n\nexport default function getExportedName(programNode, options) {\n  for (let i = 0; i < programNode.body.length; i += 1) {\n    const node = programNode.body[i]\n\n    if (node.type === 'ExportDefaultDeclaration') {\n      return getNodeName(node.declaration, options)\n    }\n\n    if (\n      node.type === 'ExpressionStatement' &&\n      node.expression.type === 'AssignmentExpression' &&\n      node.expression.left.type === 'MemberExpression' &&\n      node.expression.left.object.type === 'Identifier' &&\n      node.expression.left.object.name === 'module' &&\n      node.expression.left.property.type === 'Identifier' &&\n      node.expression.left.property.name === 'exports'\n    ) {\n      return getNodeName(node.expression.right, options)\n    }\n  }\n}\n"
  },
  {
    "path": "lib/utils/get-role.js",
    "content": "import jsxAstUtils from 'jsx-ast-utils'\nimport {elementRoles} from 'aria-query'\nimport {getElementType} from './get-element-type.js'\nimport ObjectMap from './object-map.js'\n\nconst {getProp, getLiteralPropValue} = jsxAstUtils\nconst elementRolesMap = cleanElementRolesMap()\n\n/*\n  Returns an element roles map which uses `aria-query`'s elementRoles as the foundation.\n  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.\n  In a few scenarios, we stray from the roles returned by `aria-query` and hard code the mapping.\n*/\nfunction cleanElementRolesMap() {\n  const rolesMap = new ObjectMap()\n\n  for (const [key, value] of elementRoles.entries()) {\n    // - Remove empty `attributes` key\n    if (!key.attributes || key.attributes?.length === 0) {\n      delete key.attributes\n    }\n    rolesMap.set(key, value)\n  }\n  // Remove insufficiently-disambiguated `menuitem` entry\n  rolesMap.delete({name: 'menuitem'})\n  // Disambiguate `menuitem` and `menu` roles by `type`\n  rolesMap.set({name: 'menuitem', attributes: [{name: 'type', value: 'command'}]}, ['menuitem'])\n  rolesMap.set({name: 'menuitem', attributes: [{name: 'type', value: 'radio'}]}, ['menuitemradio'])\n  rolesMap.set({name: 'menuitem', attributes: [{name: 'type', value: 'toolbar'}]}, ['toolbar'])\n  rolesMap.set({name: 'menu', attributes: [{name: 'type', value: 'toolbar'}]}, ['toolbar'])\n\n  /* 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.\n     However, we benefit more from assuming the role, than assuming it's generic or undefined so we opt to hard code the mapping */\n  rolesMap.set({name: 'aside'}, ['complementary']) // `aside` still maps to `complementary` in https://www.w3.org/TR/html-aria/#docconformance.\n  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.\n\n  return rolesMap\n}\n\n/*\n  Determine role of an element, based on its name and attributes.\n  We construct a key and look up the element's role in `elementRolesMap`.\n  If there is no match, we return undefined.\n*/\nexport function getRole(context, node) {\n  // Early return if role is explicitly set\n  const explicitRole = getLiteralPropValue(getProp(node.attributes, 'role'))\n  if (explicitRole) {\n    return explicitRole\n  } else if (getProp(node.attributes, 'role')) {\n    // If role is set to anything other than a literal prop\n    return undefined\n  }\n\n  // Assemble a key for looking-up the element’s role in the `elementRolesMap`\n  // - Get the element’s name\n  const key = {name: getElementType(context, node)}\n\n  for (const prop of [\n    'aria-label',\n    'aria-labelledby',\n    'alt',\n    'type',\n    'size',\n    'role',\n    'href',\n    'multiple',\n    'scope',\n    'name',\n  ]) {\n    if ((prop === 'aria-labelledby' || prop === 'aria-label') && !['section', 'form'].includes(key.name)) continue\n    if (prop === 'name' && key.name !== 'form') continue\n    if (prop === 'href' && key.name !== 'a' && key.name !== 'area') continue\n    if (prop === 'alt' && key.name !== 'img') continue\n\n    const propOnNode = getProp(node.attributes, prop)\n\n    if (!('attributes' in key)) {\n      key.attributes = []\n    }\n    // Disambiguate \"undefined\" props\n    if (propOnNode === undefined && prop === 'alt' && key.name === 'img') {\n      key.attributes.push({name: prop, constraints: ['undefined']})\n      continue\n    }\n\n    const value = getLiteralPropValue(propOnNode)\n    if (propOnNode) {\n      if (\n        prop === 'href' ||\n        prop === 'aria-labelledby' ||\n        prop === 'aria-label' ||\n        prop === 'name' ||\n        (prop === 'alt' && value !== '')\n      ) {\n        key.attributes.push({name: prop, constraints: ['set']})\n      } else if (value || (value === '' && prop === 'alt')) {\n        key.attributes.push({name: prop, value})\n      }\n    }\n  }\n\n  // - Remove empty `attributes` key\n  if (!key.attributes || key.attributes?.length === 0) {\n    delete key.attributes\n  }\n\n  // Get the element’s implicit role\n  return elementRolesMap.get(key)?.[0]\n}\n"
  },
  {
    "path": "lib/utils/is-ignored-filename.js",
    "content": "const ignoredFilenames = ['<text>', '<input>']\n\nexport default function isIgnoredFilename(filename) {\n  return ignoredFilenames.indexOf(filename) !== -1\n}\n"
  },
  {
    "path": "lib/utils/object-map.js",
    "content": "// @ts-check\nimport {isDeepStrictEqual} from 'node:util'\n\n/**\n * 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.\n */\nexport default class ObjectMap extends Map {\n  #data\n\n  constructor(iterable = []) {\n    super()\n    this.#data = iterable\n  }\n\n  clear() {\n    this.#data = []\n  }\n\n  delete(key) {\n    if (!this.has(key)) {\n      return false\n    }\n    this.#data = this.#data.filter(([existingKey]) => !isDeepStrictEqual(existingKey, key))\n    return true\n  }\n\n  entries() {\n    return this.#data[Symbol.iterator]()\n  }\n\n  forEach(cb) {\n    for (const [key, value] of this.#data) {\n      cb(value, key, this.#data)\n    }\n  }\n\n  get(key) {\n    return this.#data.find(([existingKey]) => isDeepStrictEqual(existingKey, key))?.[1]\n  }\n\n  has(key) {\n    return this.#data.findIndex(([existingKey]) => isDeepStrictEqual(existingKey, key)) !== -1\n  }\n\n  keys() {\n    return this.#data.map(([key]) => key)[Symbol.iterator]()\n  }\n\n  set(key, value) {\n    this.delete(key)\n    this.#data.push([key, value])\n    return this\n  }\n\n  values() {\n    return this.#data.map(([, value]) => value)[Symbol.iterator]()\n  }\n}\n"
  },
  {
    "path": "lib/utils/parse-filename.js",
    "content": "import path from 'node:path'\n\nexport default function parseFilename(filename) {\n  const ext = path.extname(filename)\n\n  return {\n    dir: path.dirname(filename),\n    base: path.basename(filename),\n    ext,\n    name: path.basename(filename, ext),\n  }\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"eslint-plugin-github\",\n  \"version\": \"0.0.0-dev\",\n  \"type\": \"module\",\n  \"description\": \"An opinionated collection of ESLint shared configs and rules used by GitHub.\",\n  \"main\": \"lib/index.js\",\n  \"entries\": [\n    \"lib/formatters/stylish-fixes.js\"\n  ],\n  \"bin\": {\n    \"eslint-ignore-errors\": \"bin/eslint-ignore-errors.js\"\n  },\n  \"scripts\": {\n    \"eslint-check\": \"eslint-config-prettier .eslintrc.js\",\n    \"lint\": \"npm-run-all \\\"lint:*\\\"\",\n    \"lint:eslint-docs\": \"npm run update:eslint-docs -- --check\",\n    \"lint:js\": \"eslint .\",\n    \"pretest\": \"mkdir -p node_modules/ && ln -fs $(pwd) node_modules/\",\n    \"test\": \"npm run eslint-check && npm run lint && mocha tests/**/*.js tests/**/*.mjs\",\n    \"update:eslint-docs\": \"eslint-doc-generator\",\n    \"test-examples:legacy\": \"cd test-examples/legacy && npm install && npm run lint\",\n    \"test-examples:flat\": \"cd test-examples/flat && npm install && npm run lint\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/github/eslint-plugin-github.git\"\n  },\n  \"author\": \"GitHub, Inc.\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/github/eslint-plugin-github/issues\"\n  },\n  \"homepage\": \"https://github.com/github/eslint-plugin-github#readme\",\n  \"dependencies\": {\n    \"@eslint/compat\": \"^2.0.0\",\n    \"@eslint/eslintrc\": \"^3.1.0\",\n    \"@eslint/js\": \"^9.14.0\",\n    \"@github/browserslist-config\": \"^1.0.0\",\n    \"@typescript-eslint/eslint-plugin\": \"^8.0.0\",\n    \"@typescript-eslint/parser\": \"^8.0.0\",\n    \"aria-query\": \"^5.3.0\",\n    \"eslint-config-prettier\": \">=8.0.0\",\n    \"eslint-plugin-escompat\": \"^3.11.3\",\n    \"eslint-plugin-eslint-comments\": \"^3.2.0\",\n    \"eslint-plugin-filenames\": \"^1.3.2\",\n    \"eslint-plugin-i18n-text\": \"^1.0.1\",\n    \"eslint-plugin-import\": \"^2.31.0\",\n    \"eslint-plugin-jsx-a11y\": \"^6.10.2\",\n    \"eslint-plugin-no-only-tests\": \"^3.0.0\",\n    \"eslint-plugin-prettier\": \"^5.2.1\",\n    \"eslint-rule-documentation\": \">=1.0.0\",\n    \"globals\": \"^16.0.0\",\n    \"jsx-ast-utils\": \"^3.3.2\",\n    \"prettier\": \"^3.0.0\",\n    \"svg-element-attributes\": \"^1.3.1\",\n    \"typescript\": \"^5.7.3\",\n    \"typescript-eslint\": \"^8.14.0\"\n  },\n  \"prettier\": \"@github/prettier-config\",\n  \"browserslist\": \"extends @github/browserslist-config\",\n  \"peerDependencies\": {\n    \"eslint\": \"^8 || ^9\"\n  },\n  \"files\": [\n    \"bin/*\",\n    \"lib/*\"\n  ],\n  \"devDependencies\": {\n    \"@github/prettier-config\": \"0.0.6\",\n    \"chai\": \"^6.0.1\",\n    \"eslint\": \"^9.14.0\",\n    \"eslint-doc-generator\": \"^2.2.2\",\n    \"eslint-plugin-eslint-plugin\": \"^7.0.0\",\n    \"mocha\": \"^11.0.1\",\n    \"npm-run-all\": \"^4.1.5\"\n  }\n}\n"
  },
  {
    "path": "test-examples/flat/eslint.config.mjs",
    "content": "import github from 'eslint-plugin-github'\n\nexport default [\n  github.getFlatConfigs().recommended,\n  github.getFlatConfigs().browser,\n  github.getFlatConfigs().react,\n  ...github.getFlatConfigs().typescript,\n  {\n    files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],\n    ignores: ['eslint.config.mjs'],\n    rules: {\n      'github/array-foreach': 'error',\n      'github/no-then': 'error',\n      'github/no-blur': 'error',\n      'github/async-preventdefault': 'error',\n      'github/async-currenttarget': 'error',\n      'github/no-useless-passive': 'error',\n      'github/filenames-match-regex': 'error',\n    },\n  },\n]\n"
  },
  {
    "path": "test-examples/flat/package.json",
    "content": "{\n  \"name\": \"flat\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"lint\": \"cross-env ESLINT_USE_FLAT_CONFIG=true eslint src --report-unused-disable-directives\"\n  },\n  \"devDependencies\": {\n    \"@eslint/js\": \"^9.5.0\",\n    \"@types/node\": \"^20.14.5\",\n    \"cross-env\": \"^7.0.3\",\n    \"eslint\": \"^9.14.0\",\n    \"eslint-plugin-github\": \"file:../..\",\n    \"typescript\": \"^5.6.3\",\n    \"typescript-eslint\": \"^8.12.2\"\n  }\n}\n"
  },
  {
    "path": "test-examples/flat/src/forEachTest.js",
    "content": "const testing = [\n  [{name: 'github'}, ['test1']],\n  [{name: 'mona'}, ['test2']],\n]\nconst objectMap = new ObjectMap(testing)\nconst values = []\nconst keys = []\n\nobjectMap.forEach((value, key) => {\n  values.push(value)\n  keys.push(key)\n})"
  },
  {
    "path": "test-examples/flat/src/getAttribute.js",
    "content": "function foobar(el) {\n  el.getAttribute('autoComplete')\n}\n\nconst title = document.createElement('h1')\ntitle.textContent = `${title}!`\n\nfoobar(title)\n\ndocument.addEventListener('click', async function (event) {\n  const data = await fetch()\n\n  event.preventDefault()\n})\n\nwindow.addEventListener(\n  'scroll',\n  () => {\n    console.log('Scroll event fired!')\n  },\n  {passive: true},\n)\n\ndocument.addEventListener('click', async function (event) {\n  // event.currentTarget will be an HTMLElement\n  const url = event.currentTarget.getAttribute('data-url')\n  const data = await fetch(url)\n\n  // But now, event.currentTarget will be null\n  const text = event.currentTarget.getAttribute('data-text')\n  // ...\n})\n"
  },
  {
    "path": "test-examples/flat/src/jsx.tsx",
    "content": "const Components = () => {\n  return (\n    <a aria-label=\"learn more\" src=\"https://www.github.com\" title=\"A home for all developers\">\n      GitHub\n    </a>\n  )\n}"
  },
  {
    "path": "test-examples/flat/src/noBlur.js",
    "content": "function focusInput() {\n  const textField = document.getElementById(\"sampleText\")\n\n  textField.focus()\n\n  setTimeout(() => {\n    textField.blur()\n  }, 3000)\n}"
  },
  {
    "path": "test-examples/flat/src/thisTypescriptTest.ts",
    "content": "class Safe {\n  contents: string;\n\n  constructor(contents: string) {\n    this.contents = contents;\n  }\n\n  printContents() {\n    console.log(this.contents);\n  }\n}\n\nvar foo = function() {\n  this.a = 0;\n  baz(() => this);\n};\n"
  },
  {
    "path": "test-examples/legacy/.eslintrc.cjs",
    "content": "module.exports = {\n  root: true,\n  env: { es2022: true },\n  extends: [\n    'eslint:recommended',\n    'plugin:github/browser',\n    'plugin:github/recommended',\n    'plugin:github/react',\n    'plugin:github/typescript',\n  ],\n  settings: {},\n  ignorePatterns: ['.eslintrc.cjs'],\n  parser: '@typescript-eslint/parser',\n  parserOptions: {\n    ecmaVersion: 'latest',\n    sourceType: 'module',\n  },\n  plugins: ['github'],\n  rules: {\n    'no-unused-vars': 'off',\n    'github/no-blur': 'error',\n    'github/no-innerText': 'warn',\n  },\n};"
  },
  {
    "path": "test-examples/legacy/package.json",
    "content": "{\n  \"name\": \"legacy\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"lint\": \"cross-env ESLINT_USE_FLAT_CONFIG=false eslint src --ext js,jsx,ts,tsx --report-unused-disable-directives\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.14.5\",\n    \"@typescript-eslint/parser\": \"^8.0.0\",\n    \"cross-env\": \"^7.0.3\",\n    \"eslint\": \"^8.57.0\",\n    \"eslint-plugin-github\": \"file:../..\",\n    \"typescript\": \"^5.6.3\",\n    \"typescript-eslint\": \"^8.12.2\"\n  }\n}"
  },
  {
    "path": "test-examples/legacy/src/forEachTest.js",
    "content": "const testing = [\n  [{name: 'github'}, ['test1']],\n  [{name: 'mona'}, ['test2']],\n]\nconst objectMap = new ObjectMap(testing)\nconst values = []\nconst keys = []\n\nobjectMap.forEach((value, key) => {\n  values.push(value)\n  keys.push(key)\n})"
  },
  {
    "path": "test-examples/legacy/src/getAttribute.js",
    "content": "function foobar(el) {\n  el.getAttribute('autoComplete')\n}\n\nconst title = document.createElement('h1')\ntitle.textContent = `${title}!`\n\nfoobar(title)\n"
  },
  {
    "path": "test-examples/legacy/src/jsx.tsx",
    "content": "const Components = () => {\n  return (\n    <a aria-label=\"learn more\" src=\"https://www.github.com\" title=\"A home for all developers\">\n      GitHub\n    </a>\n  )\n}"
  },
  {
    "path": "test-examples/legacy/src/noBlur.js",
    "content": "function focusInput() {\n  const textField = document.getElementById(\"sampleText\")\n\n  textField.focus()\n\n  setTimeout(() => {\n    textField.blur()\n  }, 3000)\n}"
  },
  {
    "path": "test-examples/legacy/src/thisTypescriptTest.ts",
    "content": "class Safe {\n  contents: string;\n\n  constructor(contents: string) {\n    this.contents = contents;\n  }\n\n  printContents() {\n    console.log(this.contents);\n  }\n}\n\nvar foo = function() {\n  this.a = 0;\n  baz(() => this);\n};"
  },
  {
    "path": "tests/a11y-aria-label-is-well-formatted.js",
    "content": "import rule from '../lib/rules/a11y-aria-label-is-well-formatted.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester({\n  parserOptions: {\n    ecmaVersion: 'latest',\n    sourceType: 'module',\n    ecmaFeatures: {\n      jsx: true,\n    },\n  },\n})\n\nconst errorMessage = '[aria-label] text should be formatted the same as you would visual text. Use sentence case.'\n\nruleTester.run('a11y-aria-label-is-well-formatted', rule, {\n  valid: [\n    {code: \"<a aria-labelledby='someId' href='#'>Read more</a>;\"},\n    {code: \"<a aria-label={someName} href='#'>Read more</a>;\"},\n    {code: \"<a aria-label='This is a label'></a>;\"},\n    {code: \"<a aria-label='Valid'></a>;\"},\n    {code: \"<a aria-label='VALID'></a>;\"},\n    {code: '<Link aria-label=\"Valid\" href=\"#\">Read more</Link>'},\n  ],\n  invalid: [\n    {code: \"<a aria-label='close modal'></a>;\", errors: [{message: errorMessage}]},\n    {code: \"<a aria-label='submit'></a>;\", errors: [{message: errorMessage}]},\n    {code: \"<a aria-label='submit.yml'></a>;\", errors: [{message: errorMessage}]},\n    {code: \"<a aria-label='this-is-not-an-id'></a>;\", errors: [{message: errorMessage}]},\n  ],\n})\n"
  },
  {
    "path": "tests/a11y-no-generic-link-text.js",
    "content": "import rule from '../lib/rules/a11y-no-generic-link-text.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester({\n  parserOptions: {\n    ecmaVersion: 'latest',\n    sourceType: 'module',\n    ecmaFeatures: {\n      jsx: true,\n    },\n  },\n})\n\nconst errorMessage =\n  'Avoid setting generic link text like `Here`, `Click here`, `Read more`. Make sure that your link text is both descriptive and concise.'\n\nruleTester.run('a11y-no-generic-link-text', rule, {\n  valid: [\n    {code: \"<a href='#'>GitHub Home</a>;\"},\n    {code: \"<Box><a href='#'>GitHub Home</a></Box>;\"},\n    {code: \"<a aria-label='Read more about our project' href='#'>Read more</a>;\"},\n    {code: \"<a aria-labelledby='someId' href='#'>Read more</a>;\"},\n    {code: '<summary>Read more</summary>;'},\n    {\n      code: '<Link as=\"button\" href=\"#\">Read more</Link>',\n      settings: {\n        github: {\n          components: {\n            Link: 'a',\n          },\n        },\n      },\n    },\n  ],\n  invalid: [\n    {\n      code: '<ButtonLink href=\"#\">Read more</ButtonLink>',\n      errors: [{message: errorMessage}],\n      settings: {\n        github: {\n          components: {\n            ButtonLink: 'a',\n          },\n        },\n      },\n    },\n    {\n      code: '<Link href=\"#\">Read more</Link>',\n      errors: [{message: errorMessage}],\n      settings: {\n        github: {\n          components: {\n            Link: 'a',\n          },\n        },\n      },\n    },\n    {\n      code: '<Test as=\"a\" href=\"#\">Read more</Test>',\n      errors: [{message: errorMessage}],\n    },\n    {\n      code: \"<Box><a href='#'>Click here</a></Box>;\",\n      errors: [{message: errorMessage}],\n    },\n    {code: '<a>Click here*</a>;', errors: [{message: errorMessage}]},\n    {code: '<a>Learn more.</a>;', errors: [{message: errorMessage}]},\n    {code: \"<a aria-label='read more!!!'></a>;\", errors: [{message: errorMessage}]},\n    {code: \"<a aria-label='click here.'></a>;\", errors: [{message: errorMessage}]},\n    {code: \"<a aria-label='Here'></a>;\", errors: [{message: errorMessage}]},\n    {\n      code: \"<a aria-label='Does not include visible label'>Read more!</a>;\",\n      errors: [\n        {\n          message: 'When using ARIA to set a more descriptive text, it must fully contain the visible label.',\n        },\n      ],\n    },\n  ],\n})\n"
  },
  {
    "path": "tests/a11y-no-title-attribute.js",
    "content": "import rule from '../lib/rules/a11y-no-title-attribute.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester({\n  parserOptions: {\n    ecmaVersion: 'latest',\n    sourceType: 'module',\n    ecmaFeatures: {\n      jsx: true,\n    },\n  },\n})\n\nconst errorMessage = 'The title attribute is not accessible and should never be used unless for an `<iframe>`.'\n\nruleTester.run('a11y-no-title-attribute', rule, {\n  valid: [\n    {code: '<button>Submit</button>'},\n    {code: '<iframe title=\"an allowed title\">GitHub</iframe>'},\n    {code: '<span>some information</span>'},\n    {code: '<a href=\"github.com\">GitHub</a>'},\n    {\n      code: '<Component title=\"some title\">Submit</Component>',\n      settings: {\n        github: {\n          components: {\n            Component: 'iframe',\n          },\n        },\n      },\n    },\n    {\n      // Note: we are only checking semantic elements. We cannot make assumptions about how a React Components is using the title prop.\n      code: '<Link title=\"some title\">Submit</Link>',\n      settings: {\n        github: {\n          components: {\n            Link: 'a',\n          },\n        },\n      },\n    },\n    {\n      // Note: we are only checking semantic elements. We cannot make assumptions about how a React Components is using the title prop.\n      code: '<Link as=\"a\" title=\"some title\">Submit</Link>',\n    },\n  ],\n  invalid: [\n    {code: '<a title=\"some title\" href=\"github.com\">GitHub</a>', errors: [{message: errorMessage}]},\n    {code: '<span><button title=\"some title\">submit</button></span>', errors: [{message: errorMessage}]},\n  ],\n})\n"
  },
  {
    "path": "tests/a11y-no-visually-hidden-interactive-element.js",
    "content": "import rule from '../lib/rules/a11y-no-visually-hidden-interactive-element.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester({\n  parserOptions: {\n    ecmaVersion: 'latest',\n    sourceType: 'module',\n    ecmaFeatures: {\n      jsx: true,\n    },\n  },\n})\n\nconst errorMessage =\n  '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.'\n\nruleTester.run('a11y-no-visually-hidden-interactive-element', rule, {\n  valid: [\n    {code: '<VisuallyHidden as=\"h2\">Submit</VisuallyHidden>'},\n    {code: \"<div className='sr-only'>Text</div>;\"},\n    {code: '<VisuallyHidden><div>Text</div></VisuallyHidden>'},\n    {code: \"<div className='other visually-hidden'>Text</div>;\"},\n    {code: \"<span className='sr-only'>Text</span>;\"},\n    {code: \"<button className='other'>Submit</button>\"},\n    {code: \"<input className='sr-only' />\"},\n    {\n      code: \"<Foo className={({isOn}) => { return isOn || isOnProps ? `${className} selected`.trim() : className ?? ''}}/>\",\n    },\n    {code: \"<a className='other show-on-focus'>skip to main content</a>\"},\n    {code: '<button>Submit</button>'},\n    {\n      code: \"<button className='sr-only'>Submit</button>\",\n      options: [\n        {\n          className: 'visually-hidden',\n        },\n      ],\n    },\n    {\n      code: \"<VisuallyHidden as='button'>Submit</VisuallyHidden>\",\n      options: [\n        {\n          componentName: 'Hidden',\n        },\n      ],\n      errors: [{message: errorMessage}],\n    },\n    {\n      code: \"<VisuallyHidden as='button'>Submit</VisuallyHidden>\",\n      settings: {\n        github: {\n          polymorphicPropName: 'html',\n        },\n      },\n    },\n  ],\n  invalid: [\n    {code: '<VisuallyHidden as=\"button\">Submit</VisuallyHidden>', errors: [{message: errorMessage}]},\n    {code: '<VisuallyHidden><button>Submit</button></VisuallyHidden>', errors: [{message: errorMessage}]},\n    {\n      code: '<VisuallyHidden><button class=\"sr-only\">Submit</button></VisuallyHidden>',\n      errors: [{message: errorMessage}],\n    },\n    {code: \"<button className='sr-only'>Submit</button>\", errors: [{message: errorMessage}]},\n    {code: '<VisuallyHidden><div><button>Submit</button></div></VisuallyHidden>', errors: [{message: errorMessage}]},\n    {code: \"<a className='other sr-only' href='github.com'>GitHub</a>\", errors: [{message: errorMessage}]},\n    {code: \"<summary className='sr-only'>Toggle open</summary>\", errors: [{message: errorMessage}]},\n    {code: \"<textarea className='sr-only' />\", errors: [{message: errorMessage}]},\n    {code: \"<select className='sr-only' />\", errors: [{message: errorMessage}]},\n    {code: \"<option className='sr-only' />\", errors: [{message: errorMessage}]},\n    {code: \"<a className='sr-only'>Read more</a>\", errors: [{message: errorMessage}]},\n    {\n      code: \"<button className='visually-hidden'>Submit</button>\",\n      options: [\n        {\n          className: 'visually-hidden',\n        },\n      ],\n      errors: [{message: errorMessage}],\n    },\n    {\n      code: \"<Hidden as='button'>Submit</Hidden>\",\n      options: [\n        {\n          componentName: 'Hidden',\n        },\n      ],\n      errors: [{message: errorMessage}],\n    },\n    {\n      code: \"<VisuallyHidden html='button'>Submit</VisuallyHidden>\",\n      errors: [{message: errorMessage}],\n      settings: {\n        github: {\n          polymorphicPropName: 'html',\n        },\n      },\n    },\n  ],\n})\n"
  },
  {
    "path": "tests/a11y-role-supports-aria-props.js",
    "content": "// @ts-check\n\n// 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:\n//\n// The MIT License (MIT) Copyright (c) 2016 Ethan Cohen\n//\n// 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:\n//\n// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n//\n// 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.\n\nimport rule from '../lib/rules/a11y-role-supports-aria-props.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester({\n  languageOptions: {\n    parserOptions: {\n      ecmaVersion: 'latest',\n      sourceType: 'module',\n      ecmaFeatures: {\n        jsx: true,\n      },\n    },\n  },\n})\n\nfunction getErrorMessage(attribute, role) {\n  return `The attribute ${attribute} is not supported by the role ${role}.`\n}\n\nruleTester.run('a11y-role-supports-aria-props', rule, {\n  valid: [\n    {\n      code: `\n      <div\n        id={id}\n        role={\n          sectionHasHeader && rowIndex.row === 0 ? 'presentation' : 'option'\n        }\n        aria-label={this.props.ariaLabel}\n      >\n        {children}\n      </div>`,\n    },\n    {code: '<Foo bar />'},\n    {code: '<div />'},\n    {code: '<div id=\"main\" />'},\n    {code: '<div role />'},\n    {code: '<div role=\"presentation\" {...props} />'},\n    {code: '<Foo.Bar baz={true} />'},\n    {code: '<Foo as=\"a\" href={url} aria-label={`Issue #${title}`} />'},\n    {code: '<Link href=\"#\" aria-checked />'},\n    // Don't try to evaluate expression\n    {code: '<Box aria-labelledby=\"some-id\" role={role} />'},\n    {code: '<Box aria-labelledby=\"some-id\" as={isNavigationOpen ? \"div\" : \"nav\"} />'},\n    // IMPLICIT ROLE TESTS\n    // A TESTS - implicit role is `link`\n    {code: '<a href=\"#\" aria-expanded />'},\n    {code: '<a href=\"#\" aria-atomic />'},\n    {code: '<a href=\"#\" aria-busy />'},\n    {code: '<a href=\"#\" aria-controls />'},\n    {code: '<a href=\"#\" aria-current />'},\n    {code: '<a href=\"#\" aria-describedby />'},\n    {code: '<a href=\"#\" aria-disabled />'},\n    {code: '<a href=\"#\" aria-dropeffect />'},\n    {code: '<a href=\"#\" aria-flowto />'},\n    {code: '<a href=\"#\" aria-haspopup />'},\n    {code: '<a href=\"#\" aria-grabbed />'},\n    {code: '<a href=\"#\" aria-hidden />'},\n    {code: '<a href=\"#\" aria-label />'},\n    {code: '<a href=\"#\" aria-labelledby />'},\n    {code: '<a href=\"#\" aria-live />'},\n    {code: '<a href=\"#\" aria-owns />'},\n    {code: '<a href=\"#\" aria-relevant />'},\n\n    // AREA TESTS - implicit role is `link`\n    {code: '<area href=\"#\" aria-expanded />'},\n    {code: '<area href=\"#\" aria-atomic />'},\n    {code: '<area href=\"#\" aria-busy />'},\n    {code: '<area href=\"#\" aria-controls />'},\n    {code: '<area href=\"#\" aria-describedby />'},\n    {code: '<area href=\"#\" aria-disabled />'},\n    {code: '<area href=\"#\" aria-dropeffect />'},\n    {code: '<area href=\"#\" aria-flowto />'},\n    {code: '<area href=\"#\" aria-grabbed />'},\n    {code: '<area href=\"#\" aria-haspopup />'},\n    {code: '<area href=\"#\" aria-hidden />'},\n    {code: '<area href=\"#\" aria-label />'},\n    {code: '<area href=\"#\" aria-labelledby />'},\n    {code: '<area href=\"#\" aria-live />'},\n    {code: '<area href=\"#\" aria-owns />'},\n    {code: '<area href=\"#\" aria-relevant />'},\n\n    // this will have role of `img`\n    {code: '<img alt=\"foobar\" aria-busy />'},\n\n    // MENU TESTS - implicit role is `toolbar` when `type=\"toolbar\"`\n    {code: '<menu type=\"toolbar\" aria-activedescendant />'},\n    {code: '<menu type=\"toolbar\" aria-atomic />'},\n    {code: '<menu type=\"toolbar\" aria-busy />'},\n    {code: '<menu type=\"toolbar\" aria-controls />'},\n    {code: '<menu type=\"toolbar\" aria-describedby />'},\n    {code: '<menu type=\"toolbar\" aria-disabled />'},\n    {code: '<menu type=\"toolbar\" aria-dropeffect />'},\n    {code: '<menu type=\"toolbar\" aria-flowto />'},\n    {code: '<menu type=\"toolbar\" aria-grabbed />'},\n    {code: '<menu type=\"toolbar\" aria-hidden />'},\n    {code: '<menu type=\"toolbar\" aria-label />'},\n    {code: '<menu type=\"toolbar\" aria-labelledby />'},\n    {code: '<menu type=\"toolbar\" aria-live />'},\n    {code: '<menu type=\"toolbar\" aria-owns />'},\n    {code: '<menu type=\"toolbar\" aria-relevant />'},\n\n    // MENUITEM TESTS\n    // when `type=\"command`, the implicit role is `menuitem`\n    {code: '<menuitem type=\"command\" aria-atomic />'},\n    {code: '<menuitem type=\"command\" aria-busy />'},\n    {code: '<menuitem type=\"command\" aria-controls />'},\n    {code: '<menuitem type=\"command\" aria-describedby />'},\n    {code: '<menuitem type=\"command\" aria-disabled />'},\n    {code: '<menuitem type=\"command\" aria-dropeffect />'},\n    {code: '<menuitem type=\"command\" aria-flowto />'},\n    {code: '<menuitem type=\"command\" aria-grabbed />'},\n    {code: '<menuitem type=\"command\" aria-haspopup />'},\n    {code: '<menuitem type=\"command\" aria-hidden />'},\n    {code: '<menuitem type=\"command\" aria-label />'},\n    {code: '<menuitem type=\"command\" aria-labelledby />'},\n    {code: '<menuitem type=\"command\" aria-live />'},\n    {code: '<menuitem type=\"command\" aria-owns />'},\n    {code: '<menuitem type=\"command\" aria-relevant />'},\n    // when `type=\"checkbox`, the implicit role is `menuitemcheckbox`\n    {code: '<menuitem type=\"checkbox\" aria-checked />'},\n    {code: '<menuitem type=\"checkbox\" aria-atomic />'},\n    {code: '<menuitem type=\"checkbox\" aria-busy />'},\n    {code: '<menuitem type=\"checkbox\" aria-controls />'},\n    {code: '<menuitem type=\"checkbox\" aria-describedby />'},\n    {code: '<menuitem type=\"checkbox\" aria-disabled />'},\n    {code: '<menuitem type=\"checkbox\" aria-dropeffect />'},\n    {code: '<menuitem type=\"checkbox\" aria-flowto />'},\n    {code: '<menuitem type=\"checkbox\" aria-grabbed />'},\n    {code: '<menuitem type=\"checkbox\" aria-haspopup />'},\n    {code: '<menuitem type=\"checkbox\" aria-hidden />'},\n    {code: '<menuitem type=\"checkbox\" aria-invalid />'},\n    {code: '<menuitem type=\"checkbox\" aria-label />'},\n    {code: '<menuitem type=\"checkbox\" aria-labelledby />'},\n    {code: '<menuitem type=\"checkbox\" aria-live />'},\n    {code: '<menuitem type=\"checkbox\" aria-owns />'},\n    {code: '<menuitem type=\"checkbox\" aria-relevant />'},\n    // when `type=\"radio`, the implicit role is `menuitemradio`\n    {code: '<menuitem type=\"radio\" aria-checked />'},\n    {code: '<menuitem type=\"radio\" aria-atomic />'},\n    {code: '<menuitem type=\"radio\" aria-busy />'},\n    {code: '<menuitem type=\"radio\" aria-controls />'},\n    {code: '<menuitem type=\"radio\" aria-describedby />'},\n    {code: '<menuitem type=\"radio\" aria-disabled />'},\n    {code: '<menuitem type=\"radio\" aria-dropeffect />'},\n    {code: '<menuitem type=\"radio\" aria-flowto />'},\n    {code: '<menuitem type=\"radio\" aria-grabbed />'},\n    {code: '<menuitem type=\"radio\" aria-haspopup />'},\n    {code: '<menuitem type=\"radio\" aria-hidden />'},\n    {code: '<menuitem type=\"radio\" aria-invalid />'},\n    {code: '<menuitem type=\"radio\" aria-label />'},\n    {code: '<menuitem type=\"radio\" aria-labelledby />'},\n    {code: '<menuitem type=\"radio\" aria-live />'},\n    {code: '<menuitem type=\"radio\" aria-owns />'},\n    {code: '<menuitem type=\"radio\" aria-relevant />'},\n    {code: '<menuitem type=\"radio\" aria-posinset />'},\n    {code: '<menuitem type=\"radio\" aria-setsize />'},\n\n    // these will have global\n    {code: '<menuitem aria-checked />'},\n    {code: '<menuitem type=\"foo\" aria-checked />'},\n\n    // INPUT TESTS\n    // when `type=\"button\"`, the implicit role is `button`\n    {code: '<input type=\"button\" aria-expanded />'},\n    {code: '<input type=\"button\" aria-pressed />'},\n    {code: '<input type=\"button\" aria-atomic />'},\n    {code: '<input type=\"button\" aria-busy />'},\n    {code: '<input type=\"button\" aria-controls />'},\n    {code: '<input type=\"button\" aria-describedby />'},\n    {code: '<input type=\"button\" aria-disabled />'},\n    {code: '<input type=\"button\" aria-dropeffect />'},\n    {code: '<input type=\"button\" aria-flowto />'},\n    {code: '<input type=\"button\" aria-grabbed />'},\n    {code: '<input type=\"button\" aria-haspopup />'},\n    {code: '<input type=\"button\" aria-hidden />'},\n    {code: '<input type=\"button\" aria-label />'},\n    {code: '<input type=\"button\" aria-labelledby />'},\n    {code: '<input type=\"button\" aria-live />'},\n    {code: '<input type=\"button\" aria-owns />'},\n    {code: '<input type=\"button\" aria-relevant />'},\n    // when `type=\"image\"`, the implicit role is `button`\n    {code: '<input type=\"image\" aria-expanded />'},\n    {code: '<input type=\"image\" aria-pressed />'},\n    {code: '<input type=\"image\" aria-atomic />'},\n    {code: '<input type=\"image\" aria-busy />'},\n    {code: '<input type=\"image\" aria-controls />'},\n    {code: '<input type=\"image\" aria-describedby />'},\n    {code: '<input type=\"image\" aria-disabled />'},\n    {code: '<input type=\"image\" aria-dropeffect />'},\n    {code: '<input type=\"image\" aria-flowto />'},\n    {code: '<input type=\"image\" aria-grabbed />'},\n    {code: '<input type=\"image\" aria-haspopup />'},\n    {code: '<input type=\"image\" aria-hidden />'},\n    {code: '<input type=\"image\" aria-label />'},\n    {code: '<input type=\"image\" aria-labelledby />'},\n    {code: '<input type=\"image\" aria-live />'},\n    {code: '<input type=\"image\" aria-owns />'},\n    {code: '<input type=\"image\" aria-relevant />'},\n    // when `type=\"reset\"`, the implicit role is `button`\n    {code: '<input type=\"reset\" aria-expanded />'},\n    {code: '<input type=\"reset\" aria-pressed />'},\n    {code: '<input type=\"reset\" aria-atomic />'},\n    {code: '<input type=\"reset\" aria-busy />'},\n    {code: '<input type=\"reset\" aria-controls />'},\n    {code: '<input type=\"reset\" aria-describedby />'},\n    {code: '<input type=\"reset\" aria-disabled />'},\n    {code: '<input type=\"reset\" aria-dropeffect />'},\n    {code: '<input type=\"reset\" aria-flowto />'},\n    {code: '<input type=\"reset\" aria-grabbed />'},\n    {code: '<input type=\"reset\" aria-haspopup />'},\n    {code: '<input type=\"reset\" aria-hidden />'},\n    {code: '<input type=\"reset\" aria-label />'},\n    {code: '<input type=\"reset\" aria-labelledby />'},\n    {code: '<input type=\"reset\" aria-live />'},\n    {code: '<input type=\"reset\" aria-owns />'},\n    {code: '<input type=\"reset\" aria-relevant />'},\n    // when `type=\"submit\"`, the implicit role is `button`\n    {code: '<input type=\"submit\" aria-expanded />'},\n    {code: '<input type=\"submit\" aria-pressed />'},\n    {code: '<input type=\"submit\" aria-atomic />'},\n    {code: '<input type=\"submit\" aria-busy />'},\n    {code: '<input type=\"submit\" aria-controls />'},\n    {code: '<input type=\"submit\" aria-describedby />'},\n    {code: '<input type=\"submit\" aria-disabled />'},\n    {code: '<input type=\"submit\" aria-dropeffect />'},\n    {code: '<input type=\"submit\" aria-flowto />'},\n    {code: '<input type=\"submit\" aria-grabbed />'},\n    {code: '<input type=\"submit\" aria-haspopup />'},\n    {code: '<input type=\"submit\" aria-hidden />'},\n    {code: '<input type=\"submit\" aria-label />'},\n    {code: '<input type=\"submit\" aria-labelledby />'},\n    {code: '<input type=\"submit\" aria-live />'},\n    {code: '<input type=\"submit\" aria-owns />'},\n    {code: '<input type=\"submit\" aria-relevant />'},\n    // when `type=\"checkbox\"`, the implicit role is `checkbox`\n    {code: '<input type=\"checkbox\" aria-atomic />'},\n    {code: '<input type=\"checkbox\" aria-busy />'},\n    {code: '<input type=\"checkbox\" aria-checked />'},\n    {code: '<input type=\"checkbox\" aria-controls />'},\n    {code: '<input type=\"checkbox\" aria-describedby />'},\n    {code: '<input type=\"checkbox\" aria-disabled />'},\n    {code: '<input type=\"checkbox\" aria-dropeffect />'},\n    {code: '<input type=\"checkbox\" aria-flowto />'},\n    {code: '<input type=\"checkbox\" aria-grabbed />'},\n    {code: '<input type=\"checkbox\" aria-hidden />'},\n    {code: '<input type=\"checkbox\" aria-invalid />'},\n    {code: '<input type=\"checkbox\" aria-label />'},\n    {code: '<input type=\"checkbox\" aria-labelledby />'},\n    {code: '<input type=\"checkbox\" aria-live />'},\n    {code: '<input type=\"checkbox\" aria-owns />'},\n    {code: '<input type=\"checkbox\" aria-relevant />'},\n    // when `type=\"radio\"`, the implicit role is `radio`\n    {code: '<input type=\"radio\" aria-atomic />'},\n    {code: '<input type=\"radio\" aria-busy />'},\n    {code: '<input type=\"radio\" aria-checked />'},\n    {code: '<input type=\"radio\" aria-controls />'},\n    {code: '<input type=\"radio\" aria-describedby />'},\n    {code: '<input type=\"radio\" aria-disabled />'},\n    {code: '<input type=\"radio\" aria-dropeffect />'},\n    {code: '<input type=\"radio\" aria-flowto />'},\n    {code: '<input type=\"radio\" aria-grabbed />'},\n    {code: '<input type=\"radio\" aria-hidden />'},\n    {code: '<input type=\"radio\" aria-label />'},\n    {code: '<input type=\"radio\" aria-labelledby />'},\n    {code: '<input type=\"radio\" aria-live />'},\n    {code: '<input type=\"radio\" aria-owns />'},\n    {code: '<input type=\"radio\" aria-relevant />'},\n    {code: '<input type=\"radio\" aria-posinset />'},\n    {code: '<input type=\"radio\" aria-setsize />'},\n    // when `type=\"range\"`, the implicit role is `slider`\n    {code: '<input type=\"range\" aria-valuemax />'},\n    {code: '<input type=\"range\" aria-valuemin />'},\n    {code: '<input type=\"range\" aria-valuenow />'},\n    {code: '<input type=\"range\" aria-orientation />'},\n    {code: '<input type=\"range\" aria-atomic />'},\n    {code: '<input type=\"range\" aria-busy />'},\n    {code: '<input type=\"range\" aria-controls />'},\n    {code: '<input type=\"range\" aria-describedby />'},\n    {code: '<input type=\"range\" aria-disabled />'},\n    {code: '<input type=\"range\" aria-dropeffect />'},\n    {code: '<input type=\"range\" aria-flowto />'},\n    {code: '<input type=\"range\" aria-grabbed />'},\n    {code: '<input type=\"range\" aria-haspopup />'},\n    {code: '<input type=\"range\" aria-hidden />'},\n    {code: '<input type=\"range\" aria-invalid />'},\n    {code: '<input type=\"range\" aria-label />'},\n    {code: '<input type=\"range\" aria-labelledby />'},\n    {code: '<input type=\"range\" aria-live />'},\n    {code: '<input type=\"range\" aria-owns />'},\n    {code: '<input type=\"range\" aria-relevant />'},\n    {code: '<input type=\"range\" aria-valuetext />'},\n\n    // these will have role of `textbox`,\n    {code: '<input type=\"email\" aria-disabled />'},\n    {code: '<input type=\"password\" aria-disabled />'},\n    {code: '<input type=\"search\" aria-disabled />'},\n    {code: '<input type=\"tel\" aria-disabled />'},\n    {code: '<input type=\"url\" aria-disabled />'},\n    {code: '<input aria-disabled />'},\n\n    // Allow null/undefined values regardless of role\n    {code: '<h2 role=\"presentation\" aria-level={null} />'},\n    {code: '<h2 role=\"presentation\" aria-level={undefined} />'},\n\n    // OTHER TESTS\n    {code: '<button aria-pressed />'},\n    {code: '<form aria-hidden />'},\n    {code: '<h1 aria-hidden />'},\n    {code: '<h2 aria-hidden />'},\n    {code: '<h3 aria-hidden />'},\n    {code: '<h4 aria-hidden />'},\n    {code: '<h5 aria-hidden />'},\n    {code: '<h6 aria-hidden />'},\n    {code: '<hr aria-hidden />'},\n    {code: '<li aria-current />'},\n    {code: '<meter aria-atomic />'},\n    {code: '<option aria-atomic />'},\n    {code: '<progress aria-atomic />'},\n    {code: '<textarea aria-hidden />'},\n    {code: '<select aria-expanded />'},\n    {code: '<datalist aria-expanded />'},\n    {code: '<div role=\"heading\" aria-level />'},\n    {code: '<div role=\"heading\" aria-level=\"1\" />'},\n    {code: '<link href=\"#\" aria-expanded />'}, // link maps to nothing\n  ],\n\n  invalid: [\n    // implicit basic checks\n    {\n      code: '<area aria-checked />',\n      errors: [getErrorMessage('aria-checked', 'generic')],\n    },\n    {\n      code: '<a aria-checked />',\n      errors: [getErrorMessage('aria-checked', 'generic')],\n    },\n    {\n      code: '<a href=\"#\" aria-checked />',\n      errors: [getErrorMessage('aria-checked', 'link')],\n    },\n    {\n      code: '<area href=\"#\" aria-checked />',\n      errors: [getErrorMessage('aria-checked', 'link')],\n    },\n    {\n      code: '<img alt=\"foobar\" aria-checked />',\n      errors: [getErrorMessage('aria-checked', 'img')],\n    },\n    {\n      code: '<menu type=\"toolbar\" aria-checked />',\n      errors: [getErrorMessage('aria-checked', 'toolbar')],\n    },\n    {\n      code: '<aside aria-checked />',\n      errors: [getErrorMessage('aria-checked', 'complementary')],\n    },\n    {\n      code: '<ul aria-expanded />',\n      errors: [getErrorMessage('aria-expanded', 'list')],\n    },\n    {\n      code: '<details aria-expanded />',\n      errors: [getErrorMessage('aria-expanded', 'group')],\n    },\n    {\n      code: '<dialog aria-expanded />',\n      errors: [getErrorMessage('aria-expanded', 'dialog')],\n    },\n    {\n      code: '<aside aria-expanded />',\n      errors: [getErrorMessage('aria-expanded', 'complementary')],\n    },\n    {\n      code: '<article aria-expanded />',\n      errors: [getErrorMessage('aria-expanded', 'article')],\n    },\n    {\n      code: '<body aria-expanded />',\n      errors: [getErrorMessage('aria-expanded', 'generic')],\n    },\n    {\n      code: '<li aria-expanded />',\n      errors: [getErrorMessage('aria-expanded', 'listitem')],\n    },\n    {\n      code: '<nav aria-expanded />',\n      errors: [getErrorMessage('aria-expanded', 'navigation')],\n    },\n    {\n      code: '<ol aria-expanded />',\n      errors: [getErrorMessage('aria-expanded', 'list')],\n    },\n    {\n      code: '<output aria-expanded />',\n      errors: [getErrorMessage('aria-expanded', 'status')],\n    },\n    {\n      code: '<section aria-expanded />',\n      errors: [getErrorMessage('aria-expanded', 'generic')],\n    },\n    {\n      code: '<section aria-label=\"something\" aria-expanded />',\n      errors: [getErrorMessage('aria-expanded', 'region')],\n    },\n    {\n      code: '<tbody aria-expanded />',\n      errors: [getErrorMessage('aria-expanded', 'rowgroup')],\n    },\n    {\n      code: '<tfoot aria-expanded />',\n      errors: [getErrorMessage('aria-expanded', 'rowgroup')],\n    },\n    {\n      code: '<thead aria-expanded />',\n      errors: [getErrorMessage('aria-expanded', 'rowgroup')],\n    },\n    {\n      code: '<input type=\"radio\" aria-invalid />',\n      errors: [getErrorMessage('aria-invalid', 'radio')],\n    },\n    {\n      code: '<input type=\"radio\" aria-selected />',\n      errors: [getErrorMessage('aria-selected', 'radio')],\n    },\n    {\n      code: '<input type=\"radio\" aria-haspopup />',\n      errors: [getErrorMessage('aria-haspopup', 'radio')],\n    },\n    {\n      code: '<input type=\"checkbox\" aria-haspopup />',\n      errors: [getErrorMessage('aria-haspopup', 'checkbox')],\n    },\n    {\n      code: '<input type=\"reset\" aria-invalid />',\n      errors: [getErrorMessage('aria-invalid', 'button')],\n    },\n    {\n      code: '<input type=\"submit\" aria-invalid />',\n      errors: [getErrorMessage('aria-invalid', 'button')],\n    },\n    {\n      code: '<input type=\"image\" aria-invalid />',\n      errors: [getErrorMessage('aria-invalid', 'button')],\n    },\n    {\n      code: '<input type=\"button\" aria-invalid />',\n      errors: [getErrorMessage('aria-invalid', 'button')],\n    },\n    {\n      code: '<menuitem type=\"command\" aria-invalid />',\n      errors: [getErrorMessage('aria-invalid', 'menuitem')],\n    },\n    {\n      code: '<menuitem type=\"radio\" aria-selected />',\n      errors: [getErrorMessage('aria-selected', 'menuitemradio')],\n    },\n    {\n      code: '<menu type=\"toolbar\" aria-haspopup />',\n      errors: [getErrorMessage('aria-haspopup', 'toolbar')],\n    },\n    {\n      code: '<menu type=\"toolbar\" aria-invalid />',\n      errors: [getErrorMessage('aria-invalid', 'toolbar')],\n    },\n    {\n      code: '<menu type=\"toolbar\" aria-expanded />',\n      errors: [getErrorMessage('aria-expanded', 'toolbar')],\n    },\n    {\n      code: '<area href=\"#\" aria-invalid />',\n      errors: [getErrorMessage('aria-invalid', 'link')],\n    },\n    {\n      code: '<a href=\"#\" aria-invalid />',\n      errors: [getErrorMessage('aria-invalid', 'link')],\n    },\n    {\n      code: '<span aria-label />',\n      errors: [getErrorMessage('aria-label', 'generic')],\n    },\n    {\n      code: '<span aria-labelledby />',\n      errors: [getErrorMessage('aria-labelledby', 'generic')],\n    },\n    {\n      code: '<div aria-labelledby />',\n      errors: [getErrorMessage('aria-labelledby', 'generic')],\n    },\n    // Determines role from literal `as` prop.\n    {\n      code: '<Box as=\"span\" aria-labelledby />',\n      errors: [getErrorMessage('aria-labelledby', 'generic')],\n    },\n    {\n      code: '<p role=\"generic\" aria-label />',\n      errors: [getErrorMessage('aria-label', 'generic')],\n    },\n  ],\n})\n"
  },
  {
    "path": "tests/a11y-svg-has-accessible-name.js",
    "content": "import rule from '../lib/rules/a11y-svg-has-accessible-name.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester({\n  parserOptions: {\n    ecmaVersion: 'latest',\n    sourceType: 'module',\n    ecmaFeatures: {\n      jsx: true,\n    },\n  },\n})\n\nconst errorMessage =\n  '`<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\"`.'\n\nruleTester.run('a11y-svg-has-accessible-name', rule, {\n  valid: [\n    {\n      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>\",\n    },\n    {\n      code: `<svg height='100' width='100'>\n              <title>Circle with a black outline and red fill</title>\n              <circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/>\n            </svg>`,\n    },\n    {\n      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>\",\n    },\n    {\n      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>\",\n    },\n    {\n      code: \"<svg aria-hidden='true' height='100' width='100'><circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/></svg>\",\n    },\n    {\n      code: \"<svg role='presentation' height='100' width='100'><circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/></svg>\",\n    },\n  ],\n  invalid: [\n    {\n      code: \"<svg height='100' width='100'><circle cx='50' cy='50' r='40' stroke='black' stroke-width='3' fill='red'/></svg>\",\n      errors: [{message: errorMessage}],\n    },\n    {\n      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>\",\n      errors: [{message: errorMessage}],\n    },\n    {\n      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>\",\n      errors: [{message: errorMessage}],\n    },\n  ],\n})\n"
  },
  {
    "path": "tests/array-foreach.js",
    "content": "import rule from '../lib/rules/array-foreach.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n\nruleTester.run('array-foreach', rule, {\n  valid: [\n    {\n      code: 'for (const el of els) { el }',\n      parserOptions: {ecmaVersion: 6},\n    },\n    {\n      code: 'els.map(el => el)',\n      parserOptions: {ecmaVersion: 6},\n    },\n    {code: 'forEach()'},\n  ],\n  invalid: [\n    {\n      code: 'els.forEach(el => el)',\n      parserOptions: {ecmaVersion: 6},\n      errors: [\n        {\n          message: 'Prefer for...of instead of Array.forEach',\n          type: 'CallExpression',\n        },\n      ],\n    },\n  ],\n})\n"
  },
  {
    "path": "tests/async-currenttarget.js",
    "content": "import rule from '../lib/rules/async-currenttarget.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n\nruleTester.run('async-currenttarget', rule, {\n  valid: [\n    {\n      code: 'document.addEventListener(function(event) { event.currentTarget })',\n    },\n    {\n      code: 'document.addEventListener(async function(event) { event.currentTarget; await delay() })',\n      parserOptions: {ecmaVersion: 2017},\n    },\n    {\n      code: 'document.addEventListener(async function(event) { const currentTarget = event.currentTarget; await delay(); foo(() => currentTarget) })',\n      parserOptions: {ecmaVersion: 2017},\n    },\n  ],\n  invalid: [\n    {\n      code: 'document.addEventListener(async function(event) { await delay(); event.currentTarget })',\n      parserOptions: {ecmaVersion: 2017},\n      errors: [\n        {\n          message: 'event.currentTarget inside an async function is error prone',\n          type: 'MemberExpression',\n        },\n      ],\n    },\n    {\n      code: 'document.addEventListener(async function(event) { await delay(); foo(() => e.currentTarget) })',\n      parserOptions: {ecmaVersion: 2017},\n      errors: [\n        {\n          message: 'event.currentTarget inside an async function is error prone',\n          type: 'MemberExpression',\n        },\n      ],\n    },\n  ],\n})\n"
  },
  {
    "path": "tests/async-preventdefault.js",
    "content": "import rule from '../lib/rules/async-preventdefault.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n\nruleTester.run('async-preventdefault', rule, {\n  valid: [\n    {\n      code: 'document.addEventListener(function(event) { event.preventDefault() })',\n    },\n    {\n      code: 'document.addEventListener(function(event) { event.target })',\n    },\n    {\n      code: 'document.addEventListener(async function(event) { event.preventDefault() })',\n      parserOptions: {ecmaVersion: 2017},\n    },\n  ],\n  invalid: [\n    {\n      code: 'document.addEventListener(async function(event) { await delay(); event.preventDefault() })',\n      parserOptions: {ecmaVersion: 2017},\n      errors: [\n        {\n          message: 'event.preventDefault() inside an async function is error prone',\n          type: 'CallExpression',\n        },\n      ],\n    },\n    {\n      code: 'document.addEventListener(async function(event) { await delay(); foo(() => event.preventDefault()) })',\n      parserOptions: {ecmaVersion: 2017},\n      errors: [\n        {\n          message: 'event.preventDefault() inside an async function is error prone',\n          type: 'CallExpression',\n        },\n      ],\n    },\n  ],\n})\n"
  },
  {
    "path": "tests/authenticity-token.js",
    "content": "import rule from '../lib/rules/authenticity-token.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n\nruleTester.run('authenticity-token', rule, {\n  valid: [],\n  invalid: [\n    {\n      code: \"document.querySelector('form').elements['authenticity_token'].value\",\n      errors: [\n        {\n          message:\n            'Form CSRF tokens (authenticity tokens) should not be created in JavaScript and their values should not be used directly for XHR requests.',\n          type: 'Literal',\n        },\n      ],\n    },\n    {\n      code: \"document.querySelector('input[name=authenticity_token]').value\",\n      errors: [\n        {\n          message:\n            'Form CSRF tokens (authenticity tokens) should not be created in JavaScript and their values should not be used directly for XHR requests.',\n          type: 'Literal',\n        },\n      ],\n    },\n    {\n      code: \"(new FormData()).append('authenticity_token', 'asdf')\",\n      errors: [\n        {\n          message:\n            'Form CSRF tokens (authenticity tokens) should not be created in JavaScript and their values should not be used directly for XHR requests.',\n          type: 'Literal',\n        },\n      ],\n    },\n  ],\n})\n"
  },
  {
    "path": "tests/check-rules.js",
    "content": "/* globals describe, it*/\nimport config from '../lib/index.js'\nimport fs from 'node:fs'\nimport assert from 'node:assert'\nimport path from 'node:path'\n\ndescribe('smoke tests', () => {\n  it('ensure all rules in lib/rules are included in index', () => {\n    const exportedRules = new Set(Object.keys(config.rules))\n    const files = new Set(fs.readdirSync('./lib/rules').map(f => path.basename(f, path.extname(f))))\n    assert.deepEqual(files, exportedRules)\n  })\n\n  it('exports every config in lib/config as .configs', () => {\n    const exportedConfigs = new Set(Object.keys(config.configs))\n    const files = new Set(fs.readdirSync('./lib/configs').map(f => path.basename(f, path.extname(f))))\n    assert.deepEqual(files, exportedConfigs)\n  })\n\n  it('exports valid rules in each config', () => {\n    const exportedRules = new Set(Object.keys(config.rules))\n    for (const flavour in config.configs) {\n      for (const rule in config.configs[flavour].rules) {\n        if (rule.startsWith('github/')) {\n          assert(exportedRules.has(rule.replace(/^github\\//, '')), `rule ${rule} is not a valid rule`)\n        }\n      }\n    }\n  })\n})\n"
  },
  {
    "path": "tests/get-attribute.js",
    "content": "import rule from '../lib/rules/get-attribute.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n\nruleTester.run('get-attribute', rule, {\n  valid: [\n    {code: \"el.getAttribute('src')\"},\n    {code: \"el.hasAttribute('src')\"},\n    {code: \"el.setAttribute('src', 'https://github.com/')\"},\n    {code: \"el.removeAttribute('src')\"},\n    {code: \"el.getAttribute('data-foo')\"},\n    {code: \"el.hasAttribute('data-foo')\"},\n    {code: \"el.setAttribute('data-foo', 'bar')\"},\n    {code: \"el.removeAttribute('data-foo')\"},\n    {code: \"el.getAttribute('data-foo1')\"},\n    // some SVG attributes must preserve case\n    {code: \"el.getAttribute('preserveAspectRatio')\"},\n    {code: \"el.getAttribute('viewBox')\"},\n  ],\n  invalid: [\n    {\n      code: \"el.getAttribute('SRC')\",\n      output: \"el.getAttribute('src')\",\n      errors: [\n        {\n          message: 'Attributes should be lowercase and hyphen separated, or part of the SVG whitelist.',\n          type: 'Literal',\n        },\n      ],\n    },\n    {\n      code: \"el.hasAttribute('SRC')\",\n      output: \"el.hasAttribute('src')\",\n      errors: [\n        {\n          message: 'Attributes should be lowercase and hyphen separated, or part of the SVG whitelist.',\n          type: 'Literal',\n        },\n      ],\n    },\n    {\n      code: \"el.getAttribute('onClick')\",\n      output: \"el.getAttribute('onclick')\",\n      errors: [\n        {\n          message: 'Attributes should be lowercase and hyphen separated, or part of the SVG whitelist.',\n          type: 'Literal',\n        },\n      ],\n    },\n    {\n      code: \"el.getAttribute('viewbox')\",\n      output: null,\n      errors: [\n        {\n          message: 'Attributes should be lowercase and hyphen separated, or part of the SVG whitelist.',\n          type: 'Literal',\n        },\n      ],\n    },\n    {\n      code: \"el.getAttribute('preserveaspectratio')\",\n      output: null,\n      errors: [\n        {\n          message: 'Attributes should be lowercase and hyphen separated, or part of the SVG whitelist.',\n          type: 'Literal',\n        },\n      ],\n    },\n  ],\n})\n"
  },
  {
    "path": "tests/js-class-name.js",
    "content": "import rule from '../lib/rules/js-class-name.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n\nruleTester.run('js-class-name', rule, {\n  valid: [\n    {code: \"document.querySelector('body')\"},\n    {code: \"document.querySelector('#js-foo')\"},\n    {code: \"document.querySelector('.js-foo')\"},\n    {code: \"document.querySelector('.js-foo > .js-bar')\"},\n    {code: 'document.querySelector(\".js-foo\")'},\n    {code: \"document.querySelector('.js-foo ' + ' > .js-bar')\"},\n    {code: \"document.querySelector('.js-foo ' + ' > ' + '.js-bar')\"},\n    {\n      code: 'document.querySelector(`.js-foo`)',\n      parserOptions: {ecmaVersion: 6},\n    },\n    {code: \"'random textjs-XXX'\"},\n  ],\n  invalid: [\n    {\n      code: \"document.querySelector('.js-Foo')\",\n      errors: [\n        {\n          message: 'js- class names should be lowercase and only contain dashes.',\n          type: 'Literal',\n        },\n      ],\n    },\n    {\n      code: \"document.querySelector('#js-Foo')\",\n      errors: [\n        {\n          message: 'js- class names should be lowercase and only contain dashes.',\n          type: 'Literal',\n        },\n      ],\n    },\n    {\n      code: \"document.querySelector('.js-foo > .js-Bar')\",\n      errors: [\n        {\n          message: 'js- class names should be lowercase and only contain dashes.',\n          type: 'Literal',\n        },\n      ],\n    },\n    {\n      code: \"document.querySelector('.js-' + foo)\",\n      errors: [\n        {\n          message: 'js- class names should be lowercase and only contain dashes.',\n          type: 'Literal',\n        },\n        {\n          message: 'js- class names should be statically defined.',\n          type: 'Literal',\n        },\n      ],\n    },\n    {\n      code: \"document.querySelector('.js-foo-' + foo)\",\n      errors: [\n        {\n          message: 'js- class names should be lowercase and only contain dashes.',\n          type: 'Literal',\n        },\n        {\n          message: 'js- class names should be statically defined.',\n          type: 'Literal',\n        },\n      ],\n    },\n    {\n      code: \"document.querySelector('.js-foo' + idx)\",\n      errors: [\n        {\n          message: 'js- class names should be statically defined.',\n          type: 'Literal',\n        },\n      ],\n    },\n    {\n      code: 'document.querySelector(`.js-${foo}`)',\n      parserOptions: {ecmaVersion: 6},\n      errors: [\n        {\n          message: 'js- class names should be lowercase and only contain dashes.',\n          type: 'TemplateElement',\n        },\n        {\n          message: 'js- class names should be statically defined.',\n          type: 'TemplateElement',\n        },\n      ],\n    },\n    {\n      code: 'document.querySelector(`.js-foo-${foo}`)',\n      parserOptions: {ecmaVersion: 6},\n      errors: [\n        {\n          message: 'js- class names should be lowercase and only contain dashes.',\n          type: 'TemplateElement',\n        },\n        {\n          message: 'js- class names should be statically defined.',\n          type: 'TemplateElement',\n        },\n      ],\n    },\n    {\n      code: 'document.querySelector(`.js-foo${idx}`)',\n      parserOptions: {ecmaVersion: 6},\n      errors: [\n        {\n          message: 'js- class names should be statically defined.',\n          type: 'TemplateElement',\n        },\n      ],\n    },\n  ],\n})\n"
  },
  {
    "path": "tests/no-blur.js",
    "content": "import rule from '../lib/rules/no-blur.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n\nruleTester.run('no-blur', rule, {\n  valid: [{code: 'target.focus()'}],\n  invalid: [\n    {\n      code: 'el.blur()',\n      errors: [\n        {\n          message: 'Do not use element.blur(), instead restore the focus of a previous element.',\n        },\n      ],\n    },\n  ],\n})\n"
  },
  {
    "path": "tests/no-d-none.js",
    "content": "import rule from '../lib/rules/no-d-none.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n\nruleTester.run('no-d-none', rule, {\n  valid: [\n    {\n      code: 'el.classList.add(\"dnone\")',\n    },\n    {\n      code: 'el.classList.toggle(\"responsive-d-none\")',\n    },\n    {\n      code: '[].pop()',\n    },\n  ],\n  invalid: [\n    {\n      code: 'el.classList.add(\"d-none\")',\n      errors: [\n        {\n          message: 'Prefer hidden property to d-none class',\n          type: 'CallExpression',\n        },\n      ],\n    },\n    {\n      code: 'el.classList.add(\"another-class\", \"d-none\")',\n      errors: [\n        {\n          message: 'Prefer hidden property to d-none class',\n          type: 'CallExpression',\n        },\n      ],\n    },\n  ],\n})\n"
  },
  {
    "path": "tests/no-dataset.js",
    "content": "import rule from '../lib/rules/no-dataset.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n\nruleTester.run('no-dataset', rule, {\n  valid: [\n    {code: \"el.getAttribute('data-cool-thing')\"},\n    {code: \"var dataset = 'this is cool'\"},\n    {code: 'function dataset() { }'},\n  ],\n  invalid: [\n    {\n      code: 'el.dataset.coolThing',\n      errors: [\n        {\n          message: \"Use getAttribute('data-your-attribute') instead of dataset.\",\n        },\n      ],\n    },\n    {\n      code: \"el.dataset['cool-thing']\",\n      errors: [\n        {\n          message: \"Use getAttribute('data-your-attribute') instead of dataset.\",\n        },\n      ],\n    },\n  ],\n})\n"
  },
  {
    "path": "tests/no-dynamic-script-tag.js",
    "content": "import rule from '../lib/rules/no-dynamic-script-tag.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n\nruleTester.run('no-dynamic-script-tag', rule, {\n  valid: [\n    {\n      code: 'document.createElement(\"div\")',\n    },\n    {\n      code: 'document.createElement(\"span\")',\n    },\n    {\n      code: 'document.createElement(\"span\").type = \"foo\"',\n    },\n  ],\n  invalid: [\n    {\n      code: 'document.createElement(\"script\")',\n      errors: [\n        {\n          message: \"Don't create dynamic script tags, add them in the server template instead.\",\n          type: 'Literal',\n        },\n      ],\n    },\n    {\n      code: 'document.createElement(\"span\").type = \"text/javascript\"',\n      errors: [\n        {\n          message: \"Don't create dynamic script tags, add them in the server template instead.\",\n          type: 'Literal',\n        },\n      ],\n    },\n  ],\n})\n"
  },
  {
    "path": "tests/no-implicit-buggy-globals.js",
    "content": "import rule from '../lib/rules/no-implicit-buggy-globals.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n\nruleTester.run('no-implicit-buggy-globals', rule, {\n  valid: [\n    {\n      code: '(function() { var foo = 1; })();',\n    },\n    {\n      code: '(function() { let foo = 1; })();',\n      parserOptions: {ecmaVersion: 6},\n    },\n    {\n      code: '(function() { const foo = 1; })();',\n      parserOptions: {ecmaVersion: 6},\n    },\n    {\n      code: 'var foo = 1;',\n      parserOptions: {sourceType: 'module', ecmaVersion: 2015},\n    },\n    {\n      code: 'let foo = 1;',\n      parserOptions: {sourceType: 'module', ecmaVersion: 2015},\n    },\n    {\n      code: 'const foo = 1;',\n      parserOptions: {sourceType: 'module', ecmaVersion: 2015},\n    },\n  ],\n  invalid: [\n    {\n      code: 'const foo = 1;',\n      parserOptions: {ecmaVersion: 6},\n      errors: [\n        {\n          message: 'Implicit global variable, assign as global property instead.',\n          type: 'VariableDeclarator',\n        },\n      ],\n    },\n    {\n      code: 'let foo = 1;',\n      parserOptions: {ecmaVersion: 6},\n      errors: [\n        {\n          message: 'Implicit global variable, assign as global property instead.',\n          type: 'VariableDeclarator',\n        },\n      ],\n    },\n    {\n      code: 'let foo = function() {};',\n      parserOptions: {ecmaVersion: 6},\n      errors: [\n        {\n          message: 'Implicit global variable, assign as global property instead.',\n          type: 'VariableDeclarator',\n        },\n      ],\n    },\n    {\n      code: 'const foo = function() {};',\n      parserOptions: {ecmaVersion: 6},\n      errors: [\n        {\n          message: 'Implicit global variable, assign as global property instead.',\n          type: 'VariableDeclarator',\n        },\n      ],\n    },\n    {\n      code: 'class Foo {}',\n      parserOptions: {ecmaVersion: 6},\n      errors: [\n        {\n          message: 'Implicit global variable, assign as global property instead.',\n          type: 'ClassDeclaration',\n        },\n      ],\n    },\n  ],\n})\n"
  },
  {
    "path": "tests/no-inner-html.js",
    "content": "import rule from '../lib/rules/no-inner-html.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n\nruleTester.run('no-inner-html', rule, {\n  valid: [\n    {\n      code: 'document.createElement(\"js-flash-text\").textContent = \"\"',\n    },\n    {\n      code: 'document.createElement(\"js-flash-text\").textContent = \"foo\"',\n    },\n  ],\n  invalid: [\n    {\n      code: 'document.createElement(\"js-flash-text\").innerHTML = \"foo\"',\n      errors: [\n        {\n          message: 'Using innerHTML poses a potential security risk and should not be used. Prefer using textContent.',\n          type: 'Identifier',\n        },\n      ],\n    },\n    {\n      code: 'document.querySelector(\"js-flash-text\").innerHTML = \"<div>code</div>\"',\n      errors: [\n        {\n          message: 'Using innerHTML poses a potential security risk and should not be used. Prefer using textContent.',\n          type: 'Identifier',\n        },\n      ],\n    },\n    {\n      code: 'document.querySelector(\"js-flash-text\").innerHTML = \"\"',\n      errors: [\n        {\n          message: 'Using innerHTML poses a potential security risk and should not be used. Prefer using textContent.',\n          type: 'Identifier',\n        },\n      ],\n    },\n  ],\n})\n"
  },
  {
    "path": "tests/no-innerText.js",
    "content": "import rule from '../lib/rules/no-innerText.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n\nruleTester.run('no-innerText', rule, {\n  valid: [\n    {\n      code: 'document.createElement(\"js-flash-text\").textContent = \"foo\"',\n    },\n    {\n      code: 'document.querySelector(\"js-flash-text\").textContent = \"bar\"',\n    },\n    {\n      // This is unrelated to the `HTMLElement.innerText` property, and should not trigger a warning\n      code: 'var text = element.textContent()',\n    },\n  ],\n  invalid: [\n    {\n      code: 'document.createElement(\"js-flash-text\").innerText = \"foo\"',\n      output: 'document.createElement(\"js-flash-text\").textContent = \"foo\"',\n      errors: [\n        {\n          message: 'Prefer textContent to innerText',\n          type: 'Identifier',\n        },\n      ],\n    },\n    {\n      code: 'document.querySelector(\"js-flash-text\").innerText = \"bar\"',\n      output: 'document.querySelector(\"js-flash-text\").textContent = \"bar\"',\n      errors: [\n        {\n          message: 'Prefer textContent to innerText',\n          type: 'Identifier',\n        },\n      ],\n    },\n  ],\n})\n"
  },
  {
    "path": "tests/no-then.js",
    "content": "import rule from '../lib/rules/no-then.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n\nruleTester.run('no-then', rule, {\n  valid: [\n    {\n      code: '(async function() { const data = await read(); console.log(data) })()',\n      parserOptions: {ecmaVersion: 2017},\n    },\n    {\n      code: '(async function() { try { await read() } catch(error) { console.error(error) } })()',\n      parserOptions: {ecmaVersion: 2017},\n    },\n  ],\n  invalid: [\n    {\n      code: '(function() { read().then(data => console.log(data)) })()',\n      parserOptions: {ecmaVersion: 2017},\n      errors: [\n        {\n          message: 'Prefer async/await to Promise.then()',\n          type: 'Identifier',\n        },\n      ],\n    },\n    {\n      code: '(function() { read().catch(error => console.error(error)) })()',\n      parserOptions: {ecmaVersion: 2017},\n      errors: [\n        {\n          message: 'Prefer async/await to Promise.catch()',\n          type: 'Identifier',\n        },\n      ],\n    },\n  ],\n})\n"
  },
  {
    "path": "tests/no-useless-passive.js",
    "content": "import rule from '../lib/rules/no-useless-passive.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n\nruleTester.run('no-useless-passive', rule, {\n  valid: [\n    {\n      code: 'document.addEventListener(\"scroll\", function(event) {})',\n    },\n    {\n      code: 'document.addEventListener(\"resize\", function(event) {})',\n    },\n    {\n      code: 'document.addEventListener(\"resize\", function(event) {}, { passive: false })',\n    },\n  ],\n  invalid: [\n    {\n      code: 'document.addEventListener(\"scroll\", function(event) {}, { passive: true })',\n      output: 'document.addEventListener(\"scroll\", function(event) {} )',\n      errors: [\n        {\n          message: '\"scroll\" event listener is not cancellable and so `passive: true` does nothing.',\n          type: 'Property',\n        },\n      ],\n    },\n    {\n      code: 'document.addEventListener(\"scroll\", function(event) {}, { passive: true, foo: 1 })',\n      output: 'document.addEventListener(\"scroll\", function(event) {}, {  foo: 1 })',\n      errors: [\n        {\n          message: '\"scroll\" event listener is not cancellable and so `passive: true` does nothing.',\n          type: 'Property',\n        },\n      ],\n    },\n  ],\n})\n"
  },
  {
    "path": "tests/prefer-observers.js",
    "content": "import rule from '../lib/rules/prefer-observers.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n\nruleTester.run('prefer-observers', rule, {\n  valid: [\n    {\n      code: 'document.addEventListener(\"touchstart\", function(event) {})',\n    },\n  ],\n  invalid: [\n    {\n      code: 'document.addEventListener(\"scroll\", function(event) {})',\n      errors: [\n        {\n          message: 'Avoid using \"scroll\" event listener. Consider using IntersectionObserver instead',\n          type: 'CallExpression',\n        },\n      ],\n    },\n    {\n      code: 'document.addEventListener(\"resize\", function(event) {})',\n      errors: [\n        {\n          message: 'Avoid using \"resize\" event listener. Consider using ResizeObserver instead',\n          type: 'CallExpression',\n        },\n      ],\n    },\n  ],\n})\n"
  },
  {
    "path": "tests/require-passive-events.js",
    "content": "import rule from '../lib/rules/require-passive-events.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n\nruleTester.run('require-passive-events', rule, {\n  valid: [\n    {\n      code: 'document.addEventListener(\"load\", function(event) {})',\n    },\n    {\n      code: 'document.addEventListener(\"click\", function(event) {})',\n    },\n    {\n      code: 'document.addEventListener(\"touchstart\", function(event) {}, { passive: true })',\n    },\n    {\n      code: 'el.addEventListener(\"touchstart\", function(event) {}, { passive: true })',\n    },\n  ],\n  invalid: [\n    {\n      code: 'document.addEventListener(\"touchstart\", function(event) {})',\n      errors: [\n        {\n          message: 'High Frequency Events like \"touchstart\" should be `passive: true`',\n          type: 'CallExpression',\n        },\n      ],\n    },\n    {\n      code: 'el.addEventListener(\"wheel\", function(event) {})',\n      errors: [\n        {\n          message: 'High Frequency Events like \"wheel\" should be `passive: true`',\n          type: 'CallExpression',\n        },\n      ],\n    },\n    {\n      code: 'document.addEventListener(\"wheel\", function(event) {})',\n      errors: [\n        {\n          message: 'High Frequency Events like \"wheel\" should be `passive: true`',\n          type: 'CallExpression',\n        },\n      ],\n    },\n    {\n      // Intentionally misspelled!\n      code: 'document.addEventListener(\"wheel\", function(event) {}, { pssive: true })',\n      errors: [\n        {\n          message: 'High Frequency Events like \"wheel\" should be `passive: true`',\n          type: 'CallExpression',\n        },\n      ],\n    },\n    {\n      code: 'document.addEventListener(\"wheel\", function(event) {}, { passive: false })',\n      errors: [\n        {\n          message: 'High Frequency Events like \"wheel\" should be `passive: true`',\n          type: 'CallExpression',\n        },\n      ],\n    },\n  ],\n})\n"
  },
  {
    "path": "tests/unescaped-html-literal.js",
    "content": "import rule from '../lib/rules/unescaped-html-literal.js'\nimport {RuleTester} from 'eslint'\n\nconst ruleTester = new RuleTester()\n\nruleTester.run('unescaped-html-literal', rule, {\n  valid: [\n    {\n      code: '`Hello World!`;',\n      parserOptions: {ecmaVersion: 2017},\n    },\n    {\n      code: \"'Hello World!'\",\n      parserOptions: {ecmaVersion: 2017},\n    },\n    {\n      code: '\"Hello World!\"',\n      parserOptions: {ecmaVersion: 2017},\n    },\n    {\n      code: 'const helloTemplate = () => html`<div>Hello World!</div>`;',\n      parserOptions: {ecmaVersion: 2017},\n    },\n    {\n      code: 'const helloTemplate = (name) => html`<div>Hello ${name}!</div>`;',\n      parserOptions: {ecmaVersion: 2017},\n    },\n  ],\n  invalid: [\n    {\n      code: \"const helloHTML = '<div>Hello, World!</div>'\",\n      parserOptions: {ecmaVersion: 2017},\n      errors: [\n        {\n          message: 'Unescaped HTML literal. Use html`` tag template literal for secure escaping.',\n          type: 'Literal',\n        },\n      ],\n    },\n    {\n      code: 'const helloHTML = \"<h1>Hello, World!</h1>\"',\n      parserOptions: {ecmaVersion: 2017},\n      errors: [\n        {\n          message: 'Unescaped HTML literal. Use html`` tag template literal for secure escaping.',\n          type: 'Literal',\n        },\n      ],\n    },\n    {\n      code: 'const helloHTML = `<div>Hello ${name}!</div>`',\n      parserOptions: {ecmaVersion: 2017},\n      errors: [\n        {\n          message: 'Unescaped HTML literal. Use html`` tag template literal for secure escaping.',\n          type: 'TemplateLiteral',\n        },\n      ],\n    },\n    {\n      code: 'const helloHTML = ` \\n\\t<div>Hello ${name}!</div>`',\n      parserOptions: {ecmaVersion: 2017},\n      errors: [\n        {\n          message: 'Unescaped HTML literal. Use html`` tag template literal for secure escaping.',\n          type: 'TemplateLiteral',\n        },\n      ],\n    },\n    {\n      code: 'const helloHTML = foo`<div>Hello ${name}!</div>`',\n      parserOptions: {ecmaVersion: 2017},\n      errors: [\n        {\n          message: 'Unescaped HTML literal. Use html`` tag template literal for secure escaping.',\n          type: 'TemplateLiteral',\n        },\n      ],\n    },\n  ],\n})\n"
  },
  {
    "path": "tests/utils/get-element-type.mjs",
    "content": "import {expect} from 'chai'\nimport {getElementType} from '../../lib/utils/get-element-type.js'\nimport {mockJSXAttribute, mockJSXConditionalAttribute, mockJSXOpeningElement} from './mocks.js'\nimport {describe, it} from 'mocha'\n\nfunction mockSetting(componentSetting = {}) {\n  return {\n    settings: {\n      github: {\n        components: componentSetting,\n        polymorphicPropName: 'as',\n      },\n    },\n  }\n}\n\ndescribe('getElementType', function () {\n  it('returns raw element type', function () {\n    const node = mockJSXOpeningElement('a')\n    expect(getElementType({}, node)).to.equal('a')\n  })\n\n  it('returns polymorphic element type', function () {\n    const node = mockJSXOpeningElement('Link', [mockJSXAttribute('as', 'button')])\n    const setting = mockSetting({\n      Link: 'a',\n    })\n    expect(getElementType(setting, node)).to.equal('button')\n  })\n\n  it('returns raw type if no default or matching prop setting', function () {\n    const setting = mockSetting({})\n\n    const node = mockJSXOpeningElement('Link')\n    expect(getElementType(setting, node)).to.equal('Link')\n  })\n\n  it('returns default type if no polymorphic prop is passed in', function () {\n    const setting = mockSetting({\n      Link: 'a',\n    })\n    const node = mockJSXOpeningElement('Link')\n    expect(getElementType(setting, node)).to.equal('a')\n  })\n\n  it('if rendered as another component check its default type', function () {\n    const setting = mockSetting({\n      Link: 'a',\n      Button: 'button',\n    })\n\n    const node = mockJSXOpeningElement('Link', [mockJSXAttribute('as', 'Button')])\n    expect(getElementType(setting, node)).to.equal('button')\n  })\n\n  it('returns raw type when polymorphic prop is set to non-literal expression', function () {\n    // <Box as={isNavigationOpen ? 'generic' : 'navigation'} />\n    const node = mockJSXOpeningElement('Box', [\n      mockJSXConditionalAttribute('as', 'isNavigationOpen', 'generic', 'navigation'),\n    ])\n    expect(getElementType({}, node)).to.equal('Box')\n  })\n\n  it('returns raw type when polymorphic prop is set to non-literal expression even with component setting', function () {\n    const setting = mockSetting({\n      Box: 'div',\n    })\n    const node = mockJSXOpeningElement('Box', [\n      mockJSXConditionalAttribute('as', 'isNavigationOpen', 'generic', 'navigation'),\n    ])\n    expect(getElementType(setting, node)).to.equal('Box')\n  })\n})\n"
  },
  {
    "path": "tests/utils/get-role.mjs",
    "content": "import {expect} from 'chai'\nimport {getRole} from '../../lib/utils/get-role.js'\nimport {mockJSXAttribute, mockJSXConditionalAttribute, mockJSXOpeningElement} from './mocks.js'\nimport {describe, it} from 'mocha'\n\ndescribe('getRole', function () {\n  it('returns undefined when polymorphic prop is set with a non-literal expression', function () {\n    // <Box as={isNavigationOpen ? 'div' : 'nav'} />\n    const node = mockJSXOpeningElement('Box', [mockJSXConditionalAttribute('as', 'isNavigationOpen', 'div', 'nav')])\n    expect(getRole({}, node)).to.equal(undefined)\n  })\n\n  it('returns undefined when role is set to non-literal expression', function () {\n    // <div role={isNavigationOpen ? 'generic' : 'navigation'} />\n    const node = mockJSXOpeningElement('div', [\n      mockJSXConditionalAttribute('role', 'isNavigationOpen', 'generic', 'navigation'),\n    ])\n    expect(getRole({}, node)).to.equal(undefined)\n  })\n\n  it('returns `role` when set to a literal expression', function () {\n    // <Box role=\"generic\" />\n    const node = mockJSXOpeningElement('Box', [mockJSXAttribute('role', 'generic')])\n    expect(getRole({}, node)).to.equal('generic')\n  })\n\n  it('returns generic role for <span> regardless of attribute', function () {\n    const node = mockJSXOpeningElement('span', [mockJSXAttribute('aria-label', 'something')])\n    expect(getRole({}, node)).to.equal('generic')\n  })\n\n  it('returns generic role for <div> regardless of attribute', function () {\n    const node = mockJSXOpeningElement('div', [mockJSXAttribute('aria-describedby', 'something')])\n    expect(getRole({}, node)).to.equal('generic')\n  })\n\n  it('returns generic role for <a> without href', function () {\n    const node = mockJSXOpeningElement('a')\n    expect(getRole({}, node)).to.equal('generic')\n  })\n\n  it('returns link role for <a> with href', function () {\n    const node = mockJSXOpeningElement('a', [mockJSXAttribute('href', '#')])\n    expect(getRole({}, node)).to.equal('link')\n  })\n\n  it('returns link role for <Foo> with polymorphic prop set to \"a\" and conditional href', function () {\n    const node = mockJSXOpeningElement('Foo', [\n      mockJSXAttribute('as', 'a'),\n      mockJSXConditionalAttribute('href', 'getUrl', '#', 'https://github.com/'),\n    ])\n    expect(getRole({}, node)).to.equal('link')\n  })\n\n  it('returns link role for <Foo> with polymorphic prop set to \"a\" and literal href', function () {\n    const node = mockJSXOpeningElement('Foo', [\n      mockJSXAttribute('as', 'a'),\n      mockJSXAttribute('href', 'https://github.com/'),\n    ])\n    expect(getRole({}, node)).to.equal('link')\n  })\n\n  it('returns generic role for <Foo> with polymorphic prop set to \"a\" and no href', function () {\n    const node = mockJSXOpeningElement('Foo', [mockJSXAttribute('as', 'a')])\n    expect(getRole({}, node)).to.equal('generic')\n  })\n\n  it('returns region role for <section> with aria-label', function () {\n    const node = mockJSXOpeningElement('section', [mockJSXAttribute('aria-label', 'something')])\n    expect(getRole({}, node)).to.equal('region')\n  })\n\n  it('returns region role for <section> with aria-labelledby', function () {\n    const node = mockJSXOpeningElement('section', [mockJSXAttribute('aria-labelledby', 'something')])\n    expect(getRole({}, node)).to.equal('region')\n  })\n\n  it('returns complementary role for <aside> with aria-label', function () {\n    const node = mockJSXOpeningElement('aside', [mockJSXAttribute('aria-label', 'something')])\n    expect(getRole({}, node)).to.equal('complementary')\n  })\n\n  it('returns complementary role for <aside> with aria-labelledby', function () {\n    const node = mockJSXOpeningElement('aside', [mockJSXAttribute('aria-labelledby', 'something')])\n    expect(getRole({}, node)).to.equal('complementary')\n  })\n\n  it('returns img role for <img> with alt set explicitly', function () {\n    const node = mockJSXOpeningElement('img', [mockJSXAttribute('alt', 'Cute cat')])\n    expect(getRole({}, node)).to.equal('img')\n  })\n\n  it('returns img role for <img> with no alt', function () {\n    const node = mockJSXOpeningElement('img')\n    expect(getRole({}, node)).to.equal('img')\n  })\n\n  it('returns presentation role for <img> with alt set to empty', function () {\n    const node = mockJSXOpeningElement('img', [mockJSXAttribute('alt', '')])\n    expect(getRole({}, node)).to.equal('presentation')\n  })\n\n  it('returns form role for <form> with aria-label', function () {\n    const node = mockJSXOpeningElement('form', [mockJSXAttribute('aria-label', 'registration')])\n    expect(getRole({}, node)).to.equal('form')\n  })\n\n  it('returns form role for <form> with name attrribute', function () {\n    const node = mockJSXOpeningElement('form', [mockJSXAttribute('name', 'registration')])\n    expect(getRole({}, node)).to.equal('form')\n  })\n\n  it('returns undefined role for <form> with no attributes', function () {\n    const node = mockJSXOpeningElement('form')\n    expect(getRole({}, node)).to.equal(undefined)\n  })\n\n  it('returns explicitly set role', function () {\n    const spanButton = mockJSXOpeningElement('span', [mockJSXAttribute('role', 'button')])\n    expect(getRole({}, spanButton)).to.equal('button')\n\n    const divNav = mockJSXOpeningElement('div', [mockJSXAttribute('role', 'navigation')])\n    expect(getRole({}, divNav)).to.equal('navigation')\n\n    const listMenu = mockJSXOpeningElement('ul', [mockJSXAttribute('role', 'menu')])\n    expect(getRole({}, listMenu)).to.equal('menu')\n  })\n\n  it('returns heading role for heading tags', function () {\n    const h1 = mockJSXOpeningElement('h1')\n    expect(getRole({}, h1)).to.equal('heading')\n\n    const h2 = mockJSXOpeningElement('h2')\n    expect(getRole({}, h2)).to.equal('heading')\n\n    const h3 = mockJSXOpeningElement('h3')\n    expect(getRole({}, h3)).to.equal('heading')\n\n    const h4 = mockJSXOpeningElement('h4')\n    expect(getRole({}, h4)).to.equal('heading')\n\n    const h5 = mockJSXOpeningElement('h5')\n    expect(getRole({}, h5)).to.equal('heading')\n\n    const h6 = mockJSXOpeningElement('h6')\n    expect(getRole({}, h6)).to.equal('heading')\n  })\n\n  it('returns navigation role for <nav>', function () {\n    const node = mockJSXOpeningElement('nav')\n    expect(getRole({}, node)).to.equal('navigation')\n  })\n\n  it('returns option role for <opt>', function () {\n    const node = mockJSXOpeningElement('option')\n    expect(getRole({}, node)).to.equal('option')\n  })\n\n  it('returns textbox role for <textarea>', function () {\n    const node = mockJSXOpeningElement('textarea')\n    expect(getRole({}, node)).to.equal('textbox')\n  })\n\n  it('returns listbox role for <select>', function () {\n    const node = mockJSXOpeningElement('textarea')\n    expect(getRole({}, node)).to.equal('textbox')\n  })\n\n  it('returns group role for <details>', function () {\n    const node = mockJSXOpeningElement('details')\n    expect(getRole({}, node)).to.equal('group')\n  })\n\n  it('returns group role for <details>', function () {\n    const node = mockJSXOpeningElement('details')\n    expect(getRole({}, node)).to.equal('group')\n  })\n\n  // <input>\n  it('returns slider role for <input> with type range', function () {\n    const node = mockJSXOpeningElement('input', [mockJSXAttribute('type', 'range')])\n    expect(getRole({}, node)).to.equal('slider')\n  })\n\n  it('returns spinbutton for <input> with type number', function () {\n    const node = mockJSXOpeningElement('input', [mockJSXAttribute('type', 'number')])\n    expect(getRole({}, node)).to.equal('spinbutton')\n  })\n\n  it('returns checkbox for <input> with type checkbox', function () {\n    const node = mockJSXOpeningElement('input', [mockJSXAttribute('type', 'checkbox')])\n    expect(getRole({}, node)).to.equal('checkbox')\n  })\n\n  it('returns button for <input> with type button, image, reset, submit', function () {\n    const button = mockJSXOpeningElement('input', [mockJSXAttribute('type', 'button')])\n    expect(getRole({}, button)).to.equal('button')\n\n    const image = mockJSXOpeningElement('input', [mockJSXAttribute('type', 'image')])\n    expect(getRole({}, image)).to.equal('button')\n\n    const reset = mockJSXOpeningElement('input', [mockJSXAttribute('type', 'reset')])\n    expect(getRole({}, reset)).to.equal('button')\n\n    const submit = mockJSXOpeningElement('input', [mockJSXAttribute('type', 'submit')])\n    expect(getRole({}, submit)).to.equal('button')\n  })\n\n  it('returns rowheader role for <th scope=\"row\">', function () {\n    const node = mockJSXOpeningElement('th', [mockJSXAttribute('scope', 'row')])\n    expect(getRole({}, node)).to.equal('rowheader')\n  })\n\n  it('returns rowheader role for <th scope=\"rowgroup\">', function () {\n    const node = mockJSXOpeningElement('th', [mockJSXAttribute('scope', 'rowgroup')])\n    expect(getRole({}, node)).to.equal('rowheader')\n  })\n\n  // Hard-coded mapping\n  it('returns listitem role for <li>', function () {\n    const node = mockJSXOpeningElement('li')\n    expect(getRole({}, node)).to.equal('listitem')\n  })\n\n  it('returns complementary role for <aside>', function () {\n    const node = mockJSXOpeningElement('aside')\n    expect(getRole({}, node)).to.equal('complementary')\n  })\n\n  // <link> does not map to anything.\n  it('returns undefined role for <link>', function () {\n    const node = mockJSXOpeningElement('link')\n    expect(getRole({}, node)).to.equal(undefined)\n  })\n\n  it('returns undefined role for <link href=\"#\">', function () {\n    const node = mockJSXOpeningElement('link', [mockJSXAttribute('href', '#')])\n    expect(getRole({}, node)).to.equal(undefined)\n  })\n})\n"
  },
  {
    "path": "tests/utils/mocks.js",
    "content": "export function mockJSXAttribute(prop, propValue) {\n  return {\n    type: 'JSXAttribute',\n    name: {\n      type: 'JSXIdentifier',\n      name: prop,\n    },\n    value: {\n      type: 'Literal',\n      value: propValue,\n    },\n  }\n}\n\n/* Mocks conditional expression\n  e.g. <Box as={isNavigationOpen ? 'generic' : 'navigation'} /> can be by calling\n  mockJSXConditionalAttribute('as', 'isNavigationOpen', 'generic', 'navigation')\n*/\nexport function mockJSXConditionalAttribute(prop, expression, consequentValue, alternateValue) {\n  return {\n    type: 'JSXAttribute',\n    name: {\n      type: 'JSXIdentifier',\n      name: prop,\n    },\n    value: {\n      type: 'JSXExpressionContainer',\n      value: prop,\n      expression: {\n        type: 'ConditionalExpression',\n        test: {\n          type: expression,\n        },\n        consequent: {\n          type: 'Literal',\n          value: consequentValue,\n        },\n        alternate: {\n          type: 'Literal',\n          value: alternateValue,\n        },\n      },\n    },\n  }\n}\n\nexport function mockJSXOpeningElement(tagName, attributes = []) {\n  return {\n    type: 'JSXOpeningElement',\n    name: {\n      type: 'JSXIdentifier',\n      name: tagName,\n    },\n    attributes,\n  }\n}\n"
  },
  {
    "path": "tests/utils/object-map.mjs",
    "content": "// @ts-check\nimport {expect} from 'chai'\nimport ObjectMap from '../../lib/utils/object-map.js'\nimport {describe, it} from 'mocha'\n\ndescribe('ObjectMap', function () {\n  it('constructs an ObjectMap', function () {\n    const objectMap = new ObjectMap()\n    expect(objectMap instanceof ObjectMap).to.be.true\n  })\n\n  it('extends Map', function () {\n    const objectMap = new ObjectMap()\n    expect(objectMap instanceof Map).to.be.true\n  })\n\n  it('populates data from constructor argument', function () {\n    const iterable = [[{name: 'form'}, ['form']]]\n    const objectMap = new ObjectMap(iterable)\n    expect(Array.from(objectMap.entries())).to.deep.equal(iterable)\n  })\n\n  it('clears data', function () {\n    const iterable = [[{name: 'form'}, ['form']]]\n    const objectMap = new ObjectMap(iterable)\n    objectMap.clear()\n    expect(Array.from(objectMap.entries())).to.be.empty\n  })\n\n  it('deletes data that exists', function () {\n    const iterable = [[{name: 'form'}, ['form']]]\n    const objectMap = new ObjectMap(iterable)\n    const returnValue = objectMap.delete({name: 'form'})\n    expect(Array.from(objectMap.entries())).to.be.empty\n    expect(returnValue).to.be.true\n  })\n\n  it('doesn’t delete data that doesn’t exist', function () {\n    const iterable = [[{name: 'form'}, ['form']]]\n    const objectMap = new ObjectMap(iterable)\n    const returnValue = objectMap.delete({name: ''})\n    expect(Array.from(objectMap.entries())).to.deep.equal(iterable)\n    expect(returnValue).to.be.false\n  })\n\n  it('lists entries that exist', function () {\n    const iterable = [\n      [{name: 'form'}, ['form']],\n      [{name: 'span'}, ['generic']],\n    ]\n    const objectMap = new ObjectMap(iterable)\n    const iterator = objectMap.entries()\n    expect(typeof iterator[Symbol.iterator]).to.equal('function')\n    expect(iterator.next()).to.deep.equal({value: iterable[0], done: false})\n    expect(iterator.next()).to.deep.equal({value: iterable[1], done: false})\n    expect(iterator.next()).to.deep.equal({value: undefined, done: true})\n  })\n\n  it('doesn’t list entries that don’t exist', function () {\n    const objectMap = new ObjectMap()\n    const iterator = objectMap.entries()\n    expect(typeof iterator[Symbol.iterator]).to.equal('function')\n    expect(iterator.next()).to.deep.equal({value: undefined, done: true})\n  })\n\n  it('finds key that exists', function () {\n    const iterable = [[{name: 'form'}, ['form']]]\n    const objectMap = new ObjectMap(iterable)\n    expect(objectMap.has({name: 'form'})).to.be.true\n  })\n\n  it('finds key that exists in a different order', function () {\n    const iterable = [[{name: 'form', attributes: [{name: 'action', value: 'POST'}]}, ['form']]]\n    const objectMap = new ObjectMap(iterable)\n    expect(objectMap.has({attributes: [{value: 'POST', name: 'action'}], name: 'form'})).to.be.true\n  })\n\n  it('doesn’t find key that doesn’t exist', function () {\n    const iterable = [[{name: 'form'}, ['form']]]\n    const objectMap = new ObjectMap(iterable)\n    expect(objectMap.has({name: ''})).to.be.false\n  })\n\n  it('doesn’t find key when none exist', function () {\n    const objectMap = new ObjectMap()\n    expect(objectMap.has({name: ''})).to.be.false\n  })\n\n  it('calls callback for each datum', function () {\n    const iterable = [\n      [{name: 'form'}, ['form']],\n      [{name: 'span'}, ['generic']],\n    ]\n    const objectMap = new ObjectMap(iterable)\n    const values = []\n    const keys = []\n    // eslint-disable-next-line github/array-foreach\n    objectMap.forEach((value, key) => {\n      values.push(value)\n      keys.push(key)\n    })\n    expect(values).to.deep.equal([['form'], ['generic']])\n    expect(keys).to.deep.equal([{name: 'form'}, {name: 'span'}])\n  })\n\n  it('finds value given key that exists', function () {\n    const iterable = [[{name: 'form'}, ['form']]]\n    const objectMap = new ObjectMap(iterable)\n    expect(objectMap.get({name: 'form'})).to.deep.equal(['form'])\n  })\n\n  it('finds value given key that exists in a different order', function () {\n    const iterable = [[{name: 'form', attributes: [{name: 'action', value: 'POST'}]}, ['form']]]\n    const objectMap = new ObjectMap(iterable)\n    expect(objectMap.get({attributes: [{value: 'POST', name: 'action'}], name: 'form'})).to.deep.equal(['form'])\n  })\n\n  it('doesn’t find value given key that doesn’t exist', function () {\n    const iterable = [[{name: 'form'}, ['form']]]\n    const objectMap = new ObjectMap(iterable)\n    expect(objectMap.get({name: ''})).to.be.undefined\n  })\n\n  it('doesn’t find value when none exist', function () {\n    const objectMap = new ObjectMap()\n    expect(objectMap.get({name: ''})).to.be.undefined\n  })\n\n  it('lists keys', function () {\n    const iterable = [\n      [{name: 'form'}, ['form']],\n      [{name: 'span'}, ['generic']],\n    ]\n    const objectMap = new ObjectMap(iterable)\n    const iterator = objectMap.keys()\n    expect(typeof iterator[Symbol.iterator]).to.equal('function')\n    expect(iterator.next()).to.deep.equal({value: iterable[0][0], done: false})\n    expect(iterator.next()).to.deep.equal({value: iterable[1][0], done: false})\n    expect(iterator.next()).to.deep.equal({value: undefined, done: true})\n  })\n\n  it('sets value when none exist', function () {\n    const key = {name: 'form'}\n    const value = ['form']\n    const objectMap = new ObjectMap()\n    objectMap.set(key, value)\n    expect(Array.from(objectMap.entries())).to.deep.equal([[key, value]])\n  })\n\n  it('sets value when some exist', function () {\n    const iterable = [[{name: 'form'}, ['form']]]\n    const key = {name: 'span'}\n    const value = ['generic']\n    const objectMap = new ObjectMap(iterable)\n    objectMap.set(key, value)\n    expect(Array.from(objectMap.entries())).to.deep.equal([iterable[0], [key, value]])\n  })\n\n  it('replaces value under existing key', function () {\n    const iterable = [[{name: 'form'}, ['form']]]\n    const key = {name: 'form'}\n    const value = ['generic']\n    const objectMap = new ObjectMap(iterable)\n    objectMap.set(key, value)\n    expect(Array.from(objectMap.entries())).to.deep.equal([[key, value]])\n  })\n\n  it('lists values', function () {\n    const iterable = [\n      [{name: 'form'}, ['form']],\n      [{name: 'span'}, ['generic']],\n    ]\n    const objectMap = new ObjectMap(iterable)\n    const iterator = objectMap.values()\n    expect(typeof iterator[Symbol.iterator]).to.equal('function')\n    expect(iterator.next()).to.deep.equal({value: iterable[0][1], done: false})\n    expect(iterator.next()).to.deep.equal({value: iterable[1][1], done: false})\n    expect(iterator.next()).to.deep.equal({value: undefined, done: true})\n  })\n})\n"
  }
]