[
  {
    "path": ".gitattributes",
    "content": "# Enforce Unix newlines\n* text=auto eol=lf\n\nbenchmark/documents/* binary\nbenchmark/jquery*.js binary\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: [cheeriojs, fb55]\nopen_collective: cheerio\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: npm\n    directory: '/'\n    schedule:\n      interval: daily\n    open-pull-requests-limit: 10\n    versioning-strategy: increase\n  - package-ecosystem: npm\n    directory: '/website'\n    schedule:\n      interval: daily\n    open-pull-requests-limit: 4\n    versioning-strategy: increase\n  - package-ecosystem: 'github-actions'\n    directory: '/'\n    schedule:\n      interval: daily\n"
  },
  {
    "path": ".github/issue_template.md",
    "content": "<!-- Thanks for your interest in cheerio!\n\nPlease note that issues should be primarily used for tracking bugs and feature requests.\nIf you have a more general question, please consider consulting StackOverflow first:\nhttps://stackoverflow.com/questions/tagged/cheerio\n\nIf you think you uncovered a bug, please try to provide a minimal example that triggers the behavior.\nPlease note that we will not investigate issues that perform HTTP requests, as the source might already have changed.\n-->\n"
  },
  {
    "path": ".github/workflows/benchmark.yml",
    "content": "name: Benchmark\n\non:\n  push:\n    branches-ignore:\n      - 'dependabot/**'\n  pull_request:\n\nenv:\n  FORCE_COLOR: 2\n\npermissions:\n  contents: read\n\njobs:\n  benchmark:\n    runs-on: ubuntu-latest\n    if:\n      \"!contains(github.event.commits[0].message, '[bench skip]') &&\n      !contains(github.event.commits[0].message, '[skip bench]')\"\n\n    steps:\n      - name: Clone repository\n        uses: actions/checkout@v6\n\n      - name: Set up Node.js\n        uses: actions/setup-node@v6.3.0\n        with:\n          node-version: lts/*\n          cache: 'npm'\n\n      - name: Install npm dependencies\n        run: npm ci\n\n      - name: Run benchmarks\n        run: npm run benchmark\n        env:\n          BENCHMARK: true\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  push:\n    branches-ignore:\n      - 'dependabot/**'\n  pull_request:\n\nenv:\n  FORCE_COLOR: 2\n  NODE_COV: lts/* # The Node.js version to run coveralls on\n\npermissions:\n  contents: read\n\njobs:\n  run:\n    permissions:\n      checks: write # for coverallsapp/github-action to create new checks\n      contents: read # for actions/checkout to fetch code\n    name: Node ${{ matrix.node }}\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix:\n        node:\n          - 20\n          - 22\n          - 24\n          - lts/*\n\n    steps:\n      - name: Clone repository\n        uses: actions/checkout@v6\n\n      - name: Set up Node.js\n        uses: actions/setup-node@v6.3.0\n        with:\n          node-version: '${{ matrix.node }}'\n          cache: 'npm'\n\n      - name: Install npm dependencies\n        run: npm ci\n\n      - name: Run tests\n        run: npm run test:vi\n        if: matrix.node != env.NODE_COV\n\n      - name: Run tests with coverage\n        run: npm run test:vi -- --coverage\n        if: matrix.node == env.NODE_COV\n\n      - name: Run Coveralls\n        uses: coverallsapp/github-action@v2.3.7\n        if: matrix.node == env.NODE_COV\n        continue-on-error: true\n        with:\n          github-token: '${{ secrets.GITHUB_TOKEN }}'\n"
  },
  {
    "path": ".github/workflows/codeql.yml",
    "content": "name: 'CodeQL'\n\non:\n  push:\n    branches:\n      - main\n      - '!dependabot/**'\n  pull_request:\n    # The branches below must be a subset of the branches above\n    branches:\n      - main\n      - '!dependabot/**'\n  schedule:\n    - cron: '0 0 * * 0'\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n    permissions:\n      actions: read\n      contents: read\n      security-events: write\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v6\n\n      - name: Initialize CodeQL\n        uses: github/codeql-action/init@v4\n        with:\n          languages: 'javascript'\n\n      - name: Perform CodeQL Analysis\n        uses: github/codeql-action/analyze@v4\n"
  },
  {
    "path": ".github/workflows/dependabot-automerge.yml",
    "content": "# Based on https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/automating-dependabot-with-github-actions#enable-auto-merge-on-a-pull-request\nname: Dependabot auto-merge\non: pull_request_target\n\npermissions:\n  pull-requests: write\n  contents: write\n\njobs:\n  dependabot:\n    runs-on: ubuntu-latest\n    if: ${{ github.actor == 'dependabot[bot]' }}\n    steps:\n      - name: Dependabot metadata\n        id: metadata\n        uses: dependabot/fetch-metadata@v2.5.0\n        with:\n          github-token: '${{ secrets.GITHUB_TOKEN }}'\n      - name: Enable auto-merge for Dependabot PRs\n        # Automatically merge semver-patch and semver-minor PRs\n        if:\n          \"${{ steps.metadata.outputs.update-type ==\n          'version-update:semver-minor' || steps.metadata.outputs.update-type ==\n          'version-update:semver-patch' }}\"\n        run: gh pr merge --auto --squash \"$PR_URL\"\n        env:\n          PR_URL: ${{github.event.pull_request.html_url}}\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n"
  },
  {
    "path": ".github/workflows/lint.yml",
    "content": "name: Lint\n\non:\n  push:\n    branches-ignore:\n      - 'dependabot/**'\n  pull_request:\n\nenv:\n  FORCE_COLOR: 2\n\npermissions:\n  contents: read\n\njobs:\n  lint:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Clone repository\n        uses: actions/checkout@v6\n\n      - name: Set up Node.js\n        uses: actions/setup-node@v6.3.0\n        with:\n          node-version: lts/*\n          cache: 'npm'\n\n      - name: Install npm dependencies\n        run: npm ci\n\n      - name: Install website npm dependencies\n        run: npm ci\n        working-directory: website\n\n      - name: Build Cheerio\n        run: npm run build\n\n      - name: Undo changes to package.json\n        run: git restore package.json\n\n      - name: Build website\n        run: npm run build\n        working-directory: website\n\n      - name: Run lint\n        run: npm run lint\n"
  },
  {
    "path": ".github/workflows/site.yml",
    "content": "name: Deploy website to GitHub Pages\n\n# Based on https://raw.githubusercontent.com/actions/starter-workflows\n\non:\n  # Runs on pushes targeting the main branch\n  push:\n    branches: [main]\n\n  # Allows you to run this workflow manually from the Actions tab\n  workflow_dispatch:\n\nenv:\n  FORCE_COLOR: 2\n\n# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages\npermissions:\n  contents: read\n  pages: write\n  id-token: write\n\n# Allow one concurrent deployment\nconcurrency:\n  group: 'pages'\n  cancel-in-progress: true\n\njobs:\n  # Build job\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n      - name: Setup Node\n        uses: actions/setup-node@v6.3.0\n        with:\n          # Use current Node LTS version\n          node-version: lts/*\n          cache: 'npm'\n      - name: Setup Pages\n        id: pages\n        uses: actions/configure-pages@v5\n      - name: Install dependencies\n        run: npm ci\n      - name: Build\n        run: npm run build\n      - name: Install website dependencies\n        working-directory: website\n        run: npm ci\n      - name: Build docs\n        working-directory: website\n        run: npm run build\n      - name: Upload artifact\n        uses: actions/upload-pages-artifact@v4\n        with:\n          path: ./website/dist\n\n  # Deployment job\n  deploy:\n    environment:\n      name: github-pages\n      url: ${{ steps.deployment.outputs.page_url }}\n    runs-on: ubuntu-latest\n    needs: build\n    if: ${{github.repository == 'cheeriojs/cheerio'}}\n    steps:\n      - name: Deploy to GitHub Pages\n        id: deployment\n        uses: actions/deploy-pages@v4\n"
  },
  {
    "path": ".github/workflows/sponsors.yml",
    "content": "name: Update Sponsors\n\non:\n  schedule:\n    # Run once a day, at 4pm\n    - cron: '0 16 * * *'\n  # Allow manual trigger\n  workflow_dispatch:\n\nenv:\n  FORCE_COLOR: 2\n\npermissions:\n  contents: read\n\njobs:\n  fetch:\n    permissions:\n      contents: write # for peter-evans/create-pull-request to create branch\n      pull-requests: write # for peter-evans/create-pull-request to create a PR\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Clone repository\n        uses: actions/checkout@v6\n\n      - name: Set up Node.js\n        uses: actions/setup-node@v6.3.0\n        with:\n          node-version: lts/*\n          cache: 'npm'\n\n      - name: Install npm dependencies\n        run: npm ci\n\n      - name: Update the README\n        run: npm run update-sponsors\n        env:\n          CHEERIO_SPONSORS_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          IMGIX_TOKEN: ${{ secrets.IMGIX_TOKEN }}\n\n      - name: Create Pull Request\n        uses: peter-evans/create-pull-request@v8\n        continue-on-error: true\n        with:\n          commit-message: 'docs(readme): Update Sponsors'\n          title: Update Sponsors\n          branch: docs/sponsors\n          delete-branch: true\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\nnpm-debug.log\n.DS_Store\n.docusaurus\n.cache-loader\n/coverage\n/.tshy\n/.tshy-build\n/dist\n\n# Website build artifacts\nwebsite/.astro/\nwebsite/dist/\nwebsite/src/content/docs/api\n"
  },
  {
    "path": ".husky/.gitignore",
    "content": "_\n"
  },
  {
    "path": ".husky/pre-commit",
    "content": "lint-staged"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Cheerio\n\nThanks for your interest in contributing to the project! Here's a rundown of how\nwe'd like to work with you:\n\n1.  File an issue on GitHub describing the contribution you'd like to make. This\n    will help us to get you started on the right foot.\n2.  Fork the project, and make your changes in a new branch based off of the\n    `main` branch:\n    1.  Follow the project's code style (see below)\n    2.  Add enough unit tests to \"prove\" that your patch is correct\n    3.  Update the project documentation as needed (see below)\n    4.  Describe your approach with as much detail as necessary in the git\n        commit message\n3.  Open a pull request, and reference the initial issue in the pull request\n    message.\n\n# Documentation\n\nAny API change should be reflected in the project's README.md file. Reuse\n[jQuery's documentation](https://api.jquery.com) wherever possible, but take\ncare to note aspects that make Cheerio distinct.\n\n# Code Style\n\nPlease make sure commit hooks are run, which will enforce the code style.\n\nWhen implementing private functionality that isn't part of the jQuery API,\nplease opt for:\n\n- _Static methods_: If the functionality does not require a reference to a\n  Cheerio instance, simply define a named function within the module it is\n  needed.\n- _Instance methods_: If the functionality requires a reference to a Cheerio\n  instance, informally define the method as \"private\" using the following\n  conventions:\n  - Define the method as a function on the Cheerio prototype\n  - Prefix the method name with an underscore (`_`) character\n  - Include `@api private` in the code comment the documents the method\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 The Cheerio contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Readme.md",
    "content": "<h1 align=\"center\">cheerio</h1>\n\n<h5 align=\"center\">The fast, flexible, and elegant library for parsing and manipulating HTML and XML.</h5>\n\n<div align=\"center\">\n  <a href=\"https://github.com/cheeriojs/cheerio/actions/workflows/ci.yml\">\n    <img src=\"https://github.com/cheeriojs/cheerio/actions/workflows/ci.yml/badge.svg\" alt=\"Build Status\">\n  </a>\n  <a href=\"https://coveralls.io/github/cheeriojs/cheerio\">\n    <img src=\"https://img.shields.io/coveralls/github/cheeriojs/cheerio/main\" alt=\"Coverage\">\n  </a>\n  <a href=\"#backers\">\n    <img src=\"https://img.shields.io/opencollective/backers/cheerio\" alt=\"OpenCollective backers\">\n  </a>\n  <a href=\"#sponsors\">\n    <img src=\"https://img.shields.io/opencollective/sponsors/cheerio\" alt=\"OpenCollective sponsors\">\n  </a>\n</div>\n\n<br>\n\n[中文文档 (Chinese Readme)](https://github.com/cheeriojs/cheerio/wiki/Chinese-README)\n\n```js\nimport * as cheerio from 'cheerio';\nconst $ = cheerio.load('<h2 class=\"title\">Hello world</h2>');\n\n$('h2.title').text('Hello there!');\n$('h2').addClass('welcome');\n\n$.html();\n//=> <html><head></head><body><h2 class=\"title welcome\">Hello there!</h2></body></html>\n```\n\n## Installation\n\nInstall Cheerio using a package manager like npm, yarn, or bun.\n\n```bash\nnpm install cheerio\n# or\nbun add cheerio\n```\n\n## Features\n\n**&#10084; Proven syntax:** Cheerio implements a subset of core jQuery. Cheerio\nremoves all the DOM inconsistencies and browser cruft from the jQuery library,\nrevealing its truly gorgeous API.\n\n**&#991; Blazingly fast:** Cheerio works with a very simple, consistent DOM\nmodel. As a result parsing, manipulating, and rendering are incredibly\nefficient.\n\n**&#10049; Incredibly flexible:** Cheerio wraps around\n[parse5](https://github.com/inikulin/parse5) for parsing HTML and can optionally\nuse the forgiving [htmlparser2](https://github.com/fb55/htmlparser2/). Cheerio\ncan parse nearly any HTML or XML document. Cheerio works in both browser and\nserver environments.\n\n## API\n\n### Loading\n\nFirst you need to load in the HTML. This step in jQuery is implicit, since\njQuery operates on the one, baked-in DOM. With Cheerio, we need to pass in the\nHTML document.\n\n```js\n// ESM or TypeScript:\nimport * as cheerio from 'cheerio';\n\n// In other environments:\nconst cheerio = require('cheerio');\n\nconst $ = cheerio.load('<ul id=\"fruits\">...</ul>');\n\n$.html();\n//=> <html><head></head><body><ul id=\"fruits\">...</ul></body></html>\n```\n\n### Selectors\n\nOnce you've loaded the HTML, you can use jQuery-style selectors to find elements\nwithin the document.\n\n#### \\$( selector, [context], [root] )\n\n`selector` searches within the `context` scope which searches within the `root`\nscope. `selector` and `context` can be a string expression, DOM Element, array\nof DOM elements, or cheerio object. `root`, if provided, is typically the HTML\ndocument string.\n\nThis selector method is the starting point for traversing and manipulating the\ndocument. Like in jQuery, it's the primary method for selecting elements in the\ndocument.\n\n```js\n$('.apple', '#fruits').text();\n//=> Apple\n\n$('ul .pear').attr('class');\n//=> pear\n\n$('li[class=orange]').html();\n//=> Orange\n```\n\n### Rendering\n\nWhen you're ready to render the document, you can call the `html` method on the\n\"root\" selection:\n\n```js\n$.root().html();\n//=>  <html>\n//      <head></head>\n//      <body>\n//        <ul id=\"fruits\">\n//          <li class=\"apple\">Apple</li>\n//          <li class=\"orange\">Orange</li>\n//          <li class=\"pear\">Pear</li>\n//        </ul>\n//      </body>\n//    </html>\n```\n\nIf you want to render the\n[`outerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/outerHTML)\nof a selection, you can use the `outerHTML` prop:\n\n```js\n$('.pear').prop('outerHTML');\n//=> <li class=\"pear\">Pear</li>\n```\n\nYou may also render the text content of a Cheerio object using the `text`\nmethod:\n\n```js\nconst $ = cheerio.load('This is <em>content</em>.');\n$('body').text();\n//=> This is content.\n```\n\n### The \"DOM Node\" object\n\nCheerio collections are made up of objects that bear some resemblance to\n[browser-based DOM nodes](https://developer.mozilla.org/en-US/docs/Web/API/Node).\nYou can expect them to define the following properties:\n\n- `tagName`\n- `parentNode`\n- `previousSibling`\n- `nextSibling`\n- `nodeValue`\n- `firstChild`\n- `childNodes`\n- `lastChild`\n\n## Screencasts\n\n[https://vimeo.com/31950192](https://vimeo.com/31950192)\n\n> This video tutorial is a follow-up to Nettut's \"How to Scrape Web Pages with\n> Node.js and jQuery\", using cheerio instead of JSDOM + jQuery. This video shows\n> how easy it is to use cheerio and how much faster cheerio is than JSDOM +\n> jQuery.\n\n## Cheerio in the real world\n\nAre you using cheerio in production? Add it to the\n[wiki](https://github.com/cheeriojs/cheerio/wiki/Cheerio-in-Production)!\n\n## Sponsors\n\nDoes your company use Cheerio in production? Please consider\n[sponsoring this project](https://github.com/cheeriojs/cheerio?sponsor=1)! Your\nhelp will allow maintainers to dedicate more time and resources to its\ndevelopment and support.\n\n**Headlining Sponsors**\n\n<!-- BEGIN SPONSORS: headliner -->\n\n<a href=\"https://github.com/\" target=\"_blank\" rel=\"noopener noreferrer\">\n            <img height=\"128px\" width=\"128px\" src=\"https://humble.imgix.net/https%3A%2F%2Fgithub.com%2Fgithub.png?ixlib=js-3.8.0&w=128&h=128&fit=fillmax&fill=solid&s=a1e87ca289de84eb32ea85432cf8ad11\" title=\"Github\" alt=\"Github\"></img>\n          </a>\n<a href=\"https://www.airbnb.com/\" target=\"_blank\" rel=\"noopener noreferrer\">\n            <img height=\"128px\" width=\"128px\" src=\"https://humble.imgix.net/https%3A%2F%2Fgithub.com%2Fairbnb.png?ixlib=js-3.8.0&w=128&h=128&fit=fillmax&fill=solid&s=384cad45e10faea516202ad10801f895\" title=\"AirBnB\" alt=\"AirBnB\"></img>\n          </a>\n<a href=\"https://hasdata.com\" target=\"_blank\" rel=\"noopener noreferrer\">\n            <img height=\"128px\" width=\"128px\" src=\"https://humble.imgix.net/https%3A%2F%2Fhasdata.com%2Ffavicon.svg?ixlib=js-3.8.0&w=128&h=128&fit=fillmax&fill=solid&s=21933842d61dec74a961fc57754e58cb\" title=\"HasData\" alt=\"HasData\"></img>\n          </a>\n<a href=\"https://brand.dev/\" target=\"_blank\" rel=\"noopener noreferrer\">\n            <img height=\"128px\" width=\"128px\" src=\"https://humble.imgix.net/https%3A%2F%2Fgithub.com%2Fbrand-dot-dev.png?ixlib=js-3.8.0&w=128&h=128&fit=fillmax&fill=solid&s=b870a71fedf0e9b2af5534a8aaf22abb\" title=\"brand.dev\" alt=\"brand.dev\"></img>\n          </a>\n\n<!-- END SPONSORS -->\n\n**Other Sponsors**\n\n<!-- BEGIN SPONSORS: sponsor -->\n\n<a href=\"https://onlinecasinosspelen.com\" target=\"_blank\" rel=\"noopener noreferrer\">\n            <img height=\"64px\" width=\"64px\" src=\"https://humble.imgix.net/https%3A%2F%2Fimages.opencollective.com%2Fonlinecasinosspelen%2F99ac6a2%2Flogo.png?ixlib=js-3.8.0&w=64&h=64&fit=fillmax&fill=solid&s=8ec1ec058845b823858f22205485be02\" title=\"OnlineCasinosSpelen\" alt=\"OnlineCasinosSpelen\"></img>\n          </a>\n<a href=\"https://Nieuwe-Casinos.net\" target=\"_blank\" rel=\"noopener noreferrer\">\n            <img height=\"64px\" width=\"64px\" src=\"https://humble.imgix.net/https%3A%2F%2Fimages.opencollective.com%2Fnieuwecasinos%2Fc67d423%2Flogo.png?ixlib=js-3.8.0&w=64&h=64&fit=fillmax&fill=solid&s=ed55d86b80b1aa8cf89b033020521945\" title=\"Nieuwe-Casinos.net\" alt=\"Nieuwe-Casinos.net\"></img>\n          </a>\n\n<!-- END SPONSORS -->\n\n## Backers\n\n[Become a backer](https://github.com/cheeriojs/cheerio?sponsor=1) to show your\nsupport for Cheerio and help us maintain and improve this open source project.\n\n<!-- BEGIN SPONSORS: backer -->\n\n<a href=\"https://kafidoff.com\" target=\"_blank\" rel=\"noopener noreferrer\">\n            <img height=\"64px\" width=\"64px\" src=\"https://humble.imgix.net/https%3A%2F%2Fimages.opencollective.com%2Fkafidoff-vasy%2Fd7ff85c%2Favatar.png?ixlib=js-3.8.0&w=64&h=64&fit=fillmax&fill=solid&s=a41c66c2f9b1d3a7a241e425e7aa2d09\" title=\"Vasy Kafidoff\" alt=\"Vasy Kafidoff\"></img>\n          </a>\n\n<!-- END SPONSORS -->\n\n## License\n\nMIT\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Supported Versions\n\nOnly the latest release will receive security updates.\n\n## Reporting a Vulnerability\n\nTo report a security vulnerability, please use the\n[Tidelift security contact](https://tidelift.com/security). Tidelift will\ncoordinate the fix and disclosure.\n"
  },
  {
    "path": "benchmark/benchmark.ts",
    "content": "import fs from 'node:fs/promises';\nimport { Script } from 'node:vm';\nimport type { Element } from 'domhandler';\nimport { JSDOM } from 'jsdom';\nimport { Bench } from 'tinybench';\nimport type { Cheerio } from '../src/cheerio.js';\nimport type { CheerioAPI } from '../src/load.js';\nimport { load } from '../src/load-parse.js';\n\nconst documentDir = new URL('documents/', import.meta.url);\nconst jQuerySrc = await fs.readFile(\n  new URL('../node_modules/jquery/dist/jquery.slim.js', import.meta.url),\n  'utf8',\n);\nconst jQueryScript = new Script(jQuerySrc);\nconst filterIndex = process.argv.indexOf('--filter') + 1;\nconst benchmarkFilter = filterIndex > 0 ? process.argv[filterIndex] : '';\n\nconst cheerioOnly = process.argv.includes('--cheerio-only');\n\ntype SuiteOptions<T> = T extends void\n  ? {\n      test(this: void, $: CheerioAPI): void;\n      setup?: (this: void, $: CheerioAPI) => T;\n    }\n  : {\n      test(this: void, $: CheerioAPI, data: T): void;\n      setup(this: void, $: CheerioAPI): T;\n    };\n\nasync function benchmark<T = void>(\n  name: string,\n  fileName: string,\n  options: SuiteOptions<T>,\n): Promise<void> {\n  if (!name.includes(benchmarkFilter)) {\n    return;\n  }\n  const markup = await fs.readFile(new URL(fileName, documentDir), 'utf8');\n\n  console.log(`Test: ${name} (file: ${fileName})`);\n\n  const bench = new Bench();\n  const { test, setup } = options;\n\n  // Add Cheerio test\n  const $ = load(markup);\n  const setupData = setup?.($) as T;\n\n  bench.add('cheerio', () => {\n    test($, setupData);\n  });\n\n  // Add JSDOM test\n  if (!cheerioOnly) {\n    const dom = new JSDOM(markup, { runScripts: 'outside-only' });\n\n    jQueryScript.runInContext(dom.getInternalVMContext());\n\n    const setupData = setup?.(dom.window['$'] as CheerioAPI) as T;\n\n    bench.add('jsdom', () => test(dom.window['$'] as CheerioAPI, setupData));\n  }\n\n  await bench.run();\n\n  console.table(bench.table());\n}\n\nawait benchmark('Select all', 'jquery.html', {\n  test: ($) => $('*').length,\n});\nawait benchmark('Select some', 'jquery.html', {\n  test: ($) => $('li').length,\n});\n\n/*\n * Manipulation Tests\n */\nconst DIVS_MARKUP = '<div>'.repeat(50);\nawait benchmark<Cheerio<Element>>('manipulation - append', 'jquery.html', {\n  setup: ($) => $('body'),\n  test: (_, $body) => $body.append(DIVS_MARKUP),\n});\n\n// JSDOM used to run out of memory on these tests\nawait benchmark<Cheerio<Element>>(\n  'manipulation - prepend - highmem',\n  'jquery.html',\n  {\n    setup: ($) => $('body'),\n    test: (_, $body) => $body.prepend(DIVS_MARKUP),\n  },\n);\nawait benchmark<Cheerio<Element>>(\n  'manipulation - after - highmem',\n  'jquery.html',\n  {\n    setup: ($) => $('body'),\n    test: (_, $body) => $body.after(DIVS_MARKUP),\n  },\n);\nawait benchmark<Cheerio<Element>>(\n  'manipulation - before - highmem',\n  'jquery.html',\n  {\n    setup: ($) => $('body'),\n    test: (_, $body) => $body.before(DIVS_MARKUP),\n  },\n);\n\nawait benchmark<Cheerio<Element>>('manipulation - remove', 'jquery.html', {\n  setup: ($) => $('body'),\n  test($, $lis) {\n    const child = $('<div>');\n    $lis.append(child);\n    child.remove();\n  },\n});\n\nawait benchmark('manipulation - replaceWith', 'jquery.html', {\n  setup($) {\n    $('body').append('<div id=\"foo\">');\n  },\n  test($) {\n    $('#foo').replaceWith('<div id=\"foo\">');\n  },\n});\n\nawait benchmark<Cheerio<Element>>('manipulation - empty', 'jquery.html', {\n  setup: ($) => $('li'),\n  test(_, $lis) {\n    $lis.empty();\n  },\n});\nawait benchmark<Cheerio<Element>>('manipulation - html', 'jquery.html', {\n  setup: ($) => $('li'),\n  test(_, $lis) {\n    $lis.html();\n    $lis.html('foo');\n  },\n});\nawait benchmark<Cheerio<Element>>('manipulation - html render', 'jquery.html', {\n  setup: ($) => $('body'),\n  test(_, $lis) {\n    $lis.html();\n  },\n});\n\nconst HTML_INDEPENDENT_MARKUP =\n  '<div class=\"foo\"><div id=\"bar\">bat<hr>baz</div> </div>'.repeat(6);\nawait benchmark('manipulation - html independent', 'jquery.html', {\n  test: ($) => $(HTML_INDEPENDENT_MARKUP).html(),\n});\nawait benchmark<Cheerio<Element>>('manipulation - text', 'jquery.html', {\n  setup: ($) => $('li'),\n  test(_, $lis) {\n    $lis.text();\n    $lis.text('foo');\n  },\n});\n\n/*\n * Traversing Tests\n */\nawait benchmark<Cheerio<Element>>('traversing - Find', 'jquery.html', {\n  setup: ($) => $('li'),\n  test: (_, $lis) => $lis.find('li').length,\n});\nawait benchmark<Cheerio<Element>>('traversing - Parent', 'jquery.html', {\n  setup: ($) => $('li'),\n  test: (_, $lis) => $lis.parent('div').length,\n});\nawait benchmark<Cheerio<Element>>('traversing - Parents', 'jquery.html', {\n  setup: ($) => $('li'),\n  test: (_, $lis) => $lis.parents('div').length,\n});\nawait benchmark<Cheerio<Element>>('traversing - Closest', 'jquery.html', {\n  setup: ($) => $('li'),\n  test: (_, $lis) => $lis.closest('div').length,\n});\nawait benchmark<Cheerio<Element>>('traversing - next', 'jquery.html', {\n  setup: ($) => $('li'),\n  test: (_, $lis) => $lis.next().length,\n});\nawait benchmark<Cheerio<Element>>('traversing - nextAll', 'jquery.html', {\n  setup: ($) => $('li'),\n  test: (_, $lis) => $lis.nextAll('li').length,\n});\nawait benchmark<Cheerio<Element>>('traversing - nextUntil', 'jquery.html', {\n  setup: ($) => $('li'),\n  test: (_, $lis) => $lis.nextUntil('li').length,\n});\nawait benchmark<Cheerio<Element>>('traversing - prev', 'jquery.html', {\n  setup: ($) => $('li'),\n  test: (_, $lis) => $lis.prev().length,\n});\nawait benchmark<Cheerio<Element>>('traversing - prevAll', 'jquery.html', {\n  setup: ($) => $('li'),\n  test: (_, $lis) => $lis.prevAll('li').length,\n});\nawait benchmark<Cheerio<Element>>('traversing - prevUntil', 'jquery.html', {\n  setup: ($) => $('li'),\n  test: (_, $lis) => $lis.prevUntil('li').length,\n});\nawait benchmark<Cheerio<Element>>('traversing - siblings', 'jquery.html', {\n  setup: ($) => $('li'),\n  test: (_, $lis) => $lis.siblings('li').length,\n});\nawait benchmark<Cheerio<Element>>('traversing - Children', 'jquery.html', {\n  setup: ($) => $('li'),\n  test: (_, $lis) => $lis.children('a').length,\n});\nawait benchmark<Cheerio<Element>>('traversing - Filter', 'jquery.html', {\n  setup: ($) => $('li'),\n  test: (_, $lis) => $lis.filter('li').length,\n});\nawait benchmark<Cheerio<Element>>('traversing - First', 'jquery.html', {\n  setup: ($) => $('li'),\n  test: (_, $lis) => $lis.first().first().length,\n});\nawait benchmark<Cheerio<Element>>('traversing - Last', 'jquery.html', {\n  setup: ($) => $('li'),\n  test: (_, $lis) => $lis.last().last().length,\n});\nawait benchmark<Cheerio<Element>>('traversing - Eq', 'jquery.html', {\n  setup: ($) => $('li'),\n  test: (_, $lis) => $lis.eq(0).eq(0).length,\n});\n\n/*\n * Attributes Tests\n */\nawait benchmark<Cheerio<Element>>('attributes - Attributes', 'jquery.html', {\n  setup: ($) => $('li'),\n  test(_, $lis) {\n    $lis.attr('foo', 'bar');\n    $lis.attr('foo');\n    $lis.removeAttr('foo');\n  },\n});\nawait benchmark<Cheerio<Element>>(\n  'attributes - Single Attribute',\n  'jquery.html',\n  {\n    setup: ($) => $('body'),\n    test(_, $lis) {\n      $lis.attr('foo', 'bar');\n      $lis.attr('foo');\n      $lis.removeAttr('foo');\n    },\n  },\n);\nawait benchmark<Cheerio<Element>>('attributes - Data', 'jquery.html', {\n  setup: ($) => $('li'),\n  test(_, $lis) {\n    $lis.data('foo', 'bar');\n    $lis.data('foo');\n  },\n});\nawait benchmark<Cheerio<Element>>('attributes - Val', 'jquery.html', {\n  setup: ($) => $('select,input,textarea,option'),\n  test($, $lis) {\n    $lis.each(function () {\n      $(this).val();\n      $(this).val('foo');\n    });\n  },\n});\n\nawait benchmark<Cheerio<Element>>('attributes - Has class', 'jquery.html', {\n  setup: ($) => $('li'),\n  test: (_, $lis) => $lis.hasClass('foo'),\n});\nawait benchmark<Cheerio<Element>>('attributes - Toggle class', 'jquery.html', {\n  setup: ($) => $('li'),\n  test: (_, $lis) => $lis.toggleClass('foo'),\n});\nawait benchmark<Cheerio<Element>>(\n  'attributes - Add Remove class',\n  'jquery.html',\n  {\n    setup: ($) => $('li'),\n    test(_, $lis) {\n      $lis.addClass('foo');\n      $lis.removeClass('foo');\n    },\n  },\n);\n"
  },
  {
    "path": "benchmark/documents/jquery.html",
    "content": "<html\n  class=\"js multiplebgs boxshadow cssgradients wf-klavikaweb-i7-active wf-klavikaweb-n7-active wf-sourcecodepro-n4-active wf-sourcecodepro-n7-active wf-active\"\n  lang=\"en-US\"\n>\n  <head data-live-domain=\"api.jquery.com\">\n    <script type=\"text/javascript\" async=\"\" src=\"null\"></script>\n    <meta charset=\"utf-8\" />\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\" />\n\n    <title>jQuery() | jQuery API Documentation</title>\n\n    <meta name=\"author\" content=\"jQuery Foundation - jquery.org\" />\n    <meta\n      name=\"description\"\n      content=\"jQuery: The Write Less, Do More, JavaScript Library\"\n    />\n\n    <meta name=\"viewport\" content=\"width=device-width\" />\n\n    <link\n      rel=\"shortcut icon\"\n      href=\"http://api.jquery.com/jquery-wp-content/themes/api.jquery.com/i/favicon.ico\"\n      src=\"null\"\n    />\n\n    <link\n      rel=\"stylesheet\"\n      href=\"http://api.jquery.com/jquery-wp-content/themes/jquery/css/base.css?v=1\"\n      src=\"null\"\n    />\n    <link\n      rel=\"stylesheet\"\n      href=\"http://api.jquery.com/jquery-wp-content/themes/api.jquery.com/style.css\"\n      src=\"null\"\n    />\n    <link rel=\"pingback\" href=\"http://api.jquery.com/xmlrpc.php\" src=\"null\" />\n    <!--[if lt IE 7\n      ]><link rel=\"stylesheet\" href=\"css/font-awesome-ie7.min.css\"\n    /><![endif]-->\n\n    <script type=\"text/javascript\" async=\"\" src=\"null\"></script>\n    <script src=\"null\"></script>\n\n    <script src=\"null\"></script>\n    <script>\n      window.jQuery ||\n        document.write(\n          unescape(\n            '%3Cscript src=\"http://api.jquery.com/jquery-wp-content/themes/jquery/js/jquery-1.9.1.min.js\"%3E%3C/script%3E'\n          )\n        );\n    </script>\n    <script src=\"null\"></script>\n\n    <script src=\"null\"></script>\n    <script src=\"null\"></script>\n\n    <script src=\"null\"></script>\n    <style type=\"text/css\">\n      .tk-source-code-pro {\n        font-family: 'source-code-pro', sans-serif;\n      }\n      .tk-klavika-web {\n        font-family: 'klavika-web', sans-serif;\n      }\n    </style>\n    <link\n      rel=\"stylesheet\"\n      href=\"http://use.typekit.net/c/d4d852/klavika-web:i7:n7,source-code-pro:n4:n7.PYh:F:2,PYg:F:2,Y1M:F:2,Y1P:F:2/d?3bb2a6e53c9684ffdc9a98f2135b2a6250f2340d8ca0853b7df9676f5fa610fe069f9d29c9b5e67ae7b6312a16ff95d3a73356eed53502d6630d88cb0fe9789e0ac2d9a6c14ac282069f97be80efceecd4f5e0e58b889e8649ff22efc0c4063e9f9f87c7a8920dcab32add3496e6b09e6a94141aaaeb81a4bb1d4a09b8a14ac46d7d0dff3bf6532c044b0313c5ed1d7226c76cac5039645b4082ff59a8953c4e06ef9a344cf9265e8de3ed48ac2f34b281583cbaf6f2f580f7709eba9ea284dc14f4722ed0e264d7faa135466fbba043f093297f0efc92bfcb7b3eb8761407436be31d8029117f1a72aa7b8b6319c956c739e1c25b7a993a45\"\n      src=\"null\"\n    />\n    <script>\n      try {\n        Typekit.load();\n      } catch (_e) {}\n    </script>\n\n    <link\n      rel=\"alternate\"\n      type=\"application/rss+xml\"\n      title=\"jQuery API Documentation » Feed\"\n      href=\"http://api.jquery.com/feed/\"\n      src=\"null\"\n    />\n    <link\n      rel=\"alternate\"\n      type=\"application/rss+xml\"\n      title=\"jQuery API Documentation » Comments Feed\"\n      href=\"http://api.jquery.com/comments/feed/\"\n      src=\"null\"\n    />\n    <link\n      rel=\"alternate\"\n      type=\"application/rss+xml\"\n      title=\"jQuery API Documentation » jQuery() Comments Feed\"\n      href=\"http://api.jquery.com/jQuery/feed/\"\n      src=\"null\"\n    />\n    <script type=\"text/javascript\" src=\"null\"></script>\n    <link\n      rel=\"EditURI\"\n      type=\"application/rsd+xml\"\n      title=\"RSD\"\n      href=\"http://api.jquery.com/xmlrpc.php?rsd\"\n      src=\"null\"\n    />\n    <link\n      rel=\"wlwmanifest\"\n      type=\"application/wlwmanifest+xml\"\n      href=\"http://api.jquery.com/wp-includes/wlwmanifest.xml\"\n      src=\"null\"\n    />\n    <link\n      rel=\"prev\"\n      title=\"jQuery.holdReady()\"\n      href=\"http://api.jquery.com/jQuery.holdReady/\"\n      src=\"null\"\n    />\n    <link\n      rel=\"next\"\n      title=\"jQuery.inArray()\"\n      href=\"http://api.jquery.com/jQuery.inArray/\"\n      src=\"null\"\n    />\n    <meta name=\"generator\" content=\"WordPress 3.7\" />\n    <link rel=\"canonical\" href=\"http://api.jquery.com/jQuery/\" src=\"null\" />\n    <link rel=\"shortlink\" href=\"http://api.jquery.com/?p=339\" src=\"null\" />\n  </head>\n  <body\n    class=\"api jquery single single-post postid-339 single-format-standard single-author singular\"\n  >\n    <!--[if lt IE 7]>\n      <p class=\"chromeframe\">\n        You are using an outdated browser.\n        <a href=\"http://browsehappy.com/\">Upgrade your browser today</a>\n        or\n        <a href=\"http://www.google.com/chromeframe/?redirect=true\"\n          >install Google Chrome Frame</a\n        >\n        to better experience this site.\n      </p>\n    <![endif]-->\n\n    <header>\n      <section id=\"global-nav\">\n        <nav>\n          <div class=\"constrain\">\n            <ul class=\"projects\">\n              <li class=\"project jquery\">\n                <a href=\"http://jquery.com/\" title=\"jQuery\" src=\"null\"\n                  >jQuery</a\n                >\n              </li>\n              <li class=\"project jquery-ui\">\n                <a href=\"http://jqueryui.com/\" title=\"jQuery UI\" src=\"null\"\n                  >jQuery UI</a\n                >\n              </li>\n              <li class=\"project jquery-mobile\">\n                <a\n                  href=\"http://jquerymobile.com/\"\n                  title=\"jQuery Mobile\"\n                  src=\"null\"\n                  >jQuery Mobile</a\n                >\n              </li>\n              <li class=\"project sizzlejs\">\n                <a href=\"http://sizzlejs.com/\" title=\"Sizzle\" src=\"null\"\n                  >Sizzle</a\n                >\n              </li>\n              <li class=\"project qunitjs\">\n                <a href=\"http://qunitjs.com/\" title=\"QUnit\" src=\"null\">QUnit</a>\n              </li>\n            </ul>\n            <ul class=\"links l_tinynav1\">\n              <li>\n                <a href=\"http://plugins.jquery.com/\" src=\"null\">Plugins</a>\n              </li>\n              <li class=\"dropdown\">\n                <a href=\"http://contribute.jquery.org/\" src=\"null\"\n                  >Contribute</a\n                >\n                <ul>\n                  <li>\n                    <a href=\"http://contribute.jquery.org/cla/\" src=\"null\"\n                      >CLA</a\n                    >\n                  </li>\n                  <li>\n                    <a\n                      href=\"http://contribute.jquery.org/style-guide/\"\n                      src=\"null\"\n                      >Style Guides</a\n                    >\n                  </li>\n                  <li>\n                    <a href=\"http://contribute.jquery.org/triage/\" src=\"null\"\n                      >Bug Triage</a\n                    >\n                  </li>\n                  <li>\n                    <a href=\"http://contribute.jquery.org/code/\" src=\"null\"\n                      >Code</a\n                    >\n                  </li>\n                  <li>\n                    <a\n                      href=\"http://contribute.jquery.org/documentation/\"\n                      src=\"null\"\n                      >Documentation</a\n                    >\n                  </li>\n                  <li>\n                    <a href=\"http://contribute.jquery.org/web-sites/\" src=\"null\"\n                      >Web Sites</a\n                    >\n                  </li>\n                </ul>\n              </li>\n              <li class=\"dropdown\">\n                <a href=\"http://events.jquery.org/\" src=\"null\">Events</a>\n                <ul class=\"wide\">\n                  <li>\n                    <a\n                      href=\"http://www.deque.com/deque-partners-jquery-create-accessibility-summit\"\n                      src=\"null\"\n                      >Oct 10-11 | jQuery Accessibility Summit</a\n                    >\n                  </li>\n                  <li>\n                    <a href=\"http://jquery.itmozg.ru/\" src=\"null\"\n                      >Oct 15 | jQuery Russia</a\n                    >\n                  </li>\n                  <li>\n                    <a\n                      href=\"http://modernweb.com/training/jquery-oct-2013.php\"\n                      src=\"null\"\n                      >Oct 15-17 | jQuery Virtual Training</a\n                    >\n                  </li>\n                  <li>\n                    <a href=\"http://2013.cssdevconf.com/\" src=\"null\"\n                      >Oct 21-22 | CSS Dev Conf</a\n                    >\n                  </li>\n                  <li>\n                    <a href=\"http://javascriptsummit.com/\" src=\"null\"\n                      >Nov 19-21 | JavaScript Summit</a\n                    >\n                  </li>\n                  <li>\n                    <a\n                      href=\"http://events.jquery.org/2014/san-diego/\"\n                      src=\"null\"\n                      >Feb 12-13 | jQuery San Diego</a\n                    >\n                  </li>\n                  <li>\n                    <a href=\"http://www.gentics.com/jquery-europe\" src=\"null\"\n                      >Feb 28-Mar 1 | jQuery Europe</a\n                    >\n                  </li>\n                  <li>\n                    <a href=\"http://jqueryuk.com\" src=\"null\"\n                      >May 16 | jQuery UK</a\n                    >\n                  </li>\n                </ul>\n              </li>\n              <li class=\"dropdown\">\n                <a href=\"https://jquery.org/support/\" src=\"null\">Support</a>\n                <ul>\n                  <li>\n                    <a href=\"http://learn.jquery.com/\" src=\"null\"\n                      >Learning Center</a\n                    >\n                  </li>\n                  <li>\n                    <a href=\"http://try.jquery.com/\" src=\"null\">Try jQuery</a>\n                  </li>\n                  <li>\n                    <a href=\"http://irc.jquery.org/\" src=\"null\">IRC/Chat</a>\n                  </li>\n                  <li>\n                    <a href=\"http://forum.jquery.com/\" src=\"null\">Forums</a>\n                  </li>\n                  <li>\n                    <a\n                      href=\"http://stackoverflow.com/tags/jquery/info\"\n                      src=\"null\"\n                      >Stack Overflow</a\n                    >\n                  </li>\n                  <li>\n                    <a href=\"https://jquery.org/support/\" src=\"null\"\n                      >Commercial Support</a\n                    >\n                  </li>\n                </ul>\n              </li>\n              <li class=\"dropdown\">\n                <a href=\"https://jquery.org/\" src=\"null\">jQuery Foundation</a>\n                <ul>\n                  <li>\n                    <a href=\"https://jquery.org/join/\" src=\"null\">Join</a>\n                  </li>\n                  <li>\n                    <a href=\"https://jquery.org/members/\" src=\"null\">Members</a>\n                  </li>\n                  <li>\n                    <a href=\"https://jquery.org/team/\" src=\"null\">Team</a>\n                  </li>\n                  <li>\n                    <a href=\"http://brand.jquery.org/\" src=\"null\"\n                      >Brand Guide</a\n                    >\n                  </li>\n                  <li>\n                    <a href=\"https://jquery.org/donate/\" src=\"null\">Donate</a>\n                  </li>\n                </ul>\n              </li>\n            </ul>\n          </div>\n        </nav>\n      </section>\n    </header>\n\n    <div id=\"container\">\n      <div id=\"logo-events\" class=\"constrain clearfix\">\n        <h2 class=\"logo\">\n          <a\n            href=\"http://jquery.com/\"\n            title=\"jQuery API Documentation\"\n            src=\"null\"\n            >jQuery API Documentation</a\n          >\n        </h2>\n\n        <aside>\n          <div id=\"broadcast\">\n            <a\n              href=\"http://engine.adzerk.net/r?e=eyJhdiI6MjIyMzYsImF0IjoxMzE0LCJjbSI6NjM3NjcsImNoIjo4MzY4LCJjciI6MTc4ODcxLCJkaSI6ImZhNDViODgwMzBhOTQxYzNhZmMyZTg0MmIwYzFiZDI1IiwiZG0iOjEsImZjIjoyMjgyOTgsImZsIjoxMTM1MjksImt3IjoidW5kZWZpbmVkIiwibnciOjU0NDksInJmIjoiaHR0cDovL2FwaS5qcXVlcnkuY29tLz9zPWpxdWVyeSIsInJ2IjowLCJwciI6MjE5MzcsInN0Ijo1MzgyOSwidHMiOjEzODU4Mzk5ODgyOTIsInVyIjoiaHR0cDovL2V2ZW50cy5qcXVlcnkub3JnLzIwMTQvc2FuLWRpZWdvLyJ9&amp;s=pUb8fXw8Ar8bGvQszZJHXjkx8Gk\"\n              rel=\"nofollow\"\n              target=\"_blank\"\n              title=\"jQuery San Diego\"\n              src=\"null\"\n              ><img\n                src=\"null\"\n                title=\"jQuery San Diego\"\n                alt=\"jQuery San Diego\"\n                border=\"0\"\n                width=\"400\"\n                height=\"100\" /></a\n            ><img height=\"0px\" width=\"0px\" border=\"0\" src=\"null\" alt=\"\" />\n          </div>\n        </aside>\n      </div>\n\n      <nav id=\"main\" class=\"constrain clearfix\">\n        <div class=\"menu-top-container\">\n          <ul id=\"menu-top\" class=\"menu l_tinynav2\">\n            <li class=\"menu-item\">\n              <a href=\"http://jquery.com/download/\" src=\"null\">Download</a>\n            </li>\n            <li class=\"menu-item current\">\n              <a href=\"http://api.jquery.com/\" src=\"null\">API Documentation</a>\n            </li>\n            <li class=\"menu-item\">\n              <a href=\"http://blog.jquery.com/\" src=\"null\">Blog</a>\n            </li>\n            <li class=\"menu-item\">\n              <a href=\"http://plugins.jquery.com/\" src=\"null\">Plugins</a>\n            </li>\n            <li class=\"menu-item\">\n              <a href=\"http://jquery.com/browser-support/\" src=\"null\"\n                >Browser Support</a\n              >\n            </li>\n          </ul>\n          <select id=\"tinynav2\" class=\"tinynav tinynav2\"\n            ><option>Navigate...</option\n            ><option value=\"http://jquery.com/download/\">Download</option\n            ><option value=\"http://api.jquery.com/\">API Documentation</option\n            ><option value=\"http://blog.jquery.com/\">Blog</option\n            ><option value=\"http://plugins.jquery.com/\">Plugins</option\n            ><option value=\"http://jquery.com/browser-support/\"\n              >Browser Support</option\n            ></select\n          >\n        </div>\n\n        <form method=\"get\" class=\"searchform\" action=\"http://api.jquery.com/\">\n          <button type=\"submit\" class=\"icon-search\">\n            <span class=\"visuallyhidden\">search</span>\n          </button>\n          <label>\n            <span class=\"visuallyhidden\">Search jQuery API Documentation</span>\n            <input type=\"text\" name=\"s\" value=\"\" placeholder=\"Search\" />\n          </label>\n          <label>\n            <span class=\"visuallyhidden\">Search jQuery API Documentation</span>\n            <input type=\"radio\" name=\"rad\" value=\"\" placeholder=\"Search\" />\n          </label>\n          <label>\n            <span class=\"visuallyhidden\">Search jQuery API Documentation</span>\n            <input type=\"radio\" name=\"rad\" value=\"foo\" placeholder=\"Search\" />\n          </label>\n        </form>\n      </nav>\n\n      <div id=\"content-wrapper\" class=\"clearfix row\">\n        <div class=\"content-right twelve columns\">\n          <div id=\"content\">\n            <article\n              id=\"post-339\"\n              class=\"post-339 post type-post status-publish format-standard hentry category-core category-10 category-14\"\n            >\n              <header class=\"entry-header\">\n                <h1 class=\"entry-title\">jQuery()</h1>\n                <hr />\n                <div class=\"entry-meta\">\n                  Categories:\n                  <span class=\"category\"\n                    ><a\n                      href=\"http://api.jquery.com/category/core/\"\n                      title=\"View all posts in Core\"\n                      src=\"null\"\n                      >Core</a\n                    ></span\n                  >\n                </div>\n                <!-- .entry-meta -->\n              </header>\n              <!-- .entry-header -->\n\n              <div class=\"entry-content\">\n                Return a collection of matched elements either found in the DOM\n                based on passed argument(s) or created by passing an HTML\n                string.\n                <div class=\"toc\">\n                  <h4><span>Contents:</span></h4>\n                  <ul class=\"toc-list\">\n                    <li>\n                      <a href=\"#jQuery1\" src=\"null\"\n                        >jQuery( selector [, context ] )</a\n                      >\n                      <ul>\n                        <li>\n                          <a href=\"#jQuery-selector-context\" src=\"null\"\n                            >jQuery( selector [, context ] )</a\n                          >\n                        </li>\n                        <li>\n                          <a href=\"#jQuery-element\" src=\"null\"\n                            >jQuery( element )</a\n                          >\n                        </li>\n                        <li>\n                          <a href=\"#jQuery-elementArray\" src=\"null\"\n                            >jQuery( elementArray )</a\n                          >\n                        </li>\n                        <li>\n                          <a href=\"#jQuery-object\" src=\"null\"\n                            >jQuery( object )</a\n                          >\n                        </li>\n                        <li>\n                          <a href=\"#jQuery-jQuery-object\" src=\"null\"\n                            >jQuery( jQuery object )</a\n                          >\n                        </li>\n                        <li>\n                          <a href=\"#jQuery\" src=\"null\">jQuery()</a>\n                        </li>\n                      </ul>\n                    </li>\n                    <li>\n                      <a href=\"#jQuery2\" src=\"null\"\n                        >jQuery( html [, ownerDocument ] )</a\n                      >\n                      <ul>\n                        <li>\n                          <a href=\"#jQuery-html-ownerDocument\" src=\"null\"\n                            >jQuery( html [, ownerDocument ] )</a\n                          >\n                        </li>\n                        <li>\n                          <a href=\"#jQuery-html-attributes\" src=\"null\"\n                            >jQuery( html, attributes )</a\n                          >\n                        </li>\n                      </ul>\n                    </li>\n                    <li>\n                      <a href=\"#jQuery3\" src=\"null\">jQuery( callback )</a>\n                      <ul>\n                        <li>\n                          <a href=\"#jQuery-callback\" src=\"null\"\n                            >jQuery( callback )</a\n                          >\n                        </li>\n                      </ul>\n                    </li>\n                  </ul>\n                </div>\n                <article id=\"jQuery1\" class=\"entry method\">\n                  <h2 class=\"section-title\">\n                    <span class=\"name\">jQuery( selector [, context ] )</span\n                    ><span class=\"returns\"\n                      >Returns:\n                      <a href=\"http://api.jquery.com/Types/#jQuery\" src=\"null\"\n                        >jQuery</a\n                      ></span\n                    >\n                  </h2>\n                  <div class=\"entry-wrapper\">\n                    <p class=\"desc\">\n                      <strong>Description: </strong>Accepts a string containing\n                      a CSS selector which is then used to match a set of\n                      elements.\n                    </p>\n                    <ul class=\"signatures\">\n                      <li class=\"signature\">\n                        <h4 class=\"name\">\n                          <span class=\"version-details\"\n                            >version added:\n                            <a href=\"/category/version/1.0/\" src=\"null\"\n                              >1.0</a\n                            ></span\n                          ><a\n                            id=\"jQuery-selector-context\"\n                            href=\"#jQuery-selector-context\"\n                            src=\"null\"\n                            ><span class=\"icon-link\"></span>jQuery( selector [,\n                            context ] )</a\n                          >\n                        </h4>\n                        <ul>\n                          <li>\n                            <div>\n                              <strong>selector</strong>\n                            </div>\n                            <div>\n                              Type:\n                              <a\n                                href=\"http://api.jquery.com/Types/#Selector\"\n                                src=\"null\"\n                                >Selector</a\n                              >\n                            </div>\n                            <div>\n                              A string containing a selector expression\n                            </div>\n                          </li>\n                          <li>\n                            <div>\n                              <strong>context</strong>\n                            </div>\n                            <div>\n                              Type:\n                              <a\n                                href=\"http://api.jquery.com/Types/#Element\"\n                                src=\"null\"\n                                >Element</a\n                              >\n                              or\n                              <a\n                                href=\"http://api.jquery.com/Types/#jQuery\"\n                                src=\"null\"\n                                >jQuery</a\n                              >\n                            </div>\n                            <div>\n                              A DOM Element, Document, or jQuery to use as\n                              context\n                            </div>\n                          </li>\n                        </ul>\n                      </li>\n                      <li class=\"signature\">\n                        <h4 class=\"name\">\n                          <span class=\"version-details\"\n                            >version added:\n                            <a href=\"/category/version/1.0/\" src=\"null\"\n                              >1.0</a\n                            ></span\n                          ><a\n                            id=\"jQuery-element\"\n                            href=\"#jQuery-element\"\n                            src=\"null\"\n                            ><span class=\"icon-link\"></span>jQuery( element )</a\n                          >\n                        </h4>\n                        <ul>\n                          <li>\n                            <div>\n                              <strong>element</strong>\n                            </div>\n                            <div>\n                              Type:\n                              <a\n                                href=\"http://api.jquery.com/Types/#Element\"\n                                src=\"null\"\n                                >Element</a\n                              >\n                            </div>\n                            <div>\n                              A DOM element to wrap in a jQuery object.\n                            </div>\n                          </li>\n                        </ul>\n                      </li>\n                      <li class=\"signature\">\n                        <h4 class=\"name\">\n                          <span class=\"version-details\"\n                            >version added:\n                            <a href=\"/category/version/1.0/\" src=\"null\"\n                              >1.0</a\n                            ></span\n                          ><a\n                            id=\"jQuery-elementArray\"\n                            href=\"#jQuery-elementArray\"\n                            src=\"null\"\n                            ><span class=\"icon-link\"></span>jQuery( elementArray\n                            )</a\n                          >\n                        </h4>\n                        <ul>\n                          <li>\n                            <div>\n                              <strong>elementArray</strong>\n                            </div>\n                            <div>\n                              Type:\n                              <a\n                                href=\"http://api.jquery.com/Types/#Array\"\n                                src=\"null\"\n                                >Array</a\n                              >\n                            </div>\n                            <div>\n                              An array containing a set of DOM elements to wrap\n                              in a jQuery object.\n                            </div>\n                          </li>\n                        </ul>\n                      </li>\n                      <li class=\"signature\">\n                        <h4 class=\"name\">\n                          <span class=\"version-details\"\n                            >version added:\n                            <a href=\"/category/version/1.0/\" src=\"null\"\n                              >1.0</a\n                            ></span\n                          ><a\n                            id=\"jQuery-object\"\n                            href=\"#jQuery-object\"\n                            src=\"null\"\n                            ><span class=\"icon-link\"></span>jQuery( object )</a\n                          >\n                        </h4>\n                        <ul>\n                          <li>\n                            <div>\n                              <strong>object</strong>\n                            </div>\n                            <div>\n                              Type:\n                              <a\n                                href=\"http://api.jquery.com/Types/#PlainObject\"\n                                src=\"null\"\n                                >PlainObject</a\n                              >\n                            </div>\n                            <div>\n                              A plain object to wrap in a jQuery object.\n                            </div>\n                          </li>\n                        </ul>\n                      </li>\n                      <li class=\"signature\">\n                        <h4 class=\"name\">\n                          <span class=\"version-details\"\n                            >version added:\n                            <a href=\"/category/version/1.0/\" src=\"null\"\n                              >1.0</a\n                            ></span\n                          ><a\n                            id=\"jQuery-jQuery-object\"\n                            href=\"#jQuery-jQuery-object\"\n                            src=\"null\"\n                            ><span class=\"icon-link\"></span>jQuery( jQuery\n                            object )</a\n                          >\n                        </h4>\n                        <ul>\n                          <li>\n                            <div>\n                              <strong>jQuery object</strong>\n                            </div>\n                            <div>\n                              Type:\n                              <a\n                                href=\"http://api.jquery.com/Types/#PlainObject\"\n                                src=\"null\"\n                                >PlainObject</a\n                              >\n                            </div>\n                            <div>\n                              An existing jQuery object to clone.\n                            </div>\n                          </li>\n                        </ul>\n                      </li>\n                      <li class=\"signature\">\n                        <h4 class=\"name\">\n                          <span class=\"version-details\"\n                            >version added:\n                            <a href=\"/category/version/1.4/\" src=\"null\"\n                              >1.4</a\n                            ></span\n                          ><a id=\"jQuery\" href=\"#jQuery\" src=\"null\"\n                            ><span class=\"icon-link\"></span>jQuery()</a\n                          >\n                        </h4>\n                        <ul>\n                          <li>\n                            <div class=\"null-signature\">\n                              This signature does not accept any arguments.\n                            </div>\n                          </li>\n                        </ul>\n                      </li>\n                    </ul>\n                    <div class=\"longdesc\" id=\"entry-longdesc\">\n                      <p>\n                        In the first formulation listed above,\n                        <code>jQuery()</code> — which can also be written as\n                        <code>$()</code> — searches through the DOM for any\n                        elements that match the provided selector and creates a\n                        new jQuery object that references these elements:\n                      </p>\n                      <div class=\"syntaxhighlighter javascript nogutter\">\n                        <table>\n                          <tbody>\n                            <tr>\n                              <td class=\"gutter\">\n                                <div class=\"line n1\">\n                                  1\n                                </div>\n                              </td>\n                              <td class=\"code\">\n                                <pre><div class=\"container\"><div class=\"line\"><code>$( <span class=\"string\">\"div.foo\"</span> );</code></div></div></pre>\n                              </td>\n                            </tr>\n                          </tbody>\n                        </table>\n                      </div>\n\n                      <p>\n                        If no elements match the provided selector, the new\n                        jQuery object is \"empty\"; that is, it contains no\n                        elements and has\n                        <code><a href=\"/length/\" src=\"null\">.length</a></code>\n                        property of 0.\n                      </p>\n                      <h4 id=\"selector-context\">\n                        Selector Context\n                      </h4>\n                      <p>\n                        By default, selectors perform their searches within the\n                        DOM starting at the document root. However, an alternate\n                        context can be given for the search by using the\n                        optional second parameter to the\n                        <code>$()</code> function. For example, to do a search\n                        within an event handler, the search can be restricted\n                        like so:\n                      </p>\n                      <div class=\"syntaxhighlighter javascript nogutter\">\n                        <table>\n                          <tbody>\n                            <tr>\n                              <td class=\"gutter\">\n                                <div class=\"line n1\">\n                                  1\n                                </div>\n\n                                <div class=\"line n2\">\n                                  2\n                                </div>\n\n                                <div class=\"line n3\">\n                                  3\n                                </div>\n                              </td>\n                              <td class=\"code\">\n                                <pre><div class=\"container\"><div class=\"line\"><code>$( <span class=\"string\">\"div.foo\"</span> ).click(<span class=\"keyword\">function</span>() {</code></div></div><div class=\"container\"><div class=\"line\"><code>  $( <span class=\"string\">\"span\"</span>, <span class=\"keyword\">this</span> ).addClass( <span class=\"string\">\"bar\"</span> );</code></div></div><div class=\"container\"><div class=\"line\"><code>});</code></div></div></pre>\n                              </td>\n                            </tr>\n                          </tbody>\n                        </table>\n                      </div>\n\n                      <p>\n                        When the search for the span selector is restricted to\n                        the context of <code>this</code>, only spans within the\n                        clicked element will get the additional class.\n                      </p>\n                      <p>\n                        Internally, selector context is implemented with the\n                        <code>.find()</code> method, so\n                        <code>$( \"span\", this )</code>\n                        is equivalent to\n                        <code>$( this ).find( \"span\" )</code>.\n                      </p>\n\n                      <h4 id=\"using-dom-elements\">\n                        Using DOM elements\n                      </h4>\n                      <p>\n                        The second and third formulations of this function\n                        create a jQuery object using one or more DOM elements\n                        that were already selected in some other way.\n                      </p>\n                      <p>\n                        <strong>Note:</strong> These formulations are meant to\n                        consume only DOM elements; feeding mixed data to the\n                        elementArray form is particularly discouraged.\n                      </p>\n                      <p>\n                        A common use of this facility is to call jQuery methods\n                        on an element that has been passed to a callback\n                        function through the keyword <code>this</code>:\n                      </p>\n                      <div class=\"syntaxhighlighter javascript nogutter\">\n                        <table>\n                          <tbody>\n                            <tr>\n                              <td class=\"gutter\">\n                                <div class=\"line n1\">\n                                  1\n                                </div>\n\n                                <div class=\"line n2\">\n                                  2\n                                </div>\n\n                                <div class=\"line n3\">\n                                  3\n                                </div>\n                              </td>\n                              <td class=\"code\">\n                                <pre><div class=\"container\"><div class=\"line\"><code>$( <span class=\"string\">\"div.foo\"</span> ).click(<span class=\"keyword\">function</span>() {</code></div></div><div class=\"container\"><div class=\"line\"><code>  $( <span class=\"keyword\">this</span> ).slideUp();</code></div></div><div class=\"container\"><div class=\"line\"><code>});</code></div></div></pre>\n                              </td>\n                            </tr>\n                          </tbody>\n                        </table>\n                      </div>\n\n                      <p>\n                        This example causes elements to be hidden with a sliding\n                        animation when clicked. Because the handler receives the\n                        clicked item in the\n                        <code>this</code> keyword as a bare DOM element, the\n                        element must be passed to the <code>$()</code> function\n                        before applying jQuery methods to it.\n                      </p>\n                      <p>\n                        XML data returned from an Ajax call can be passed to the\n                        <code>$()</code> function so individual elements of the\n                        XML structure can be retrieved using\n                        <code>.find()</code> and other DOM traversal methods.\n                      </p>\n                      <div class=\"syntaxhighlighter javascript nogutter\">\n                        <table>\n                          <tbody>\n                            <tr>\n                              <td class=\"gutter\">\n                                <div class=\"line n1\">\n                                  1\n                                </div>\n\n                                <div class=\"line n2\">\n                                  2\n                                </div>\n\n                                <div class=\"line n3\">\n                                  3\n                                </div>\n                              </td>\n                              <td class=\"code\">\n                                <pre><div class=\"container\"><div class=\"line\"><code>$.post( <span class=\"string\">\"url.xml\"</span>, <span class=\"keyword\">function</span>( data ) {</code></div></div><div class=\"container\"><div class=\"line\"><code>  <span class=\"keyword\">var</span> $child = $( data ).find( <span class=\"string\">\"child\"</span> );</code></div></div><div class=\"container\"><div class=\"line\"><code>});</code></div></div></pre>\n                              </td>\n                            </tr>\n                          </tbody>\n                        </table>\n                      </div>\n\n                      <h4 id=\"cloning-jquery-objects\">\n                        Cloning jQuery Objects\n                      </h4>\n                      <p>\n                        When a jQuery object is passed to the\n                        <code>$()</code> function, a clone of the object is\n                        created. This new jQuery object references the same DOM\n                        elements as the initial one.\n                      </p>\n\n                      <h4 id=\"returning-empty-set\">\n                        Returning an Empty Set\n                      </h4>\n                      <p>\n                        As of jQuery 1.4, calling the\n                        <code>jQuery()</code> method with\n                        <em>no arguments</em> returns an empty jQuery set (with\n                        a\n                        <code><a href=\"/length/\" src=\"null\">.length</a></code>\n                        property of 0). In previous versions of jQuery, this\n                        would return a set containing the document node.\n                      </p>\n                      <h4 id=\"working-with-plain-objects\">\n                        Working With Plain Objects\n                      </h4>\n                      <p>\n                        At present, the only operations supported on plain\n                        JavaScript objects wrapped in jQuery are:\n                        <code>.data()</code\n                        >,<code>.prop()</code>,<code>.on()</code>,\n                        <code>.off()</code>, <code>.trigger()</code> and\n                        <code>.triggerHandler()</code>. The use of\n                        <code>.data()</code> (or any method requiring\n                        <code>.data()</code>) on a plain object will result in a\n                        new property on the object called jQuery{randomNumber}\n                        (eg. jQuery123456789).\n                      </p>\n                      <div class=\"syntaxhighlighter javascript nogutter\">\n                        <table>\n                          <tbody>\n                            <tr>\n                              <td class=\"gutter\">\n                                <div class=\"line n1\">\n                                  1\n                                </div>\n\n                                <div class=\"line n2\">\n                                  2\n                                </div>\n\n                                <div class=\"line n3\">\n                                  3\n                                </div>\n\n                                <div class=\"line n4\">\n                                  4\n                                </div>\n\n                                <div class=\"line n5\">\n                                  5\n                                </div>\n\n                                <div class=\"line n6\">\n                                  6\n                                </div>\n\n                                <div class=\"line n7\">\n                                  7\n                                </div>\n\n                                <div class=\"line n8\">\n                                  8\n                                </div>\n\n                                <div class=\"line n9\">\n                                  9\n                                </div>\n\n                                <div class=\"line n10\">\n                                  10\n                                </div>\n\n                                <div class=\"line n11\">\n                                  11\n                                </div>\n\n                                <div class=\"line n12\">\n                                  12\n                                </div>\n\n                                <div class=\"line n13\">\n                                  13\n                                </div>\n\n                                <div class=\"line n14\">\n                                  14\n                                </div>\n\n                                <div class=\"line n15\">\n                                  15\n                                </div>\n\n                                <div class=\"line n16\">\n                                  16\n                                </div>\n\n                                <div class=\"line n17\">\n                                  17\n                                </div>\n\n                                <div class=\"line n18\">\n                                  18\n                                </div>\n\n                                <div class=\"line n19\">\n                                  19\n                                </div>\n\n                                <div class=\"line n20\">\n                                  20\n                                </div>\n\n                                <div class=\"line n21\">\n                                  21\n                                </div>\n\n                                <div class=\"line n22\">\n                                  22\n                                </div>\n\n                                <div class=\"line n23\">\n                                  23\n                                </div>\n                              </td>\n                              <td class=\"code\">\n                                <pre><div class=\"container\"><div class=\"line\"><code><span class=\"comment\">// Define a plain object</span></code></div></div><div class=\"container\"><div class=\"line\"><code><span class=\"keyword\">var</span> foo = { foo: <span class=\"string\">\"bar\"</span>, hello: <span class=\"string\">\"world\"</span> };</code></div></div><div class=\"container\"><div class=\"line\"><code> </code></div></div><div class=\"container\"><div class=\"line\"><code><span class=\"comment\">// Pass it to the jQuery function</span></code></div></div><div class=\"container\"><div class=\"line\"><code><span class=\"keyword\">var</span> $foo = $( foo );</code></div></div><div class=\"container\"><div class=\"line\"><code> </code></div></div><div class=\"container\"><div class=\"line\"><code><span class=\"comment\">// Test accessing property values</span></code></div></div><div class=\"container\"><div class=\"line\"><code><span class=\"keyword\">var</span> test1 = $foo.prop( <span class=\"string\">\"foo\"</span> ); <span class=\"comment\">// bar</span></code></div></div><div class=\"container\"><div class=\"line\"><code> </code></div></div><div class=\"container\"><div class=\"line\"><code><span class=\"comment\">// Test setting property values</span></code></div></div><div class=\"container\"><div class=\"line\"><code>$foo.prop( <span class=\"string\">\"foo\"</span>, <span class=\"string\">\"foobar\"</span> );</code></div></div><div class=\"container\"><div class=\"line\"><code><span class=\"keyword\">var</span> test2 = $foo.prop( <span class=\"string\">\"foo\"</span> ); <span class=\"comment\">// foobar</span></code></div></div><div class=\"container\"><div class=\"line\"><code> </code></div></div><div class=\"container\"><div class=\"line\"><code><span class=\"comment\">// Test using .data() as summarized above</span></code></div></div><div class=\"container\"><div class=\"line\"><code>$foo.data( <span class=\"string\">\"keyName\"</span>, <span class=\"string\">\"someValue\"</span> );</code></div></div><div class=\"container\"><div class=\"line\"><code>console.log( $foo ); <span class=\"comment\">// will now contain a jQuery{randomNumber} property</span></code></div></div><div class=\"container\"><div class=\"line\"><code> </code></div></div><div class=\"container\"><div class=\"line\"><code><span class=\"comment\">// Test binding an event name and triggering</span></code></div></div><div class=\"container\"><div class=\"line\"><code>$foo.on( <span class=\"string\">\"eventName\"</span>, <span class=\"function\"><span class=\"keyword\">function</span> <span class=\"params\">()</span> {</span></code></div></div><div class=\"container\"><div class=\"line\"><code>  console.log( <span class=\"string\">\"eventName was called\"</span> );</code></div></div><div class=\"container\"><div class=\"line\"><code>});</code></div></div><div class=\"container\"><div class=\"line\"><code> </code></div></div><div class=\"container\"><div class=\"line\"><code>$foo.trigger( <span class=\"string\">\"eventName\"</span> ); <span class=\"comment\">// Logs \"eventName was called\"</span></code></div></div></pre>\n                              </td>\n                            </tr>\n                          </tbody>\n                        </table>\n                      </div>\n\n                      <p>\n                        Should\n                        <code>.trigger( \"eventName\" )</code>\n                        be used, it will search for an \"eventName\" property on\n                        the object and attempt to execute it after any attached\n                        jQuery handlers are executed. It does not check whether\n                        the property is a function or not. To avoid this\n                        behavior,\n                        <code>.triggerHandler( \"eventName\" )</code>\n                        should be used instead.\n                      </p>\n                      <div class=\"syntaxhighlighter javascript nogutter\">\n                        <table>\n                          <tbody>\n                            <tr>\n                              <td class=\"gutter\">\n                                <div class=\"line n1\">\n                                  1\n                                </div>\n                              </td>\n                              <td class=\"code\">\n                                <pre><div class=\"container\"><div class=\"line\"><code>$foo.triggerHandler( <span class=\"string\">\"eventName\"</span> ); <span class=\"comment\">// Also logs \"eventName was called\"</span></code></div></div></pre>\n                              </td>\n                            </tr>\n                          </tbody>\n                        </table>\n                      </div>\n                    </div>\n                    <section class=\"entry-examples\" id=\"entry-examples\">\n                      <header><h2>Examples:</h2></header>\n                      <div class=\"entry-example\" id=\"example-0\">\n                        <h4>\n                          Example:\n                          <span class=\"desc\"\n                            >Find all p elements that are children of a div\n                            element and apply a border to them.</span\n                          >\n                        </h4>\n                        <div class=\"syntaxhighlighter xml\">\n                          <table>\n                            <tbody>\n                              <tr>\n                                <td class=\"gutter\">\n                                  <div class=\"line n1\">\n                                    1\n                                  </div>\n\n                                  <div class=\"line n2\">\n                                    2\n                                  </div>\n\n                                  <div class=\"line n3\">\n                                    3\n                                  </div>\n\n                                  <div class=\"line n4\">\n                                    4\n                                  </div>\n\n                                  <div class=\"line n5\">\n                                    5\n                                  </div>\n\n                                  <div class=\"line n6\">\n                                    6\n                                  </div>\n\n                                  <div class=\"line n7\">\n                                    7\n                                  </div>\n\n                                  <div class=\"line n8\">\n                                    8\n                                  </div>\n\n                                  <div class=\"line n9\">\n                                    9\n                                  </div>\n\n                                  <div class=\"line n10\">\n                                    10\n                                  </div>\n\n                                  <div class=\"line n11\">\n                                    11\n                                  </div>\n\n                                  <div class=\"line n12\">\n                                    12\n                                  </div>\n\n                                  <div class=\"line n13\">\n                                    13\n                                  </div>\n\n                                  <div class=\"line n14\">\n                                    14\n                                  </div>\n\n                                  <div class=\"line n15\">\n                                    15\n                                  </div>\n\n                                  <div class=\"line n16\">\n                                    16\n                                  </div>\n\n                                  <div class=\"line n17\">\n                                    17\n                                  </div>\n\n                                  <div class=\"line n18\">\n                                    18\n                                  </div>\n\n                                  <div class=\"line n19\">\n                                    19\n                                  </div>\n                                </td>\n                                <td class=\"code\">\n                                  <pre><div class=\"container\"><div class=\"line\"><code><span class=\"doctype\">&lt;!doctype html&gt;</span></code></div></div><div class=\"container\"><div class=\"line\"><code><span class=\"tag\">&lt;<span class=\"title\">html</span> <span class=\"attribute\">lang</span>=<span class=\"value\">\"en\"</span>&gt;</span></code></div></div><div class=\"container\"><div class=\"line\"><code><span class=\"tag\">&lt;<span class=\"title\">head</span>&gt;</span></code></div></div><div class=\"container\"><div class=\"line\"><code>  <span class=\"tag\">&lt;<span class=\"title\">meta</span> <span class=\"attribute\">charset</span>=<span class=\"value\">\"utf-8\"</span>&gt;</span></code></div></div><div class=\"container\"><div class=\"line\"><code>  <span class=\"tag\">&lt;<span class=\"title\">title</span>&gt;</span>jQuery demo<span class=\"tag\">&lt;/<span class=\"title\">title</span>&gt;</span></code></div></div><div class=\"container\"><div class=\"line\"><code>  <span class=\"tag\">&lt;<span class=\"title\">script</span> <span class=\"attribute\">src</span>=<span class=\"value\">\"http://code.jquery.com/jquery-1.9.1.js\"</span>&gt;</span><span class=\"javascript\"></span><span class=\"tag\">&lt;/<span class=\"title\">script</span>&gt;</span></code></div></div><div class=\"container\"><div class=\"line\"><code><span class=\"tag\">&lt;/<span class=\"title\">head</span>&gt;</span></code></div></div><div class=\"container\"><div class=\"line\"><code><span class=\"tag\">&lt;<span class=\"title\">body</span>&gt;</span></code></div></div><div class=\"container\"><div class=\"line\"><code> </code></div></div><div class=\"container\"><div class=\"line\"><code><span class=\"tag\">&lt;<span class=\"title\">p</span>&gt;</span>one<span class=\"tag\">&lt;/<span class=\"title\">p</span>&gt;</span></code></div></div><div class=\"container\"><div class=\"line\"><code><span class=\"tag\">&lt;<span class=\"title\">div</span>&gt;</span><span class=\"tag\">&lt;<span class=\"title\">p</span>&gt;</span>two<span class=\"tag\">&lt;/<span class=\"title\">p</span>&gt;</span><span class=\"tag\">&lt;/<span class=\"title\">div</span>&gt;</span></code></div></div><div class=\"container\"><div class=\"line\"><code><span class=\"tag\">&lt;<span class=\"title\">p</span>&gt;</span>three<span class=\"tag\">&lt;/<span class=\"title\">p</span>&gt;</span></code></div></div><div class=\"container\"><div class=\"line\"><code> </code></div></div><div class=\"container\"><div class=\"line\"><code><span class=\"tag\">&lt;<span class=\"title\">script</span>&gt;</span><span class=\"javascript\"></span></code></div></div><div class=\"container\"><div class=\"line\"><code>$( <span class=\"string\">\"div &gt; p\"</span> ).css( <span class=\"string\">\"border\"</span>, <span class=\"string\">\"1px solid gray\"</span> );</code></div></div><div class=\"container\"><div class=\"line\"><code><span class=\"tag\">&lt;/<span class=\"title\">script</span>&gt;</span></code></div></div><div class=\"container\"><div class=\"line\"><code> </code></div></div><div class=\"container\"><div class=\"line\"><code><span class=\"tag\">&lt;/<span class=\"title\">body</span>&gt;</span></code></div></div><div class=\"container\"><div class=\"line\"><code><span class=\"tag\">&lt;/<span class=\"title\">html</span>&gt;</span></code></div></div></pre>\n                                </td>\n                              </tr>\n                            </tbody>\n                          </table>\n                        </div>\n\n                        <h4>Demo:</h4>\n                        <div class=\"demo code-demo\">\n                          <iframe width=\"100%\" height=\"250\" title=\"Demo\"></iframe>\n                        </div>\n                      </div>\n                      <div class=\"entry-example\" id=\"example-1\">\n                        <h4>\n                          Example:\n                          <span class=\"desc\"\n                            >Find all inputs of type radio within the first form\n                            in the document.</span\n                          >\n                        </h4>\n                        <div class=\"syntaxhighlighter javascript\">\n                          <table>\n                            <tbody>\n                              <tr>\n                                <td class=\"gutter\">\n                                  <div class=\"line n1\">\n                                    1\n                                  </div>\n                                </td>\n                                <td class=\"code\">\n                                  <pre><div class=\"container\"><div class=\"line\"><code>$( <span class=\"string\">\"input:radio\"</span>, document.forms[ <span class=\"number\">0</span> ] );</code></div></div></pre>\n                                </td>\n                              </tr>\n                            </tbody>\n                          </table>\n                        </div>\n                      </div>\n                      <div class=\"entry-example\" id=\"example-2\">\n                        <h4>\n                          Example:\n                          <span class=\"desc\"\n                            >Find all div elements within an XML document from\n                            an Ajax response.</span\n                          >\n                        </h4>\n                        <div class=\"syntaxhighlighter javascript\">\n                          <table>\n                            <tbody>\n                              <tr>\n                                <td class=\"gutter\">\n                                  <div class=\"line n1\">\n                                    1\n                                  </div>\n                                </td>\n                                <td class=\"code\">\n                                  <pre><div class=\"container\"><div class=\"line\"><code>$( <span class=\"string\">\"div\"</span>, xml.responseXML );</code></div></div></pre>\n                                </td>\n                              </tr>\n                            </tbody>\n                          </table>\n                        </div>\n                      </div>\n                      <div class=\"entry-example\" id=\"example-3\">\n                        <h4>\n                          Example:\n                          <span class=\"desc\"\n                            >Set the background color of the page to\n                            black.</span\n                          >\n                        </h4>\n                        <div class=\"syntaxhighlighter javascript\">\n                          <table>\n                            <tbody>\n                              <tr>\n                                <td class=\"gutter\">\n                                  <div class=\"line n1\">\n                                    1\n                                  </div>\n                                </td>\n                                <td class=\"code\">\n                                  <pre><div class=\"container\"><div class=\"line\"><code>$( document.body ).css( <span class=\"string\">\"background\"</span>, <span class=\"string\">\"black\"</span> );</code></div></div></pre>\n                                </td>\n                              </tr>\n                            </tbody>\n                          </table>\n                        </div>\n                      </div>\n                      <div class=\"entry-example\" id=\"example-4\">\n                        <h4>\n                          Example:\n                          <span class=\"desc\"\n                            >Hide all the input elements within a form.</span\n                          >\n                        </h4>\n                        <div class=\"syntaxhighlighter javascript\">\n                          <table>\n                            <tbody>\n                              <tr>\n                                <td class=\"gutter\">\n                                  <div class=\"line n1\">\n                                    1\n                                  </div>\n                                </td>\n                                <td class=\"code\">\n                                  <pre><div class=\"container\"><div class=\"line\"><code>$( myForm.elements ).hide();</code></div></div></pre>\n                                </td>\n                              </tr>\n                            </tbody>\n                          </table>\n                        </div>\n                      </div>\n                    </section>\n                  </div>\n                </article>\n                <article id=\"jQuery2\" class=\"entry method\">\n                  <h2 class=\"section-title\">\n                    <span class=\"name\">jQuery( html [, ownerDocument ] )</span\n                    ><span class=\"returns\"\n                      >Returns:\n                      <a href=\"http://api.jquery.com/Types/#jQuery\" src=\"null\"\n                        >jQuery</a\n                      ></span\n                    >\n                  </h2>\n                  <div class=\"entry-wrapper\">\n                    <p class=\"desc\">\n                      <strong>Description: </strong>Creates DOM elements on the\n                      fly from the provided string of raw HTML.\n                    </p>\n                    <ul class=\"signatures\">\n                      <li class=\"signature\">\n                        <h4 class=\"name\">\n                          <span class=\"version-details\"\n                            >version added:\n                            <a href=\"/category/version/1.0/\" src=\"null\"\n                              >1.0</a\n                            ></span\n                          ><a\n                            id=\"jQuery-html-ownerDocument\"\n                            href=\"#jQuery-html-ownerDocument\"\n                            src=\"null\"\n                            ><span class=\"icon-link\"></span>jQuery( html [,\n                            ownerDocument ] )</a\n                          >\n                        </h4>\n                        <ul>\n                          <li>\n                            <div>\n                              <strong>html</strong>\n                            </div>\n                            <div>\n                              Type:\n                              <a\n                                href=\"http://api.jquery.com/Types/#htmlString\"\n                                src=\"null\"\n                                >htmlString</a\n                              >\n                            </div>\n                            <div>\n                              A string of HTML to create on the fly. Note that\n                              this parses HTML,\n                              <strong>not</strong>\n                              XML.\n                            </div>\n                          </li>\n                          <li>\n                            <div>\n                              <strong>ownerDocument</strong>\n                            </div>\n                            <div>\n                              Type:\n                              <a\n                                href=\"http://api.jquery.com/Types/#document\"\n                                src=\"null\"\n                                >document</a\n                              >\n                            </div>\n                            <div>\n                              A document in which the new elements will be\n                              created.\n                            </div>\n                          </li>\n                        </ul>\n                      </li>\n                      <li class=\"signature\">\n                        <h4 class=\"name\">\n                          <span class=\"version-details\"\n                            >version added:\n                            <a href=\"/category/version/1.4/\" src=\"null\"\n                              >1.4</a\n                            ></span\n                          ><a\n                            id=\"jQuery-html-attributes\"\n                            href=\"#jQuery-html-attributes\"\n                            src=\"null\"\n                            ><span class=\"icon-link\"></span>jQuery( html,\n                            attributes )</a\n                          >\n                        </h4>\n                        <ul>\n                          <li>\n                            <div>\n                              <strong>html</strong>\n                            </div>\n                            <div>\n                              Type:\n                              <a\n                                href=\"http://api.jquery.com/Types/#htmlString\"\n                                src=\"null\"\n                                >htmlString</a\n                              >\n                            </div>\n                            <div>\n                              A string defining a single, standalone, HTML\n                              element (e.g. &lt;div/&gt; or\n                              &lt;div&gt;&lt;/div&gt;).\n                            </div>\n                          </li>\n                          <li>\n                            <div>\n                              <strong>attributes</strong>\n                            </div>\n                            <div>\n                              Type:\n                              <a\n                                href=\"http://api.jquery.com/Types/#PlainObject\"\n                                src=\"null\"\n                                >PlainObject</a\n                              >\n                            </div>\n                            <div>\n                              An object of attributes, events, and methods to\n                              call on the newly-created element.\n                            </div>\n                          </li>\n                        </ul>\n                      </li>\n                    </ul>\n                    <div class=\"longdesc\" id=\"entry-longdesc-1\">\n                      <h4 id=\"creating-new-elements\">\n                        Creating New Elements\n                      </h4>\n                      <p>\n                        If a string is passed as the parameter to\n                        <code>$()</code>, jQuery examines the string to see if\n                        it looks like HTML (i.e., it starts with\n                        <code>&lt;tag ... &gt;</code>). If not, the string is\n                        interpreted as a selector expression, as explained\n                        above. But if the string appears to be an HTML snippet,\n                        jQuery attempts to create new DOM elements as described\n                        by the HTML. Then a jQuery object is created and\n                        returned that refers to these elements. You can perform\n                        any of the usual jQuery methods on this object:\n                      </p>\n                      <div class=\"syntaxhighlighter javascript nogutter\">\n                        <table>\n                          <tbody>\n                            <tr>\n                              <td class=\"gutter\">\n                                <div class=\"line n1\">\n                                  1\n                                </div>\n                              </td>\n                              <td class=\"code\">\n                                <pre><div class=\"container\"><div class=\"line\"><code>$( <span class=\"string\">\"&lt;p id='test'&gt;My &lt;em&gt;new&lt;/em&gt; text&lt;/p&gt;\"</span> ).appendTo( <span class=\"string\">\"body\"</span> );</code></div></div></pre>\n                              </td>\n                            </tr>\n                          </tbody>\n                        </table>\n                      </div>\n\n                      <p>\n                        For explicit parsing of a string to HTML, use the\n                        <a href=\"/jQuery.parseHTML/\" src=\"null\"\n                          >$.parseHTML()</a\n                        >\n                        method.\n                      </p>\n                      <p>\n                        By default, elements are created with an\n                        <code>ownerDocument</code>\n                        matching the document into which the jQuery library was\n                        loaded. Elements being injected into a different\n                        document should be created using that document, e.g.,\n                        <code\n                          >$(\"&lt;p&gt;hello iframe&lt;/p&gt;\",\n                          $(\"#myiframe\").prop(\"contentWindow\").document)</code\n                        >.\n                      </p>\n                      <p>\n                        If the HTML is more complex than a single tag without\n                        attributes, as it is in the above example, the actual\n                        creation of the elements is handled by the browser's\n                        <code>innerHTML</code>\n                        mechanism. In most cases, jQuery creates a new\n                        &lt;div&gt; element and sets the innerHTML property of\n                        the element to the HTML snippet that was passed in. When\n                        the parameter has a single tag (with optional closing\n                        tag or quick-closing) —\n                        <code>$( \"&lt;img&nbsp;/&gt;\" )</code>\n                        or\n                        <code>$( \"&lt;img&gt;\" )</code>,\n                        <code>$( \"&lt;a&gt;&lt;/a&gt;\" )</code>\n                        or\n                        <code>$( \"&lt;a&gt;\" )</code> — jQuery creates the\n                        element using the native JavaScript\n                        <code>createElement()</code>\n                        function.\n                      </p>\n                      <p>\n                        When passing in complex HTML, some browsers may not\n                        generate a DOM that exactly replicates the HTML source\n                        provided. As mentioned, jQuery uses the browser\"s\n                        <code>.innerHTML</code> property to parse the passed\n                        HTML and insert it into the current document. During\n                        this process, some browsers filter out certain elements\n                        such as <code>&lt;html&gt;</code>,\n                        <code>&lt;title&gt;</code>, or\n                        <code>&lt;head&gt;</code>\n                        elements. As a result, the elements inserted may not be\n                        representative of the original string passed.\n                      </p>\n                      <p>\n                        Filtering isn't, however, limited to these tags. For\n                        example, Internet Explorer prior to version 8 will also\n                        convert all <code>href</code> properties on links to\n                        absolute URLs, and Internet Explorer prior to version 9\n                        will not correctly handle HTML5 elements without the\n                        addition of a separate\n                        <a href=\"http://code.google.com/p/html5shiv/\" src=\"null\"\n                          >compatibility layer</a\n                        >.\n                      </p>\n                      <p>\n                        To ensure cross-platform compatibility, the snippet must\n                        be well-formed. Tags that can contain other elements\n                        should be paired with a closing tag:\n                      </p>\n                      <div class=\"syntaxhighlighter javascript nogutter\">\n                        <table>\n                          <tbody>\n                            <tr>\n                              <td class=\"gutter\">\n                                <div class=\"line n1\">\n                                  1\n                                </div>\n                              </td>\n                              <td class=\"code\">\n                                <pre><div class=\"container\"><div class=\"line\"><code>$( <span class=\"string\">\"&lt;a href='http://jquery.com'&gt;&lt;/a&gt;\"</span> );</code></div></div></pre>\n                              </td>\n                            </tr>\n                          </tbody>\n                        </table>\n                      </div>\n\n                      <p>\n                        Tags that cannot contain elements may be quick-closed or\n                        not:\n                      </p>\n                      <div class=\"syntaxhighlighter javascript nogutter\">\n                        <table>\n                          <tbody>\n                            <tr>\n                              <td class=\"gutter\">\n                                <div class=\"line n1\">\n                                  1\n                                </div>\n\n                                <div class=\"line n2\">\n                                  2\n                                </div>\n                              </td>\n                              <td class=\"code\">\n                                <pre><div class=\"container\"><div class=\"line\"><code>$( <span class=\"string\">\"&lt;img&gt;\"</span> );</code></div></div><div class=\"container\"><div class=\"line\"><code>$( <span class=\"string\">\"&lt;input&gt;\"</span> );</code></div></div></pre>\n                              </td>\n                            </tr>\n                          </tbody>\n                        </table>\n                      </div>\n\n                      <p>\n                        When passing HTML to\n                        <code>jQuery()</code>, please also note that text nodes\n                        are not treated as DOM elements. With the exception of a\n                        few methods (such as <code>.content()</code>), they are\n                        generally otherwise ignored or removed. E.g:\n                      </p>\n                      <div class=\"syntaxhighlighter javascript nogutter\">\n                        <table>\n                          <tbody>\n                            <tr>\n                              <td class=\"gutter\">\n                                <div class=\"line n1\">\n                                  1\n                                </div>\n\n                                <div class=\"line n2\">\n                                  2\n                                </div>\n                              </td>\n                              <td class=\"code\">\n                                <pre><div class=\"container\"><div class=\"line\"><code><span class=\"keyword\">var</span> el = $( <span class=\"string\">\"1&lt;br&gt;2&lt;br&gt;3\"</span> ); <span class=\"comment\">// returns [&lt;br&gt;, \"2\", &lt;br&gt;]</span></code></div></div><div class=\"container\"><div class=\"line\"><code>el = $( <span class=\"string\">\"1&lt;br&gt;2&lt;br&gt;3 &gt;\"</span> ); <span class=\"comment\">// returns [&lt;br&gt;, \"2\", &lt;br&gt;, \"3 &amp;gt;\"]</span></code></div></div></pre>\n                              </td>\n                            </tr>\n                          </tbody>\n                        </table>\n                      </div>\n\n                      <p>This behavior is expected.</p>\n                      <p>\n                        As of jQuery 1.4, the second argument to\n                        <code>jQuery()</code> can accept a plain object\n                        consisting of a superset of the properties that can be\n                        passed to the\n                        <a href=\"/attr/\" src=\"null\">.attr()</a>\n                        method.\n                      </p>\n                      <p>\n                        <strong>Important:</strong> If the second argument is\n                        passed, the HTML string in the first argument must\n                        represent a a simple element with no attributes.\n                        <strong>As of jQuery 1.4</strong>, any\n                        <a href=\"/category/events/\" src=\"null\">event type</a>\n                        can be passed in, and the following jQuery methods can\n                        be called:\n                        <a href=\"/val/\" src=\"null\">val</a>,\n                        <a href=\"/css/\" src=\"null\">css</a>,\n                        <a href=\"/html/\" src=\"null\">html</a>,\n                        <a href=\"/text/\" src=\"null\">text</a>,\n                        <a href=\"/data/\" src=\"null\">data</a>,\n                        <a href=\"/width/\" src=\"null\">width</a>,\n                        <a href=\"/height/\" src=\"null\">height</a>, or\n                        <a href=\"/offset/\" src=\"null\">offset</a>.\n                      </p>\n                      <p>\n                        <strong>As of jQuery 1.8</strong>, any jQuery instance\n                        method (a method of <code>jQuery.fn</code>) can be used\n                        as a property of the object passed to the second\n                        parameter:\n                      </p>\n                      <div class=\"syntaxhighlighter javascript nogutter\">\n                        <table>\n                          <tbody>\n                            <tr>\n                              <td class=\"gutter\">\n                                <div class=\"line n1\">\n                                  1\n                                </div>\n\n                                <div class=\"line n2\">\n                                  2\n                                </div>\n\n                                <div class=\"line n3\">\n                                  3\n                                </div>\n\n                                <div class=\"line n4\">\n                                  4\n                                </div>\n\n                                <div class=\"line n5\">\n                                  5\n                                </div>\n\n                                <div class=\"line n6\">\n                                  6\n                                </div>\n\n                                <div class=\"line n7\">\n                                  7\n                                </div>\n\n                                <div class=\"line n8\">\n                                  8\n                                </div>\n                              </td>\n                              <td class=\"code\">\n                                <pre><div class=\"container\"><div class=\"line\"><code>$( <span class=\"string\">\"&lt;div&gt;&lt;/div&gt;\"</span>, {</code></div></div><div class=\"container\"><div class=\"line\"><code>  <span class=\"string\">\"class\"</span>: <span class=\"string\">\"my-div\"</span>,</code></div></div><div class=\"container\"><div class=\"line\"><code>  on: {</code></div></div><div class=\"container\"><div class=\"line\"><code>    touchstart: <span class=\"keyword\">function</span>( event ) {</code></div></div><div class=\"container\"><div class=\"line\"><code>      <span class=\"comment\">// Do something</span></code></div></div><div class=\"container\"><div class=\"line\"><code>    }</code></div></div><div class=\"container\"><div class=\"line\"><code>  }</code></div></div><div class=\"container\"><div class=\"line\"><code>}).appendTo( <span class=\"string\">\"body\"</span> );</code></div></div></pre>\n                              </td>\n                            </tr>\n                          </tbody>\n                        </table>\n                      </div>\n\n                      <p>\n                        The name\n                        <code>\"class\"</code> must be quoted in the object since\n                        it is a JavaScript reserved word, and\n                        <code>\"className\"</code> cannot be used since it refers\n                        to the DOM property, not the attribute.\n                      </p>\n                      <p>\n                        While the second argument is convenient, its flexibility\n                        can lead to unintended consequences (e.g.\n                        <code>$( \"&lt;input&gt;\", {size: \"4\"} )</code>\n                        calling the\n                        <code>.size()</code> method instead of setting the size\n                        attribute). The previous code block could thus be\n                        written instead as:\n                      </p>\n                      <div class=\"syntaxhighlighter javascript nogutter\">\n                        <table>\n                          <tbody>\n                            <tr>\n                              <td class=\"gutter\">\n                                <div class=\"line n1\">\n                                  1\n                                </div>\n\n                                <div class=\"line n2\">\n                                  2\n                                </div>\n\n                                <div class=\"line n3\">\n                                  3\n                                </div>\n\n                                <div class=\"line n4\">\n                                  4\n                                </div>\n\n                                <div class=\"line n5\">\n                                  5\n                                </div>\n\n                                <div class=\"line n6\">\n                                  6\n                                </div>\n\n                                <div class=\"line n7\">\n                                  7\n                                </div>\n\n                                <div class=\"line n8\">\n                                  8\n                                </div>\n                              </td>\n                              <td class=\"code\">\n                                <pre><div class=\"container\"><div class=\"line\"><code>$( <span class=\"string\">\"&lt;div&gt;&lt;/div&gt;\"</span> )</code></div></div><div class=\"container\"><div class=\"line\"><code>  .addClass( <span class=\"string\">\"my-div\"</span> )</code></div></div><div class=\"container\"><div class=\"line\"><code>  .on({</code></div></div><div class=\"container\"><div class=\"line\"><code>    touchstart: <span class=\"keyword\">function</span>( event ) {</code></div></div><div class=\"container\"><div class=\"line\"><code>      <span class=\"comment\">// Do something</span></code></div></div><div class=\"container\"><div class=\"line\"><code>    }</code></div></div><div class=\"container\"><div class=\"line\"><code>  })</code></div></div><div class=\"container\"><div class=\"line\"><code>    .appendTo( <span class=\"string\">\"body\"</span> );</code></div></div></pre>\n                              </td>\n                            </tr>\n                          </tbody>\n                        </table>\n                      </div>\n                    </div>\n                    <section class=\"entry-examples\" id=\"entry-examples-1\">\n                      <header><h2>Examples:</h2></header>\n                      <div class=\"entry-example\" id=\"example-1-0\">\n                        <h4>\n                          Example:\n                          <span class=\"desc\"\n                            >Create a div element (and all of its contents)\n                            dynamically and append it to the body element.\n                            Internally, an element is created and its innerHTML\n                            property set to the given markup.</span\n                          >\n                        </h4>\n                        <div class=\"syntaxhighlighter javascript\">\n                          <table>\n                            <tbody>\n                              <tr>\n                                <td class=\"gutter\">\n                                  <div class=\"line n1\">\n                                    1\n                                  </div>\n                                </td>\n                                <td class=\"code\">\n                                  <pre><div class=\"container\"><div class=\"line\"><code>$( <span class=\"string\">\"&lt;div&gt;&lt;p&gt;Hello&lt;/p&gt;&lt;/div&gt;\"</span> ).appendTo( <span class=\"string\">\"body\"</span> )</code></div></div></pre>\n                                </td>\n                              </tr>\n                            </tbody>\n                          </table>\n                        </div>\n                      </div>\n                      <div class=\"entry-example\" id=\"example-1-1\">\n                        <h4>\n                          Example:\n                          <span class=\"desc\">Create some DOM elements.</span>\n                        </h4>\n                        <div class=\"syntaxhighlighter javascript\">\n                          <table>\n                            <tbody>\n                              <tr>\n                                <td class=\"gutter\">\n                                  <div class=\"line n1\">\n                                    1\n                                  </div>\n\n                                  <div class=\"line n2\">\n                                    2\n                                  </div>\n\n                                  <div class=\"line n3\">\n                                    3\n                                  </div>\n\n                                  <div class=\"line n4\">\n                                    4\n                                  </div>\n\n                                  <div class=\"line n5\">\n                                    5\n                                  </div>\n\n                                  <div class=\"line n6\">\n                                    6\n                                  </div>\n\n                                  <div class=\"line n7\">\n                                    7\n                                  </div>\n\n                                  <div class=\"line n8\">\n                                    8\n                                  </div>\n                                </td>\n                                <td class=\"code\">\n                                  <pre><div class=\"container\"><div class=\"line\"><code>$( <span class=\"string\">\"&lt;div/&gt;\"</span>, {</code></div></div><div class=\"container\"><div class=\"line\"><code>  <span class=\"string\">\"class\"</span>: <span class=\"string\">\"test\"</span>,</code></div></div><div class=\"container\"><div class=\"line\"><code>  text: <span class=\"string\">\"Click me!\"</span>,</code></div></div><div class=\"container\"><div class=\"line\"><code>  click: <span class=\"keyword\">function</span>() {</code></div></div><div class=\"container\"><div class=\"line\"><code>    $( <span class=\"keyword\">this</span> ).toggleClass( <span class=\"string\">\"test\"</span> );</code></div></div><div class=\"container\"><div class=\"line\"><code>  }</code></div></div><div class=\"container\"><div class=\"line\"><code>})</code></div></div><div class=\"container\"><div class=\"line\"><code>  .appendTo( <span class=\"string\">\"body\"</span> );</code></div></div></pre>\n                                </td>\n                              </tr>\n                            </tbody>\n                          </table>\n                        </div>\n                      </div>\n                    </section>\n                  </div>\n                </article>\n                <article id=\"jQuery3\" class=\"entry method\">\n                  <h2 class=\"section-title\">\n                    <span class=\"name\">jQuery( callback )</span\n                    ><span class=\"returns\"\n                      >Returns:\n                      <a href=\"http://api.jquery.com/Types/#jQuery\" src=\"null\"\n                        >jQuery</a\n                      ></span\n                    >\n                  </h2>\n                  <div class=\"entry-wrapper\">\n                    <p class=\"desc\">\n                      <strong>Description: </strong>Binds a function to be\n                      executed when the DOM has finished loading.\n                    </p>\n                    <ul class=\"signatures\">\n                      <li class=\"signature\">\n                        <h4 class=\"name\">\n                          <span class=\"version-details\"\n                            >version added:\n                            <a href=\"/category/version/1.0/\" src=\"null\"\n                              >1.0</a\n                            ></span\n                          ><a\n                            id=\"jQuery-callback\"\n                            href=\"#jQuery-callback\"\n                            src=\"null\"\n                            ><span class=\"icon-link\"></span>jQuery( callback\n                            )</a\n                          >\n                        </h4>\n                        <ul>\n                          <li>\n                            <div>\n                              <strong>callback</strong>\n                            </div>\n                            <div>\n                              Type:\n                              <a\n                                href=\"http://api.jquery.com/Types/#Function\"\n                                src=\"null\"\n                                >Function</a\n                              >()\n                            </div>\n                            <div>\n                              The function to execute when the DOM is ready.\n                            </div>\n                          </li>\n                        </ul>\n                      </li>\n                    </ul>\n                    <div class=\"longdesc\" id=\"entry-longdesc-2\">\n                      <p>\n                        This function behaves just like\n                        <code>$( document ).ready()</code>, in that it should be\n                        used to wrap other <code>$()</code> operations on your\n                        page that depend on the DOM being ready. While this\n                        function is, technically, chainable, there really isn\"t\n                        much use for chaining against it.\n                      </p>\n                    </div>\n                    <section class=\"entry-examples\" id=\"entry-examples-2\">\n                      <header><h2>Examples:</h2></header>\n                      <div class=\"entry-example\" id=\"example-2-0\">\n                        <h4>\n                          Example:\n                          <span class=\"desc\"\n                            >Execute the function when the DOM is ready to be\n                            used.</span\n                          >\n                        </h4>\n                        <div class=\"syntaxhighlighter javascript\">\n                          <table>\n                            <tbody>\n                              <tr>\n                                <td class=\"gutter\">\n                                  <div class=\"line n1\">\n                                    1\n                                  </div>\n\n                                  <div class=\"line n2\">\n                                    2\n                                  </div>\n\n                                  <div class=\"line n3\">\n                                    3\n                                  </div>\n                                </td>\n                                <td class=\"code\">\n                                  <pre><div class=\"container\"><div class=\"line\"><code>$(<span class=\"keyword\">function</span>() {</code></div></div><div class=\"container\"><div class=\"line\"><code>  <span class=\"comment\">// Document is ready</span></code></div></div><div class=\"container\"><div class=\"line\"><code>});</code></div></div></pre>\n                                </td>\n                              </tr>\n                            </tbody>\n                          </table>\n                        </div>\n                      </div>\n                      <div class=\"entry-example\" id=\"example-2-1\">\n                        <h4>\n                          Example:\n                          <span class=\"desc\"\n                            >Use both the shortcut for $(document).ready() and\n                            the argument to write failsafe jQuery code using the\n                            $ alias, without relying on the global alias.</span\n                          >\n                        </h4>\n                        <div class=\"syntaxhighlighter javascript\">\n                          <table>\n                            <tbody>\n                              <tr>\n                                <td class=\"gutter\">\n                                  <div class=\"line n1\">\n                                    1\n                                  </div>\n\n                                  <div class=\"line n2\">\n                                    2\n                                  </div>\n\n                                  <div class=\"line n3\">\n                                    3\n                                  </div>\n                                </td>\n                                <td class=\"code\">\n                                  <pre><div class=\"container\"><div class=\"line\"><code>jQuery(<span class=\"keyword\">function</span>( $ ) {</code></div></div><div class=\"container\"><div class=\"line\"><code>  <span class=\"comment\">// Your code using failsafe $ alias here...</span></code></div></div><div class=\"container\"><div class=\"line\"><code>});</code></div></div></pre>\n                                </td>\n                              </tr>\n                            </tbody>\n                          </table>\n                        </div>\n                      </div>\n                    </section>\n                  </div>\n                </article>\n              </div>\n              <!-- .entry-content -->\n            </article>\n            <!-- #post-339 -->\n          </div>\n\n          <div id=\"sidebar\" class=\"widget-area\" role=\"complementary\">\n            <aside id=\"categories\" class=\"widget\">\n              <ul>\n                <li class=\"cat-item cat-item-1\">\n                  <a\n                    href=\"http://api.jquery.com/category/ajax/\"\n                    title=\"View all posts filed under Ajax\"\n                    src=\"null\"\n                    >Ajax</a\n                  >\n                  <ul class=\"children\">\n                    <li class=\"cat-item cat-item-2\">\n                      <a\n                        href=\"http://api.jquery.com/category/ajax/global-ajax-event-handlers/\"\n                        title=\"View all posts filed under Global Ajax Event Handlers\"\n                        src=\"null\"\n                        >Global Ajax Event Handlers</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-3\">\n                      <a\n                        href=\"http://api.jquery.com/category/ajax/helper-functions/\"\n                        title=\"View all posts filed under Helper Functions\"\n                        src=\"null\"\n                        >Helper Functions</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-4\">\n                      <a\n                        href=\"http://api.jquery.com/category/ajax/low-level-interface/\"\n                        title=\"View all posts filed under Low-Level Interface\"\n                        src=\"null\"\n                        >Low-Level Interface</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-5\">\n                      <a\n                        href=\"http://api.jquery.com/category/ajax/shorthand-methods/\"\n                        title=\"View all posts filed under Shorthand Methods\"\n                        src=\"null\"\n                        >Shorthand Methods</a\n                      >\n                    </li>\n                  </ul>\n                </li>\n                <li class=\"cat-item cat-item-6\">\n                  <a\n                    href=\"http://api.jquery.com/category/attributes/\"\n                    title=\"View all posts filed under Attributes\"\n                    src=\"null\"\n                    >Attributes</a\n                  >\n                </li>\n                <li class=\"cat-item cat-item-7\">\n                  <a\n                    href=\"http://api.jquery.com/category/callbacks-object/\"\n                    title=\"View all posts filed under Callbacks Object\"\n                    src=\"null\"\n                    >Callbacks Object</a\n                  >\n                </li>\n                <li class=\"cat-item cat-item-8 current-cat\">\n                  <a\n                    href=\"http://api.jquery.com/category/core/\"\n                    title=\"View all posts filed under Core\"\n                    src=\"null\"\n                    >Core</a\n                  >\n                </li>\n                <li class=\"cat-item cat-item-9\">\n                  <a\n                    href=\"http://api.jquery.com/category/css/\"\n                    title=\"View all posts filed under CSS\"\n                    src=\"null\"\n                    >CSS</a\n                  >\n                </li>\n                <li class=\"cat-item cat-item-10\">\n                  <a\n                    href=\"http://api.jquery.com/category/data/\"\n                    title=\"View all posts filed under Data\"\n                    src=\"null\"\n                    >Data</a\n                  >\n                </li>\n                <li class=\"cat-item cat-item-11\">\n                  <a\n                    href=\"http://api.jquery.com/category/deferred-object/\"\n                    title=\"View all posts filed under Deferred Object\"\n                    src=\"null\"\n                    >Deferred Object</a\n                  >\n                </li>\n                <li class=\"cat-item cat-item-87\">\n                  <a\n                    href=\"http://api.jquery.com/category/deprecated/\"\n                    title=\"View all posts filed under Deprecated\"\n                    src=\"null\"\n                    >Deprecated</a\n                  >\n                  <ul class=\"children\">\n                    <li class=\"cat-item cat-item-93\">\n                      <a\n                        href=\"http://api.jquery.com/category/deprecated/deprecated-1.10/\"\n                        title=\"View all posts filed under Deprecated 1.10\"\n                        src=\"null\"\n                        >Deprecated 1.10</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-90\">\n                      <a\n                        href=\"http://api.jquery.com/category/deprecated/deprecated-1.3/\"\n                        title=\"View all posts filed under Deprecated 1.3\"\n                        src=\"null\"\n                        >Deprecated 1.3</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-88\">\n                      <a\n                        href=\"http://api.jquery.com/category/deprecated/deprecated-1.7/\"\n                        title=\"View all posts filed under Deprecated 1.7\"\n                        src=\"null\"\n                        >Deprecated 1.7</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-89\">\n                      <a\n                        href=\"http://api.jquery.com/category/deprecated/deprecated-1.8/\"\n                        title=\"View all posts filed under Deprecated 1.8\"\n                        src=\"null\"\n                        >Deprecated 1.8</a\n                      >\n                    </li>\n                  </ul>\n                </li>\n                <li class=\"cat-item cat-item-12\">\n                  <a\n                    href=\"http://api.jquery.com/category/dimensions/\"\n                    title=\"View all posts filed under Dimensions\"\n                    src=\"null\"\n                    >Dimensions</a\n                  >\n                </li>\n                <li class=\"cat-item cat-item-13\">\n                  <a\n                    href=\"http://api.jquery.com/category/effects/\"\n                    title=\"View all posts filed under Effects\"\n                    src=\"null\"\n                    >Effects</a\n                  >\n                  <ul class=\"children\">\n                    <li class=\"cat-item cat-item-14\">\n                      <a\n                        href=\"http://api.jquery.com/category/effects/basics/\"\n                        title=\"View all posts filed under Basics\"\n                        src=\"null\"\n                        >Basics</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-15\">\n                      <a\n                        href=\"http://api.jquery.com/category/effects/custom-effects/\"\n                        title=\"View all posts filed under Custom\"\n                        src=\"null\"\n                        >Custom</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-16\">\n                      <a\n                        href=\"http://api.jquery.com/category/effects/fading/\"\n                        title=\"View all posts filed under Fading\"\n                        src=\"null\"\n                        >Fading</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-17\">\n                      <a\n                        href=\"http://api.jquery.com/category/effects/sliding/\"\n                        title=\"View all posts filed under Sliding\"\n                        src=\"null\"\n                        >Sliding</a\n                      >\n                    </li>\n                  </ul>\n                </li>\n                <li class=\"cat-item cat-item-18\">\n                  <a\n                    href=\"http://api.jquery.com/category/events/\"\n                    title=\"View all posts filed under Events\"\n                    src=\"null\"\n                    >Events</a\n                  >\n                  <ul class=\"children\">\n                    <li class=\"cat-item cat-item-19\">\n                      <a\n                        href=\"http://api.jquery.com/category/events/browser-events/\"\n                        title=\"View all posts filed under Browser Events\"\n                        src=\"null\"\n                        >Browser Events</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-20\">\n                      <a\n                        href=\"http://api.jquery.com/category/events/document-loading/\"\n                        title=\"View all posts filed under Document Loading\"\n                        src=\"null\"\n                        >Document Loading</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-21\">\n                      <a\n                        href=\"http://api.jquery.com/category/events/event-handler-attachment/\"\n                        title=\"View all posts filed under Event Handler Attachment\"\n                        src=\"null\"\n                        >Event Handler Attachment</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-22\">\n                      <a\n                        href=\"http://api.jquery.com/category/events/event-object/\"\n                        title=\"View all posts filed under Event Object\"\n                        src=\"null\"\n                        >Event Object</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-23\">\n                      <a\n                        href=\"http://api.jquery.com/category/events/form-events/\"\n                        title=\"View all posts filed under Form Events\"\n                        src=\"null\"\n                        >Form Events</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-24\">\n                      <a\n                        href=\"http://api.jquery.com/category/events/keyboard-events/\"\n                        title=\"View all posts filed under Keyboard Events\"\n                        src=\"null\"\n                        >Keyboard Events</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-25\">\n                      <a\n                        href=\"http://api.jquery.com/category/events/mouse-events/\"\n                        title=\"View all posts filed under Mouse Events\"\n                        src=\"null\"\n                        >Mouse Events</a\n                      >\n                    </li>\n                  </ul>\n                </li>\n                <li class=\"cat-item cat-item-26\">\n                  <a\n                    href=\"http://api.jquery.com/category/forms/\"\n                    title=\"View all posts filed under Forms\"\n                    src=\"null\"\n                    >Forms</a\n                  >\n                </li>\n                <li class=\"cat-item cat-item-27\">\n                  <a\n                    href=\"http://api.jquery.com/category/internals/\"\n                    title=\"View all posts filed under Internals\"\n                    src=\"null\"\n                    >Internals</a\n                  >\n                </li>\n                <li class=\"cat-item cat-item-28\">\n                  <a\n                    href=\"http://api.jquery.com/category/manipulation/\"\n                    title=\"View all posts filed under Manipulation\"\n                    src=\"null\"\n                    >Manipulation</a\n                  >\n                  <ul class=\"children\">\n                    <li class=\"cat-item cat-item-29\">\n                      <a\n                        href=\"http://api.jquery.com/category/manipulation/class-attribute/\"\n                        title=\"View all posts filed under Class Attribute\"\n                        src=\"null\"\n                        >Class Attribute</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-30\">\n                      <a\n                        href=\"http://api.jquery.com/category/manipulation/copying/\"\n                        title=\"View all posts filed under Copying\"\n                        src=\"null\"\n                        >Copying</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-32\">\n                      <a\n                        href=\"http://api.jquery.com/category/manipulation/dom-insertion-around/\"\n                        title=\"View all posts filed under DOM Insertion, Around\"\n                        src=\"null\"\n                        >DOM Insertion, Around</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-33\">\n                      <a\n                        href=\"http://api.jquery.com/category/manipulation/dom-insertion-inside/\"\n                        title=\"View all posts filed under DOM Insertion, Inside\"\n                        src=\"null\"\n                        >DOM Insertion, Inside</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-34\">\n                      <a\n                        href=\"http://api.jquery.com/category/manipulation/dom-insertion-outside/\"\n                        title=\"View all posts filed under DOM Insertion, Outside\"\n                        src=\"null\"\n                        >DOM Insertion, Outside</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-35\">\n                      <a\n                        href=\"http://api.jquery.com/category/manipulation/dom-removal/\"\n                        title=\"View all posts filed under DOM Removal\"\n                        src=\"null\"\n                        >DOM Removal</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-36\">\n                      <a\n                        href=\"http://api.jquery.com/category/manipulation/dom-replacement/\"\n                        title=\"View all posts filed under DOM Replacement\"\n                        src=\"null\"\n                        >DOM Replacement</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-37\">\n                      <a\n                        href=\"http://api.jquery.com/category/manipulation/general-attributes/\"\n                        title=\"View all posts filed under General Attributes\"\n                        src=\"null\"\n                        >General Attributes</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-38\">\n                      <a\n                        href=\"http://api.jquery.com/category/manipulation/style-properties/\"\n                        title=\"View all posts filed under Style Properties\"\n                        src=\"null\"\n                        >Style Properties</a\n                      >\n                    </li>\n                  </ul>\n                </li>\n                <li class=\"cat-item cat-item-39\">\n                  <a\n                    href=\"http://api.jquery.com/category/miscellaneous/\"\n                    title=\"View all posts filed under Miscellaneous\"\n                    src=\"null\"\n                    >Miscellaneous</a\n                  >\n                  <ul class=\"children\">\n                    <li class=\"cat-item cat-item-40\">\n                      <a\n                        href=\"http://api.jquery.com/category/miscellaneous/collection-manipulation/\"\n                        title=\"View all posts filed under Collection Manipulation\"\n                        src=\"null\"\n                        >Collection Manipulation</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-41\">\n                      <a\n                        href=\"http://api.jquery.com/category/miscellaneous/data-storage/\"\n                        title=\"View all posts filed under Data Storage\"\n                        src=\"null\"\n                        >Data Storage</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-42\">\n                      <a\n                        href=\"http://api.jquery.com/category/miscellaneous/dom-element-methods/\"\n                        title=\"View all posts filed under DOM Element Methods\"\n                        src=\"null\"\n                        >DOM Element Methods</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-43\">\n                      <a\n                        href=\"http://api.jquery.com/category/miscellaneous/setup-methods/\"\n                        title=\"View all posts filed under Setup Methods\"\n                        src=\"null\"\n                        >Setup Methods</a\n                      >\n                    </li>\n                  </ul>\n                </li>\n                <li class=\"cat-item cat-item-44\">\n                  <a\n                    href=\"http://api.jquery.com/category/offset/\"\n                    title=\"View all posts filed under Offset\"\n                    src=\"null\"\n                    >Offset</a\n                  >\n                </li>\n                <li class=\"cat-item cat-item-45\">\n                  <a\n                    href=\"http://api.jquery.com/category/properties/\"\n                    title=\"View all posts filed under Properties\"\n                    src=\"null\"\n                    >Properties</a\n                  >\n                  <ul class=\"children\">\n                    <li class=\"cat-item cat-item-46\">\n                      <a\n                        href=\"http://api.jquery.com/category/properties/jquery-object-instance-properties/\"\n                        title=\"View all posts filed under Properties of jQuery Object Instances\"\n                        src=\"null\"\n                        >Properties of jQuery Object Instances</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-47\">\n                      <a\n                        href=\"http://api.jquery.com/category/properties/global-jquery-object-properties/\"\n                        title=\"View all posts filed under Properties of the Global jQuery Object\"\n                        src=\"null\"\n                        >Properties of the Global jQuery Object</a\n                      >\n                    </li>\n                  </ul>\n                </li>\n                <li class=\"cat-item cat-item-92\">\n                  <a\n                    href=\"http://api.jquery.com/category/removed/\"\n                    title=\"View all posts filed under Removed\"\n                    src=\"null\"\n                    >Removed</a\n                  >\n                </li>\n                <li class=\"cat-item cat-item-48\">\n                  <a\n                    href=\"http://api.jquery.com/category/selectors/\"\n                    title=\"View all posts filed under Selectors\"\n                    src=\"null\"\n                    >Selectors</a\n                  >\n                  <ul class=\"children\">\n                    <li class=\"cat-item cat-item-49\">\n                      <a\n                        href=\"http://api.jquery.com/category/selectors/attribute-selectors/\"\n                        title=\"View all posts filed under Attribute\"\n                        src=\"null\"\n                        >Attribute</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-50\">\n                      <a\n                        href=\"http://api.jquery.com/category/selectors/basic-css-selectors/\"\n                        title=\"View all posts filed under Basic\"\n                        src=\"null\"\n                        >Basic</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-51\">\n                      <a\n                        href=\"http://api.jquery.com/category/selectors/basic-filter-selectors/\"\n                        title=\"View all posts filed under Basic Filter\"\n                        src=\"null\"\n                        >Basic Filter</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-52\">\n                      <a\n                        href=\"http://api.jquery.com/category/selectors/child-filter-selectors/\"\n                        title=\"View all posts filed under Child Filter\"\n                        src=\"null\"\n                        >Child Filter</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-53\">\n                      <a\n                        href=\"http://api.jquery.com/category/selectors/content-filter-selector/\"\n                        title=\"View all posts filed under Content Filter\"\n                        src=\"null\"\n                        >Content Filter</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-54\">\n                      <a\n                        href=\"http://api.jquery.com/category/selectors/form-selectors/\"\n                        title=\"View all posts filed under Form\"\n                        src=\"null\"\n                        >Form</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-55\">\n                      <a\n                        href=\"http://api.jquery.com/category/selectors/hierarchy-selectors/\"\n                        title=\"View all posts filed under Hierarchy\"\n                        src=\"null\"\n                        >Hierarchy</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-56\">\n                      <a\n                        href=\"http://api.jquery.com/category/selectors/jquery-selector-extensions/\"\n                        title=\"View all posts filed under jQuery Extensions\"\n                        src=\"null\"\n                        >jQuery Extensions</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-57\">\n                      <a\n                        href=\"http://api.jquery.com/category/selectors/visibility-filter-selectors/\"\n                        title=\"View all posts filed under Visibility Filter\"\n                        src=\"null\"\n                        >Visibility Filter</a\n                      >\n                    </li>\n                  </ul>\n                </li>\n                <li class=\"cat-item cat-item-58\">\n                  <a\n                    href=\"http://api.jquery.com/category/traversing/\"\n                    title=\"View all posts filed under Traversing\"\n                    src=\"null\"\n                    >Traversing</a\n                  >\n                  <ul class=\"children\">\n                    <li class=\"cat-item cat-item-59\">\n                      <a\n                        href=\"http://api.jquery.com/category/traversing/filtering/\"\n                        title=\"View all posts filed under Filtering\"\n                        src=\"null\"\n                        >Filtering</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-60\">\n                      <a\n                        href=\"http://api.jquery.com/category/traversing/miscellaneous-traversal/\"\n                        title=\"View all posts filed under Miscellaneous Traversing\"\n                        src=\"null\"\n                        >Miscellaneous Traversing</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-61\">\n                      <a\n                        href=\"http://api.jquery.com/category/traversing/tree-traversal/\"\n                        title=\"View all posts filed under Tree Traversal\"\n                        src=\"null\"\n                        >Tree Traversal</a\n                      >\n                    </li>\n                  </ul>\n                </li>\n                <li class=\"cat-item cat-item-63\">\n                  <a\n                    href=\"http://api.jquery.com/category/utilities/\"\n                    title=\"View all posts filed under Utilities\"\n                    src=\"null\"\n                    >Utilities</a\n                  >\n                </li>\n                <li class=\"cat-item cat-item-64\">\n                  <a\n                    href=\"http://api.jquery.com/category/version/\"\n                    title=\"View all posts filed under Version\"\n                    src=\"null\"\n                    >Version</a\n                  >\n                  <ul class=\"children\">\n                    <li class=\"cat-item cat-item-65\">\n                      <a\n                        href=\"http://api.jquery.com/category/version/1.0/\"\n                        title=\"View all posts filed under Version 1.0\"\n                        src=\"null\"\n                        >Version 1.0</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-66\">\n                      <a\n                        href=\"http://api.jquery.com/category/version/1.0.4/\"\n                        title=\"View all posts filed under Version 1.0.4\"\n                        src=\"null\"\n                        >Version 1.0.4</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-67\">\n                      <a\n                        href=\"http://api.jquery.com/category/version/1.1/\"\n                        title=\"View all posts filed under Version 1.1\"\n                        src=\"null\"\n                        >Version 1.1</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-68\">\n                      <a\n                        href=\"http://api.jquery.com/category/version/1.1.2/\"\n                        title=\"View all posts filed under Version 1.1.2\"\n                        src=\"null\"\n                        >Version 1.1.2</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-69\">\n                      <a\n                        href=\"http://api.jquery.com/category/version/1.1.3/\"\n                        title=\"View all posts filed under Version 1.1.3\"\n                        src=\"null\"\n                        >Version 1.1.3</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-70\">\n                      <a\n                        href=\"http://api.jquery.com/category/version/1.1.4/\"\n                        title=\"View all posts filed under Version 1.1.4\"\n                        src=\"null\"\n                        >Version 1.1.4</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-71\">\n                      <a\n                        href=\"http://api.jquery.com/category/version/1.2/\"\n                        title=\"View all posts filed under Version 1.2\"\n                        src=\"null\"\n                        >Version 1.2</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-72\">\n                      <a\n                        href=\"http://api.jquery.com/category/version/1.2.3/\"\n                        title=\"View all posts filed under Version 1.2.3\"\n                        src=\"null\"\n                        >Version 1.2.3</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-73\">\n                      <a\n                        href=\"http://api.jquery.com/category/version/1.2.6/\"\n                        title=\"View all posts filed under Version 1.2.6\"\n                        src=\"null\"\n                        >Version 1.2.6</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-74\">\n                      <a\n                        href=\"http://api.jquery.com/category/version/1.3/\"\n                        title=\"View all posts filed under Version 1.3\"\n                        src=\"null\"\n                        >Version 1.3</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-75\">\n                      <a\n                        href=\"http://api.jquery.com/category/version/1.4/\"\n                        title=\"View all posts filed under Version 1.4\"\n                        src=\"null\"\n                        >Version 1.4</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-76\">\n                      <a\n                        href=\"http://api.jquery.com/category/version/1.4.1/\"\n                        title=\"View all posts filed under Version 1.4.1\"\n                        src=\"null\"\n                        >Version 1.4.1</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-77\">\n                      <a\n                        href=\"http://api.jquery.com/category/version/1.4.2/\"\n                        title=\"View all posts filed under Version 1.4.2\"\n                        src=\"null\"\n                        >Version 1.4.2</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-78\">\n                      <a\n                        href=\"http://api.jquery.com/category/version/1.4.3/\"\n                        title=\"View all posts filed under Version 1.4.3\"\n                        src=\"null\"\n                        >Version 1.4.3</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-79\">\n                      <a\n                        href=\"http://api.jquery.com/category/version/1.4.4/\"\n                        title=\"View all posts filed under Version 1.4.4\"\n                        src=\"null\"\n                        >Version 1.4.4</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-80\">\n                      <a\n                        href=\"http://api.jquery.com/category/version/1.5/\"\n                        title=\"View all posts filed under Version 1.5\"\n                        src=\"null\"\n                        >Version 1.5</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-81\">\n                      <a\n                        href=\"http://api.jquery.com/category/version/1.5.1/\"\n                        title=\"View all posts filed under Version 1.5.1\"\n                        src=\"null\"\n                        >Version 1.5.1</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-82\">\n                      <a\n                        href=\"http://api.jquery.com/category/version/1.6/\"\n                        title=\"View all posts filed under Version 1.6\"\n                        src=\"null\"\n                        >Version 1.6</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-83\">\n                      <a\n                        href=\"http://api.jquery.com/category/version/1.7/\"\n                        title=\"View all posts filed under Version 1.7\"\n                        src=\"null\"\n                        >Version 1.7</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-84\">\n                      <a\n                        href=\"http://api.jquery.com/category/version/1.8/\"\n                        title=\"View all posts filed under Version 1.8\"\n                        src=\"null\"\n                        >Version 1.8</a\n                      >\n                    </li>\n                    <li class=\"cat-item cat-item-86\">\n                      <a\n                        href=\"http://api.jquery.com/category/version/1.9/\"\n                        title=\"View all posts filed under Version 1.9\"\n                        src=\"null\"\n                        >Version 1.9</a\n                      >\n                    </li>\n                  </ul>\n                </li>\n              </ul>\n            </aside>\n          </div>\n        </div>\n      </div>\n    </div>\n\n    <footer class=\"clearfix simple\">\n      <div class=\"constrain\">\n        <div class=\"row\">\n          <div class=\"eight columns\">\n            <h3><span>Quick Access</span></h3>\n            <div class=\"cdn\">\n              <strong>CDN</strong>\n              <input\n                value=\"//code.jquery.com/jquery-1.10.2.min.js\"\n                readonly=\"\"\n              />\n            </div>\n            <div class=\"download\">\n              <strong\n                ><a href=\"http://jquery.com/download/\" src=\"null\"\n                  >Download jQuery 1.10.2 →</a\n                ></strong\n              >\n            </div>\n            <div class=\"tinynav-container\">\n              <h3><span>More jQuery Sites</span></h3>\n              <select id=\"tinynav1\" class=\"tinynav tinynav1\"\n                ><option>Browse...</option\n                ><option value=\"http://plugins.jquery.com/\">Plugins</option\n                ><option value=\"http://contribute.jquery.org/\"\n                  >Contribute</option\n                ><option value=\"http://contribute.jquery.org/cla/\">- CLA</option\n                ><option value=\"http://contribute.jquery.org/style-guide/\"\n                  >- Style Guides</option\n                ><option value=\"http://contribute.jquery.org/triage/\"\n                  >- Bug Triage</option\n                ><option value=\"http://contribute.jquery.org/code/\"\n                  >- Code</option\n                ><option value=\"http://contribute.jquery.org/documentation/\"\n                  >- Documentation</option\n                ><option value=\"http://contribute.jquery.org/web-sites/\"\n                  >- Web Sites</option\n                ><option value=\"http://events.jquery.org/\">Events</option\n                ><option\n                  value=\"http://www.deque.com/deque-partners-jquery-create-accessibility-summit\"\n                  >- Oct 10-11 | jQuery Accessibility Summit</option\n                ><option value=\"http://jquery.itmozg.ru/\"\n                  >- Oct 15 | jQuery Russia</option\n                ><option\n                  value=\"http://modernweb.com/training/jquery-oct-2013.php\"\n                  >- Oct 15-17 | jQuery Virtual Training</option\n                ><option value=\"http://2013.cssdevconf.com/\"\n                  >- Oct 21-22 | CSS Dev Conf</option\n                ><option value=\"http://javascriptsummit.com/\"\n                  >- Nov 19-21 | JavaScript Summit</option\n                ><option value=\"http://events.jquery.org/2014/san-diego/\"\n                  >- Feb 12-13 | jQuery San Diego</option\n                ><option value=\"http://www.gentics.com/jquery-europe\"\n                  >- Feb 28-Mar 1 | jQuery Europe</option\n                ><option value=\"http://jqueryuk.com\"\n                  >- May 16 | jQuery UK</option\n                ><option value=\"https://jquery.org/support/\">Support</option\n                ><option value=\"http://learn.jquery.com/\"\n                  >- Learning Center</option\n                ><option value=\"http://try.jquery.com/\">- Try jQuery</option\n                ><option value=\"http://irc.jquery.org/\">- IRC/Chat</option\n                ><option value=\"http://forum.jquery.com/\">- Forums</option\n                ><option value=\"http://stackoverflow.com/tags/jquery/info\"\n                  >- Stack Overflow</option\n                ><option value=\"https://jquery.org/support/\"\n                  >- Commercial Support</option\n                ><option value=\"https://jquery.org/\">jQuery Foundation</option\n                ><option value=\"https://jquery.org/join/\">- Join</option\n                ><option value=\"https://jquery.org/members/\">- Members</option\n                ><option value=\"https://jquery.org/team/\">- Team</option\n                ><option value=\"http://brand.jquery.org/\">- Brand Guide</option\n                ><option value=\"https://jquery.org/donate/\"\n                  >- Donate</option\n                ></select\n              >\n            </div>\n            <ul class=\"footer-icon-links\">\n              <li>\n                <a\n                  class=\"icon-github\"\n                  href=\"http://github.com/jquery/jquery\"\n                  src=\"null\"\n                  >GitHub <small>jQuery <br />Source</small></a\n                >\n              </li>\n              <li>\n                <a class=\"icon-group\" href=\"http://forum.jquery.com\" src=\"null\"\n                  >Forum <small>Community <br />Support</small></a\n                >\n              </li>\n              <li>\n                <a\n                  class=\"icon-warning-sign\"\n                  href=\"http://bugs.jquery.com\"\n                  src=\"null\"\n                  >Bugs <small>Issue <br />Tracker</small></a\n                >\n              </li>\n            </ul>\n          </div>\n\n          <div class=\"four columns\">\n            <h3><span>Books</span></h3>\n            <ul class=\"books\">\n              <li>\n                <a\n                  href=\"http://www.packtpub.com/learning-jquery-with-simple-javascript-techniques-fourth-edition/book\"\n                  src=\"null\"\n                >\n                  <span class=\"bottom\"\n                    ><img\n                      src=\"null\"\n                      alt=\"Learning jQuery 4th Edition by Karl Swedberg and Jonathan Chaffer\"\n                      width=\"92\"\n                      height=\"114\"\n                  /></span>\n                  <strong>Learning jQuery Fourth Edition</strong><br />\n                  <cite>Karl Swedberg and Jonathan Chaffer</cite>\n                </a>\n              </li>\n              <li>\n                <a\n                  href=\"http://www.manning.com/affiliate/idevaffiliate.php?id=648_176\"\n                  src=\"null\"\n                >\n                  <span\n                    ><img\n                      src=\"null\"\n                      alt=\"jQuery in Action by Bear Bibeault and Yehuda Katz\"\n                      width=\"92\"\n                      height=\"114\"\n                  /></span>\n                  <strong>jQuery in Action</strong><br />\n                  <cite>Bear Bibeault and Yehuda Katz</cite>\n                </a>\n              </li>\n              <li>\n                <a\n                  href=\"http://www.syncfusion.com/resources/techportal/ebooks/jquery?utm_medium=BizDev-jQuery.org0513\"\n                  src=\"null\"\n                >\n                  <span\n                    ><img\n                      src=\"null\"\n                      alt=\"jQuery Succinctly by Cody Lindley\"\n                      width=\"92\"\n                      height=\"114\"\n                  /></span>\n                  <strong>jQuery Succinctly</strong><br />\n                  <cite>Cody Lindley</cite>\n                </a>\n              </li>\n            </ul>\n          </div>\n        </div>\n\n        <div id=\"legal\">\n          <ul class=\"footer-site-links\">\n            <li>\n              <a class=\"icon-pencil\" href=\"http://learn.jquery.com/\" src=\"null\"\n                >Learning Center</a\n              >\n            </li>\n            <li>\n              <a class=\"icon-group\" href=\"http://forum.jquery.com/\" src=\"null\"\n                >Forum</a\n              >\n            </li>\n            <li>\n              <a class=\"icon-wrench\" href=\"http://api.jquery.com/\" src=\"null\"\n                >API</a\n              >\n            </li>\n            <li>\n              <a\n                class=\"icon-twitter\"\n                href=\"http://twitter.com/jquery\"\n                src=\"null\"\n                >Twitter</a\n              >\n            </li>\n            <li>\n              <a class=\"icon-comments\" href=\"http://irc.jquery.org/\" src=\"null\"\n                >IRC</a\n              >\n            </li>\n          </ul>\n          <p class=\"copyright\">\n            Copyright 2013\n            <a href=\"https://jquery.org/team/\" src=\"null\"\n              >The jQuery Foundation</a\n            >.<br />\n            <span class=\"sponsor-line\"\n              ><a\n                href=\"http://mediatemple.net\"\n                rel=\"noindex,nofollow\"\n                class=\"mt-link\"\n                src=\"null\"\n                >Web hosting by Media Temple</a\n              >\n              |\n              <a\n                href=\"http://www.maxcdn.com\"\n                rel=\"noindex,nofollow\"\n                class=\"mc-link\"\n                src=\"null\"\n                >CDN by MaxCDN</a\n              >\n              |\n              <a href=\"http://wordpress.org/\" class=\"wp-link\" src=\"null\"\n                >Powered by WordPress</a\n              >\n              | Thanks:\n              <a href=\"https://jquery.org/members/\" src=\"null\">Members</a>,\n              <a href=\"https://jquery.org/sponsors/\" src=\"null\"\n                >Sponsors</a\n              ></span\n            >\n          </p>\n        </div>\n      </div>\n    </footer>\n\n    <script>\n      // biome-ignore lint/correctness/noInvalidUseBeforeDeclaration: Vendor script\n      var _gaq = _gaq || [];\n      _gaq.push(['_setAccount', 'UA-1076265-1']);\n      _gaq.push(['_setDomainName', 'api.jquery.com']);\n      _gaq.push(['_setAllowLinker', true]);\n      _gaq.push(['_trackPageview']);\n\n      (() => {\n        var ga = document.createElement('script');\n        ga.type = 'text/javascript';\n        ga.async = true;\n        ga.src =\n          `${document.location.protocol === 'https:' \n            ? 'https://ssl'\n            : 'http://www'}.google-analytics.com/ga.js`;\n        var s = document.getElementsByTagName('script')[0];\n        s.parentNode.insertBefore(ga, s);\n      })();\n    </script>\n\n    <div id=\"cboxOverlay\" style=\"display: none;\"></div>\n    <div id=\"colorbox\" class=\"\" style=\"display: none;\">\n      <div id=\"cboxWrapper\">\n        <div>\n          <div id=\"cboxTopLeft\" style=\"float: left;\"></div>\n          <div id=\"cboxTopCenter\" style=\"float: left;\"></div>\n          <div id=\"cboxTopRight\" style=\"float: left;\"></div>\n        </div>\n        <div style=\"clear: left;\">\n          <div id=\"cboxMiddleLeft\" style=\"float: left;\"></div>\n          <div id=\"cboxContent\" style=\"float: left;\">\n            <div\n              id=\"cboxLoadedContent\"\n              style=\"width: 0px; height: 0px; overflow: hidden; float: left;\"\n            ></div>\n            <div id=\"cboxTitle\" style=\"float: left;\"></div>\n            <div id=\"cboxCurrent\" style=\"float: left;\"></div>\n            <div id=\"cboxNext\" style=\"float: left;\"></div>\n            <div id=\"cboxPrevious\" style=\"float: left;\"></div>\n            <div id=\"cboxSlideshow\" style=\"float: left;\"></div>\n            <div id=\"cboxClose\" style=\"float: left;\"></div>\n          </div>\n          <div id=\"cboxMiddleRight\" style=\"float: left;\"></div>\n        </div>\n        <div style=\"clear: left;\">\n          <div id=\"cboxBottomLeft\" style=\"float: left;\"></div>\n          <div id=\"cboxBottomCenter\" style=\"float: left;\"></div>\n          <div id=\"cboxBottomRight\" style=\"float: left;\"></div>\n        </div>\n      </div>\n      <div\n        style=\"\n          position: absolute;\n          width: 9999px;\n          visibility: hidden;\n          display: none;\n        \"\n      ></div>\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "biome.json",
    "content": "{\n  \"$schema\": \"https://biomejs.dev/schemas/2.4.6/schema.json\",\n  \"vcs\": {\n    \"enabled\": true,\n    \"clientKind\": \"git\",\n    \"useIgnoreFile\": true\n  },\n  \"files\": {\n    \"ignoreUnknown\": true\n  },\n  \"formatter\": {\n    \"enabled\": true,\n    \"indentStyle\": \"space\",\n    \"indentWidth\": 2\n  },\n  \"javascript\": {\n    \"formatter\": {\n      \"quoteStyle\": \"single\"\n    }\n  },\n  \"linter\": {\n    \"enabled\": true,\n    \"rules\": {\n      \"recommended\": true,\n      \"complexity\": {\n        \"useLiteralKeys\": \"off\",\n        \"noArguments\": \"off\",\n        \"noCommaOperator\": \"off\",\n        \"noUselessStringConcat\": \"error\",\n        \"noUselessUndefined\": \"error\",\n        \"useSimplifiedLogicExpression\": \"error\",\n        \"useWhile\": \"error\"\n      },\n      \"style\": {\n        \"noNonNullAssertion\": \"off\",\n        \"noInferrableTypes\": \"error\",\n        \"noNegationElse\": \"error\",\n        \"noUnusedTemplateLiteral\": \"error\",\n        \"noUselessElse\": \"error\",\n        \"noYodaExpression\": \"error\",\n        \"useAsConstAssertion\": \"error\",\n        \"useCollapsedElseIf\": \"error\",\n        \"useCollapsedIf\": \"error\",\n        \"useConsistentArrayType\": \"error\",\n        \"useConsistentArrowReturn\": \"error\",\n        \"useConsistentMemberAccessibility\": \"error\",\n        \"useConsistentObjectDefinitions\": \"error\",\n        \"useConsistentTypeDefinitions\": \"error\",\n        \"useDefaultParameterLast\": \"error\",\n        \"useExplicitLengthCheck\": \"error\",\n        \"useFilenamingConvention\": \"error\",\n        \"useNumberNamespace\": \"error\",\n        \"useNumericSeparators\": \"error\",\n        \"useObjectSpread\": \"error\",\n        \"useShorthandAssign\": \"error\",\n        \"useUnifiedTypeSignatures\": \"error\"\n      },\n      \"suspicious\": {\n        \"noAssignInExpressions\": \"off\",\n        \"noConfusingVoidType\": \"off\",\n        \"noConstEnum\": \"off\",\n        \"noExplicitAny\": \"off\",\n        \"noImplicitAnyLet\": \"off\",\n        \"noShadowRestrictedNames\": \"off\",\n        \"noUnsafeDeclarationMerging\": \"off\",\n        \"noConstantBinaryExpressions\": \"error\",\n        \"useAwait\": \"error\"\n      },\n      \"performance\": {\n        \"useTopLevelRegex\": \"error\"\n      }\n    }\n  },\n  \"css\": {\n    \"parser\": {\n      \"tailwindDirectives\": true\n    }\n  },\n  \"assist\": {\n    \"enabled\": true,\n    \"actions\": {\n      \"source\": {\n        \"organizeImports\": \"on\"\n      }\n    }\n  },\n  \"overrides\": [\n    {\n      \"includes\": [\"**/*.{test,spec}.ts\", \"test/**/*.ts\"],\n      \"javascript\": {\n        \"globals\": [\n          \"jest\",\n          \"describe\",\n          \"it\",\n          \"beforeEach\",\n          \"afterEach\",\n          \"expect\",\n          \"vi\"\n        ]\n      }\n    },\n    {\n      \"includes\": [\"**/*.astro\"],\n      \"linter\": {\n        \"rules\": {\n          \"correctness\": {\n            \"noUnusedImports\": \"off\",\n            \"noUnusedVariables\": \"off\"\n          },\n          \"style\": {\n            \"useFilenamingConvention\": \"off\"\n          },\n          \"performance\": {\n            \"useTopLevelRegex\": \"off\"\n          }\n        }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "eslint.config.js",
    "content": "import { fileURLToPath } from 'node:url'; // Added for .gitignore path\nimport { includeIgnoreFile } from '@eslint/compat'; // Added for .gitignore\nimport feedicFlatConfig from '@feedic/eslint-config';\nimport { commonTypeScriptRules } from '@feedic/eslint-config/typescript';\nimport eslintPluginVitest from '@vitest/eslint-plugin';\nimport { defineConfig } from 'eslint/config';\nimport eslintConfigBiome from 'eslint-config-biome';\nimport globals from 'globals';\nimport tseslint from 'typescript-eslint';\n\nconst gitignorePath = fileURLToPath(new URL('.gitignore', import.meta.url));\n\nexport default defineConfig(\n  includeIgnoreFile(gitignorePath), // Handle .gitignore patterns\n\n  // Global linter options\n  {\n    linterOptions: {\n      reportUnusedDisableDirectives: 'error',\n    },\n  },\n\n  // Base configurations for all relevant files\n  ...feedicFlatConfig,\n\n  {\n    rules: {\n      'jsdoc/tag-lines': [2, 'any', { startLines: 1 }],\n      'jsdoc/require-param-type': 0,\n      'jsdoc/require-returns-type': 0,\n      'jsdoc/no-types': 2,\n      'jsdoc/require-returns-check': 0,\n      'jsdoc/check-tag-names': [\n        2,\n        {\n          definedTags: ['private'],\n        },\n      ],\n    },\n  },\n\n  // Global custom rules and language options\n  {\n    languageOptions: {\n      globals: globals.node,\n      parserOptions: {\n        projectService: {\n          allowDefaultProject: ['*.js'],\n          defaultProject: 'tsconfig.json',\n        },\n        tsconfigRootDir: import.meta.dirname, // eslint-disable-line n/no-unsupported-features/node-builtins\n      },\n    },\n    rules: {\n      'n/file-extension-in-import': [2, 'always'],\n      'no-lonely-if': 2,\n      'no-proto': 2,\n      'no-else-return': [2, { allowElseIf: false }],\n      'no-unused-expressions': 2,\n      'no-useless-call': 2,\n      'no-constant-binary-expression': 2,\n      'no-void': 2,\n      'unicorn/no-array-callback-reference': 0,\n      'unicorn/no-array-reduce': 0,\n      'unicorn/no-for-loop': 0,\n      'unicorn/no-useless-undefined': 0,\n      'unicorn/prefer-array-find': 0,\n      'unicorn/prevent-abbreviations': 0,\n    },\n  },\n\n  // TypeScript specific configurations\n  {\n    // Custom overrides and settings for TypeScript files\n    files: ['**/*.{c,m,}ts', '**/*.tsx'], // Ensure this block specifically targets TS files\n    extends: [\n      ...tseslint.configs.recommendedTypeChecked,\n      ...tseslint.configs.stylisticTypeChecked,\n    ],\n    languageOptions: {\n      parser: tseslint.parser,\n    },\n    rules: {\n      ...commonTypeScriptRules,\n      // Enabling this in cheerio currently triggers broad churn across src + website.\n      '@typescript-eslint/no-unnecessary-condition': 0,\n    },\n  },\n\n  // Vitest specific configuration (for *.spec.ts files)\n  {\n    files: ['**/*.spec.ts'],\n    plugins: { vitest: eslintPluginVitest },\n    languageOptions: {\n      globals: globals.vitest, // Add Vitest globals\n    },\n    rules: {\n      // Assuming \"recommended\" is the flat config equivalent for \"legacy-recommended\"\n      ...eslintPluginVitest.configs.recommended.rules,\n      'n/no-unpublished-import': 0, // Allow importing devDependencies\n    },\n  },\n\n  // Website specific configuration\n  {\n    files: ['website/**/*.{m,}ts{x,}'],\n    languageOptions: {\n      parserOptions: {\n        projectService: {\n          allowDefaultProject: ['*.mjs'],\n        },\n        tsconfigRootDir: `${import.meta.dirname}/website`, // eslint-disable-line n/no-unsupported-features/node-builtins\n      },\n    },\n  },\n\n  // Prettier - must be the last configuration to override styling rules\n  eslintConfigBiome,\n);\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"cheerio\",\n  \"version\": \"1.2.0\",\n  \"description\": \"The fast, flexible & elegant library for parsing and manipulating HTML and XML.\",\n  \"keywords\": [\n    \"htmlparser\",\n    \"jquery\",\n    \"selector\",\n    \"scraper\",\n    \"parser\",\n    \"dom\",\n    \"xml\",\n    \"html\"\n  ],\n  \"homepage\": \"https://cheerio.js.org/\",\n  \"bugs\": {\n    \"url\": \"https://github.com/cheeriojs/cheerio/issues\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git://github.com/cheeriojs/cheerio.git\"\n  },\n  \"funding\": \"https://github.com/cheeriojs/cheerio?sponsor=1\",\n  \"license\": \"MIT\",\n  \"author\": \"Matt Mueller <mattmuelle@gmail.com>\",\n  \"sideEffects\": false,\n  \"maintainers\": [\n    \"Felix Boehm <me@feedic.com>\"\n  ],\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": {\n      \"browser\": {\n        \"types\": \"./dist/browser/index.d.ts\",\n        \"default\": \"./dist/browser/index.js\"\n      },\n      \"import\": {\n        \"types\": \"./dist/esm/index.d.ts\",\n        \"default\": \"./dist/esm/index.js\"\n      },\n      \"require\": {\n        \"types\": \"./dist/commonjs/index.d.ts\",\n        \"default\": \"./dist/commonjs/index.js\"\n      }\n    },\n    \"./package.json\": \"./package.json\",\n    \"./slim\": {\n      \"browser\": {\n        \"types\": \"./dist/browser/slim.d.ts\",\n        \"default\": \"./dist/browser/slim.js\"\n      },\n      \"import\": {\n        \"types\": \"./dist/esm/slim.d.ts\",\n        \"default\": \"./dist/esm/slim.js\"\n      },\n      \"require\": {\n        \"types\": \"./dist/commonjs/slim.d.ts\",\n        \"default\": \"./dist/commonjs/slim.js\"\n      }\n    },\n    \"./utils\": {\n      \"browser\": {\n        \"types\": \"./dist/browser/utils.d.ts\",\n        \"default\": \"./dist/browser/utils.js\"\n      },\n      \"import\": {\n        \"types\": \"./dist/esm/utils.d.ts\",\n        \"default\": \"./dist/esm/utils.js\"\n      },\n      \"require\": {\n        \"types\": \"./dist/commonjs/utils.d.ts\",\n        \"default\": \"./dist/commonjs/utils.js\"\n      }\n    }\n  },\n  \"main\": \"./dist/commonjs/index.js\",\n  \"module\": \"./dist/esm/index.js\",\n  \"browser\": \"./dist/browser/index.js\",\n  \"types\": \"./dist/commonjs/index.d.ts\",\n  \"files\": [\n    \"dist\",\n    \"src\",\n    \"!**/*.spec.{t,j}s\",\n    \"!**/__tests__/*\",\n    \"!**/__fixtures__/*\"\n  ],\n  \"scripts\": {\n    \"benchmark\": \"node --import=tsx benchmark/benchmark.ts\",\n    \"build\": \"tshy\",\n    \"format\": \"npm run format:es && npm run format:biome\",\n    \"format:biome\": \"biome check --write .\",\n    \"format:es\": \"npm run lint:es -- --fix\",\n    \"lint\": \"npm run lint:es && npm run lint:ts && npm run lint:biome\",\n    \"lint:biome\": \"biome check .\",\n    \"lint:es\": \"eslint .\",\n    \"lint:ts\": \"tsc --noEmit\",\n    \"prepare\": \"husky\",\n    \"prepublishOnly\": \"npm run build\",\n    \"test\": \"npm run lint && npm run test:vi\",\n    \"test:vi\": \"vitest run\",\n    \"update-sponsors\": \"tsx scripts/fetch-sponsors.mts\"\n  },\n  \"lint-staged\": {\n    \"*.js\": [\n      \"biome check --write\",\n      \"eslint --fix\"\n    ],\n    \"*.{json,md,ts}\": [\n      \"biome check --write\"\n    ]\n  },\n  \"dependencies\": {\n    \"cheerio-select\": \"^2.1.0\",\n    \"dom-serializer\": \"^2.0.0\",\n    \"domhandler\": \"^5.0.3\",\n    \"domutils\": \"^3.2.2\",\n    \"encoding-sniffer\": \"^0.2.1\",\n    \"htmlparser2\": \"^10.1.0\",\n    \"parse5\": \"^7.3.0\",\n    \"parse5-htmlparser2-tree-adapter\": \"^7.1.0\",\n    \"parse5-parser-stream\": \"^7.1.2\",\n    \"undici\": \"^7.24.4\",\n    \"whatwg-mimetype\": \"^4.0.0\"\n  },\n  \"devDependencies\": {\n    \"@biomejs/biome\": \"^2.4.4\",\n    \"@eslint/compat\": \"^2.0.3\",\n    \"@feedic/eslint-config\": \"^0.3.1\",\n    \"@imgix/js-core\": \"^3.8.0\",\n    \"@octokit/graphql\": \"^9.0.3\",\n    \"@types/jsdom\": \"^28.0.0\",\n    \"@types/node\": \"^25.5.0\",\n    \"@types/whatwg-mimetype\": \"^3.0.2\",\n    \"@vitest/coverage-v8\": \"^4.1.0\",\n    \"@vitest/eslint-plugin\": \"^1.6.12\",\n    \"eslint\": \"^10.0.3\",\n    \"eslint-config-biome\": \"^2.1.3\",\n    \"globals\": \"^17.4.0\",\n    \"husky\": \"^9.1.7\",\n    \"jquery\": \"^4.0.0\",\n    \"jsdom\": \"^29.0.0\",\n    \"lint-staged\": \"^16.4.0\",\n    \"prettier-plugin-jsdoc\": \"^1.8.0\",\n    \"tinybench\": \"^6.0.0\",\n    \"tshy\": \"^3.3.2\",\n    \"tsx\": \"^4.21.0\",\n    \"typescript\": \"^5.9.3\",\n    \"typescript-eslint\": \"^8.57.1\",\n    \"vitest\": \"^4.0.18\"\n  },\n  \"engines\": {\n    \"node\": \">=20.18.1\"\n  },\n  \"tshy\": {\n    \"esmDialects\": [\n      \"browser\"\n    ],\n    \"exports\": {\n      \".\": \"./src/index.ts\",\n      \"./slim\": \"./src/slim.ts\",\n      \"./utils\": \"./src/utils.ts\",\n      \"./package.json\": \"./package.json\"\n    },\n    \"exclude\": [\n      \"**/*.spec.ts\",\n      \"**/__fixtures__/*\",\n      \"**/__tests__/*\"\n    ]\n  }\n}\n"
  },
  {
    "path": "scripts/fetch-sponsors.mts",
    "content": "/**\n * @file Script To fetch sponsor data from Open Collective and GitHub.\n *\n *   Adapted from\n *   https://github.com/eslint/website/blob/230e73457dcdc2353ad7934e876a5a222a17b1d7/_tools/fetch-sponsors.js.\n */\n/* eslint-disable @typescript-eslint/no-unsafe-assignment,\n                  @typescript-eslint/no-unsafe-return,\n                  @typescript-eslint/no-unsafe-call,\n                  @typescript-eslint/no-unsafe-argument,\n                  @typescript-eslint/no-unsafe-member-access,\n                  @typescript-eslint/prefer-nullish-coalescing */\nimport * as fs from 'node:fs/promises';\nimport ImgixClient from '@imgix/js-core';\nimport { graphql as githubGraphQL } from '@octokit/graphql';\nimport { request } from 'undici';\n\ntype Tier = 'headliner' | 'sponsor' | 'professional' | 'backer';\n\ninterface Sponsor {\n  createdAt: string;\n  name: string;\n  image: string;\n  url: string;\n  type: 'ORGANIZATION' | 'INDIVIDUAL' | 'FUND';\n  monthlyDonation: number;\n  totalDonations: number;\n  source: 'github' | 'opencollective' | 'manual';\n  tier: Tier | null;\n}\n\nconst tierSponsors: Record<Tier, Sponsor[]> = {\n  headliner: [\n    // Some sponsors are manually added here.\n    {\n      createdAt: '2022-06-24',\n      name: 'Github',\n      image: 'https://github.com/github.png',\n      url: 'https://github.com/',\n      type: 'ORGANIZATION',\n      monthlyDonation: 0,\n      totalDonations: 0,\n      source: 'manual',\n      tier: 'headliner',\n    },\n    {\n      createdAt: '2018-05-02',\n      name: 'AirBnB',\n      image: 'https://github.com/airbnb.png',\n      url: 'https://www.airbnb.com/',\n      type: 'ORGANIZATION',\n      monthlyDonation: 0,\n      totalDonations: 0,\n      source: 'manual',\n      tier: 'headliner',\n    },\n    {\n      createdAt: '2026-01-21',\n      name: 'HasData',\n      image: 'https://hasdata.com/favicon.svg',\n      url: 'https://hasdata.com',\n      type: 'ORGANIZATION',\n      monthlyDonation: 0,\n      totalDonations: 0,\n      source: 'manual',\n      tier: 'headliner',\n    },\n    {\n      createdAt: '2026-01-28',\n      name: 'brand.dev',\n      image: 'https://github.com/brand-dot-dev.png',\n      url: 'https://brand.dev/',\n      type: 'ORGANIZATION',\n      monthlyDonation: 0,\n      totalDonations: 0,\n      source: 'manual',\n      tier: 'headliner',\n    },\n  ],\n  sponsor: [],\n  professional: [],\n  backer: [],\n};\n\nconst { CHEERIO_SPONSORS_GITHUB_TOKEN, IMGIX_TOKEN } = process.env;\n\nif (!CHEERIO_SPONSORS_GITHUB_TOKEN) {\n  throw new Error('Missing CHEERIO_SPONSORS_GITHUB_TOKEN.');\n}\n\n// @ts-expect-error - Types don't have a constructor\nconst imgix = new ImgixClient({\n  domain: 'humble.imgix.net',\n  secureURLToken: IMGIX_TOKEN,\n});\n\n/**\n * Returns the tier ID for a given donation amount.\n *\n * @param monthlyDonation - The monthly donation in dollars.\n * @returns The ID of the tier the donation belongs to.\n */\nfunction getTierSlug(monthlyDonation: number): Tier | null {\n  if (monthlyDonation >= 250) {\n    return 'headliner';\n  }\n\n  if (monthlyDonation >= 100) {\n    return 'sponsor';\n  }\n\n  if (monthlyDonation >= 25) {\n    return 'professional';\n  }\n\n  if (monthlyDonation >= 5) {\n    return 'backer';\n  }\n\n  return null;\n}\n\n/**\n * Fetch order data from Open Collective using the GraphQL API.\n *\n * @returns An array of sponsors.\n */\nasync function fetchOpenCollectiveSponsors(): Promise<Sponsor[]> {\n  const endpoint = 'https://api.opencollective.com/graphql/v2';\n\n  const query = `{\n        account(slug: \"cheerio\") {\n          orders(status: ACTIVE, filter: INCOMING) {\n            nodes {\n              createdAt\n              fromAccount {\n                name\n                website\n                imageUrl\n                type\n              }\n              amount {\n                value\n              }\n              tier {\n                slug\n              }\n              frequency\n              totalDonations {\n                value\n              }\n            }\n          }\n        }\n      }`;\n\n  const { body } = await request(endpoint, {\n    method: 'POST',\n    headers: { 'Content-Type': 'application/json' },\n    body: JSON.stringify({ query }),\n  });\n\n  const payload: any = await body.json();\n\n  return payload.data.account.orders.nodes.map((order: any): Sponsor => {\n    const donation = order.amount.value * 100;\n    const monthlyDonation =\n      order.frequency === 'YEARLY' ? Math.round(donation / 12) : donation;\n\n    return {\n      createdAt: order.createdAt,\n      name: order.fromAccount.name,\n      url: order.fromAccount.website,\n      image: order.fromAccount.imageUrl,\n      type: order.fromAccount.type,\n      monthlyDonation,\n      totalDonations: order.totalDonations.value * 100,\n      source: 'opencollective',\n      tier: getTierSlug(monthlyDonation / 100),\n    };\n  });\n}\n\nfunction getMonthsActive(date: string): number {\n  const now = new Date();\n  const then = new Date(date);\n  const months = (now.getFullYear() - then.getFullYear()) * 12;\n  return months - then.getMonth() + now.getMonth() + 1;\n}\n\n/**\n * Fetches GitHub Sponsors data using the GraphQL API.\n *\n * @returns An array of sponsors.\n */\nasync function fetchGitHubSponsors(): Promise<Sponsor[]> {\n  const { organization } = await githubGraphQL<any>(\n    `{\n      organization(login: \"cheeriojs\") {\n        sponsorshipsAsMaintainer(first: 100) {\n          nodes {\n            sponsor: sponsorEntity {\n              ... on User {\n                name\n                login\n                avatarUrl\n                url\n                websiteUrl\n                isViewer\n              }\n              ... on Organization {\n                name\n                login\n                avatarUrl\n                url\n                websiteUrl\n                viewerCanAdminister\n              }\n            }\n            tier {\n              monthlyPriceInDollars\n            }\n            createdAt\n          }\n        }\n      }\n    }\n    `,\n    {\n      headers: {\n        authorization: `token ${CHEERIO_SPONSORS_GITHUB_TOKEN}`,\n      },\n    },\n  );\n\n  // Return an array in the same format as Open Collective\n  return organization.sponsorshipsAsMaintainer.nodes.map(\n    ({ sponsor, tier, createdAt }: any): Sponsor => ({\n      createdAt,\n      name: sponsor.name,\n      image: `${sponsor.avatarUrl}&s=128`,\n      url: sponsor.websiteUrl || sponsor.url,\n      type:\n        // Workaround to get the type — fetch a field that only exists on users.\n        sponsor.isViewer === undefined ? 'ORGANIZATION' : 'INDIVIDUAL',\n      monthlyDonation: (tier?.monthlyPriceInDollars ?? 0) * 100,\n      totalDonations:\n        getMonthsActive(createdAt) * tier?.monthlyPriceInDollars * 100,\n      source: 'github',\n      tier: getTierSlug(tier?.monthlyPriceInDollars ?? 0),\n    }),\n  );\n}\n\nasync function fetchSponsors(): Promise<Sponsor[]> {\n  const results = await Promise.all([\n    fetchOpenCollectiveSponsors(),\n    fetchGitHubSponsors(),\n  ]);\n\n  return results.flat();\n}\n\n/*\n * Remove sponsors from lower tiers that have individual accounts,\n * but are clearly orgs.\n */\nconst MISLABELED_ORGS =\n  /[ck]as[iy]+no|bet$|poker|gambling|coffee|tuxedo|(?:ph|f)oto/i;\n\nconst README_PATH = new URL('../Readme.md', import.meta.url);\nconst JSON_PATH = new URL('../website/sponsors.json', import.meta.url);\n\nconst SECTION_START_BEGINNING = '<!-- BEGIN SPONSORS:';\nconst SECTION_START_END = '-->';\nconst SECTION_END = '<!-- END SPONSORS -->';\n\nconst professionalToBackerOverrides = new Map([\n  ['Vasy Kafidoff', 'https://kafidoff.com'],\n]);\n\nconst sponsors = await fetchSponsors();\n\nconsole.log('Received sponsors:', sponsors);\n\n// Remove sponsors that are already in the pre-populated headliners\nfor (let i = 0; i < sponsors.length; i++) {\n  if (\n    tierSponsors.headliner.some((sponsor) => sponsor.url === sponsors[i].url)\n  ) {\n    sponsors.splice(i, 1);\n    i--;\n  }\n}\n\nsponsors.sort((a, b) => Date.parse(a.createdAt) - Date.parse(b.createdAt));\n\n// Process into a useful format\nfor (const sponsor of sponsors) {\n  if (\n    !sponsor.tier || // Always skip if sponsor has no tier (e.g., donation < $5)\n    // OR if it's a 'professional' or 'backer' tier AND meets specific filtering criteria\n    ((sponsor.tier === 'professional' || sponsor.tier === 'backer') &&\n      (sponsor.type === 'ORGANIZATION' ||\n        MISLABELED_ORGS.test(sponsor.name) ||\n        MISLABELED_ORGS.test(sponsor.url)))\n  ) {\n    continue;\n  }\n\n  if (\n    (sponsor.tier === 'professional' || sponsor.tier === 'backer') &&\n    professionalToBackerOverrides.has(sponsor.name)\n  ) {\n    sponsor.url = professionalToBackerOverrides.get(sponsor.name)!;\n  }\n\n  tierSponsors[sponsor.tier].push(sponsor);\n}\n\nfor (const tier of Object.values(tierSponsors)) {\n  // Sort order based on total donations\n  tier.sort((a: Sponsor, b: Sponsor) => b.totalDonations - a.totalDonations);\n\n  // Set all donations to 0 before writing to JSON\n  for (const sponsor of tier) {\n    sponsor.monthlyDonation = 0;\n    sponsor.totalDonations = 0;\n  }\n}\n\n// Write sponsors.json\nawait fs.writeFile(JSON_PATH, JSON.stringify(tierSponsors, null, 2), 'utf8');\n\n// Prepend professionals to backers for now\ntierSponsors.backer.unshift(...tierSponsors.professional);\n\nlet readme = await fs.readFile(README_PATH, 'utf8');\n\nconst TIER_IMAGE_SIZES: Record<Tier, number> = {\n  headliner: 128,\n  sponsor: 64,\n  professional: 64,\n  backer: 48,\n};\n\nfor (let sectionStartIndex = 0; ; ) {\n  sectionStartIndex = readme.indexOf(\n    SECTION_START_BEGINNING,\n    sectionStartIndex,\n  );\n\n  if (sectionStartIndex < 0) break;\n\n  sectionStartIndex += SECTION_START_BEGINNING.length;\n\n  const sectionStartEndIndex = readme.indexOf(\n    SECTION_START_END,\n    sectionStartIndex,\n  );\n  const sectionName = readme\n    .slice(sectionStartIndex, sectionStartEndIndex)\n    .trim() as Tier;\n\n  const sectionContentStart = sectionStartEndIndex + SECTION_START_END.length;\n\n  const sectionEndIndex = readme.indexOf(SECTION_END, sectionContentStart);\n\n  readme = `${readme.slice(0, sectionContentStart)}\\n\\n${tierSponsors[\n    sectionName\n  ]\n    .map((s: Sponsor) => {\n      const size = TIER_IMAGE_SIZES[s.tier ?? sectionName];\n      // Display each sponsor's image in the README.\n      return `<a href=\"${s.url}\" target=\"_blank\" rel=\"noopener noreferrer\">\n            <img height=\"${size}px\" width=\"${size}px\" src=\"${imgix.buildURL(\n              s.image,\n              {\n                w: size,\n                h: size,\n                fit: 'fillmax',\n                fill: 'solid',\n              },\n            )}\" title=\"${s.name}\" alt=\"${s.name}\"></img>\n          </a>`;\n    })\n    .join('\\n')}\\n\\n${readme.slice(sectionEndIndex)}`;\n}\n\nawait fs.writeFile(README_PATH, readme, {\n  encoding: 'utf8',\n});\n"
  },
  {
    "path": "src/__fixtures__/fixtures.ts",
    "content": "import type { CheerioAPI } from '../load.js';\nimport { load } from '../load-parse.js';\n\n/** A Cheerio instance with no content. */\nexport const cheerio: CheerioAPI = load([]);\n\nexport const fruits: string = [\n  '<ul id=\"fruits\">',\n  '<li class=\"apple\">Apple</li>',\n  '<li class=\"orange\">Orange</li>',\n  '<li class=\"pear\">Pear</li>',\n  '</ul>',\n].join('');\n\nexport const vegetables: string = [\n  '<ul id=\"vegetables\">',\n  '<li class=\"carrot\">Carrot</li>',\n  '<li class=\"sweetcorn\">Sweetcorn</li>',\n  '</ul>',\n].join('');\n\nexport const divcontainers: string = [\n  '<div class=\"container\">',\n  '<div class=\"inner\">First</div>',\n  '<div class=\"inner\">Second</div>',\n  '</div>',\n  '<div class=\"container\">',\n  '<div class=\"inner\">Third</div>',\n  '<div class=\"inner\">Fourth</div>',\n  '</div>',\n  '<div id=\"new\"><div>',\n  '<div>\\n\\n<p><em><b></b></em></p>\\n\\n</div>',\n  '</div>',\n].join('');\n\nexport const chocolates: string = [\n  '<ul id=\"chocolates\">',\n  '<li class=\"linth\" data-highlight=\"Lindor\" data-origin=\"swiss\">Linth</li>',\n  '<li class=\"frey\" data-taste=\"sweet\" data-best-collection=\"Mahony\">Frey</li>',\n  '<li class=\"cailler\">Cailler</li>',\n  '</ul>',\n].join('');\n\nexport const drinks: string = [\n  '<ul id=\"drinks\">',\n  '<li class=\"beer\">Beer</li>',\n  '<li class=\"juice\">Juice</li>',\n  '<li class=\"milk\">Milk</li>',\n  '<li class=\"water\">Water</li>',\n  '<li class=\"cider\">Cider</li>',\n  '</ul>',\n].join('');\n\nexport const food: string = [\n  '<ul id=\"food\">',\n  fruits,\n  vegetables,\n  '</ul>',\n].join('');\n\nexport const eleven = `\n<html>\n  <body>\n    <ul>\n      <li>One</li>\n      <li>Two</li>\n      <li class=\"blue sel\">Three</li>\n      <li class=\"red\">Four</li>\n    </ul>\n\n    <ul>\n      <li class=\"red\">Five</li>\n      <li>Six</li>\n      <li class=\"blue\">Seven</li>\n    </ul>\n\n    <ul>\n      <li>Eight</li>\n      <li class=\"red sel\">Nine</li>\n      <li>Ten</li>\n      <li class=\"sel\">Eleven</li>\n    </ul>\n  </body>\n</html>\n`;\n\nexport const unwrapspans: string = [\n  '<div id=unwrap style=\"display: none;\">',\n  '<div id=unwrap1><span class=unwrap>a</span><span class=unwrap>b</span></div>',\n  '<div id=unwrap2><span class=unwrap>c</span><span class=unwrap>d</span></div>',\n  '<div id=unwrap3><b><span class=\"unwrap unwrap3\">e</span></b><b><span class=\"unwrap unwrap3\">f</span></b></div>',\n  '</div>',\n].join('');\n\nexport const inputs: string = [\n  '<button id=\"btn-value\" value=\"button\">Button</button>',\n  '<button id=\"btn-valueless\">Button</button>',\n  '<select id=\"one\"><option value=\"option_not_selected\">Option not selected</option><option value=\"option_selected\" selected>Option selected</option></select>',\n  '<select id=\"one-valueless\"><option>Option not selected</option><option selected>Option selected</option></select>',\n  '<select id=\"one-html-entity\"><option>Option not selected</option><option selected>Option &lt;selected&gt;</option></select>',\n  '<select id=\"one-nested\"><option>Option not selected</option><option selected>Option <span>selected</span></option></select>',\n  '<input type=\"text\" value=\"input_text\" />',\n  '<input type=\"checkbox\" name=\"checkbox_off\" value=\"off\" /><input type=\"checkbox\" name=\"checkbox_on\" value=\"on\" checked />',\n  '<input type=\"checkbox\" name=\"checkbox_valueless\" />',\n  '<input type=\"radio\" value=\"off\" name=\"radio\" /><input type=\"radio\" name=\"radio\" value=\"on\" checked />',\n  '<input type=\"radio\" value=\"off\" name=\"radio[brackets]\" /><input type=\"radio\" name=\"radio[brackets]\" value=\"on\" checked />',\n  '<input type=\"radio\" name=\"radio_valueless\" />',\n  '<select id=\"multi\" multiple><option value=\"1\">1</option><option value=\"2\" selected>2</option><option value=\"3\" selected>3</option><option value=\"4\">4</option></select>',\n  '<select id=\"multi-valueless\" multiple><option>1</option><option selected>2</option><option selected>3</option><option>4</option></select>',\n].join('');\n\nexport const text: string = [\n  '<p>Apples, <b>oranges</b> and pears.</p>',\n  '<p>Carrots and <!-- sweetcorn --></p>',\n].join('');\n\nexport const forms: string = [\n  '<form id=\"simple\"><input type=\"text\" name=\"fruit\" value=\"Apple\" /></form>',\n  '<form id=\"nested\"><div><input type=\"text\" name=\"fruit\" value=\"Apple\" /></div><input type=\"text\" name=\"vegetable\" value=\"Carrot\" /></form>',\n  '<form id=\"disabled\"><input type=\"text\" name=\"fruit\" value=\"Apple\" disabled /></form>',\n  '<form id=\"submit\"><input type=\"text\" name=\"fruit\" value=\"Apple\" /><input type=\"submit\" name=\"submit\" value=\"Submit\" /></form>',\n  '<form id=\"select\"><select name=\"fruit\"><option value=\"Apple\">Apple</option><option value=\"Orange\" selected>Orange</option></select></form>',\n  '<form id=\"unnamed\"><input type=\"text\" name=\"fruit\" value=\"Apple\" /><input type=\"text\" value=\"Carrot\" /></form>',\n  '<form id=\"multiple\"><select name=\"fruit\" multiple><option value=\"Apple\" selected>Apple</option><option value=\"Orange\" selected>Orange</option><option value=\"Carrot\">Carrot</option></select></form>',\n  '<form id=\"textarea\"><textarea name=\"fruits\">Apple\\nOrange</textarea></form>',\n  '<form id=\"spaces\"><input type=\"text\" name=\"fruit\" value=\"Blood orange\" /></form>',\n].join('');\n\nexport const noscript: string = [\n  '</body>',\n  '<noscript>',\n  '<!-- anchor linking to external file -->',\n  '<a href=\"https://github.com/cheeriojs/cheerio\">External Link</a>',\n  '</noscript>',\n  '<p>Rocks!</p>',\n  '</body>',\n].join('');\n\nexport const script: string = [\n  '<div>',\n  '<a>A</a>',\n  '<script>',\n  '  var foo = \"bar\";',\n  '</script>',\n  '<b>B</b>',\n  '</div>',\n].join('');\n\nexport const mixedText = '<a>1</a>TEXT<b>2</b>';\n"
  },
  {
    "path": "src/__tests__/deprecated.spec.ts",
    "content": "/**\n * This file includes tests for deprecated APIs. The methods are expected to be\n * removed in the next major release of Cheerio, but their stability should be\n * maintained until that time.\n */\nimport { beforeEach, describe, expect, it } from 'vitest';\nimport { cheerio, food, fruits } from '../__fixtures__/fixtures.js';\n\ndescribe('deprecated APIs', () => {\n  describe('cheerio module', () => {\n    describe('.parseHTML', () => {\n      it('(html) : should preserve content', () => {\n        const html = '<div>test div</div>';\n        expect(cheerio(cheerio.parseHTML(html)[0]).html()).toBe('test div');\n      });\n    });\n\n    describe('.merge', () => {\n      it('should be a function', () => {\n        expect(typeof cheerio.merge).toBe('function');\n      });\n\n      // #1674 - merge, wont accept Cheerio object\n      it('should be a able merge array and cheerio object', () => {\n        const ret = cheerio.merge<unknown>(cheerio(), ['elem1', 'elem2']);\n        expect(typeof ret).toBe('object');\n        expect(ret).toHaveLength(2);\n      });\n\n      it('(arraylike, arraylike) : should modify the first array, but not the second', () => {\n        const arr1 = [1, 2, 3];\n        const arr2 = [4, 5, 6];\n        const ret = cheerio.merge(arr1, arr2);\n\n        expect(typeof ret).toBe('object');\n        expect(Array.isArray(ret)).toBe(true);\n        expect(ret).toBe(arr1);\n        expect(arr1).toHaveLength(6);\n        expect(arr2).toHaveLength(3);\n      });\n\n      it('(arraylike, arraylike) : should handle objects that arent arrays, but are arraylike', () => {\n        const arr1: ArrayLike<string> = {\n          length: 3,\n          0: 'a',\n          1: 'b',\n          2: 'c',\n        };\n        const arr2 = {\n          length: 3,\n          0: 'd',\n          1: 'e',\n          2: 'f',\n        };\n\n        cheerio.merge(arr1, arr2);\n        expect(arr1).toHaveLength(6);\n        expect(arr1[3]).toBe('d');\n        expect(arr1[4]).toBe('e');\n        expect(arr1[5]).toBe('f');\n        expect(arr2).toHaveLength(3);\n      });\n\n      it('(?, ?) : should gracefully reject invalid inputs', () => {\n        expect(cheerio.merge([4], 3 as never)).toBeUndefined();\n        expect(cheerio.merge({} as never, {} as never)).toBeUndefined();\n        expect(cheerio.merge([], {} as never)).toBeUndefined();\n        expect(cheerio.merge({} as never, [])).toBeUndefined();\n\n        const fakeArray = { length: 3, 0: 'a', 1: 'b', 3: 'd' };\n        expect(cheerio.merge(fakeArray, [])).toBeUndefined();\n        expect(cheerio.merge([], fakeArray)).toBeUndefined();\n\n        expect(cheerio.merge({ length: '7' } as never, [])).toBeUndefined();\n        expect(cheerio.merge({ length: -1 }, [])).toBeUndefined();\n      });\n\n      it('(?, ?) : should no-op on invalid inputs', () => {\n        const fakeArray1 = { length: 3, 0: 'a', 1: 'b', 3: 'd' };\n        cheerio.merge(fakeArray1, []);\n        expect(fakeArray1).toHaveLength(3);\n        expect(fakeArray1[0]).toBe('a');\n        expect(fakeArray1[1]).toBe('b');\n        expect(fakeArray1[3]).toBe('d');\n        cheerio.merge([], fakeArray1);\n        expect(fakeArray1).toHaveLength(3);\n        expect(fakeArray1[0]).toBe('a');\n        expect(fakeArray1[1]).toBe('b');\n        expect(fakeArray1[3]).toBe('d');\n      });\n    });\n\n    describe('.contains', () => {\n      let $: typeof cheerio;\n\n      beforeEach(() => {\n        $ = cheerio.load(food);\n      });\n\n      it('(container, contained) : should correctly detect the provided element', () => {\n        const $food = $('#food');\n        const $fruits = $('#fruits');\n        const $apple = $('.apple');\n\n        expect(cheerio.contains($food[0], $fruits[0])).toBe(true);\n        expect(cheerio.contains($food[0], $apple[0])).toBe(true);\n      });\n\n      it('(container, other) : should not detect elements that are not contained', () => {\n        const $fruits = $('#fruits');\n        const $vegetables = $('#vegetables');\n        const $apple = $('.apple');\n\n        expect(cheerio.contains($vegetables[0], $apple[0])).toBe(false);\n        expect(cheerio.contains($fruits[0], $vegetables[0])).toBe(false);\n        expect(cheerio.contains($vegetables[0], $fruits[0])).toBe(false);\n        expect(cheerio.contains($fruits[0], $fruits[0])).toBe(false);\n        expect(cheerio.contains($vegetables[0], $vegetables[0])).toBe(false);\n      });\n    });\n\n    describe('.root', () => {\n      it('returns an empty selection', () => {\n        const $empty = cheerio.root();\n        expect($empty).toHaveLength(1);\n        expect($empty[0].children).toHaveLength(0);\n      });\n    });\n  });\n\n  describe('Cheerio function', () => {\n    it('.load', () => {\n      const $1 = cheerio.load(fruits);\n      const $2 = $1.load('<div><p>Some <a>text</a>.</p></div>');\n\n      expect($2('a')).toHaveLength(1);\n    });\n\n    /**\n     * The `.html` static method defined on the \"loaded\" Cheerio factory\n     * function is deprecated.\n     *\n     * In order to promote consistency with the jQuery library, users are\n     * encouraged to instead use the instance method of the same name.\n     *\n     * @example\n     *\n     * ```js\n     * const $ = cheerio.load('<h1>Hello, <span>world</span>.</h1>');\n     *\n     * $('h1').html();\n     * //=> 'Hello, <span>world</span>.'\n     * ```\n     *\n     * @example <caption>To render the markup of an entire document, invoke the\n     * `html` function exported by the Cheerio module with a \"root\"\n     * selection.</caption>\n     *\n     * ```js\n     * cheerio.html($.root());\n     * //=> '<html><head></head><body><h1>Hello, <span>world</span>.</h1></body></html>'\n     * ```\n     */\n    describe('.html - deprecated API', () => {\n      it('() : of empty cheerio object should return null', () => {\n        /*\n         * Note: the direct invocation of the Cheerio constructor function is\n         * also deprecated.\n         */\n        const $ = cheerio();\n        expect($.html()).toBe(null);\n      });\n\n      it('(selector) : should return the outerHTML of the selected element', () => {\n        const $ = cheerio.load(fruits);\n        expect($.html('.pear')).toBe('<li class=\"pear\">Pear</li>');\n      });\n    });\n\n    /**\n     * The `.xml` static method defined on the \"loaded\" Cheerio factory function\n     * is deprecated. Users are encouraged to instead use the `xml` function\n     * exported by the Cheerio module.\n     *\n     * @example\n     *\n     * ```js\n     * cheerio.xml($.root());\n     * ```\n     */\n    describe('.xml  - deprecated API', () => {\n      it('() :  renders XML', () => {\n        const $ = cheerio.load('<foo></foo>', { xmlMode: true });\n        expect($.xml()).toBe('<foo/>');\n      });\n    });\n\n    /**\n     * The `.text` static method defined on the \"loaded\" Cheerio factory\n     * function is deprecated.\n     *\n     * In order to promote consistency with the jQuery library, users are\n     * encouraged to instead use the instance method of the same name.\n     *\n     * @example\n     *\n     * ```js\n     * const $ = cheerio.load('<h1>Hello, <span>world</span>.</h1>');\n     * $('h1').text();\n     * //=> 'Hello, world.'\n     * ```\n     *\n     * @example <caption>To render the text content of an entire document,\n     * invoke the `text` function exported by the Cheerio module with a \"root\"\n     * selection. </caption>\n     *\n     * ```js\n     * cheerio.text($.root());\n     * //=> 'Hello, world.'\n     * ```\n     */\n    describe('.text  - deprecated API', () => {\n      it('(cheerio object) : should return the text contents of the specified elements', () => {\n        const $ = cheerio.load('<a>This is <em>content</em>.</a>');\n        expect($.text($('a'))).toBe('This is content.');\n      });\n\n      it('(cheerio object) : should omit comment nodes', () => {\n        const $ = cheerio.load(\n          '<a>This is <!-- a comment --> not a comment.</a>',\n        );\n        expect($.text($('a'))).toBe('This is  not a comment.');\n      });\n\n      it('(cheerio object) : should include text contents of children recursively', () => {\n        const $ = cheerio.load(\n          '<a>This is <div>a child with <span>another child and <!-- a comment --> not a comment</span> followed by <em>one last child</em> and some final</div> text.</a>',\n        );\n        expect($.text($('a'))).toBe(\n          'This is a child with another child and  not a comment followed by one last child and some final text.',\n        );\n      });\n\n      it('() : should return the rendered text content of the root', () => {\n        const $ = cheerio.load(\n          '<a>This is <div>a child with <span>another child and <!-- a comment --> not a comment</span> followed by <em>one last child</em> and some final</div> text.</a>',\n        );\n        expect($.text()).toBe(\n          'This is a child with another child and  not a comment followed by one last child and some final text.',\n        );\n      });\n\n      it('(cheerio object) : should not omit script tags', () => {\n        const $ = cheerio.load('<script>console.log(\"test\")</script>');\n        expect($.text()).toBe('console.log(\"test\")');\n      });\n\n      it('(cheerio object) : should omit style tags', () => {\n        const $ = cheerio.load(\n          '<style type=\"text/css\">.cf-hidden { display: none; }</style>',\n        );\n        expect($.text()).toBe('.cf-hidden { display: none; }');\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "src/__tests__/xml.spec.ts",
    "content": "import { describe, expect, it } from 'vitest';\nimport { load } from '../index.js';\nimport type { CheerioOptions } from '../options.js';\n\nfunction xml(str: string, options?: CheerioOptions) {\n  options = { xml: true, ...options };\n  const $ = load(str, options);\n  return $.xml();\n}\n\nfunction dom(str: string, options?: CheerioOptions) {\n  const $ = load('', options);\n  return $(str).html();\n}\n\ndescribe('render', () => {\n  describe('(xml)', () => {\n    it('should render <media:thumbnail /> tags correctly', () => {\n      const str =\n        '<media:thumbnail url=\"http://www.foo.com/keyframe.jpg\" width=\"75\" height=\"50\" time=\"12:05:01.123\" />';\n      expect(xml(str)).toBe(\n        '<media:thumbnail url=\"http://www.foo.com/keyframe.jpg\" width=\"75\" height=\"50\" time=\"12:05:01.123\"/>',\n      );\n    });\n\n    it('should render <link /> tags (RSS) correctly', () => {\n      const str = '<link>http://www.github.com/</link>';\n      expect(xml(str)).toBe('<link>http://www.github.com/</link>');\n    });\n\n    it('should escape entities', () => {\n      const str = '<tag attr=\"foo &amp; bar\"/>';\n      expect(xml(str)).toBe(str);\n    });\n\n    it('should render HTML as XML', () => {\n      const $ = load('<foo></foo>', null, false);\n      expect($.xml()).toBe('<foo/>');\n    });\n  });\n\n  describe('(dom)', () => {\n    it('should not keep camelCase for new nodes', () => {\n      const str = '<g><someElem someAttribute=\"something\">hello</someElem></g>';\n      expect(dom(str, { xml: false })).toBe(\n        '<someelem someattribute=\"something\">hello</someelem>',\n      );\n    });\n\n    it('should keep camelCase for new nodes', () => {\n      const str = '<g><someElem someAttribute=\"something\">hello</someElem></g>';\n      expect(dom(str, { xml: true })).toBe(\n        '<someElem someAttribute=\"something\">hello</someElem>',\n      );\n    });\n\n    it('should maintain the parsing options of distinct contexts independently', () => {\n      const str = '<g><someElem someAttribute=\"something\">hello</someElem></g>';\n      const $ = load('', { xml: false });\n\n      expect($(str).html()).toBe(\n        '<someelem someattribute=\"something\">hello</someelem>',\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "src/api/attributes.spec.ts",
    "content": "import type { Element } from 'domhandler';\nimport { beforeEach, describe, expect, it } from 'vitest';\nimport {\n  cheerio,\n  chocolates,\n  food,\n  fruits,\n  inputs,\n  mixedText,\n  script,\n  vegetables,\n} from '../__fixtures__/fixtures.js';\nimport { type Cheerio, type CheerioAPI, load } from '../index.js';\n\nfunction withClass(attr: string) {\n  return cheerio(`<div class=\"${attr}\"></div>`);\n}\n\ndescribe('$(...)', () => {\n  describe('.attr', () => {\n    let $: CheerioAPI;\n\n    beforeEach(() => {\n      $ = load(fruits);\n    });\n\n    it('() : should get all the attributes', () => {\n      const attrs = $('ul').attr();\n      expect(attrs).toHaveProperty('id', 'fruits');\n    });\n\n    it('(invalid key) : invalid attr should get undefined', () => {\n      const attr = $('.apple').attr('lol');\n      expect(attr).toBeUndefined();\n    });\n\n    it('(valid key) : valid attr should get value', () => {\n      const cls = $('.apple').attr('class');\n      expect(cls).toBe('apple');\n    });\n\n    it('(valid key) : valid attr should get name when boolean', () => {\n      const attr = $('<input name=email autofocus>').attr('autofocus');\n      expect(attr).toBe('autofocus');\n    });\n\n    it('(key, value) : should set one attr', () => {\n      const $pear = $('.pear').attr('id', 'pear');\n      expect($('#pear')).toHaveLength(1);\n      expect($pear).toBeInstanceOf($);\n    });\n\n    it('(key, value) : should set multiple attr', () => {\n      const $el = cheerio('<div></div> <div></div>').attr(\n        'class',\n        'pear',\n      ) as Cheerio<Element>;\n\n      expect($el[0].attribs).toHaveProperty('class', 'pear');\n      expect($el[1].attribs).toBeUndefined();\n      expect($el[2].attribs).toHaveProperty('class', 'pear');\n    });\n\n    it('(key, value) : should return an empty object for an empty object', () => {\n      const $src = $().attr('key', 'value');\n      expect($src.length).toBe(0);\n      expect($src[0]).toBeUndefined();\n    });\n\n    it('(map) : object map should set multiple attributes', () => {\n      $('.apple').attr({\n        id: 'apple',\n        style: 'color:red;',\n        'data-url': 'http://apple.com',\n      });\n      const attrs = $('.apple').attr();\n      expect(attrs).toHaveProperty('id', 'apple');\n      expect(attrs).toHaveProperty('style', 'color:red;');\n      expect(attrs).toHaveProperty('data-url', 'http://apple.com');\n    });\n\n    it('(map, val) : should throw with wrong combination of arguments', () => {\n      expect(() =>\n        $('.apple').attr(\n          {\n            id: 'apple',\n            style: 'color:red;',\n            'data-url': 'http://apple.com',\n          } as never,\n          () => '',\n        ),\n      ).toThrow('Bad combination of arguments.');\n    });\n\n    it('(key, function) : should call the function and update the attribute with the return value', () => {\n      const $fruits = $('#fruits');\n      $fruits.attr('id', (index, value) => {\n        expect(index).toBe(0);\n        expect(value).toBe('fruits');\n        return 'ninja';\n      });\n      const attrs = $fruits.attr();\n      expect(attrs).toHaveProperty('id', 'ninja');\n    });\n\n    it('(key, function) : should ignore text nodes', () => {\n      const $text = $(mixedText);\n      $text.attr('class', () => 'ninja');\n      const className = $text.attr('class');\n      expect(className).toBe('ninja');\n    });\n\n    it('(key, value) : should correctly encode then decode unsafe values', () => {\n      const $apple = $('.apple');\n      $apple.attr(\n        'href',\n        'http://github.com/\"><script>alert(\"XSS!\")</script><br',\n      );\n      expect($apple.attr('href')).toBe(\n        'http://github.com/\"><script>alert(\"XSS!\")</script><br',\n      );\n\n      $apple.attr(\n        'href',\n        'http://github.com/\"><script>alert(\"XSS!\")</script><br',\n      );\n      expect($apple.html()).not.toContain('<script>alert(\"XSS!\")</script>');\n    });\n\n    it('(key, value) : should coerce values to a string', () => {\n      const $apple = $('.apple');\n      $apple.attr('data-test', 1 as never);\n      expect($apple[0].attribs['data-test']).toBe('1');\n      expect($apple.attr('data-test')).toBe('1');\n    });\n\n    it('(key, value) : handle removed boolean attributes', () => {\n      const $apple = $('.apple');\n      $apple.attr('autofocus', 'autofocus');\n      expect($apple.attr('autofocus')).toBe('autofocus');\n      $apple.removeAttr('autofocus');\n      expect($apple.attr('autofocus')).toBeUndefined();\n    });\n\n    it('(key, value) : should remove non-boolean attributes with names or values similar to boolean ones', () => {\n      const $apple = $('.apple');\n      $apple.attr('data-autofocus', 'autofocus');\n      expect($apple.attr('data-autofocus')).toBe('autofocus');\n      $apple.removeAttr('data-autofocus');\n      expect($apple.attr('data-autofocus')).toBeUndefined();\n    });\n\n    it('(key, value) : should remove attributes when called with null value', () => {\n      const $pear = $('.pear').attr('autofocus', 'autofocus');\n      expect($pear.attr('autofocus')).toBe('autofocus');\n      $pear.attr('autofocus', null);\n      expect($pear.attr('autofocus')).toBeUndefined();\n    });\n\n    it('(map) : should remove attributes with null values', () => {\n      const $pear = $('.pear').attr({\n        autofocus: 'autofocus',\n        style: 'color:red',\n      });\n      expect($pear.attr('autofocus')).toBe('autofocus');\n      expect($pear.attr('style')).toBe('color:red');\n      $pear.attr({ autofocus: null, style: 'color:blue' });\n      expect($pear.attr('autofocus')).toBeUndefined();\n      expect($pear.attr('style')).toBe('color:blue');\n    });\n\n    it('(chaining) setting value and calling attr returns result', () => {\n      const pearAttr = $('.pear').attr('foo', 'bar').attr('foo');\n      expect(pearAttr).toBe('bar');\n    });\n\n    it('(chaining) setting attr to null returns a $', () => {\n      const $pear = $('.pear').attr('foo', null);\n      expect($pear).toBeInstanceOf($);\n    });\n\n    it('(chaining) setting attr to undefined returns a $', () => {\n      const $pear = $('.pear').attr('foo', undefined);\n      expect($('.pear')).toHaveLength(1);\n      expect($('.pear').attr('foo')).toBeUndefined();\n      expect($pear).toBeInstanceOf($);\n    });\n\n    it(\"(bool) shouldn't treat boolean attributes differently in XML mode\", () => {\n      const $xml = $.load('<input checked=checked disabled=yes />', {\n        xml: true,\n      })('input');\n\n      expect($xml.attr('checked')).toBe('checked');\n      expect($xml.attr('disabled')).toBe('yes');\n    });\n  });\n\n  describe('.prop', () => {\n    let $: CheerioAPI;\n    let checkbox: Cheerio<Element>;\n\n    beforeEach(() => {\n      $ = load(inputs);\n      checkbox = $('input[name=checkbox_on]');\n    });\n\n    it('(valid key) : valid prop should get value', () => {\n      expect(checkbox.prop('checked')).toBe(true);\n      checkbox.css('display', 'none');\n      expect(checkbox.prop('style')).toHaveProperty('display', 'none');\n      expect(checkbox.prop('style')).toHaveLength(1);\n      expect(checkbox.prop('style')).toContain('display');\n      expect(checkbox.prop('tagName')).toBe('INPUT');\n      expect(checkbox.prop('nodeName')).toBe('INPUT');\n    });\n\n    it('(valid key) : should return on empty collection', () => {\n      expect($(undefined).prop('checked')).toBeUndefined();\n      expect($(undefined).prop('style')).toBeUndefined();\n      expect($(undefined).prop('tagName')).toBeUndefined();\n      expect($(undefined).prop('nodeName')).toBeUndefined();\n    });\n\n    it('(invalid key) : invalid prop should get undefined', () => {\n      expect(checkbox.prop('lol')).toBeUndefined();\n      expect(checkbox.prop(4 as never)).toBeUndefined();\n      expect(checkbox.prop(true as never)).toBeUndefined();\n    });\n\n    it('(key, value) : should set prop', () => {\n      expect(checkbox.prop('checked')).toBe(true);\n      checkbox.prop('checked', false);\n      expect(checkbox.prop('checked')).toBe(false);\n      checkbox.prop('checked', true);\n      expect(checkbox.prop('checked')).toBe(true);\n    });\n\n    it('(key, value) : should update attribute', () => {\n      expect(checkbox.prop('checked')).toBe(true);\n      expect(checkbox.attr('checked')).toBe('checked');\n      checkbox.prop('checked', false);\n      expect(checkbox.prop('checked')).toBe(false);\n      expect(checkbox.attr('checked')).toBeUndefined();\n      checkbox.prop('checked', true);\n      expect(checkbox.prop('checked')).toBe(true);\n      expect(checkbox.attr('checked')).toBe('checked');\n    });\n\n    it('(key, value) : should update namespace', () => {\n      const imgs = $('<img>\\n\\n<img>\\n\\n<img>');\n      const nsHtml = 'http://www.w3.org/1999/xhtml';\n      imgs.prop('src', '#').prop('namespace', nsHtml);\n      expect(imgs.prop('namespace')).toBe(nsHtml);\n      imgs.prop('attribs', null);\n      expect(imgs.prop('src')).toBeUndefined();\n      expect(imgs.prop('data-foo')).toBeUndefined();\n    });\n\n    it('(key, value) : should ignore empty collection', () => {\n      expect($(undefined).prop('checked')).toBeUndefined();\n      $(undefined).prop('checked', true);\n      expect($(undefined).prop('checked')).toBeUndefined();\n    });\n\n    it('(map) : object map should set multiple props', () => {\n      checkbox.prop({\n        id: 'check',\n        checked: false,\n      });\n      expect(checkbox.prop('id')).toBe('check');\n      expect(checkbox.prop('checked')).toBe(false);\n    });\n\n    it('(map, val) : should throw with wrong combination of arguments', () => {\n      expect(() =>\n        $('.apple').prop(\n          {\n            id: 'check',\n            checked: false,\n          } as never,\n          () => '',\n        ),\n      ).toThrow('Bad combination of arguments.');\n    });\n\n    it('(key, function) : should call the function and update the prop with the return value', () => {\n      checkbox.prop('checked', (index, value) => {\n        expect(index).toBe(0);\n        expect(value).toBe(true);\n        return false;\n      });\n      expect(checkbox.prop('checked')).toBe(false);\n    });\n\n    it('(key, value) : should support chaining after setting props', () => {\n      expect(checkbox.prop('checked', false)).toBe(checkbox);\n    });\n\n    it('(invalid element/tag) : prop should return undefined', () => {\n      expect($(undefined).prop('prop')).toBeUndefined();\n      expect($(null as never).prop('prop')).toBeUndefined();\n    });\n\n    it('(\"href\") : should resolve links with `baseURI`', () => {\n      const $ = load(\n        `\n          <a id=\"1\" href=\"http://example.org\">example1</a>\n          <a id=\"2\" href=\"//example.org\">example2</a>\n          <a id=\"3\" href=\"/example.org\">example3</a>\n          <a id=\"4\" href=\"example.org\">example4</a>\n        `,\n        { baseURI: 'http://example.com/page/1' },\n      );\n\n      expect($('#1').prop('href')).toBe('http://example.org/');\n      expect($('#2').prop('href')).toBe('http://example.org/');\n      expect($('#3').prop('href')).toBe('http://example.com/example.org');\n      expect($('#4').prop('href')).toBe('http://example.com/page/example.org');\n\n      expect($(undefined).prop('href')).toBeUndefined();\n    });\n\n    it('(\"href\") : should skip values without an href', () => {\n      const $ = load('<a id=\"1\">example1</a>');\n      expect($('#1').prop('href')).toBeUndefined();\n    });\n\n    it('(\"src\") : should resolve links with `baseURI`', () => {\n      const $ = load(\n        `\n          <img id=\"1\" src=\"http://example.org/image.png\">\n          <iframe id=\"2\" src=\"//example.org/page.html\"></iframe>\n          <audio id=\"3\" src=\"/example.org/song.mp3\"></audio>\n          <source id=\"4\" src=\"example.org/image.png\">\n        `,\n        { baseURI: 'http://example.com/page/1' },\n      );\n\n      expect($('#1').prop('src')).toBe('http://example.org/image.png');\n      expect($('#2').prop('src')).toBe('http://example.org/page.html');\n      expect($('#3').prop('src')).toBe(\n        'http://example.com/example.org/song.mp3',\n      );\n      expect($('#4').prop('src')).toBe(\n        'http://example.com/page/example.org/image.png',\n      );\n\n      expect($(undefined).prop('src')).toBeUndefined();\n    });\n\n    it('(\"outerHTML\") : should render properly', () => {\n      const outerHtml = '<div><a></a></div>';\n      const $a = $(outerHtml);\n\n      expect($a.prop('outerHTML')).toBe(outerHtml);\n\n      expect($(undefined).prop('outerHTML')).toBeUndefined();\n    });\n\n    it('(\"outerHTML\") : should support root nodes', () => {\n      const $ = load('<div></div>');\n      expect($.root().prop('outerHTML')).toBe(\n        '<html><head></head><body><div></div></body></html>',\n      );\n    });\n\n    it('(\"innerHTML\") : should render properly', () => {\n      const $a = $('<div><a></a></div>');\n\n      expect($a.prop('innerHTML')).toBe('<a></a>');\n\n      expect($(undefined).prop('innerHTML')).toBeUndefined();\n    });\n\n    it('(\"textContent\") : should render properly', () => {\n      expect($('select').children().prop('textContent')).toBe(\n        'Option not selected',\n      );\n\n      expect($(script).prop('textContent')).toBe('A  var foo = \"bar\";B');\n\n      expect($(undefined).prop('textContent')).toBeUndefined();\n    });\n\n    it('(\"textContent\") : should include style and script tags', () => {\n      const $ = load(\n        '<body>Welcome <div>Hello, testing text function,<script>console.log(\"hello\")</script></div><style type=\"text/css\">.cf-hidden { display: none; }</style>End of message</body>',\n      );\n      expect($('body').prop('textContent')).toBe(\n        'Welcome Hello, testing text function,console.log(\"hello\").cf-hidden { display: none; }End of message',\n      );\n      expect($('style').prop('textContent')).toBe(\n        '.cf-hidden { display: none; }',\n      );\n      expect($('script').prop('textContent')).toBe('console.log(\"hello\")');\n    });\n\n    it('(\"innerText\") : should render properly', () => {\n      expect($('select').children().prop('innerText')).toBe(\n        'Option not selected',\n      );\n\n      expect($(script).prop('innerText')).toBe('AB');\n\n      expect($(undefined).prop('innerText')).toBeUndefined();\n    });\n\n    it('(\"innerText\") : should omit style and script tags', () => {\n      const $ = load(\n        '<body>Welcome <div>Hello, testing text function,<script>console.log(\"hello\")</script></div><style type=\"text/css\">.cf-hidden { display: none; }</style>End of message</body>',\n      );\n      expect($('body').prop('innerText')).toBe(\n        'Welcome Hello, testing text function,End of message',\n      );\n      expect($('style').prop('innerText')).toBe('');\n      expect($('script').prop('innerText')).toBe('');\n    });\n\n    it('(inherited properties) : prop should support inherited properties', () => {\n      expect($('select').prop('childNodes')).toBe($('select')[0].childNodes);\n    });\n\n    it('(key) : should skip text nodes', () => {\n      const $text = load(mixedText);\n      const $body = $text($text('body')[0].children);\n\n      expect($text($body[1]).prop('tagName')).toBeUndefined();\n\n      $body.prop('test-name', () => 'tester');\n      expect($text('body').html()).toBe(\n        '<a test-name=\"tester\">1</a>TEXT<b test-name=\"tester\">2</b>',\n      );\n    });\n\n    it(\"(bool) shouldn't treat boolean attributes differently in XML mode\", () => {\n      const $xml = $.load('<input checked=checked disabled=yes />', {\n        xml: true,\n      })('input');\n\n      expect($xml.prop('checked')).toBe('checked');\n      expect($xml.prop('disabled')).toBe('yes');\n    });\n  });\n\n  describe('.data', () => {\n    let $: CheerioAPI;\n\n    beforeEach(() => {\n      $ = load(chocolates);\n    });\n\n    it('() : should get all data attributes initially declared in the markup', () => {\n      const data = $('.linth').data();\n      expect(data).toStrictEqual({\n        highlight: 'Lindor',\n        origin: 'swiss',\n      });\n    });\n\n    it('() : should get all data set via `data`', () => {\n      const $el = cheerio('<div>');\n      $el.data('a', 1);\n      $el.data('b', 2);\n\n      expect($el.data()).toStrictEqual({\n        a: 1,\n        b: 2,\n      });\n    });\n\n    it('() : should get all data attributes initially declared in the markup merged with all data additionally set via `data`', () => {\n      const $el = cheerio('<div data-a=\"a\" data-b=\"b\">');\n      $el.data('b', 'b-modified');\n      $el.data('c', 'c');\n\n      expect($el.data()).toStrictEqual({\n        a: 'a',\n        b: 'b-modified',\n        c: 'c',\n      });\n    });\n\n    it('() : no data attribute should return an empty object', () => {\n      const data = $('.cailler').data();\n      expect(Object.keys(data)).toHaveLength(0);\n      expect($('.free').data()).toBeUndefined();\n    });\n\n    it('(invalid key) : invalid data attribute should return `undefined`', () => {\n      const data = $('.frey').data('lol');\n      expect(data).toBeUndefined();\n    });\n\n    it('(valid key) : valid data attribute should get value', () => {\n      const highlight = $('.linth').data('highlight');\n      const origin = $('.linth').data('origin');\n\n      expect(highlight).toBe('Lindor');\n      expect(origin).toBe('swiss');\n    });\n\n    it('(key) : should translate camel-cased key values to hyphen-separated versions', () => {\n      const $el = cheerio(\n        '<div data--three-word-attribute=\"a\" data-foo-Bar_BAZ-=\"b\">',\n      );\n\n      expect($el.data('ThreeWordAttribute')).toBe('a');\n      expect($el.data('fooBar_baz-')).toBe('b');\n    });\n\n    it('(key) : should retrieve object values', () => {\n      const data = {};\n      const $el = cheerio('<div>');\n\n      $el.data('test', data);\n\n      expect($el.data('test')).toBe(data);\n    });\n\n    it('(key) : should parse JSON data derived from the markup', () => {\n      const $el = cheerio('<div data-json=\"[1, 2, 3]\">');\n\n      expect($el.data('json')).toStrictEqual([1, 2, 3]);\n    });\n\n    it('(key) : should not parse JSON data set via the `data` API', () => {\n      const $el = cheerio('<div>');\n      $el.data('json', '[1, 2, 3]');\n\n      expect($el.data('json')).toBe('[1, 2, 3]');\n    });\n\n    // See https://api.jquery.com/data/ and https://bugs.jquery.com/ticket/14523\n    it('(key) : should ignore the markup value after the first access', () => {\n      const $el = cheerio('<div data-test=\"a\">');\n\n      expect($el.data('test')).toBe('a');\n\n      $el.attr('data-test', 'b');\n\n      expect($el.data('test')).toBe('a');\n    });\n\n    it('(key) : should recover from malformed JSON', () => {\n      const $el = cheerio('<div data-custom=\"{{templatevar}}\">');\n\n      expect($el.data('custom')).toBe('{{templatevar}}');\n    });\n\n    it('(\"\") : should accept the empty string as a name', () => {\n      const $el = cheerio('<div data-=\"a\">');\n\n      expect($el.data('')).toBe('a');\n    });\n\n    it('(hyphen key) : data addribute with hyphen should be camelized ;-)', () => {\n      const data = $('.frey').data();\n      expect(data).toStrictEqual({\n        taste: 'sweet',\n        bestCollection: 'Mahony',\n      });\n    });\n\n    it('(key, value) : should set data attribute', () => {\n      // Adding as object.\n      const a = $('.frey').data({\n        balls: 'giandor',\n      });\n      // Adding as string.\n      const b = $('.linth').data('snack', 'chocoletti');\n\n      expect(() => {\n        a.data(4 as never, 'throw');\n      }).not.toThrow();\n      expect(a.data('balls')).toStrictEqual('giandor');\n      expect(b.data('snack')).toStrictEqual('chocoletti');\n    });\n\n    it('(key, value) : should set data for all elements in the selection', () => {\n      $('li').data('foo', 'bar');\n\n      expect($('li').eq(0).data('foo')).toStrictEqual('bar');\n      expect($('li').eq(1).data('foo')).toStrictEqual('bar');\n      expect($('li').eq(2).data('foo')).toStrictEqual('bar');\n    });\n\n    it('(map) : object map should set multiple data attributes', () => {\n      const { data } = $('.linth').data({\n        id: 'Cailler',\n        flop: 'Pippilotti Rist',\n        top: 'Frigor',\n        url: 'http://www.cailler.ch/',\n      })[0] as never;\n\n      expect(data).toHaveProperty('id', 'Cailler');\n      expect(data).toHaveProperty('flop', 'Pippilotti Rist');\n      expect(data).toHaveProperty('top', 'Frigor');\n      expect(data).toHaveProperty('url', 'http://www.cailler.ch/');\n    });\n\n    describe('(attr) : data-* attribute type coercion :', () => {\n      it('boolean', () => {\n        const $el = cheerio('<div data-bool=\"true\">');\n        expect($el.data('bool')).toBe(true);\n      });\n\n      it('number', () => {\n        const $el = cheerio('<div data-number=\"23\">');\n        expect($el.data('number')).toBe(23);\n      });\n\n      it('number (scientific notation is not coerced)', () => {\n        const $el = cheerio('<div data-sci=\"1E10\">');\n        expect($el.data('sci')).toBe('1E10');\n      });\n\n      it('null', () => {\n        const $el = cheerio('<div data-null=\"null\">');\n        expect($el.data('null')).toBe(null);\n      });\n\n      it('object', () => {\n        const $el = cheerio('<div data-obj=\\'{ \"a\": 45 }\\'>');\n        expect($el.data('obj')).toStrictEqual({ a: 45 });\n      });\n\n      it('array', () => {\n        const $el = cheerio('<div data-array=\"[1, 2, 3]\">');\n        expect($el.data('array')).toStrictEqual([1, 2, 3]);\n      });\n    });\n\n    it('(key, value) : should skip text nodes', () => {\n      const $text = load(mixedText);\n      const $body = $text($text('body')[0].children);\n\n      $body.data('snack', 'chocoletti');\n\n      expect($text('b').data('snack')).toBe('chocoletti');\n    });\n  });\n\n  describe('.val', () => {\n    let $: CheerioAPI;\n\n    beforeEach(() => {\n      $ = load(inputs);\n    });\n\n    it('(): on div should get undefined', () => {\n      expect($('<div>').val()).toBeUndefined();\n    });\n\n    it('(): on button should get value', () => {\n      const val = $('#btn-value').val();\n      expect(val).toBe('button');\n    });\n    it('(): on button with no value should get undefined', () => {\n      const val = $('#btn-valueless').val();\n      expect(val).toBeUndefined();\n    });\n    it('(): on select should get value', () => {\n      const val = $('select#one').val();\n      expect(val).toBe('option_selected');\n    });\n    it('(): on select with no value should get text', () => {\n      const val = $('select#one-valueless').val();\n      expect(val).toBe('Option selected');\n    });\n    it('(): on select with no value should get converted HTML', () => {\n      const val = $('select#one-html-entity').val();\n      expect(val).toBe('Option <selected>');\n    });\n    it('(): on select with no value should get text content', () => {\n      const val = $('select#one-nested').val();\n      expect(val).toBe('Option selected');\n    });\n    it('(): on option should get value', () => {\n      const val = $('select#one option').eq(0).val();\n      expect(val).toBe('option_not_selected');\n    });\n    it('(): on text input should get value', () => {\n      const val = $('input[type=\"text\"]').val();\n      expect(val).toBe('input_text');\n    });\n    it('(): on checked checkbox should get value', () => {\n      const val = $('input[name=\"checkbox_on\"]').val();\n      expect(val).toBe('on');\n    });\n    it('(): on unchecked checkbox should get value', () => {\n      const val = $('input[name=\"checkbox_off\"]').val();\n      expect(val).toBe('off');\n    });\n    it('(): on valueless checkbox should get value', () => {\n      const val = $('input[name=\"checkbox_valueless\"]').val();\n      expect(val).toBe('on');\n    });\n    it('(): on radio should get value', () => {\n      const val = $('input[type=\"radio\"]').val();\n      expect(val).toBe('off');\n    });\n    it('(): on valueless radio should get value', () => {\n      const val = $('input[name=\"radio_valueless\"]').val();\n      expect(val).toBe('on');\n    });\n    it('(): on multiple select should get an array of values', () => {\n      const val = $('select#multi').val();\n      expect(val).toStrictEqual(['2', '3']);\n    });\n    it('(): on multiple select with no value attribute should get an array of text content', () => {\n      const val = $('select#multi-valueless').val();\n      expect(val).toStrictEqual(['2', '3']);\n    });\n    it('(): with no selector matches should return nothing', () => {\n      const val = $('.nasty').val();\n      expect(val).toBeUndefined();\n    });\n    it('(invalid value): should only handle arrays when it has the attribute multiple', () => {\n      const val = $('select#one').val([]);\n      expect(val).not.toBeUndefined();\n    });\n    it('(value): on empty set should get `this`', () => {\n      const $empty = $([]);\n      expect($empty.val('test')).toBe($empty);\n    });\n    it('(value): on input text should set value', () => {\n      const element = $('input[type=\"text\"]').val('test');\n      expect(element.val()).toBe('test');\n    });\n    it('(value): on select should set value', () => {\n      const element = $('select#one').val('option_not_selected');\n      expect(element.val()).toBe('option_not_selected');\n    });\n    it('(value): on option should set value', () => {\n      const element = $('select#one option').eq(0).val('option_changed');\n      expect(element.val()).toBe('option_changed');\n    });\n    it('(value): on radio should set value', () => {\n      const element = $('input[name=\"radio\"]').val('off');\n      expect(element.val()).toBe('off');\n    });\n    it('(value): on radio with special characters should set value', () => {\n      const element = $('input[name=\"radio[brackets]\"]').val('off');\n      expect(element.val()).toBe('off');\n    });\n    it('(values): on multiple select should set multiple values', () => {\n      const element = $('select#multi').val(['1', '3', '4']);\n      expect(element.val()).toHaveLength(3);\n    });\n  });\n\n  describe('.removeAttr', () => {\n    let $: CheerioAPI;\n\n    beforeEach(() => {\n      $ = load(fruits);\n    });\n\n    it('(key) : should remove a single attr', () => {\n      const $fruits = $('#fruits');\n      expect($fruits.attr('id')).not.toBeUndefined();\n      $fruits.removeAttr('id');\n      expect($fruits.attr('id')).toBeUndefined();\n    });\n\n    it('(key key) : should remove multiple attrs', () => {\n      const $apple = $('.apple');\n      $apple.attr('id', 'favorite');\n      $apple.attr('size', 'small');\n\n      expect($apple.attr('id')).toBe('favorite');\n      expect($apple.attr('class')).toBe('apple');\n      expect($apple.attr('size')).toBe('small');\n      $apple.removeAttr('id class');\n      expect($apple.attr('id')).toBeUndefined();\n      expect($apple.attr('class')).toBeUndefined();\n      expect($apple.attr('size')).toBe('small');\n    });\n\n    it('(key) : should return cheerio object', () => {\n      const obj = $('ul').removeAttr('id');\n      expect(obj).toBeInstanceOf($);\n    });\n\n    it('(key) : should skip text nodes', () => {\n      const $text = load(mixedText);\n      const $body = $text($text('body')[0].children);\n\n      $body.addClass(() => 'test');\n\n      expect($text('body').html()).toBe(\n        '<a class=\"test\">1</a>TEXT<b class=\"test\">2</b>',\n      );\n\n      $body.removeAttr('class');\n\n      expect($text('body').html()).toBe(mixedText);\n    });\n  });\n\n  describe('.hasClass', () => {\n    let $: CheerioAPI;\n\n    beforeEach(() => {\n      $ = load(fruits);\n    });\n\n    it('(valid class) : should return true', () => {\n      const cls = $('.apple').hasClass('apple');\n      expect(cls).toBe(true);\n\n      expect(withClass('foo').hasClass('foo')).toBe(true);\n      expect(withClass('foo bar').hasClass('foo')).toBe(true);\n      expect(withClass('bar foo').hasClass('foo')).toBe(true);\n      expect(withClass('bar foo bar').hasClass('foo')).toBe(true);\n    });\n\n    it('(invalid class) : should return false', () => {\n      const cls = $('#fruits').hasClass('fruits');\n      expect(cls).toBe(false);\n      expect(withClass('foo-bar').hasClass('foo')).toBe(false);\n      expect(withClass('foo-bar').hasClass('foo')).toBe(false);\n      expect(withClass('foo-bar').hasClass('foo-ba')).toBe(false);\n    });\n\n    it('should check multiple classes', () => {\n      // Add a class\n      $('.apple').addClass('red');\n      expect($('.apple').hasClass('apple')).toBe(true);\n      expect($('.apple').hasClass('red')).toBe(true);\n\n      // Remove one and test again\n      $('.apple').removeClass('apple');\n      expect($('li').eq(0).hasClass('apple')).toBe(false);\n    });\n\n    it('(empty string argument) : should return false', () => {\n      expect(withClass('foo').hasClass('')).toBe(false);\n      expect(withClass('foo bar').hasClass('')).toBe(false);\n      expect(withClass('foo bar').removeClass('foo').hasClass('')).toBe(false);\n    });\n  });\n\n  describe('.addClass', () => {\n    let $: CheerioAPI;\n\n    beforeEach(() => {\n      $ = load(fruits);\n    });\n\n    it('(first class) : should add the class to the element', () => {\n      const $fruits = $('#fruits');\n      $fruits.addClass('fruits');\n      const cls = $fruits.hasClass('fruits');\n      expect(cls).toBe(true);\n    });\n\n    it('(single class) : should add the class to the element', () => {\n      $('.apple').addClass('fruit');\n      const cls = $('.apple').hasClass('fruit');\n      expect(cls).toBe(true);\n    });\n\n    it('(class): adds classes to many selected items', () => {\n      $('li').addClass('fruit');\n      expect($('.apple').hasClass('fruit')).toBe(true);\n      expect($('.orange').hasClass('fruit')).toBe(true);\n      expect($('.pear').hasClass('fruit')).toBe(true);\n\n      // Mixed with text nodes\n      const $red = $('<html>\\n<ul id=one>\\n</ul>\\t</html>').addClass('red');\n      expect($red).toHaveLength(3);\n      expect($red[0].type).toBe('text');\n      expect($red[1].type).toBe('tag');\n      expect($red[2].type).toBe('text');\n      expect($red.hasClass('red')).toBe(true);\n    });\n\n    it('(class class class) : should add multiple classes to the element', () => {\n      $('.apple').addClass('fruit red tasty');\n      expect($('.apple').hasClass('apple')).toBe(true);\n      expect($('.apple').hasClass('fruit')).toBe(true);\n      expect($('.apple').hasClass('red')).toBe(true);\n      expect($('.apple').hasClass('tasty')).toBe(true);\n    });\n\n    it('(fn) : should add classes returned from the function', () => {\n      const $fruits = $('#fruits').children().add($('#fruits'));\n      const args: [i: number, className: string][] = [];\n      const thisVals: Element[] = [];\n      const toAdd = ['main', 'apple red', '', undefined];\n\n      $fruits.addClass(function (...myArgs) {\n        args.push(myArgs);\n        thisVals.push(this);\n        return toAdd[myArgs[0]];\n      });\n\n      expect(args).toStrictEqual([\n        [0, ''],\n        [1, 'apple'],\n        [2, 'orange'],\n        [3, 'pear'],\n      ]);\n      expect(thisVals).toStrictEqual([\n        $fruits[0],\n        $fruits[1],\n        $fruits[2],\n        $fruits[3],\n      ]);\n      expect($fruits.eq(0).hasClass('main')).toBe(true);\n      expect($fruits.eq(0).hasClass('apple')).toBe(false);\n      expect($fruits.eq(1).hasClass('apple')).toBe(true);\n      expect($fruits.eq(1).hasClass('red')).toBe(true);\n      expect($fruits.eq(2).hasClass('orange')).toBe(true);\n      expect($fruits.eq(3).hasClass('pear')).toBe(true);\n    });\n  });\n\n  describe('.removeClass', () => {\n    let $: CheerioAPI;\n\n    beforeEach(() => {\n      $ = load(fruits);\n    });\n\n    it('() : should remove all the classes', () => {\n      $('.pear').addClass('fruit');\n      $('.pear').removeClass();\n      expect($('.pear').attr('class')).toBeUndefined();\n    });\n\n    it('(\"\") : should not modify class list', () => {\n      const $fruits = $('#fruits');\n      $fruits.children().removeClass('');\n      expect($('.apple')).toHaveLength(1);\n    });\n\n    it('(invalid class) : should not remove anything', () => {\n      $('.pear').removeClass('fruit');\n      expect($('.pear').hasClass('pear')).toBe(true);\n    });\n\n    it('(no class attribute) : should not throw an exception', () => {\n      const $vegetables = cheerio(vegetables);\n\n      expect(() => {\n        $('li', $vegetables).removeClass('vegetable');\n      }).not.toThrow();\n    });\n\n    it('(single class) : should remove a single class from the element', () => {\n      $('.pear').addClass('fruit');\n      expect($('.pear').hasClass('fruit')).toBe(true);\n      $('.pear').removeClass('fruit');\n      expect($('.pear').hasClass('fruit')).toBe(false);\n      expect($('.pear').hasClass('pear')).toBe(true);\n\n      // Remove one class from set\n      const $li = $('li').removeClass('orange');\n      expect($li.eq(0).attr('class')).toBe('apple');\n      expect($li.eq(1).attr('class')).toBe('');\n      expect($li.eq(2).attr('class')).toBe('pear');\n\n      // Mixed with text nodes\n      const $red = $('<html>\\n<ul class=one>\\n</ul>\\t</html>').removeClass(\n        'one',\n      );\n      expect($red).toHaveLength(3);\n      expect($red[0].type).toBe('text');\n      expect($red[1].type).toBe('tag');\n      expect($red[2].type).toBe('text');\n      expect($red.eq(1).attr('class')).toBe('');\n      expect($red.eq(1).prop('tagName')).toBe('UL');\n    });\n\n    it('(single class) : should remove a single class from multiple classes on the element', () => {\n      $('.pear').addClass('fruit green tasty');\n      expect($('.pear').hasClass('fruit')).toBe(true);\n      expect($('.pear').hasClass('green')).toBe(true);\n      expect($('.pear').hasClass('tasty')).toBe(true);\n\n      $('.pear').removeClass('green');\n      expect($('.pear').hasClass('fruit')).toBe(true);\n      expect($('.pear').hasClass('green')).toBe(false);\n      expect($('.pear').hasClass('tasty')).toBe(true);\n    });\n\n    it('(class class class) : should remove multiple classes from the element', () => {\n      $('.apple').addClass('fruit red tasty');\n      expect($('.apple').hasClass('apple')).toBe(true);\n      expect($('.apple').hasClass('fruit')).toBe(true);\n      expect($('.apple').hasClass('red')).toBe(true);\n      expect($('.apple').hasClass('tasty')).toBe(true);\n\n      $('.apple').removeClass('apple red tasty');\n      expect($('.fruit').hasClass('apple')).toBe(false);\n      expect($('.fruit').hasClass('red')).toBe(false);\n      expect($('.fruit').hasClass('tasty')).toBe(false);\n      expect($('.fruit').hasClass('fruit')).toBe(true);\n    });\n\n    it('(class) : should remove all occurrences of a class name', () => {\n      const $div = cheerio('<div class=\"x x y x z\"></div>');\n      expect($div.removeClass('x').hasClass('x')).toBe(false);\n    });\n\n    it('(fn) : should remove classes returned from the function', () => {\n      const $fruits = $('#fruits').children();\n      const args: [number, string][] = [];\n      const thisVals: Element[] = [];\n      const toAdd = ['apple red', '', undefined];\n\n      $fruits.removeClass(function (...myArgs) {\n        args.push(myArgs);\n        thisVals.push(this);\n        return toAdd[myArgs[0]];\n      });\n\n      expect(args).toStrictEqual([\n        [0, 'apple'],\n        [1, 'orange'],\n        [2, 'pear'],\n      ]);\n      expect(thisVals).toStrictEqual([$fruits[0], $fruits[1], $fruits[2]]);\n      expect($fruits.eq(0).hasClass('apple')).toBe(false);\n      expect($fruits.eq(0).hasClass('red')).toBe(false);\n      expect($fruits.eq(1).hasClass('orange')).toBe(true);\n      expect($fruits.eq(2).hasClass('pear')).toBe(true);\n    });\n\n    it('(fn) : should no op elements without attributes', () => {\n      const $inputs = $(inputs);\n      const val = $inputs.removeClass(() => 'tasty');\n      expect(val).toHaveLength(17);\n    });\n\n    it('(fn) : should skip text nodes', () => {\n      const $text = load(mixedText);\n      const $body = $text($text('body')[0].children);\n\n      $body.addClass(() => 'test');\n\n      expect($text('body').html()).toBe(\n        '<a class=\"test\">1</a>TEXT<b class=\"test\">2</b>',\n      );\n\n      $body.removeClass(() => 'test');\n\n      expect($text('body').html()).toBe(\n        '<a class=\"\">1</a>TEXT<b class=\"\">2</b>',\n      );\n    });\n  });\n\n  describe('.toggleClass', () => {\n    let $: CheerioAPI;\n\n    beforeEach(() => {\n      $ = load(fruits);\n    });\n\n    it('(class class) : should toggle multiple classes from the element', () => {\n      $('.apple').addClass('fruit');\n      expect($('.apple').hasClass('apple')).toBe(true);\n      expect($('.apple').hasClass('fruit')).toBe(true);\n      expect($('.apple').hasClass('red')).toBe(false);\n\n      $('.apple').toggleClass('apple red');\n      expect($('.fruit').hasClass('apple')).toBe(false);\n      expect($('.fruit').hasClass('red')).toBe(true);\n      expect($('.fruit').hasClass('fruit')).toBe(true);\n\n      // Mixed with text nodes\n      const $red = $('<html>\\n<ul class=one>\\n</ul>\\t</html>').toggleClass(\n        'red',\n      );\n      expect($red).toHaveLength(3);\n      expect($red.hasClass('red')).toBe(true);\n      expect($red.hasClass('one')).toBe(true);\n      $red.toggleClass('one');\n      expect($red.hasClass('red')).toBe(true);\n      expect($red.hasClass('one')).toBe(false);\n    });\n\n    it('(class class, true) : should add multiple classes to the element', () => {\n      $('.apple').addClass('fruit');\n      expect($('.apple').hasClass('apple')).toBe(true);\n      expect($('.apple').hasClass('fruit')).toBe(true);\n      expect($('.apple').hasClass('red')).toBe(false);\n\n      $('.apple').toggleClass('apple red', true);\n      expect($('.fruit').hasClass('apple')).toBe(true);\n      expect($('.fruit').hasClass('red')).toBe(true);\n      expect($('.fruit').hasClass('fruit')).toBe(true);\n    });\n\n    it('(class true) : should add only one instance of class', () => {\n      $('.apple').toggleClass('tasty', true);\n      $('.apple').toggleClass('tasty', true);\n      expect($('.apple').attr('class')).toMatch(/tasty/g);\n    });\n\n    it('(class class, false) : should remove multiple classes from the element', () => {\n      $('.apple').addClass('fruit');\n      expect($('.apple').hasClass('apple')).toBe(true);\n      expect($('.apple').hasClass('fruit')).toBe(true);\n      expect($('.apple').hasClass('red')).toBe(false);\n\n      $('.apple').toggleClass('apple red', false);\n      expect($('.fruit').hasClass('apple')).toBe(false);\n      expect($('.fruit').hasClass('red')).toBe(false);\n      expect($('.fruit').hasClass('fruit')).toBe(true);\n    });\n\n    it('(fn) : should toggle classes returned from the function', () => {\n      const $ = load(food);\n\n      $('.apple').addClass('fruit');\n      $('.carrot').addClass('vegetable');\n      expect($('.apple').hasClass('fruit')).toBe(true);\n      expect($('.apple').hasClass('vegetable')).toBe(false);\n      expect($('.orange').hasClass('fruit')).toBe(false);\n      expect($('.orange').hasClass('vegetable')).toBe(false);\n      expect($('.carrot').hasClass('fruit')).toBe(false);\n      expect($('.carrot').hasClass('vegetable')).toBe(true);\n      expect($('.sweetcorn').hasClass('fruit')).toBe(false);\n      expect($('.sweetcorn').hasClass('vegetable')).toBe(false);\n\n      $('li').toggleClass(function () {\n        return $(this).parent().is('#fruits') ? 'fruit' : 'vegetable';\n      });\n      expect($('.apple').hasClass('fruit')).toBe(false);\n      expect($('.apple').hasClass('vegetable')).toBe(false);\n      expect($('.orange').hasClass('fruit')).toBe(true);\n      expect($('.orange').hasClass('vegetable')).toBe(false);\n      expect($('.carrot').hasClass('fruit')).toBe(false);\n      expect($('.carrot').hasClass('vegetable')).toBe(false);\n      expect($('.sweetcorn').hasClass('fruit')).toBe(false);\n      expect($('.sweetcorn').hasClass('vegetable')).toBe(true);\n    });\n\n    it('(fn) : should work with no initial class attribute', () => {\n      const $inputs = load(inputs);\n      $inputs('input, select').toggleClass(function () {\n        return $inputs(this).get(0)!.tagName === 'select'\n          ? 'selectable'\n          : 'inputable';\n      });\n      expect($inputs('.selectable')).toHaveLength(6);\n      expect($inputs('.inputable')).toHaveLength(9);\n    });\n\n    it('(fn) : should skip text nodes', () => {\n      const $text = load(mixedText);\n      const $body = $text($text('body')[0].children);\n\n      $body.toggleClass(() => 'test');\n\n      expect($text('body').html()).toBe(\n        '<a class=\"test\">1</a>TEXT<b class=\"test\">2</b>',\n      );\n\n      $body.toggleClass(() => 'test');\n\n      expect($text('body').html()).toBe(\n        '<a class=\"\">1</a>TEXT<b class=\"\">2</b>',\n      );\n    });\n\n    it('(invalid) : should be a no-op for invalid inputs', () => {\n      const original = $('.apple');\n      const testAgainst = original.attr('class');\n      expect(original.toggleClass().attr('class')).toStrictEqual(testAgainst);\n\n      for (const value of [undefined, true, false, null, 0, 1, {}]) {\n        expect(\n          original.toggleClass(value as never).attr('class'),\n        ).toStrictEqual(testAgainst);\n      }\n    });\n  });\n});\n"
  },
  {
    "path": "src/api/attributes.ts",
    "content": "/**\n * Methods for getting and modifying attributes.\n *\n * @module cheerio/attributes\n */\n\nimport { type AnyNode, type Element, isTag } from 'domhandler';\nimport { innerText, textContent } from 'domutils';\nimport { ElementType } from 'htmlparser2';\nimport type { Cheerio } from '../cheerio.js';\nimport { text } from '../static.js';\nimport { camelCase, cssCase, domEach } from '../utils.js';\n\nconst rspace = /\\s+/;\nconst dataAttrPrefix = 'data-';\n\n// Attributes that are booleans\nconst rboolean =\n  /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i;\n// Matches strings that look like JSON objects or arrays\nconst rbrace = /^{[\\s\\S]*}$|^\\[[\\s\\S]*]$/;\n\n/**\n * Gets a node's attribute. For boolean attributes, it will return the value's\n * name should it be set.\n *\n * Also supports getting the `value` of several form elements.\n *\n * @category Attributes\n * @param elem - Element to get the attribute of.\n * @param name - Name of the attribute.\n * @param xmlMode - Disable handling of special HTML attributes.\n * @returns The attribute's value.\n */\nfunction getAttr(\n  elem: AnyNode,\n  name: undefined,\n  xmlMode?: boolean,\n): Record<string, string> | undefined;\nfunction getAttr(\n  elem: AnyNode,\n  name: string,\n  xmlMode?: boolean,\n): string | undefined;\nfunction getAttr(\n  elem: AnyNode,\n  name: string | undefined,\n  xmlMode?: boolean,\n): Record<string, string> | string | undefined;\nfunction getAttr(\n  elem: AnyNode,\n  name: string | undefined,\n  xmlMode?: boolean,\n): Record<string, string> | string | undefined {\n  if (!(elem && isTag(elem))) return;\n\n  elem.attribs ??= {};\n\n  // Return the entire attribs object if no attribute specified\n  if (!name) {\n    return elem.attribs;\n  }\n\n  if (Object.hasOwn(elem.attribs, name)) {\n    // Get the (decoded) attribute\n    return !xmlMode && rboolean.test(name) ? name : elem.attribs[name];\n  }\n\n  // Mimic the DOM and return text content as value for `option's`\n  if (elem.name === 'option' && name === 'value') {\n    return text(elem.children);\n  }\n\n  // Mimic DOM with default value for radios/checkboxes\n  if (\n    elem.name === 'input' &&\n    (elem.attribs['type'] === 'radio' || elem.attribs['type'] === 'checkbox') &&\n    name === 'value'\n  ) {\n    return 'on';\n  }\n\n  return;\n}\n\n/**\n * Sets the value of an attribute. The attribute will be deleted if the value is\n * `null`.\n *\n * @private\n * @param el - The element to set the attribute on.\n * @param name - The attribute's name.\n * @param value - The attribute's value.\n */\nfunction setAttr(el: Element, name: string, value: string | null) {\n  if (value === null) {\n    removeAttribute(el, name);\n  } else {\n    el.attribs[name] = `${value}`;\n  }\n}\n\n/**\n * Method for getting attributes. Gets the attribute value for only the first\n * element in the matched set.\n *\n * @category Attributes\n * @example\n *\n * ```js\n * $('ul').attr('id');\n * //=> fruits\n * ```\n *\n * @param name - Name of the attribute.\n * @returns The attribute's value.\n * @see {@link https://api.jquery.com/attr/}\n */\nexport function attr<T extends AnyNode>(\n  this: Cheerio<T>,\n  name: string,\n): string | undefined;\n/**\n * Method for getting all attributes and their values of the first element in\n * the matched set.\n *\n * @category Attributes\n * @example\n *\n * ```js\n * $('ul').attr();\n * //=> { id: 'fruits' }\n * ```\n *\n * @returns The attribute's values.\n * @see {@link https://api.jquery.com/attr/}\n */\nexport function attr<T extends AnyNode>(\n  this: Cheerio<T>,\n): Record<string, string> | undefined;\n/**\n * Method for setting attributes. Sets the attribute value for all elements in\n * the matched set. If you set an attribute's value to `null`, you remove that\n * attribute. You may also pass a `map` and `function`.\n *\n * @category Attributes\n * @example\n *\n * ```js\n * $('.apple').attr('id', 'favorite').prop('outerHTML');\n * //=> <li class=\"apple\" id=\"favorite\">Apple</li>\n * ```\n *\n * @param name - Name of the attribute.\n * @param value - The new value of the attribute.\n * @returns The instance itself.\n * @see {@link https://api.jquery.com/attr/}\n */\nexport function attr<T extends AnyNode>(\n  this: Cheerio<T>,\n  name: string,\n  value?:\n    | string\n    | null\n    | ((this: Element, i: number, attrib: string) => string | null),\n): Cheerio<T>;\n/**\n * Method for setting multiple attributes at once. Sets the attribute value for\n * all elements in the matched set. If you set an attribute's value to `null`,\n * you remove that attribute.\n *\n * @category Attributes\n * @example\n *\n * ```js\n * $('.apple').attr({ id: 'favorite' }).prop('outerHTML');\n * //=> <li class=\"apple\" id=\"favorite\">Apple</li>\n * ```\n *\n * @param values - Map of attribute names and values.\n * @returns The instance itself.\n * @see {@link https://api.jquery.com/attr/}\n */\nexport function attr<T extends AnyNode>(\n  this: Cheerio<T>,\n  values: Record<string, string | null>,\n): Cheerio<T>;\nexport function attr<T extends AnyNode>(\n  this: Cheerio<T>,\n  name?: string | Record<string, string | null>,\n  value?:\n    | string\n    | null\n    | ((this: Element, i: number, attrib: string) => string | null),\n): string | Cheerio<T> | undefined | Record<string, string> {\n  // Set the value (with attr map support)\n  if (typeof name === 'object' || value !== undefined) {\n    if (typeof value === 'function') {\n      if (typeof name !== 'string') {\n        throw new TypeError('Bad combination of arguments.');\n      }\n      return domEach(this, (el, i) => {\n        if (isTag(el)) setAttr(el, name, value.call(el, i, el.attribs[name]));\n      });\n    }\n    return domEach(this, (el) => {\n      if (!isTag(el)) return;\n\n      if (typeof name === 'object') {\n        for (const objName of Object.keys(name)) {\n          const objValue = name[objName];\n          setAttr(el, objName, objValue);\n        }\n      } else {\n        if (typeof name !== 'string') {\n          throw new TypeError('Bad combination of arguments.');\n        }\n        setAttr(el, name, value ?? null);\n      }\n    });\n  }\n\n  return arguments.length > 1\n    ? this\n    : getAttr(this[0], name, this.options.xmlMode);\n}\n\n/**\n * Gets a node's prop.\n *\n * @private\n * @category Attributes\n * @param el - Element to get the prop of.\n * @param name - Name of the prop.\n * @param xmlMode - Disable handling of special HTML attributes.\n * @returns The prop's value.\n */\nfunction getProp(\n  el: Element,\n  name: string,\n  xmlMode?: boolean,\n): string | undefined | boolean | Element[keyof Element] {\n  return name in el\n    ? // @ts-expect-error TS doesn't like us accessing the value directly here.\n      (el[name] as string | undefined)\n    : !xmlMode && rboolean.test(name)\n      ? getAttr(el, name, false) !== undefined\n      : getAttr(el, name, xmlMode);\n}\n\n/**\n * Sets the value of a prop.\n *\n * @private\n * @param el - The element to set the prop on.\n * @param name - The prop's name.\n * @param value - The prop's value.\n * @param xmlMode - Disable handling of special HTML attributes.\n */\nfunction setProp(el: Element, name: string, value: unknown, xmlMode?: boolean) {\n  if (name in el) {\n    // @ts-expect-error Overriding value\n    el[name] = value;\n  } else {\n    setAttr(\n      el,\n      name,\n      !xmlMode && rboolean.test(name)\n        ? value\n          ? ''\n          : null\n        : `${value as string}`,\n    );\n  }\n}\n\ninterface StyleProp {\n  length: number;\n  [key: string]: string | number;\n  [index: number]: string;\n}\n/**\n * Get a string representation of the element.\n *\n * @param name Name of the property.\n */\nexport function prop<T extends AnyNode>(\n  this: Cheerio<T>,\n  name: 'innerHTML' | 'outerHTML' | 'innerText' | 'textContent',\n): string | null;\n/**\n * Get a parsed CSS style object.\n *\n * @param name - Name of the property.\n * @returns The style object, or `undefined` if the element has no `style`\n *   attribute.\n */\nexport function prop<T extends AnyNode>(\n  this: Cheerio<T>,\n  name: 'style',\n): StyleProp | undefined;\n\n/**\n * Method for getting and setting properties. Gets the property value for only\n * the first element in the matched set.\n *\n * @category Attributes\n * @example\n *\n * ```js\n * $('input[type=\"checkbox\"]').prop('checked');\n * //=> false\n *\n * $('input[type=\"checkbox\"]').prop('checked', true).val();\n * //=> ok\n * ```\n *\n * @param name - Name of the property.\n * @returns If `value` is specified the instance itself, otherwise the prop's\n *   value.\n * @see {@link https://api.jquery.com/prop/}\n */\n// biome-ignore lint/style/useUnifiedTypeSignatures: Separate overloads needed for accurate docs\nexport function prop<T extends AnyNode>(\n  this: Cheerio<T>,\n  name: 'tagName' | 'nodeName',\n): string | undefined;\n/**\n * Resolve `href` or `src` of supported elements. Requires the `baseURI` option\n * to be set, and a global `URL` object to be part of the environment.\n *\n * @example With `baseURI` set to `'https://example.com'`:\n *\n * ```js\n * $('<img src=\"image.png\">').prop('src');\n * //=> 'https://example.com/image.png'\n * ```\n *\n * @param name - Name of the property.\n * @returns The resolved URL, or `undefined` if the element is not supported.\n */\nexport function prop<T extends AnyNode>(\n  this: Cheerio<T>,\n  name: 'href' | 'src',\n): string | undefined;\n/**\n * Get a property of an element.\n *\n * @param name - Name of the property.\n * @returns The property's value.\n */\nexport function prop<T extends AnyNode, K extends keyof Element>(\n  this: Cheerio<T>,\n  name: K,\n): Element[K];\n/**\n * Set a property of an element.\n *\n * @param name - Name of the property.\n * @param value - Value to set the property to.\n * @returns The instance itself.\n */\nexport function prop<T extends AnyNode, K extends keyof Element>(\n  this: Cheerio<T>,\n  name: K,\n  value:\n    | Element[K]\n    | ((this: Element, i: number, prop: K) => Element[keyof Element]),\n): Cheerio<T>;\n/**\n * Set multiple properties of an element.\n *\n * @example\n *\n * ```js\n * $('input[type=\"checkbox\"]').prop({\n *   checked: true,\n *   disabled: false,\n * });\n * ```\n *\n * @param map - Object of properties to set.\n * @returns The instance itself.\n */\nexport function prop<T extends AnyNode>(\n  this: Cheerio<T>,\n  map: Record<string, string | Element[keyof Element] | boolean>,\n): Cheerio<T>;\n/**\n * Set a property of an element.\n *\n * @param name - Name of the property.\n * @param value - Value to set the property to.\n * @returns The instance itself.\n */\nexport function prop<T extends AnyNode>(\n  this: Cheerio<T>,\n  name: string,\n  value:\n    | string\n    | boolean\n    | null\n    | ((this: Element, i: number, prop: string) => string | boolean),\n): Cheerio<T>;\n/**\n * Get a property of an element.\n *\n * @param name - The property's name.\n * @returns The property's value.\n */\nexport function prop<T extends AnyNode>(this: Cheerio<T>, name: string): string;\nexport function prop<T extends AnyNode>(\n  this: Cheerio<T>,\n  name: string | Record<string, string | Element[keyof Element] | boolean>,\n  value?: unknown,\n):\n  | Cheerio<T>\n  | string\n  | boolean\n  | undefined\n  | null\n  | Element[keyof Element]\n  | StyleProp {\n  if (typeof name === 'string' && value === undefined) {\n    const el = this[0];\n\n    if (!el) return;\n\n    switch (name) {\n      case 'style': {\n        const property = this.css() as StyleProp;\n        const keys = Object.keys(property);\n        for (let i = 0; i < keys.length; i++) {\n          property[i] = keys[i];\n        }\n\n        property.length = keys.length;\n\n        return property;\n      }\n      case 'tagName':\n      case 'nodeName': {\n        if (!isTag(el)) return;\n        return el.name.toUpperCase();\n      }\n\n      case 'href':\n      case 'src': {\n        if (!isTag(el)) return;\n        const prop = el.attribs?.[name];\n\n        if (\n          typeof URL !== 'undefined' &&\n          ((name === 'href' && (el.tagName === 'a' || el.tagName === 'link')) ||\n            (name === 'src' &&\n              (el.tagName === 'img' ||\n                el.tagName === 'iframe' ||\n                el.tagName === 'audio' ||\n                el.tagName === 'video' ||\n                el.tagName === 'source'))) &&\n          prop !== undefined &&\n          this.options.baseURI\n        ) {\n          return new URL(prop, this.options.baseURI).href;\n        }\n\n        return prop;\n      }\n\n      case 'innerText': {\n        return innerText(el);\n      }\n\n      case 'textContent': {\n        return textContent(el);\n      }\n\n      case 'outerHTML': {\n        if (el.type === ElementType.Root) return this.html();\n        return this.clone().wrap('<container />').parent().html();\n      }\n\n      case 'innerHTML': {\n        return this.html();\n      }\n\n      default: {\n        if (!isTag(el)) return;\n        return getProp(el, name, this.options.xmlMode);\n      }\n    }\n  }\n\n  if (typeof name === 'object' || value !== undefined) {\n    if (typeof value === 'function') {\n      if (typeof name === 'object') {\n        throw new TypeError('Bad combination of arguments.');\n      }\n      return domEach(this, (el, i) => {\n        if (isTag(el)) {\n          setProp(\n            el,\n            name,\n            value.call(el, i, getProp(el, name, this.options.xmlMode)),\n            this.options.xmlMode,\n          );\n        }\n      });\n    }\n\n    return domEach(this, (el) => {\n      if (!isTag(el)) return;\n\n      if (typeof name === 'object') {\n        for (const key of Object.keys(name)) {\n          const val = name[key];\n          setProp(el, key, val, this.options.xmlMode);\n        }\n      } else {\n        setProp(el, name, value, this.options.xmlMode);\n      }\n    });\n  }\n\n  return;\n}\n\n/**\n * An element with a data attribute.\n *\n * @private\n */\ninterface DataElement extends Element {\n  /** The data attribute. */\n  data?: Record<string, unknown>;\n}\n\n/**\n * Sets the value of a data attribute.\n *\n * @private\n * @param elem - The element to set the data attribute on.\n * @param name - The data attribute's name.\n * @param value - The data attribute's value.\n */\nfunction setData(\n  elem: DataElement,\n  name: string | Record<string, unknown>,\n  value?: unknown,\n) {\n  elem.data ??= {};\n\n  if (typeof name === 'object') Object.assign(elem.data, name);\n  else if (typeof name === 'string' && value !== undefined) {\n    elem.data[name] = value;\n  }\n}\n\n/**\n * Read _all_ HTML5 `data-*` attributes from the equivalent HTML5 `data-*`\n * attribute, and cache the value in the node's internal data store.\n *\n * @private\n * @category Attributes\n * @param el - Element to get the data attribute of.\n * @returns A map with all of the data attributes.\n */\nfunction readAllData(el: DataElement): unknown {\n  const data = (el.data ??= {});\n\n  for (const domName of Object.keys(el.attribs)) {\n    if (!domName.startsWith(dataAttrPrefix)) {\n      continue;\n    }\n\n    const jsName = camelCase(domName.slice(dataAttrPrefix.length));\n\n    if (!Object.hasOwn(data, jsName)) {\n      data[jsName] = parseDataValue(el.attribs[domName]);\n    }\n  }\n\n  return data;\n}\n\n/**\n * Read the specified attribute from the equivalent HTML5 `data-*` attribute,\n * and (if present) cache the value in the node's internal data store.\n *\n * @category Attributes\n * @param el - Element to get the data attribute of.\n * @param name - Name of the data attribute.\n * @returns The data attribute's value.\n */\nfunction readData(el: DataElement, name: string): unknown {\n  const domName = dataAttrPrefix + cssCase(name);\n  const data = (el.data ??= {});\n\n  if (Object.hasOwn(data, name)) {\n    return data[name];\n  }\n\n  if (Object.hasOwn(el.attribs, domName)) {\n    data[name] = parseDataValue(el.attribs[domName]);\n    return data[name];\n  }\n\n  return;\n}\n\n/**\n * Coerce string data-* attributes to their corresponding JavaScript primitives.\n *\n * @category Attributes\n * @param value - The value to parse.\n * @returns The parsed value.\n */\nfunction parseDataValue(value: string): unknown {\n  if (value === 'null') return null;\n  if (value === 'true') return true;\n  if (value === 'false') return false;\n  const num = Number(value);\n  if (value === String(num)) return num;\n  if (rbrace.test(value)) {\n    try {\n      return JSON.parse(value);\n    } catch {\n      /* Ignore */\n    }\n  }\n  return value;\n}\n\n/**\n * Method for getting data attributes, for only the first element in the matched\n * set.\n *\n * @category Attributes\n * @example\n *\n * ```js\n * $('<div data-apple-color=\"red\"></div>').data('apple-color');\n * //=> 'red'\n * ```\n *\n * @param name - Name of the data attribute.\n * @returns The data attribute's value, or `undefined` if the attribute does not\n *   exist.\n * @see {@link https://api.jquery.com/data/}\n */\nexport function data<T extends AnyNode>(\n  this: Cheerio<T>,\n  name: string,\n): unknown;\n/**\n * Method for getting all of an element's data attributes, for only the first\n * element in the matched set.\n *\n * @category Attributes\n * @example\n *\n * ```js\n * $('<div data-apple-color=\"red\"></div>').data();\n * //=> { appleColor: 'red' }\n * ```\n *\n * @returns A map with all of the data attributes.\n * @see {@link https://api.jquery.com/data/}\n */\nexport function data<T extends AnyNode>(\n  this: Cheerio<T>,\n): Record<string, unknown>;\n/**\n * Method for setting data attributes, for only the first element in the matched\n * set.\n *\n * @category Attributes\n * @example\n *\n * ```js\n * const apple = $('.apple').data('kind', 'mac');\n *\n * apple.data('kind');\n * //=> 'mac'\n * ```\n *\n * @param name - Name of the data attribute.\n * @param value - The new value.\n * @returns The instance itself.\n * @see {@link https://api.jquery.com/data/}\n */\nexport function data<T extends AnyNode>(\n  this: Cheerio<T>,\n  name: string,\n  value: unknown,\n): Cheerio<T>;\n/**\n * Method for setting multiple data attributes at once, for only the first\n * element in the matched set.\n *\n * @category Attributes\n * @example\n *\n * ```js\n * const apple = $('.apple').data({ kind: 'mac' });\n *\n * apple.data('kind');\n * //=> 'mac'\n * ```\n *\n * @param values - Map of names to values.\n * @returns The instance itself.\n * @see {@link https://api.jquery.com/data/}\n */\nexport function data<T extends AnyNode>(\n  this: Cheerio<T>,\n  values: Record<string, unknown>,\n): Cheerio<T>;\nexport function data<T extends AnyNode>(\n  this: Cheerio<T>,\n  name?: string | Record<string, unknown>,\n  value?: unknown,\n): unknown {\n  const elem = this[0];\n\n  if (!(elem && isTag(elem))) return;\n\n  const dataEl: DataElement = elem;\n  dataEl.data ??= {};\n\n  // Return the entire data object if no data specified\n  if (name == null) {\n    return readAllData(dataEl);\n  }\n\n  // Set the value (with attr map support)\n  if (typeof name === 'object' || value !== undefined) {\n    domEach(this, (el) => {\n      if (isTag(el)) {\n        if (typeof name === 'object') setData(el, name);\n        else setData(el, name, value);\n      }\n    });\n    return this;\n  }\n\n  return readData(dataEl, name);\n}\n\n/**\n * Method for getting the value of input, select, and textarea. Note: Support\n * for `map`, and `function` has not been added yet.\n *\n * @category Attributes\n * @example\n *\n * ```js\n * $('input[type=\"text\"]').val();\n * //=> input_text\n * ```\n *\n * @returns The value.\n * @see {@link https://api.jquery.com/val/}\n */\nexport function val<T extends AnyNode>(\n  this: Cheerio<T>,\n): string | undefined | string[];\n/**\n * Method for setting the value of input, select, and textarea. Note: Support\n * for `map`, and `function` has not been added yet.\n *\n * @category Attributes\n * @example\n *\n * ```js\n * $('input[type=\"text\"]').val('test').prop('outerHTML');\n * //=> <input type=\"text\" value=\"test\"/>\n * ```\n *\n * @param value - The new value.\n * @returns The instance itself.\n * @see {@link https://api.jquery.com/val/}\n */\nexport function val<T extends AnyNode>(\n  this: Cheerio<T>,\n  value: string | string[],\n): Cheerio<T>;\nexport function val<T extends AnyNode>(\n  this: Cheerio<T>,\n  value?: string | string[],\n): string | string[] | Cheerio<T> | undefined {\n  const querying = arguments.length === 0;\n  const element = this[0];\n\n  if (!(element && isTag(element))) return querying ? undefined : this;\n\n  switch (element.name) {\n    case 'textarea': {\n      return this.text(value as string);\n    }\n    case 'select': {\n      const option = this.find('option:selected');\n      if (!querying) {\n        if (this.attr('multiple') == null && typeof value === 'object') {\n          return this;\n        }\n\n        this.find('option').removeAttr('selected');\n\n        const values = typeof value === 'object' ? value : [value];\n        for (const val of values) {\n          this.find(`option[value=\"${val}\"]`).attr('selected', '');\n        }\n\n        return this;\n      }\n\n      return this.attr('multiple')\n        ? option.toArray().map((el) => text(el.children))\n        : option.attr('value');\n    }\n    case 'button':\n    case 'input':\n    case 'option': {\n      return querying\n        ? this.attr('value')\n        : this.attr('value', value as string);\n    }\n  }\n\n  return;\n}\n\n/**\n * Remove an attribute.\n *\n * @param elem - Node to remove attribute from.\n * @param name - Name of the attribute to remove.\n */\nfunction removeAttribute(elem: Element, name: string) {\n  if (!(elem.attribs && Object.hasOwn(elem.attribs, name))) return;\n\n  delete elem.attribs[name];\n}\n\n/**\n * Splits a space-separated list of names to individual names.\n *\n * @category Attributes\n * @param names - Names to split.\n * @returns - Split names.\n */\nfunction splitNames(names?: string): string[] {\n  return names ? names.trim().split(rspace) : [];\n}\n\n/**\n * Method for removing attributes by `name`.\n *\n * @category Attributes\n * @example\n *\n * ```js\n * $('.pear').removeAttr('class').prop('outerHTML');\n * //=> <li>Pear</li>\n *\n * $('.apple').attr('id', 'favorite');\n * $('.apple').removeAttr('id class').prop('outerHTML');\n * //=> <li>Apple</li>\n * ```\n *\n * @param name - Name of the attribute.\n * @returns The instance itself.\n * @see {@link https://api.jquery.com/removeAttr/}\n */\nexport function removeAttr<T extends AnyNode>(\n  this: Cheerio<T>,\n  name: string,\n): Cheerio<T> {\n  const attrNames = splitNames(name);\n\n  for (const attrName of attrNames) {\n    domEach(this, (elem) => {\n      if (isTag(elem)) removeAttribute(elem, attrName);\n    });\n  }\n\n  return this;\n}\n\n/**\n * Check to see if _any_ of the matched elements have the given `className`.\n *\n * @category Attributes\n * @example\n *\n * ```js\n * $('.pear').hasClass('pear');\n * //=> true\n *\n * $('apple').hasClass('fruit');\n * //=> false\n *\n * $('li').hasClass('pear');\n * //=> true\n * ```\n *\n * @param className - Name of the class.\n * @returns Indicates if an element has the given `className`.\n * @see {@link https://api.jquery.com/hasClass/}\n */\nexport function hasClass<T extends AnyNode>(\n  this: Cheerio<T>,\n  className: string,\n): boolean {\n  return this.toArray().some((elem) => {\n    const clazz = isTag(elem) && elem.attribs['class'];\n\n    if (clazz && className.length > 0) {\n      for (\n        let idx = clazz.indexOf(className);\n        idx > -1;\n        idx = clazz.indexOf(className, idx + 1)\n      ) {\n        const end = idx + className.length;\n\n        if (\n          (idx === 0 || rspace.test(clazz[idx - 1])) &&\n          (end === clazz.length || rspace.test(clazz[end]))\n        ) {\n          return true;\n        }\n      }\n    }\n\n    return false;\n  });\n}\n\n/**\n * Adds class(es) to all of the matched elements. Also accepts a `function`.\n *\n * @category Attributes\n * @example\n *\n * ```js\n * $('.pear').addClass('fruit').prop('outerHTML');\n * //=> <li class=\"pear fruit\">Pear</li>\n *\n * $('.apple').addClass('fruit red').prop('outerHTML');\n * //=> <li class=\"apple fruit red\">Apple</li>\n * ```\n *\n * @param value - Name of new class.\n * @returns The instance itself.\n * @see {@link https://api.jquery.com/addClass/}\n */\nexport function addClass<T extends AnyNode, R extends ArrayLike<T>>(\n  this: R,\n  value?:\n    | string\n    | ((this: Element, i: number, className: string) => string | undefined),\n): R {\n  // Support functions\n  if (typeof value === 'function') {\n    return domEach(this, (el, i) => {\n      if (isTag(el)) {\n        const className = el.attribs['class'] || '';\n        addClass.call([el], value.call(el, i, className));\n      }\n    });\n  }\n\n  // Return if no value or not a string or function\n  if (!value || typeof value !== 'string') return this;\n\n  const classNames = value.split(rspace);\n  const numElements = this.length;\n\n  for (let i = 0; i < numElements; i++) {\n    const el = this[i];\n    // If selected element isn't a tag, move on\n    if (!isTag(el)) continue;\n\n    // If we don't already have classes — always set xmlMode to false here, as it doesn't matter for classes\n    const className = getAttr(el, 'class', false);\n\n    if (className) {\n      let setClass = ` ${className} `;\n\n      // Check if class already exists\n      for (const cn of classNames) {\n        const appendClass = `${cn} `;\n        if (!setClass.includes(` ${appendClass}`)) setClass += appendClass;\n      }\n\n      setAttr(el, 'class', setClass.trim());\n    } else {\n      setAttr(el, 'class', classNames.join(' ').trim());\n    }\n  }\n\n  return this;\n}\n\n/**\n * Removes one or more space-separated classes from the selected elements. If no\n * `className` is defined, all classes will be removed. Also accepts a\n * `function`.\n *\n * @category Attributes\n * @example\n *\n * ```js\n * $('.pear').removeClass('pear').prop('outerHTML');\n * //=> <li class=\"\">Pear</li>\n *\n * $('.apple').addClass('red').removeClass().prop('outerHTML');\n * //=> <li class=\"\">Apple</li>\n * ```\n *\n * @param name - Name of the class. If not specified, removes all elements.\n * @returns The instance itself.\n * @see {@link https://api.jquery.com/removeClass/}\n */\nexport function removeClass<T extends AnyNode, R extends ArrayLike<T>>(\n  this: R,\n  name?:\n    | string\n    | ((this: Element, i: number, className: string) => string | undefined),\n): R {\n  // Handle if value is a function\n  if (typeof name === 'function') {\n    return domEach(this, (el, i) => {\n      if (isTag(el)) {\n        removeClass.call([el], name.call(el, i, el.attribs['class'] || ''));\n      }\n    });\n  }\n\n  const classes = splitNames(name);\n  const numClasses = classes.length;\n  const removeAll = arguments.length === 0;\n\n  return domEach(this, (el) => {\n    if (!isTag(el)) return;\n\n    if (removeAll) {\n      // Short circuit the remove all case as this is the nice one\n      el.attribs['class'] = '';\n    } else {\n      const elClasses = splitNames(el.attribs['class']);\n      let changed = false;\n\n      for (let j = 0; j < numClasses; j++) {\n        const index = elClasses.indexOf(classes[j]);\n\n        if (index !== -1) {\n          elClasses.splice(index, 1);\n          changed = true;\n\n          /*\n           * We have to do another pass to ensure that there are not duplicate\n           * classes listed\n           */\n          j--;\n        }\n      }\n      if (changed) {\n        el.attribs['class'] = elClasses.join(' ');\n      }\n    }\n  });\n}\n\n/**\n * Add or remove class(es) from the matched elements, depending on either the\n * class's presence or the value of the switch argument. Also accepts a\n * `function`.\n *\n * @category Attributes\n * @example\n *\n * ```js\n * $('.apple.green').toggleClass('fruit green red').prop('outerHTML');\n * //=> <li class=\"apple fruit red\">Apple</li>\n *\n * $('.apple.green').toggleClass('fruit green red', true).prop('outerHTML');\n * //=> <li class=\"apple green fruit red\">Apple</li>\n * ```\n *\n * @param value - Name of the class. Can also be a function.\n * @param stateVal - If specified the state of the class.\n * @returns The instance itself.\n * @see {@link https://api.jquery.com/toggleClass/}\n */\nexport function toggleClass<T extends AnyNode, R extends ArrayLike<T>>(\n  this: R,\n  value?:\n    | string\n    | ((\n        this: Element,\n        i: number,\n        className: string,\n        stateVal?: boolean,\n      ) => string),\n  stateVal?: boolean,\n): R {\n  // Support functions\n  if (typeof value === 'function') {\n    return domEach(this, (el, i) => {\n      if (isTag(el)) {\n        toggleClass.call(\n          [el],\n          value.call(el, i, el.attribs['class'] || '', stateVal),\n          stateVal,\n        );\n      }\n    });\n  }\n\n  // Return if no value or not a string or function\n  if (!value || typeof value !== 'string') return this;\n\n  const classNames = value.split(rspace);\n  const numClasses = classNames.length;\n  const state = typeof stateVal === 'boolean' ? (stateVal ? 1 : -1) : 0;\n  const numElements = this.length;\n\n  for (let i = 0; i < numElements; i++) {\n    const el = this[i];\n    // If selected element isn't a tag, move on\n    if (!isTag(el)) continue;\n\n    const elementClasses = splitNames(el.attribs['class']);\n\n    // Check if class already exists\n    for (let j = 0; j < numClasses; j++) {\n      // Check if the class name is currently defined\n      const index = elementClasses.indexOf(classNames[j]);\n\n      // Add if stateValue === true or we are toggling and there is no value\n      if (state >= 0 && index === -1) {\n        elementClasses.push(classNames[j]);\n      } else if (state <= 0 && index !== -1) {\n        // Otherwise remove but only if the item exists\n        elementClasses.splice(index, 1);\n      }\n    }\n\n    el.attribs['class'] = elementClasses.join(' ');\n  }\n\n  return this;\n}\n"
  },
  {
    "path": "src/api/css.spec.ts",
    "content": "import type { Element } from 'domhandler';\nimport { beforeEach, describe, expect, it } from 'vitest';\nimport { cheerio, mixedText } from '../__fixtures__/fixtures.js';\nimport { type Cheerio, load } from '../index.js';\n\ndescribe('$(...)', () => {\n  describe('.css', () => {\n    it('(prop): should return a css property value', () => {\n      const el = cheerio('<li style=\"hai: there\">');\n      expect(el.css('hai')).toBe('there');\n    });\n\n    it('([prop1, prop2]): should return the specified property values as an object', () => {\n      const el = cheerio(\n        '<li style=\"margin: 1px; padding: 2px; color: blue;\">',\n      );\n      expect(el.css(['margin', 'color'])).toStrictEqual({\n        margin: '1px',\n        color: 'blue',\n      });\n    });\n\n    it('(prop, val): should set a css property', () => {\n      const el = cheerio('<li style=\"margin: 0;\"></li><li></li>');\n      el.css('color', 'red');\n      expect(el.attr('style')).toBe('margin: 0; color: red;');\n      expect(el.eq(1).attr('style')).toBe('color: red;');\n    });\n\n    it('(prop, val) : should skip text nodes', () => {\n      const $text = load(mixedText);\n      const $body = $text($text('body')[0].children);\n\n      $body.css('test', 'value');\n\n      expect($text('body').html()).toBe(\n        '<a style=\"test: value;\">1</a>TEXT<b style=\"test: value;\">2</b>',\n      );\n    });\n\n    it('(prop, \"\"): should unset a css property', () => {\n      const el = cheerio('<li style=\"padding: 1px; margin: 0;\">');\n      el.css('padding', '');\n      expect(el.attr('style')).toBe('margin: 0;');\n    });\n\n    it('(any, val): should ignore unsupported prop types', () => {\n      const el = cheerio('<li style=\"padding: 1px;\">');\n      el.css(123 as never, 'test');\n      expect(el.attr('style')).toBe('padding: 1px;');\n    });\n\n    it('(prop): should not mangle embedded urls', () => {\n      const el = cheerio(\n        '<li style=\"background-image:url(http://example.com/img.png);\">',\n      );\n      expect(el.css('background-image')).toBe(\n        'url(http://example.com/img.png)',\n      );\n    });\n\n    it('(prop): should ignore blank properties', () => {\n      const el = cheerio('<li style=\":#ccc;color:#aaa;\">');\n      expect(el.css()).toStrictEqual({ color: '#aaa' });\n    });\n\n    it('(prop): should ignore blank values', () => {\n      const el = cheerio('<li style=\"color:;position:absolute;\">');\n      expect(el.css()).toStrictEqual({ position: 'absolute' });\n    });\n\n    it('(prop): should return undefined for unmatched elements', () => {\n      const $ = load('<li style=\"color:;position:absolute;\">');\n      expect($('ul').css('background-image')).toBeUndefined();\n    });\n\n    it('(prop): should return undefined for unmatched styles', () => {\n      const el = cheerio('<li style=\"color:;position:absolute;\">');\n      expect(el.css('margin')).toBeUndefined();\n    });\n\n    describe('(prop, function):', () => {\n      let $el: Cheerio<Element>;\n      beforeEach(() => {\n        const $ = load(\n          '<div style=\"margin: 0px;\"></div><div style=\"margin: 1px;\"></div><div style=\"margin: 2px;\">',\n        );\n        $el = $('div');\n      });\n\n      it('should iterate over the selection', () => {\n        let count = 0;\n        $el.css('margin', function (idx, value) {\n          expect(idx).toBe(count);\n          expect(value).toBe(`${count}px`);\n          expect(this).toBe($el[count]);\n          count++;\n          return;\n        });\n        expect(count).toBe(3);\n      });\n\n      it('should set each attribute independently', () => {\n        const values = ['4px', '', undefined];\n        $el.css('margin', (idx) => values[idx]);\n        expect($el.eq(0).attr('style')).toBe('margin: 4px;');\n        expect($el.eq(1).attr('style')).toBe('');\n        expect($el.eq(2).attr('style')).toBe('margin: 2px;');\n      });\n    });\n\n    it('(obj): should set each key and val', () => {\n      const el = cheerio('<li style=\"padding: 0;\"></li><li></li>');\n      el.css({ foo: 0 } as never);\n      expect(el.eq(0).attr('style')).toBe('padding: 0; foo: 0;');\n      expect(el.eq(1).attr('style')).toBe('foo: 0;');\n    });\n\n    describe('parser', () => {\n      it('should allow any whitespace between declarations', () => {\n        const el = cheerio('<li style=\"one \\t:\\n 0;\\n two \\f\\r:\\v 1\">');\n        expect(el.css(['one', 'two', 'five'])).toStrictEqual({\n          one: '0',\n          two: '1',\n        });\n      });\n\n      it('should add malformed values to previous field (#1134)', () => {\n        const el = cheerio(\n          '<button style=\"background-image: url(data:image/png;base64,iVBORw0KGgo)\"></button>',\n        );\n        expect(el.css('background-image')).toStrictEqual(\n          'url(data:image/png;base64,iVBORw0KGgo)',\n        );\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "src/api/css.ts",
    "content": "import { type AnyNode, type Element, isTag } from 'domhandler';\nimport type { Cheerio } from '../cheerio.js';\nimport { domEach } from '../utils.js';\n\n/**\n * Get the value of a style property for the first element in the set of matched\n * elements.\n *\n * @category CSS\n * @param names - Optionally the names of the properties of interest.\n * @returns A map of all of the style properties.\n * @see {@link https://api.jquery.com/css/}\n */\nexport function css<T extends AnyNode>(\n  this: Cheerio<T>,\n  names?: string[],\n): Record<string, string> | undefined;\n/**\n * Get the value of a style property for the first element in the set of matched\n * elements.\n *\n * @category CSS\n * @param name - The name of the property.\n * @returns The property value for the given name.\n * @see {@link https://api.jquery.com/css/}\n */\nexport function css<T extends AnyNode>(\n  this: Cheerio<T>,\n  name: string,\n): string | undefined;\n/**\n * Set one CSS property for every matched element.\n *\n * @category CSS\n * @param prop - The name of the property.\n * @param val - The new value.\n * @returns The instance itself.\n * @see {@link https://api.jquery.com/css/}\n */\nexport function css<T extends AnyNode>(\n  this: Cheerio<T>,\n  prop: string,\n  val: string | ((this: Element, i: number, style: string) => string | void),\n): Cheerio<T>;\n/**\n * Set multiple CSS properties for every matched element.\n *\n * @category CSS\n * @param map - A map of property names and values.\n * @returns The instance itself.\n * @see {@link https://api.jquery.com/css/}\n */\nexport function css<T extends AnyNode>(\n  this: Cheerio<T>,\n  map: Record<string, string>,\n): Cheerio<T>;\n/**\n * Set multiple CSS properties for every matched element.\n *\n * @category CSS\n * @param prop - The names of the properties.\n * @param val - The new values.\n * @returns The instance itself.\n * @see {@link https://api.jquery.com/css/}\n */\nexport function css<T extends AnyNode>(\n  this: Cheerio<T>,\n  prop?: string | string[] | Record<string, string>,\n  val?: string | ((this: Element, i: number, style: string) => string | void),\n): Cheerio<T> | Record<string, string> | string | undefined {\n  if (\n    (prop != null && val != null) ||\n    // When `prop` is a \"plain\" object\n    (typeof prop === 'object' && !Array.isArray(prop))\n  ) {\n    return domEach(this, (el, i) => {\n      if (isTag(el)) {\n        // `prop` can't be an array here anymore.\n        setCss(el, prop as string, val, i);\n      }\n    });\n  }\n\n  if (this.length === 0) {\n    return;\n  }\n\n  return getCss(this[0], prop as string);\n}\n\n/**\n * Set styles of all elements.\n *\n * @private\n * @param el - Element to set style of.\n * @param prop - Name of property.\n * @param value - Value to set property to.\n * @param idx - Optional index within the selection.\n */\nfunction setCss(\n  el: Element,\n  prop: string | Record<string, string>,\n  value:\n    | string\n    | ((this: Element, i: number, style: string) => string | void)\n    | undefined,\n  idx: number,\n) {\n  if (typeof prop === 'string') {\n    const styles = getCss(el);\n\n    const val =\n      typeof value === 'function' ? value.call(el, idx, styles[prop]) : value;\n\n    if (val === '') {\n      delete styles[prop];\n    } else if (val != null) {\n      styles[prop] = val;\n    }\n\n    el.attribs['style'] = stringify(styles);\n  } else if (typeof prop === 'object') {\n    const keys = Object.keys(prop);\n    for (let i = 0; i < keys.length; i++) {\n      const k = keys[i];\n      setCss(el, k, prop[k], i);\n    }\n  }\n}\n\n/**\n * Get the parsed styles of the first element.\n *\n * @private\n * @category CSS\n * @param el - Element to get styles from.\n * @param props - Optionally the names of the properties of interest.\n * @returns The parsed styles.\n */\nfunction getCss(el: AnyNode, props?: string[]): Record<string, string>;\n/**\n * Get a property from the parsed styles of the first element.\n *\n * @private\n * @category CSS\n * @param el - Element to get styles from.\n * @param prop - Name of the prop.\n * @returns The value of the property.\n */\nfunction getCss(el: AnyNode, prop: string): string | undefined;\nfunction getCss(\n  el: AnyNode,\n  prop?: string | string[],\n): Record<string, string> | string | undefined {\n  if (!(el && isTag(el))) return;\n\n  const styles = parse(el.attribs['style']);\n  if (typeof prop === 'string') {\n    return styles[prop];\n  }\n  if (Array.isArray(prop)) {\n    const newStyles: Record<string, string> = {};\n    for (const item of prop) {\n      if (styles[item] != null) {\n        newStyles[item] = styles[item];\n      }\n    }\n    return newStyles;\n  }\n  return styles;\n}\n\n/**\n * Stringify `obj` to styles.\n *\n * @private\n * @category CSS\n * @param obj - Object to stringify.\n * @returns The serialized styles.\n */\nfunction stringify(obj: Record<string, string>): string {\n  return Object.keys(obj).reduce(\n    (str, prop) => `${str}${str ? ' ' : ''}${prop}: ${obj[prop]};`,\n    '',\n  );\n}\n\n/**\n * Parse `styles`.\n *\n * @private\n * @category CSS\n * @param styles - Styles to be parsed.\n * @returns The parsed styles.\n */\nfunction parse(styles: string): Record<string, string> {\n  styles = (styles || '').trim();\n\n  if (!styles) return {};\n\n  const obj: Record<string, string> = {};\n\n  let key: string | undefined;\n\n  for (const str of styles.split(';')) {\n    const n = str.indexOf(':');\n    // If there is no :, or if it is the first/last character, add to the previous item's value\n    if (n < 1 || n === str.length - 1) {\n      const trimmed = str.trimEnd();\n      if (trimmed.length > 0 && key !== undefined) {\n        obj[key] += `;${trimmed}`;\n      }\n    } else {\n      key = str.slice(0, n).trim();\n      obj[key] = str.slice(n + 1).trim();\n    }\n  }\n\n  return obj;\n}\n"
  },
  {
    "path": "src/api/extract.spec.ts",
    "content": "import { describe, expect, expectTypeOf, it } from 'vitest';\nimport * as fixtures from '../__fixtures__/fixtures.js';\nimport { load } from '../load-parse.js';\n\ninterface RedSelObject {\n  red: string | undefined;\n  sel: string | undefined;\n}\n\ninterface RedSelMultipleObject {\n  red: string[];\n  sel: string[];\n}\n\ndescribe('$.extract', () => {\n  it('should return an empty object when no selectors are provided', () => {\n    const $ = load(fixtures.eleven);\n    const $root = $.root();\n\n    expectTypeOf($root.extract({})).toEqualTypeOf<Record<never, never>>();\n    const emptyExtract = $root.extract({});\n    expect(emptyExtract).toStrictEqual({});\n  });\n\n  it('should return undefined for selectors that do not match any elements', () => {\n    const $ = load(fixtures.eleven);\n    const $root = $.root();\n\n    expectTypeOf($root.extract({ foo: 'bar' })).toEqualTypeOf<{\n      foo: string | undefined;\n    }>();\n    const simpleExtract = $root.extract({ foo: 'bar' });\n    expect(simpleExtract).toStrictEqual({ foo: undefined });\n  });\n\n  it('should extract values for existing selectors', () => {\n    const $ = load(fixtures.eleven);\n    const $root = $.root();\n\n    expectTypeOf($root.extract({ red: '.red' })).toEqualTypeOf<{\n      red: string | undefined;\n    }>();\n    expect($root.extract({ red: '.red' })).toStrictEqual({ red: 'Four' });\n\n    expectTypeOf(\n      $root.extract({ red: '.red', sel: '.sel' }),\n    ).toEqualTypeOf<RedSelObject>();\n    expect($root.extract({ red: '.red', sel: '.sel' })).toStrictEqual({\n      red: 'Four',\n      sel: 'Three',\n    });\n  });\n\n  it('should extract values using descriptor objects', () => {\n    const $ = load(fixtures.eleven);\n    const $root = $.root();\n\n    expectTypeOf(\n      $root.extract({\n        red: { selector: '.red' },\n        sel: { selector: '.sel' },\n      }),\n    ).toEqualTypeOf<RedSelObject>();\n    expect(\n      $root.extract({\n        red: { selector: '.red' },\n        sel: { selector: '.sel' },\n      }),\n    ).toStrictEqual({ red: 'Four', sel: 'Three' });\n  });\n\n  it('should extract multiple values for selectors', () => {\n    const $ = load(fixtures.eleven);\n    const $root = $.root();\n\n    expectTypeOf(\n      $root.extract({\n        red: ['.red'],\n        sel: ['.sel'],\n      }),\n    ).toEqualTypeOf<{ red: string[]; sel: string[] }>();\n    const multipleExtract = $root.extract({\n      red: ['.red'],\n      sel: ['.sel'],\n    });\n    expectTypeOf(multipleExtract).toEqualTypeOf<RedSelMultipleObject>();\n    expect(multipleExtract).toStrictEqual({\n      red: ['Four', 'Five', 'Nine'],\n      sel: ['Three', 'Nine', 'Eleven'],\n    });\n  });\n\n  it('should extract custom properties specified by the user', () => {\n    const $ = load(fixtures.eleven);\n    const $root = $.root();\n\n    expectTypeOf(\n      $root.extract({\n        red: { selector: '.red', value: 'outerHTML' },\n        sel: { selector: '.sel', value: 'tagName' },\n      }),\n    ).toEqualTypeOf<RedSelObject>();\n    expect(\n      $root.extract({\n        red: { selector: '.red', value: 'outerHTML' },\n        sel: { selector: '.sel', value: 'tagName' },\n      }),\n    ).toStrictEqual({ red: '<li class=\"red\">Four</li>', sel: 'LI' });\n  });\n\n  it('should extract multiple custom properties for selectors', () => {\n    const $ = load(fixtures.eleven);\n    const $root = $.root();\n\n    expectTypeOf(\n      $root.extract({\n        red: [{ selector: '.red', value: 'outerHTML' }],\n      }),\n    ).toEqualTypeOf<{ red: string[] }>();\n    expect(\n      $root.extract({\n        red: [{ selector: '.red', value: 'outerHTML' }],\n      }),\n    ).toStrictEqual({\n      red: [\n        '<li class=\"red\">Four</li>',\n        '<li class=\"red\">Five</li>',\n        '<li class=\"red sel\">Nine</li>',\n      ],\n    });\n  });\n\n  it('should extract values using custom extraction functions', () => {\n    const $ = load(fixtures.eleven);\n    const $root = $.root();\n\n    expectTypeOf(\n      $root.extract({\n        red: {\n          selector: '.red',\n          value: (el, key) => `${key}=${$(el).text()}`,\n        },\n      }),\n    ).toEqualTypeOf<{ red: string | undefined }>();\n    expect(\n      $root.extract({\n        red: {\n          selector: '.red',\n          value: (el, key) => `${key}=${$(el).text()}`,\n        },\n      }),\n    ).toStrictEqual({ red: 'red=Four' });\n  });\n\n  it('should correctly type check custom extraction functions returning non-string values', () => {\n    const $ = load(fixtures.eleven);\n    const $root = $.root();\n\n    expectTypeOf(\n      $root.extract({\n        red: {\n          selector: '.red',\n          value: (el) => $(el).text().length,\n        },\n      }),\n    ).toEqualTypeOf<{ red: number | undefined }>();\n    expect(\n      $root.extract({\n        red: {\n          selector: '.red',\n          value: (el) => $(el).text().length,\n        },\n      }),\n    ).toStrictEqual({ red: 4 });\n  });\n\n  it('should extract multiple values using custom extraction functions', () => {\n    const $ = load(fixtures.eleven);\n    const $root = $.root();\n\n    expectTypeOf(\n      $root.extract({\n        red: [\n          {\n            selector: '.red',\n            value: (el, key) => `${key}=${$(el).text()}`,\n          },\n        ],\n      }),\n    ).toEqualTypeOf<{ red: string[] }>();\n    expect(\n      $root.extract({\n        red: [\n          {\n            selector: '.red',\n            value: (el, key) => `${key}=${$(el).text()}`,\n          },\n        ],\n      }),\n    ).toStrictEqual({ red: ['red=Four', 'red=Five', 'red=Nine'] });\n  });\n\n  it('should extract nested objects based on selectors', () => {\n    const $ = load(fixtures.eleven);\n    const $root = $.root();\n\n    expectTypeOf(\n      $root.extract({\n        section: {\n          selector: 'ul:nth(1)',\n          value: {\n            red: '.red',\n            sel: '.blue',\n          },\n        },\n      }),\n    ).toEqualTypeOf<{\n      section: { red: string | undefined; sel: string | undefined } | undefined;\n    }>();\n    const subExtractObject = $root.extract({\n      section: {\n        selector: 'ul:nth(1)',\n        value: {\n          red: '.red',\n          sel: '.blue',\n        },\n      },\n    });\n    expectTypeOf(subExtractObject).toEqualTypeOf<{\n      section: RedSelObject | undefined;\n    }>();\n    expect(subExtractObject).toStrictEqual({\n      section: {\n        red: 'Five',\n        sel: 'Seven',\n      },\n    });\n  });\n\n  it('should correctly type check nested objects returning non-string values', () => {\n    const $ = load(fixtures.eleven);\n    const $root = $.root();\n\n    expectTypeOf(\n      $root.extract({\n        section: {\n          selector: 'ul:nth(1)',\n          value: {\n            red: {\n              selector: '.red',\n              value: (el) => $(el).text().length,\n            },\n          },\n        },\n      }),\n    ).toEqualTypeOf<{\n      section: { red: number | undefined } | undefined;\n    }>();\n    expect(\n      $root.extract({\n        section: {\n          selector: 'ul:nth(1)',\n          value: {\n            red: {\n              selector: '.red',\n              value: (el) => $(el).text().length,\n            },\n          },\n        },\n      }),\n    ).toStrictEqual({\n      section: {\n        red: 4,\n      },\n    });\n  });\n\n  it('should handle missing href properties without errors (#4239)', () => {\n    const $ = load(fixtures.eleven);\n    expect<{ links: string[] }>(\n      $.extract({ links: [{ selector: 'li', value: 'href' }] }),\n    ).toStrictEqual({ links: [] });\n  });\n});\n"
  },
  {
    "path": "src/api/extract.ts",
    "content": "import type { AnyNode, Element } from 'domhandler';\nimport type { Cheerio } from '../cheerio.js';\nimport type { prop } from './attributes.js';\n\ntype ExtractDescriptorFn = (\n  el: Element,\n  key: string,\n  // TODO: This could be typed with ExtractedMap\n  obj: Record<string, unknown>,\n) => unknown;\n\ninterface ExtractDescriptor {\n  selector: string;\n  value?: string | ExtractDescriptorFn | ExtractMap;\n}\n\ntype ExtractValue = string | ExtractDescriptor | [string | ExtractDescriptor];\n\n/** Descriptor map used by {@link extract}. */\nexport type ExtractMap = Record<string, ExtractValue>;\n\ntype ExtractedValue<V extends ExtractValue> = V extends [\n  string | ExtractDescriptor,\n]\n  ? NonNullable<ExtractedValue<V[0]>>[]\n  : V extends string\n    ? string | undefined\n    : V extends ExtractDescriptor\n      ? V['value'] extends infer U\n        ? U extends ExtractMap\n          ? ExtractedMap<U> | undefined\n          : U extends ExtractDescriptorFn\n            ? ReturnType<U> | undefined\n            : ReturnType<typeof prop> | undefined\n        : never\n      : never;\n\n/** Result type computed from an {@link ExtractMap}. */\nexport type ExtractedMap<M extends ExtractMap> = {\n  [key in keyof M]: ExtractedValue<M[key]>;\n};\n\nfunction getExtractDescr(\n  descr: string | ExtractDescriptor,\n): Required<ExtractDescriptor> {\n  if (typeof descr === 'string') {\n    return { selector: descr, value: 'textContent' };\n  }\n\n  return {\n    selector: descr.selector,\n    value: descr.value ?? 'textContent',\n  };\n}\n\n/**\n * Extract multiple values from a document, and store them in an object.\n *\n * @param map - An object containing key-value pairs. The keys are the names of\n *   the properties to be created on the object, and the values are the\n *   selectors to be used to extract the values.\n * @returns An object containing the extracted values.\n */\nexport function extract<M extends ExtractMap, T extends AnyNode>(\n  this: Cheerio<T>,\n  map: M,\n): ExtractedMap<M> {\n  const ret: Record<string, unknown> = {};\n\n  for (const key in map) {\n    const descr = map[key];\n    const isArray = Array.isArray(descr);\n\n    const { selector, value } = getExtractDescr(isArray ? descr[0] : descr);\n\n    const fn: ExtractDescriptorFn =\n      typeof value === 'function'\n        ? value\n        : typeof value === 'string'\n          ? (el: Element) => this._make(el).prop(value)\n          : (el: Element) => this._make(el).extract(value);\n\n    if (isArray) {\n      ret[key] = this._findBySelector(selector, Number.POSITIVE_INFINITY)\n        .map((_, el) => fn(el, key, ret))\n        .get();\n    } else {\n      const $ = this._findBySelector(selector, 1);\n      ret[key] = $.length > 0 ? fn($[0], key, ret) : undefined;\n    }\n  }\n\n  return ret as ExtractedMap<M>;\n}\n"
  },
  {
    "path": "src/api/forms.spec.ts",
    "content": "import { beforeEach, describe, expect, it } from 'vitest';\nimport { cheerio, forms } from '../__fixtures__/fixtures.js';\nimport type { CheerioAPI } from '../index.js';\n\ndescribe('$(...)', () => {\n  let $: CheerioAPI;\n\n  beforeEach(() => {\n    $ = cheerio.load(forms);\n  });\n\n  describe('.serializeArray', () => {\n    it('() : should get form controls', () => {\n      expect($('form#simple').serializeArray()).toStrictEqual([\n        {\n          name: 'fruit',\n          value: 'Apple',\n        },\n      ]);\n    });\n\n    it('() : should get nested form controls', () => {\n      expect($('form#nested').serializeArray()).toHaveLength(2);\n      const data = $('form#nested').serializeArray();\n      data.sort((a, b) => (a.value > b.value ? 1 : -1));\n      expect(data).toStrictEqual([\n        {\n          name: 'fruit',\n          value: 'Apple',\n        },\n        {\n          name: 'vegetable',\n          value: 'Carrot',\n        },\n      ]);\n    });\n\n    it('() : should not get disabled form controls', () => {\n      expect($('form#disabled').serializeArray()).toStrictEqual([]);\n    });\n\n    it('() : should not get form controls with the wrong type', () => {\n      expect($('form#submit').serializeArray()).toStrictEqual([\n        {\n          name: 'fruit',\n          value: 'Apple',\n        },\n      ]);\n    });\n\n    it('() : should get selected options', () => {\n      expect($('form#select').serializeArray()).toStrictEqual([\n        {\n          name: 'fruit',\n          value: 'Orange',\n        },\n      ]);\n    });\n\n    it('() : should not get unnamed form controls', () => {\n      expect($('form#unnamed').serializeArray()).toStrictEqual([\n        {\n          name: 'fruit',\n          value: 'Apple',\n        },\n      ]);\n    });\n\n    it('() : should get multiple selected options', () => {\n      expect($('form#multiple').serializeArray()).toHaveLength(2);\n      const data = $('form#multiple').serializeArray();\n      data.sort((a, b) => (a.value > b.value ? 1 : -1));\n      expect(data).toStrictEqual([\n        {\n          name: 'fruit',\n          value: 'Apple',\n        },\n        {\n          name: 'fruit',\n          value: 'Orange',\n        },\n      ]);\n    });\n\n    it('() : should get individually selected elements', () => {\n      const data = $('form#nested input').serializeArray();\n      data.sort((a, b) => (a.value > b.value ? 1 : -1));\n      expect(data).toStrictEqual([\n        {\n          name: 'fruit',\n          value: 'Apple',\n        },\n        {\n          name: 'vegetable',\n          value: 'Carrot',\n        },\n      ]);\n    });\n\n    it('() : should standardize line breaks', () => {\n      expect($('form#textarea').serializeArray()).toStrictEqual([\n        {\n          name: 'fruits',\n          value: 'Apple\\r\\nOrange',\n        },\n      ]);\n    });\n\n    it(\"() : shouldn't serialize the empty string\", () => {\n      expect($('<input value=pineapple>').serializeArray()).toStrictEqual([]);\n      expect(\n        $('<input name=\"\" value=pineapple>').serializeArray(),\n      ).toStrictEqual([]);\n      expect(\n        $('<input name=\"fruit\" value=pineapple>').serializeArray(),\n      ).toStrictEqual([\n        {\n          name: 'fruit',\n          value: 'pineapple',\n        },\n      ]);\n    });\n\n    it('() : should serialize inputs without value attributes', () => {\n      expect($('<input name=\"fruit\">').serializeArray()).toStrictEqual([\n        {\n          name: 'fruit',\n          value: '',\n        },\n      ]);\n    });\n  });\n\n  describe('.serialize', () => {\n    it('() : should get form controls', () => {\n      expect($('form#simple').serialize()).toBe('fruit=Apple');\n    });\n\n    it('() : should get nested form controls', () => {\n      expect($('form#nested').serialize()).toBe('fruit=Apple&vegetable=Carrot');\n    });\n\n    it('() : should not get disabled form controls', () => {\n      expect($('form#disabled').serialize()).toBe('');\n    });\n\n    it('() : should get multiple selected options', () => {\n      expect($('form#multiple').serialize()).toBe('fruit=Apple&fruit=Orange');\n    });\n\n    it(\"() : should encode spaces as +'s\", () => {\n      expect($('form#spaces').serialize()).toBe('fruit=Blood+orange');\n    });\n  });\n});\n"
  },
  {
    "path": "src/api/forms.ts",
    "content": "import { type AnyNode, isTag } from 'domhandler';\nimport type { Cheerio } from '../cheerio.js';\n\n/*\n * https://github.com/jquery/jquery/blob/2.1.3/src/manipulation/var/rcheckableType.js\n * https://github.com/jquery/jquery/blob/2.1.3/src/serialize.js\n */\nconst submittableSelector = 'input,select,textarea,keygen';\nconst r20 = /%20/g;\nconst rCRLF = /\\r?\\n/g;\n\n/**\n * Encode a set of form elements as a string for submission.\n *\n * @category Forms\n * @example\n *\n * ```js\n * $('<form><input name=\"foo\" value=\"bar\" /></form>').serialize();\n * //=> 'foo=bar'\n * ```\n *\n * @returns The serialized form.\n * @see {@link https://api.jquery.com/serialize/}\n */\nexport function serialize<T extends AnyNode>(this: Cheerio<T>): string {\n  // Convert form elements into name/value objects\n  const arr = this.serializeArray();\n\n  // Serialize each element into a key/value string\n  const retArr = arr.map(\n    (data) =>\n      `${encodeURIComponent(data.name)}=${encodeURIComponent(data.value)}`,\n  );\n\n  // Return the resulting serialization\n  return retArr.join('&').replace(r20, '+');\n}\n\n/**\n * Encode a set of form elements as an array of names and values.\n *\n * @category Forms\n * @example\n *\n * ```js\n * $('<form><input name=\"foo\" value=\"bar\" /></form>').serializeArray();\n * //=> [ { name: 'foo', value: 'bar' } ]\n * ```\n *\n * @returns The serialized form.\n * @see {@link https://api.jquery.com/serializeArray/}\n */\nexport function serializeArray<T extends AnyNode>(\n  this: Cheerio<T>,\n): {\n  name: string;\n  value: string;\n}[] {\n  // Resolve all form elements from either forms or collections of form elements\n  return this.map((_, elem) => {\n    const $elem = this._make(elem);\n    if (isTag(elem) && elem.name === 'form') {\n      return $elem.find(submittableSelector).toArray();\n    }\n    return $elem.filter(submittableSelector).toArray();\n  })\n    .filter(\n      // Verify elements have a name (`attr.name`) and are not disabled (`:enabled`)\n      '[name!=\"\"]:enabled' +\n        // And cannot be clicked (`[type=submit]`) or are used in `x-www-form-urlencoded` (`[type=file]`)\n        ':not(:submit, :button, :image, :reset, :file)' +\n        // And are either checked/don't have a checkable state\n        ':matches([checked], :not(:checkbox, :radio))',\n      // Convert each of the elements to its value(s)\n    )\n    .map<\n      AnyNode,\n      {\n        name: string;\n        value: string;\n      }\n    >((_, elem) => {\n      const $elem = this._make(elem);\n      const name = $elem.attr('name');\n      if (!name) return [];\n      // If there is no value set (e.g. `undefined`, `null`), then default value to empty\n      const value = $elem.val() ?? '';\n\n      // If we have an array of values (e.g. `<select multiple>`), return an array of key/value pairs\n      if (Array.isArray(value)) {\n        return value.map((val) =>\n          /*\n           * We trim replace any line endings (e.g. `\\r` or `\\r\\n` with `\\r\\n`) to guarantee consistency across platforms\n           * These can occur inside of `<textarea>'s`\n           */\n          ({ name, value: val.replace(rCRLF, '\\r\\n') }),\n        );\n      }\n      // Otherwise (e.g. `<input type=\"text\">`, return only one key/value pair\n      return { name, value: value.replace(rCRLF, '\\r\\n') };\n    })\n    .toArray();\n}\n"
  },
  {
    "path": "src/api/manipulation.spec.ts",
    "content": "import type { AnyNode, Element } from 'domhandler';\nimport { beforeEach, describe, expect, it } from 'vitest';\nimport {\n  divcontainers,\n  fruits,\n  mixedText,\n  unwrapspans,\n} from '../__fixtures__/fixtures.js';\nimport { type Cheerio, type CheerioAPI, load } from '../index.js';\n\ndescribe('$(...)', () => {\n  let $: CheerioAPI;\n  let $fruits: Cheerio<Element>;\n\n  beforeEach(() => {\n    $ = load(fruits);\n    $fruits = $('#fruits');\n  });\n\n  describe('.wrap', () => {\n    it('(Cheerio object) : should insert the element and add selected element(s) as its child', () => {\n      const $redFruits = $('<div class=\"red-fruits\"></div>');\n      $('.apple').wrap($redFruits);\n\n      expect($fruits.children().eq(0).hasClass('red-fruits')).toBe(true);\n      expect($('.red-fruits').children().eq(0).hasClass('apple')).toBe(true);\n      expect($fruits.children().eq(1).hasClass('orange')).toBe(true);\n      expect($redFruits.children()).toHaveLength(1);\n    });\n\n    it('(element) : should wrap the base element correctly', () => {\n      $('ul').wrap('<a></a>');\n      expect($('body').children()[0].tagName).toBe('a');\n    });\n\n    it('(document) : should ignore document node', () => {\n      $.root().wrap('<a></a>');\n      expect($.root()[0]).toHaveProperty('type', 'root');\n    });\n\n    it('(element) : should insert the element and add selected element(s) as its child', () => {\n      const $redFruits = $('<div class=\"red-fruits\"></div>');\n      $('.apple').wrap($redFruits[0]);\n\n      expect($fruits.children()[0]).toBe($redFruits[0]);\n      expect($redFruits.children()).toHaveLength(1);\n      expect($redFruits.children()[0]).toBe($('.apple')[0]);\n      expect($fruits.children()[1]).toBe($('.orange')[0]);\n    });\n\n    it('(html) : should insert the markup and add selected element(s) as its child', () => {\n      $('.apple').wrap('<div class=\"red-fruits\"> </div>');\n      expect($fruits.children().eq(0).hasClass('red-fruits')).toBe(true);\n      expect($('.red-fruits').children().eq(0).hasClass('apple')).toBe(true);\n      expect($fruits.children().eq(1).hasClass('orange')).toBe(true);\n      expect($('.red-fruits').children()).toHaveLength(1);\n    });\n\n    it('(html) : discards extraneous markup', () => {\n      $('.apple').wrap('<div></div><p></p>');\n      expect($('div')).toHaveLength(1);\n      expect($('p')).toHaveLength(0);\n    });\n\n    it('(html) : wraps with nested elements', () => {\n      const $orangeFruits = $(\n        '<div class=\"orange-fruits\"><div class=\"and-stuff\"></div></div>',\n      );\n      $('.orange').wrap($orangeFruits);\n\n      expect($fruits.children().eq(1).hasClass('orange-fruits')).toBe(true);\n      expect($('.orange-fruits').children().eq(0).hasClass('and-stuff')).toBe(\n        true,\n      );\n      expect($fruits.children().eq(2).hasClass('pear')).toBe(true);\n      expect($('.orange-fruits').children()).toHaveLength(1);\n    });\n\n    it('(html) : should only worry about the first tag children', () => {\n      const delicious = '<span> This guy is delicious: <b></b></span>';\n      $('.apple').wrap(delicious);\n      expect($('b>.apple')).toHaveLength(1);\n    });\n\n    it('(selector) : wraps the content with a copy of the first matched element', () => {\n      $('.apple').wrap('.orange, .pear');\n\n      const $oranges = $('.orange');\n      expect($('.pear')).toHaveLength(1);\n      expect($oranges).toHaveLength(2);\n      expect($oranges.eq(0).parent()[0]).toBe($fruits[0]);\n      expect($oranges.eq(0).children()).toHaveLength(1);\n      expect($oranges.eq(0).children()[0]).toBe($('.apple')[0]);\n      expect($('.apple').parent()[0]).toBe($oranges[0]);\n      expect($oranges.eq(1).children()).toHaveLength(0);\n    });\n\n    it('(fn) : should invoke the provided function with the correct arguments and context', () => {\n      const $children = $fruits.children();\n      const args: [number, AnyNode][] = [];\n      const thisValues: AnyNode[] = [];\n\n      $children.wrap(function (...myArgs) {\n        args.push(myArgs);\n        thisValues.push(this);\n        return '';\n      });\n\n      expect(args).toStrictEqual([\n        [0, $children[0]],\n        [1, $children[1]],\n        [2, $children[2]],\n      ]);\n      expect(thisValues).toStrictEqual([\n        $children[0],\n        $children[1],\n        $children[2],\n      ]);\n    });\n\n    it('(fn) : should use the returned HTML to wrap each element', () => {\n      const $children = $fruits.children();\n      const tagNames = ['div', 'span', 'p'];\n\n      $children.wrap(() => `<${tagNames.shift()}>`);\n\n      expect($fruits.find('div')).toHaveLength(1);\n      expect($fruits.find('div')[0]).toBe($fruits.children()[0]);\n      expect($fruits.find('.apple')).toHaveLength(1);\n      expect($fruits.find('.apple').parent()[0]).toBe($fruits.find('div')[0]);\n\n      expect($fruits.find('span')).toHaveLength(1);\n      expect($fruits.find('span')[0]).toBe($fruits.children()[1]);\n      expect($fruits.find('.orange')).toHaveLength(1);\n      expect($fruits.find('.orange').parent()[0]).toBe($fruits.find('span')[0]);\n\n      expect($fruits.find('p')).toHaveLength(1);\n      expect($fruits.find('p')[0]).toBe($fruits.children()[2]);\n      expect($fruits.find('.pear')).toHaveLength(1);\n      expect($fruits.find('.pear').parent()[0]).toBe($fruits.find('p')[0]);\n    });\n\n    it('(fn) : should use the returned Cheerio object to wrap each element', () => {\n      const $children = $fruits.children();\n      const tagNames = ['span', 'p', 'div'];\n\n      $children.wrap(() => $(`<${tagNames.shift()}>`));\n\n      expect($fruits.find('span')).toHaveLength(1);\n      expect($fruits.find('span')[0]).toBe($fruits.children()[0]);\n      expect($fruits.find('.apple')).toHaveLength(1);\n      expect($fruits.find('.apple').parent()[0]).toBe($fruits.find('span')[0]);\n\n      expect($fruits.find('p')).toHaveLength(1);\n      expect($fruits.find('p')[0]).toBe($fruits.children()[1]);\n      expect($fruits.find('.orange')).toHaveLength(1);\n      expect($fruits.find('.orange').parent()[0]).toBe($fruits.find('p')[0]);\n\n      expect($fruits.find('div')).toHaveLength(1);\n      expect($fruits.find('div')[0]).toBe($fruits.children()[2]);\n      expect($fruits.find('.pear')).toHaveLength(1);\n      expect($fruits.find('.pear').parent()[0]).toBe($fruits.find('div')[0]);\n    });\n\n    it('($(...)) : for each element it should add a wrapper element and add the selected element as its child', () => {\n      const $fruitDecorator = $('<div class=\"fruit-decorator\"></div>');\n      $('li').wrap($fruitDecorator);\n      expect($fruits.children().eq(0).hasClass('fruit-decorator')).toBe(true);\n      expect($fruits.children().eq(0).children().eq(0).hasClass('apple')).toBe(\n        true,\n      );\n      expect($fruits.children().eq(1).hasClass('fruit-decorator')).toBe(true);\n      expect($fruits.children().eq(1).children().eq(0).hasClass('orange')).toBe(\n        true,\n      );\n      expect($fruits.children().eq(2).hasClass('fruit-decorator')).toBe(true);\n      expect($fruits.children().eq(2).children().eq(0).hasClass('pear')).toBe(\n        true,\n      );\n    });\n  });\n\n  describe('.wrapInner', () => {\n    it('(Cheerio object) : should insert the element and add selected element(s) as its parent', () => {\n      const $container = $('<div class=\"container\"></div>') as Cheerio<Element>;\n      $fruits.wrapInner($container);\n\n      expect($fruits.children()[0]).toBe($container[0]);\n      expect($container[0].parent).toBe($fruits[0]);\n      expect($container[0].children[0]).toBe($('.apple')[0]);\n      expect($container[0].children[1]).toBe($('.orange')[0]);\n      expect($('.apple')[0].parent).toBe($container[0]);\n      expect($fruits.children()).toHaveLength(1);\n      expect($container.children()).toHaveLength(3);\n    });\n\n    it('(element) : should insert the element and add selected element(s) as its parent', () => {\n      const $container = $('<div class=\"container\"></div>') as Cheerio<Element>;\n      $fruits.wrapInner($container[0]);\n\n      expect($fruits.children()[0]).toBe($container[0]);\n      expect($container[0].parent).toBe($fruits[0]);\n      expect($container[0].children[0]).toBe($('.apple')[0]);\n      expect($container[0].children[1]).toBe($('.orange')[0]);\n      expect($('.apple')[0].parent).toBe($container[0]);\n      expect($fruits.children()).toHaveLength(1);\n      expect($container.children()).toHaveLength(3);\n    });\n\n    it('(html) : should ignore text nodes', () => {\n      const $test = load(mixedText);\n      $test($test('body')[0].children).wrapInner('<test>');\n\n      expect($test('body').html()).toBe(\n        '<a><test>1</test></a>TEXT<b><test>2</test></b>',\n      );\n    });\n\n    it('(html) : should insert the element and add selected element(s) as its parent', () => {\n      $fruits.wrapInner('<div class=\"container\"></div>');\n\n      expect($fruits.children()[0]).toBe($('.container')[0]);\n      expect($('.container')[0].parent).toBe($fruits[0]);\n      expect($('.container')[0].children[0]).toBe($('.apple')[0]);\n      expect($('.container')[0].children[1]).toBe($('.orange')[0]);\n      expect($('.apple')[0].parent).toBe($('.container')[0]);\n      expect($fruits.children()).toHaveLength(1);\n      expect($('.container').children()).toHaveLength(3);\n    });\n\n    it(\"(selector) : should wrap the html of the element with the selector's first match\", () => {\n      $('.apple').wrapInner('.orange, .pear');\n      const $oranges = $('.orange');\n      expect($('.pear')).toHaveLength(1);\n      expect($oranges).toHaveLength(2);\n      expect($oranges.eq(0).parent()[0]).toBe($('.apple')[0]);\n      expect($oranges.eq(0).text()).toBe('Apple');\n      expect($('.apple').eq(0).children()[0]).toBe($oranges[0]);\n      expect($oranges.eq(1).parent()[0]).toBe($fruits[0]);\n      expect($oranges.eq(1).text()).toBe('Orange');\n    });\n\n    it('(fn) : should invoke the provided function with the correct arguments and context', () => {\n      const $children = $fruits.children();\n      const args: [number, AnyNode][] = [];\n      const thisValues: AnyNode[] = [];\n\n      $children.wrapInner(function (...myArgs) {\n        args.push(myArgs);\n        thisValues.push(this);\n        return this;\n      });\n\n      expect(args).toStrictEqual([\n        [0, $children[0]],\n        [1, $children[1]],\n        [2, $children[2]],\n      ]);\n      expect(thisValues).toStrictEqual([\n        $children[0],\n        $children[1],\n        $children[2],\n      ]);\n    });\n\n    it(\"(fn) : should use the returned HTML to wrap each element's contents\", () => {\n      const $children = $fruits.children();\n      const tagNames = ['div', 'span', 'p'];\n\n      $children.wrapInner(() => `<${tagNames.shift()}>`);\n\n      expect($fruits.find('div')).toHaveLength(1);\n      expect($fruits.find('div')[0]).toBe($('.apple').children()[0]);\n      expect($fruits.find('.apple')).toHaveLength(1);\n\n      expect($fruits.find('span')).toHaveLength(1);\n      expect($fruits.find('span')[0]).toBe($('.orange').children()[0]);\n      expect($fruits.find('.orange')).toHaveLength(1);\n\n      expect($fruits.find('p')).toHaveLength(1);\n      expect($fruits.find('p')[0]).toBe($('.pear').children()[0]);\n      expect($fruits.find('.pear')).toHaveLength(1);\n    });\n\n    it(\"(fn) : should use the returned Cheerio object to wrap each element's contents\", () => {\n      const $children = $fruits.children();\n      const tags = [$('<div></div>'), $('<span></span>'), $('<p></p>')];\n\n      $children.wrapInner(() => tags.shift()!);\n\n      expect($fruits.find('div')).toHaveLength(1);\n      expect($fruits.find('div')[0]).toBe($('.apple').children()[0]);\n      expect($fruits.find('.apple')).toHaveLength(1);\n\n      expect($fruits.find('span')).toHaveLength(1);\n      expect($fruits.find('span')[0]).toBe($('.orange').children()[0]);\n      expect($fruits.find('.orange')).toHaveLength(1);\n\n      expect($fruits.find('p')).toHaveLength(1);\n      expect($fruits.find('p')[0]).toBe($('.pear').children()[0]);\n      expect($fruits.find('.pear')).toHaveLength(1);\n    });\n\n    it('($(...)) : for each element it should add a wrapper element and add the selected element as its child', () => {\n      const $fruitDecorator = $('<div class=\"fruit-decorator\"></div>');\n      const $children = $fruits.children();\n      $('li').wrapInner($fruitDecorator);\n\n      expect($('.fruit-decorator')).toHaveLength(3);\n      expect($children.eq(0).children().eq(0).hasClass('fruit-decorator')).toBe(\n        true,\n      );\n      expect($children.eq(0).hasClass('apple')).toBe(true);\n      expect($children.eq(1).children().eq(0).hasClass('fruit-decorator')).toBe(\n        true,\n      );\n      expect($children.eq(1).hasClass('orange')).toBe(true);\n      expect($children.eq(2).children().eq(0).hasClass('fruit-decorator')).toBe(\n        true,\n      );\n      expect($children.eq(2).hasClass('pear')).toBe(true);\n    });\n\n    it('(html) : wraps with nested elements', () => {\n      const $badOrangeJoke = $(\n        '<div class=\"orange-you-glad\"><div class=\"i-didnt-say-apple\"></div></div>',\n      );\n      $('.orange').wrapInner($badOrangeJoke);\n\n      expect($('.orange').children().eq(0).hasClass('orange-you-glad')).toBe(\n        true,\n      );\n      expect(\n        $('.orange-you-glad').children().eq(0).hasClass('i-didnt-say-apple'),\n      ).toBe(true);\n      expect($fruits.children().eq(2).hasClass('pear')).toBe(true);\n      expect($('.orange-you-glad').children()).toHaveLength(1);\n    });\n\n    it('(html) : should only worry about the first tag children', () => {\n      const delicious = '<span> This guy is delicious: <b></b></span>';\n      $('.apple').wrapInner(delicious);\n      expect($('.apple>span>b')).toHaveLength(1);\n      expect($('.apple>span>b').text()).toBe('Apple');\n    });\n  });\n\n  describe('.unwrap', () => {\n    let $elem: CheerioAPI;\n\n    beforeEach(() => {\n      $elem = load(unwrapspans);\n    });\n\n    it('() : should be unwrap span elements', () => {\n      const abcd = $elem('#unwrap1 > span, #unwrap2 > span').get();\n      const abcdef = $elem('#unwrap span').get();\n\n      // Make #unwrap1 and #unwrap2 go away\n      expect(\n        $elem('#unwrap1 span').add('#unwrap2 span:first-child').unwrap(),\n      ).toHaveLength(3);\n\n      /*\n       * .toEqual\n       *  all four spans should still exist\n       */\n      expect($elem('#unwrap > span').get()).toEqual(abcd);\n\n      // Make all b elements in #unwrap3 go away\n      expect($elem('#unwrap3 span').unwrap().get()).toEqual(\n        $elem('#unwrap3 > span').get(),\n      );\n\n      // Make #unwrap3 go away\n      expect($elem('#unwrap3 span').unwrap().get()).toEqual(\n        $elem('#unwrap > span.unwrap3').get(),\n      );\n\n      // #unwrap only contains 6 child spans\n      expect($elem('#unwrap').children().get()).toEqual(abcdef);\n\n      // Make the 6 spans become children of body\n      expect($elem('#unwrap > span').unwrap().get()).toEqual(\n        $elem('body > span.unwrap').get(),\n      );\n\n      // Can't unwrap children of body\n      expect($elem('body > span.unwrap').unwrap().get()).toEqual(\n        $elem('body > span.unwrap').get(),\n      );\n\n      // Can't unwrap children of body\n      expect($elem('body > span.unwrap').unwrap().get()).toEqual(abcdef);\n\n      // Can't unwrap children of body\n      expect($elem('body > span.unwrap').get()).toEqual(abcdef);\n    });\n\n    it('(selector) : should only unwrap element parent what specified', () => {\n      const abcd = $elem('#unwrap1 > span, #unwrap2 > span').get();\n\n      // Shouldn't unwrap, no match\n      $elem('#unwrap1 span').unwrap('#unwrap2');\n      expect($elem('#unwrap1')).toHaveLength(1);\n\n      // Shouldn't unwrap, no match\n      $elem('#unwrap1 span').unwrap('span');\n      expect($elem('#unwrap1')).toHaveLength(1);\n\n      // Unwraps\n      $elem('#unwrap1 span').unwrap('#unwrap1');\n      expect($elem('#unwrap1')).toHaveLength(0);\n\n      // Should not unwrap - unmatched unwrap\n      $elem('#unwrap2 span').unwrap('quote');\n      expect($elem('#unwrap > span')).toHaveLength(2);\n\n      // Check return values - matched unwrap\n      $elem('#unwrap2 span').unwrap('#unwrap2');\n      expect($elem('#unwrap > span').get()).toEqual(abcd);\n    });\n  });\n\n  describe('.wrapAll', () => {\n    let doc: CheerioAPI;\n    let $inner: Cheerio<Element>;\n\n    beforeEach(() => {\n      doc = load(divcontainers);\n      $inner = doc('.inner');\n    });\n\n    it('(Cheerio object) : should insert the element and wrap elements with it', () => {\n      $inner.wrapAll(doc('#new'));\n      const $container = doc('.container');\n      const $wrap = doc('b');\n\n      expect($container).toHaveLength(2);\n      expect($container[0].children).toHaveLength(1);\n      expect($container[1].children).toHaveLength(0);\n      expect($container[0].children[0]).toBe(doc('#new')[0]);\n\n      expect($inner).toHaveLength(4);\n      expect($wrap[0].children).toHaveLength(4);\n      expect($inner[0].parent).toBe($wrap[0]);\n      expect($inner[1].parent).toBe($wrap[0]);\n      expect($inner[2].parent).toBe($wrap[0]);\n      expect($inner[3].parent).toBe($wrap[0]);\n    });\n\n    it('(html) : should wrap elements with it', () => {\n      $inner.wrapAll('<div class=\"wrap\"></div>');\n      const $container = doc('.container');\n      const $wrap = doc('.wrap');\n\n      expect($inner).toHaveLength(4);\n      expect($container).toHaveLength(2);\n      expect($wrap).toHaveLength(1);\n      expect($wrap[0].children).toHaveLength(4);\n      expect($container[0].children).toHaveLength(1);\n      expect($container[1].children).toHaveLength(0);\n      expect($inner[0].parent).toBe($wrap[0]);\n      expect($inner[1].parent).toBe($wrap[0]);\n      expect($inner[2].parent).toBe($wrap[0]);\n      expect($inner[3].parent).toBe($wrap[0]);\n      expect($wrap[0].parent).toBe($container[0]);\n      expect($container[0].children[0]).toBe($wrap[0]);\n    });\n\n    it('(html) : should wrap single element with it', () => {\n      const parent = doc('<p>').wrapAll('<div></div>').parent();\n      expect(parent).toHaveLength(1);\n      expect(parent.is('div')).toBe(true);\n    });\n\n    it('(selector) : should find element from dom, wrap elements with it', () => {\n      $inner.wrapAll('#new');\n      const $container = doc('.container');\n      const $wrap = doc('b');\n      const $new = doc('#new');\n\n      expect($inner).toHaveLength(4);\n      expect($container).toHaveLength(2);\n      expect($container[0].children).toHaveLength(1);\n      expect($container[1].children).toHaveLength(0);\n      expect($wrap[0].children).toHaveLength(4);\n      expect($inner[0].parent).toBe($wrap[0]);\n      expect($inner[1].parent).toBe($wrap[0]);\n      expect($inner[2].parent).toBe($wrap[0]);\n      expect($inner[3].parent).toBe($wrap[0]);\n      expect($new[0].parent).toBe($container[0]);\n      expect($container[0].children[0]).toBe($new[0]);\n    });\n\n    it('(function) : check execution', () => {\n      const $container = doc('.container');\n      const p = $container[0].parent;\n\n      const result = $container.wrapAll(\n        () => \"<div class='red'><div class='tmp'></div></div>\",\n      );\n\n      expect(result.parent()).toHaveLength(1);\n      expect($container.eq(0).parent().parent().is('.red')).toBe(true);\n      expect($container.eq(1).parent().parent().is('.red')).toBe(true);\n      expect($container.eq(0).parent().parent().parent().is(p)).toBe(true);\n    });\n\n    it('(function) : check execution characteristics', () => {\n      const $new = doc('#new');\n      let i = 0;\n\n      doc('no-result').wrapAll(() => {\n        i++;\n        return '';\n      });\n      expect(i).toBeFalsy();\n\n      $new.wrapAll(function (index) {\n        expect(this).toBe($new[0]);\n        expect(index).toBe(0);\n        return this;\n      });\n    });\n\n    it('(nodes) : should skip text nodes', () => {\n      const $text = load(mixedText);\n      const $body = $text($text('body')[0].children);\n\n      $body.wrapAll($text('body')[0].children.slice(1));\n\n      expect($text('body').html()).toBe('TEXT<b>2<a>1</a>TEXT<b>2</b></b>');\n    });\n  });\n\n  describe('.append', () => {\n    it('() : should do nothing', () => {\n      expect($('#fruits').append()[0].tagName).toBe('ul');\n    });\n\n    it('(null) :  should do nothing', () => {\n      $fruits.append(null as never);\n      expect($fruits.children()).toHaveLength(3);\n    });\n\n    it('(html) : should add element as last child', () => {\n      $fruits.append('<li class=\"plum\">Plum</li>');\n      expect($fruits.children().eq(3).hasClass('plum')).toBe(true);\n    });\n\n    it('(html) : should not fail on text nodes', () => {\n      expect($(mixedText).append(' UP').html()).toBe('1 UP');\n    });\n\n    it('($(...)) : should add element as last child', () => {\n      const $plum = $('<li class=\"plum\">Plum</li>');\n      $fruits.append($plum);\n      expect($fruits.children().eq(3).hasClass('plum')).toBe(true);\n    });\n\n    it('(Node) : should add element as last child', () => {\n      const plum = $('<li class=\"plum\">Plum</li>')[0];\n      $fruits.append(plum);\n      expect($fruits.children().eq(3).hasClass('plum')).toBe(true);\n    });\n\n    it('(existing Node) : should remove node from previous location', () => {\n      const apple = $fruits.children()[0];\n\n      expect($fruits.children()).toHaveLength(3);\n      $fruits.append(apple);\n      const $children = $fruits.children();\n\n      expect($children).toHaveLength(3);\n      expect($children[0]).not.toBe(apple);\n      expect($children[2]).toBe(apple);\n    });\n\n    it('(existing Node) : should remove existing node from previous location', () => {\n      const apple = $fruits.children()[0];\n      const $dest = $('<div></div>');\n\n      expect($fruits.children()).toHaveLength(3);\n      $dest.append(apple);\n      const $children = $fruits.children();\n\n      expect($children).toHaveLength(2);\n      expect($children[0]).not.toBe(apple);\n\n      expect($dest.children()).toHaveLength(1);\n      expect($dest.children()[0]).toBe(apple);\n    });\n\n    it('(existing Node) : should update original direct siblings', () => {\n      $('.pear').append($('.orange'));\n      expect($('.apple').next()[0]).toBe($('.pear')[0]);\n      expect($('.pear').prev()[0]).toBe($('.apple')[0]);\n    });\n\n    it('(existing Node) : should clone all but the last occurrence', () => {\n      const $originalApple = $('.apple');\n\n      $('.orange, .pear').append($originalApple);\n\n      const $apples = $('.apple');\n      expect($apples).toHaveLength(2);\n      expect($apples.eq(0).parent()[0]).toBe($('.orange')[0]);\n      expect($apples.eq(1).parent()[0]).toBe($('.pear')[0]);\n      expect($apples[1]).toBe($originalApple[0]);\n    });\n\n    it('(elem) : should NOP if removed', () => {\n      const $apple = $('.apple');\n\n      $apple.remove();\n      $fruits.append($apple);\n      expect($fruits.children().eq(2).hasClass('apple')).toBe(true);\n    });\n\n    it('($(...), html) : should add multiple elements as last children', () => {\n      const $plum = $('<li class=\"plum\">Plum</li>');\n      const grape = '<li class=\"grape\">Grape</li>';\n      $fruits.append($plum, grape);\n      expect($fruits.children().eq(3).hasClass('plum')).toBe(true);\n      expect($fruits.children().eq(4).hasClass('grape')).toBe(true);\n    });\n\n    it('(Array) : should append all elements in the array', () => {\n      const more = $(\n        '<li class=\"plum\">Plum</li><li class=\"grape\">Grape</li>',\n      ).get();\n      $fruits.append(more);\n      expect($fruits.children().eq(3).hasClass('plum')).toBe(true);\n      expect($fruits.children().eq(4).hasClass('grape')).toBe(true);\n    });\n\n    it('(fn) : should invoke the callback with the correct arguments and context', () => {\n      const $fruits = $('#fruits').children();\n      const args: [number, string][] = [];\n      const thisValues: AnyNode[] = [];\n\n      $fruits.append(function (...myArgs) {\n        args.push(myArgs);\n        thisValues.push(this);\n        return this;\n      });\n\n      expect(args).toStrictEqual([\n        [0, 'Apple'],\n        [1, 'Orange'],\n        [2, 'Pear'],\n      ]);\n      expect(thisValues).toStrictEqual([$fruits[0], $fruits[1], $fruits[2]]);\n    });\n\n    it('(fn) : should add returned string as last child', () => {\n      const $fruits = $('#fruits').children();\n\n      $fruits.append(() => '<div class=\"first\">');\n\n      const $apple = $fruits.eq(0);\n      const $orange = $fruits.eq(1);\n      const $pear = $fruits.eq(2);\n\n      expect($apple.find('.first')[0]).toBe($apple.contents()[1]);\n      expect($orange.find('.first')[0]).toBe($orange.contents()[1]);\n      expect($pear.find('.first')[0]).toBe($pear.contents()[1]);\n    });\n\n    it('(fn) : should add returned Cheerio object as last child', () => {\n      const $fruits = $('#fruits').children();\n\n      $fruits.append(() => $('<div class=\"second\">'));\n\n      const $apple = $fruits.eq(0);\n      const $orange = $fruits.eq(1);\n      const $pear = $fruits.eq(2);\n\n      expect($apple.find('.second')[0]).toBe($apple.contents()[1]);\n      expect($orange.find('.second')[0]).toBe($orange.contents()[1]);\n      expect($pear.find('.second')[0]).toBe($pear.contents()[1]);\n    });\n\n    it('(fn) : should add returned Node as last child', () => {\n      const $fruits = $('#fruits').children();\n\n      $fruits.append(() => $('<div class=\"third\">')[0]);\n\n      const $apple = $fruits.eq(0);\n      const $orange = $fruits.eq(1);\n      const $pear = $fruits.eq(2);\n\n      expect($apple.find('.third')[0]).toBe($apple.contents()[1]);\n      expect($orange.find('.third')[0]).toBe($orange.contents()[1]);\n      expect($pear.find('.third')[0]).toBe($pear.contents()[1]);\n    });\n\n    it('should maintain correct object state (Issue: #10)', () => {\n      const $obj = $('<div></div>')\n        .append('<div><div></div></div>')\n        .children()\n        .children()\n        .parent();\n      expect($obj).toBeTruthy();\n    });\n\n    it('($(...)) : should remove from root element', () => {\n      const $plum = $('<li class=\"plum\">Plum</li>');\n      const { parent } = $plum[0];\n      expect(parent).toBeTruthy();\n\n      $fruits.append($plum);\n      expect($plum[0].parent?.type).not.toBe('root');\n      expect(parent?.childNodes).not.toContain($plum[0]);\n    });\n  });\n\n  describe('.prepend', () => {\n    it('() : should do nothing', () => {\n      expect($('#fruits').prepend()[0].tagName).toBe('ul');\n    });\n\n    it('(html) : should add element as first child', () => {\n      $fruits.prepend('<li class=\"plum\">Plum</li>');\n      expect($fruits.children().eq(0).hasClass('plum')).toBe(true);\n    });\n\n    it('($(...)) : should add element as first child', () => {\n      const $plum = $('<li class=\"plum\">Plum</li>');\n      $fruits.prepend($plum);\n      expect($fruits.children().eq(0).hasClass('plum')).toBe(true);\n    });\n\n    it('($(...)) : should add style element as first child', () => {\n      const $style = $('<style>.foo {}</style>');\n      $fruits.prepend($style);\n      const styleTag = $fruits.children().get(0);\n      expect(styleTag?.tagName).toBe('style');\n      expect(styleTag?.children[0]).toHaveProperty('data', '.foo {}');\n    });\n\n    it('($(...)) : should add script element as first child', () => {\n      const $script = $('<script>var foo;</script>');\n      $fruits.prepend($script);\n      const scriptTag = $fruits.children().get(0);\n      expect(scriptTag?.tagName).toBe('script');\n      expect(scriptTag?.children[0]).toHaveProperty('data', 'var foo;');\n    });\n\n    it('(Node) : should add node as first child', () => {\n      const plum = $('<li class=\"plum\">Plum</li>')[0];\n      $fruits.prepend(plum);\n      expect($fruits.children().eq(0).hasClass('plum')).toBe(true);\n    });\n\n    it('(existing Node) : should remove existing nodes from previous locations', () => {\n      const pear = $fruits.children()[2];\n\n      expect($fruits.children()).toHaveLength(3);\n      $fruits.prepend(pear);\n      const $children = $fruits.children();\n\n      expect($children).toHaveLength(3);\n      expect($children[2]).not.toBe(pear);\n      expect($children[0]).toBe(pear);\n    });\n\n    it('(existing Node) : should update original direct siblings', () => {\n      $('.pear').prepend($('.orange'));\n      expect($('.apple').next()[0]).toBe($('.pear')[0]);\n      expect($('.pear').prev()[0]).toBe($('.apple')[0]);\n    });\n\n    it('(existing Node) : should clone all but the last occurrence', () => {\n      const $originalApple = $('.apple');\n\n      $('.orange, .pear').prepend($originalApple);\n\n      const $apples = $('.apple');\n      expect($apples).toHaveLength(2);\n      expect($apples.eq(0).parent()[0]).toBe($('.orange')[0]);\n      expect($apples.eq(1).parent()[0]).toBe($('.pear')[0]);\n      expect($apples[1]).toBe($originalApple[0]);\n    });\n\n    it('(elem) : should handle if removed', () => {\n      const $apple = $('.apple');\n\n      $apple.remove();\n      $fruits.prepend($apple);\n      expect($fruits.children().eq(0).hasClass('apple')).toBe(true);\n    });\n\n    it('(Array) : should add all elements in the array as initial children', () => {\n      const more = $(\n        '<li class=\"plum\">Plum</li><li class=\"grape\">Grape</li>',\n      ).get();\n      $fruits.prepend(more);\n      expect($fruits.children().eq(0).hasClass('plum')).toBe(true);\n      expect($fruits.children().eq(1).hasClass('grape')).toBe(true);\n    });\n\n    it('(html, $(...), html) : should add multiple elements as first children', () => {\n      const $plum = $('<li class=\"plum\">Plum</li>');\n      const grape = '<li class=\"grape\">Grape</li>';\n      $fruits.prepend($plum, grape);\n      expect($fruits.children().eq(0).hasClass('plum')).toBe(true);\n      expect($fruits.children().eq(1).hasClass('grape')).toBe(true);\n    });\n\n    it('(fn) : should invoke the callback with the correct arguments and context', () => {\n      const args: [number, string][] = [];\n      const thisValues: AnyNode[] = [];\n      const $fruits = $('#fruits').children();\n\n      $fruits.prepend(function (...myArgs) {\n        args.push(myArgs);\n        thisValues.push(this);\n        return this;\n      });\n\n      expect(args).toStrictEqual([\n        [0, 'Apple'],\n        [1, 'Orange'],\n        [2, 'Pear'],\n      ]);\n      expect(thisValues).toStrictEqual([$fruits[0], $fruits[1], $fruits[2]]);\n    });\n\n    it('(fn) : should add returned string as first child', () => {\n      const $fruits = $('#fruits').children();\n\n      $fruits.prepend(() => '<div class=\"first\">');\n\n      const $apple = $fruits.eq(0);\n      const $orange = $fruits.eq(1);\n      const $pear = $fruits.eq(2);\n\n      expect($apple.find('.first')[0]).toBe($apple.contents()[0]);\n      expect($orange.find('.first')[0]).toBe($orange.contents()[0]);\n      expect($pear.find('.first')[0]).toBe($pear.contents()[0]);\n    });\n\n    it('(fn) : should add returned Cheerio object as first child', () => {\n      const $fruits = $('#fruits').children();\n\n      $fruits.prepend(() => $('<div class=\"second\">'));\n\n      const $apple = $fruits.eq(0);\n      const $orange = $fruits.eq(1);\n      const $pear = $fruits.eq(2);\n\n      expect($apple.find('.second')[0]).toBe($apple.contents()[0]);\n      expect($orange.find('.second')[0]).toBe($orange.contents()[0]);\n      expect($pear.find('.second')[0]).toBe($pear.contents()[0]);\n    });\n\n    it('(fn) : should add returned Node as first child', () => {\n      const $fruits = $('#fruits').children();\n\n      $fruits.prepend(() => $('<div class=\"third\">')[0]);\n\n      const $apple = $fruits.eq(0);\n      const $orange = $fruits.eq(1);\n      const $pear = $fruits.eq(2);\n\n      expect($apple.find('.third')[0]).toBe($apple.contents()[0]);\n      expect($orange.find('.third')[0]).toBe($orange.contents()[0]);\n      expect($pear.find('.third')[0]).toBe($pear.contents()[0]);\n    });\n\n    it('($(...)) : should remove from root element', () => {\n      const $plum = $('<li class=\"plum\">Plum</li>');\n      const root = $plum[0].parent;\n      expect(root?.type).toBe('root');\n\n      $fruits.prepend($plum);\n      expect($plum[0].parent?.type).not.toBe('root');\n      expect(root?.childNodes).not.toContain($plum[0]);\n    });\n  });\n\n  describe('.appendTo', () => {\n    it('(html) : should add element as last child', () => {\n      const $plum = $('<li class=\"plum\">Plum</li>').appendTo(fruits);\n      expect($plum.parent().children().eq(3).hasClass('plum')).toBe(true);\n    });\n\n    it('($(...)) : should add element as last child', () => {\n      $('<li class=\"plum\">Plum</li>').appendTo($fruits);\n      expect($fruits.children().eq(3).hasClass('plum')).toBe(true);\n    });\n\n    it('(Node) : should add element as last child', () => {\n      $('<li class=\"plum\">Plum</li>').appendTo($fruits[0]);\n      expect($fruits.children().eq(3).hasClass('plum')).toBe(true);\n    });\n\n    it('(selector) : should add element as last child', () => {\n      $('<li class=\"plum\">Plum</li>').appendTo('#fruits');\n      expect($fruits.children().eq(3).hasClass('plum')).toBe(true);\n    });\n\n    it('(Array) : should add element as last child of all elements in the array', () => {\n      const $multiple = $('<ul><li>Apple</li></ul><ul><li>Orange</li></ul>');\n      $('<li class=\"plum\">Plum</li>').appendTo($multiple.get());\n      expect($multiple.first().children().eq(1).hasClass('plum')).toBe(true);\n      expect($multiple.last().children().eq(1).hasClass('plum')).toBe(true);\n    });\n  });\n\n  describe('.prependTo', () => {\n    it('(html) : should add element as first child', () => {\n      const $plum = $('<li class=\"plum\">Plum</li>').prependTo(fruits);\n      expect($plum.parent().children().eq(0).hasClass('plum')).toBe(true);\n    });\n\n    it('($(...)) : should add element as first child', () => {\n      $('<li class=\"plum\">Plum</li>').prependTo($fruits);\n      expect($fruits.children().eq(0).hasClass('plum')).toBe(true);\n    });\n\n    it('(Node) : should add node as first child', () => {\n      $('<li class=\"plum\">Plum</li>').prependTo($fruits[0]);\n      expect($fruits.children().eq(0).hasClass('plum')).toBe(true);\n    });\n\n    it('(selector) : should add element as first child', () => {\n      $('<li class=\"plum\">Plum</li>').prependTo('#fruits');\n      expect($fruits.children().eq(0).hasClass('plum')).toBe(true);\n    });\n\n    it('(Array) : should add element as first child of all elements in the array', () => {\n      const $multiple = $('<ul><li>Apple</li></ul><ul><li>Orange</li></ul>');\n      $('<li class=\"plum\">Plum</li>').prependTo($multiple.get());\n      expect($multiple.first().children().eq(0).hasClass('plum')).toBe(true);\n      expect($multiple.last().children().eq(0).hasClass('plum')).toBe(true);\n    });\n  });\n\n  describe('.after', () => {\n    it('() : should do nothing', () => {\n      expect($fruits.after()[0].tagName).toBe('ul');\n    });\n\n    it('(html) : should add element as next sibling', () => {\n      const grape = '<li class=\"grape\">Grape</li>';\n      $('.apple').after(grape);\n      expect($('.apple').next().hasClass('grape')).toBe(true);\n    });\n\n    it('(Array) : should add all elements in the array as next sibling', () => {\n      const more = $(\n        '<li class=\"plum\">Plum</li><li class=\"grape\">Grape</li>',\n      ).get();\n      $('.apple').after(more);\n      expect($fruits.children().eq(1).hasClass('plum')).toBe(true);\n      expect($fruits.children().eq(2).hasClass('grape')).toBe(true);\n    });\n\n    it('($(...)) : should add element as next sibling', () => {\n      const $plum = $('<li class=\"plum\">Plum</li>');\n      $('.apple').after($plum);\n      expect($('.apple').next().hasClass('plum')).toBe(true);\n    });\n\n    it('(Node) : should add element as next sibling', () => {\n      const plum = $('<li class=\"plum\">Plum</li>')[0];\n      $('.apple').after(plum);\n      expect($('.apple').next().hasClass('plum')).toBe(true);\n    });\n\n    it('(existing Node) : should remove existing nodes from previous locations', () => {\n      const pear = $fruits.children()[2];\n\n      $('.apple').after(pear);\n\n      const $children = $fruits.children();\n      expect($children).toHaveLength(3);\n      expect($children[1]).toBe(pear);\n    });\n\n    it('(existing Node) : should update original direct siblings', () => {\n      $('.pear').after($('.orange'));\n      expect($('.apple').next()[0]).toBe($('.pear')[0]);\n      expect($('.pear').prev()[0]).toBe($('.apple')[0]);\n    });\n\n    it('(existing Node) : should clone all but the last occurrence', () => {\n      const $originalApple = $('.apple');\n      $('.orange, .pear').after($originalApple);\n\n      expect($('.apple')).toHaveLength(2);\n      expect($('.apple').eq(0).prev()[0]).toBe($('.orange')[0]);\n      expect($('.apple').eq(0).next()[0]).toBe($('.pear')[0]);\n      expect($('.apple').eq(1).prev()[0]).toBe($('.pear')[0]);\n      expect($('.apple').eq(1).next()).toHaveLength(0);\n      expect($('.apple')[0]).not.toStrictEqual($originalApple[0]);\n      expect($('.apple')[1]).toStrictEqual($originalApple[0]);\n    });\n\n    it('(elem) : should handle if removed', () => {\n      const $apple = $('.apple');\n      const $plum = $('<li class=\"plum\">Plum</li>');\n\n      $apple.remove();\n      $apple.after($plum);\n      expect($plum.prev()).toHaveLength(0);\n    });\n\n    it('($(...), html) : should add multiple elements as next siblings', () => {\n      const $plum = $('<li class=\"plum\">Plum</li>');\n      const grape = '<li class=\"grape\">Grape</li>';\n      $('.apple').after($plum, grape);\n      expect($('.apple').next().hasClass('plum')).toBe(true);\n      expect($('.plum').next().hasClass('grape')).toBe(true);\n    });\n\n    it('(fn) : should invoke the callback with the correct arguments and context', () => {\n      const args: [number, string][] = [];\n      const thisValues: AnyNode[] = [];\n      const $fruits = $('#fruits').children();\n\n      $fruits.after(function (...myArgs) {\n        args.push(myArgs);\n        thisValues.push(this);\n        return this;\n      });\n\n      expect(args).toStrictEqual([\n        [0, 'Apple'],\n        [1, 'Orange'],\n        [2, 'Pear'],\n      ]);\n      expect(thisValues).toStrictEqual([$fruits[0], $fruits[1], $fruits[2]]);\n    });\n\n    it('(fn) : should add returned string as next sibling', () => {\n      const $fruits = $('#fruits').children();\n\n      $fruits.after(() => '<li class=\"first\">');\n\n      expect($('.first')[0]).toBe($('#fruits').contents()[1]);\n      expect($('.first')[1]).toBe($('#fruits').contents()[3]);\n      expect($('.first')[2]).toBe($('#fruits').contents()[5]);\n    });\n\n    it('(fn) : should add returned Cheerio object as next sibling', () => {\n      const $fruits = $('#fruits').children();\n\n      $fruits.after(() => $('<li class=\"second\">'));\n\n      expect($('.second')[0]).toBe($('#fruits').contents()[1]);\n      expect($('.second')[1]).toBe($('#fruits').contents()[3]);\n      expect($('.second')[2]).toBe($('#fruits').contents()[5]);\n    });\n\n    it('(fn) : should add returned element as next sibling', () => {\n      const $fruits = $('#fruits').children();\n\n      $fruits.after(() => $('<li class=\"third\">')[0]);\n\n      expect($('.third')[0]).toBe($('#fruits').contents()[1]);\n      expect($('.third')[1]).toBe($('#fruits').contents()[3]);\n      expect($('.third')[2]).toBe($('#fruits').contents()[5]);\n    });\n\n    it('(fn) : should support text nodes', () => {\n      const $text = load(mixedText);\n\n      $text($text('body')[0].children).after(\n        (_, content) => `<c>${content}added</c>`,\n      );\n\n      expect($text('body').html()).toBe(\n        '<a>1</a><c>1added</c>TEXT<b>2</b><c>2added</c>',\n      );\n    });\n\n    it('($(...)) : should remove from root element', () => {\n      const $plum = $('<li class=\"plum\">Plum</li>');\n      const root = $plum[0].parent;\n      expect(root?.type).toBe('root');\n\n      $fruits.after($plum);\n      expect($plum[0].parent?.type).not.toBe('root');\n      expect(root?.childNodes).not.toContain($plum[0]);\n    });\n  });\n\n  describe('.insertAfter', () => {\n    it('(selector) : should create element and add as next sibling', () => {\n      const grape = $('<li class=\"grape\">Grape</li>');\n      grape.insertAfter('.apple');\n      expect($('.apple').next().hasClass('grape')).toBe(true);\n    });\n\n    it('(selector) : should create element and add as next sibling of multiple elements', () => {\n      const grape = $('<li class=\"grape\">Grape</li>');\n      grape.insertAfter('.apple, .pear');\n      expect($('.apple').next().hasClass('grape')).toBe(true);\n      expect($('.pear').next().hasClass('grape')).toBe(true);\n    });\n\n    it('($(...)) : should create element and add as next sibling', () => {\n      const grape = $('<li class=\"grape\">Grape</li>');\n      grape.insertAfter($('.apple'));\n      expect($('.apple').next().hasClass('grape')).toBe(true);\n    });\n\n    it('($(...)) : should create element and add as next sibling of multiple elements', () => {\n      const grape = $('<li class=\"grape\">Grape</li>');\n      grape.insertAfter($('.apple, .pear'));\n      expect($('.apple').next().hasClass('grape')).toBe(true);\n      expect($('.pear').next().hasClass('grape')).toBe(true);\n    });\n\n    it('($(...)) : should create all elements in the array and add as next siblings', () => {\n      const more = $('<li class=\"plum\">Plum</li><li class=\"grape\">Grape</li>');\n      more.insertAfter($('.apple'));\n      expect($fruits.children().eq(0).hasClass('apple')).toBe(true);\n      expect($fruits.children().eq(1).hasClass('plum')).toBe(true);\n      expect($fruits.children().eq(2).hasClass('grape')).toBe(true);\n    });\n\n    it('(existing Node) : should remove existing nodes from previous locations', () => {\n      $('.orange').insertAfter('.pear');\n      expect($fruits.children().eq(1).hasClass('orange')).toBe(false);\n      expect($fruits.children().length).toBe(3);\n      expect($('.orange').length).toBe(1);\n    });\n\n    it('(existing Node) : should update original direct siblings', () => {\n      $('.orange').insertAfter('.pear');\n      expect($('.apple').next().hasClass('pear')).toBe(true);\n      expect($('.pear').prev().hasClass('apple')).toBe(true);\n      expect($('.pear').next().hasClass('orange')).toBe(true);\n      expect($('.orange').next()).toHaveLength(0);\n    });\n\n    it('(existing Node) : should update original direct siblings of multiple elements', () => {\n      $('.apple').insertAfter('.orange, .pear');\n      expect($('.orange').prev()).toHaveLength(0);\n      expect($('.orange').next().hasClass('apple')).toBe(true);\n      expect($('.pear').next().hasClass('apple')).toBe(true);\n      expect($('.pear').prev().hasClass('apple')).toBe(true);\n      expect($fruits.children().length).toBe(4);\n      const apples = $('.apple');\n      expect(apples.length).toBe(2);\n      expect(apples.eq(0).prev().hasClass('orange')).toBe(true);\n      expect(apples.eq(1).prev().hasClass('pear')).toBe(true);\n    });\n\n    it('(elem) : should handle if removed', () => {\n      const $apple = $('.apple');\n      const $plum = $('<li class=\"plum\">Plum</li>');\n      $apple.remove();\n      $plum.insertAfter($apple);\n      expect($plum.prev()).toHaveLength(0);\n    });\n\n    it('(single) should return the new element for chaining', () => {\n      const $grape = $('<li class=\"grape\">Grape</li>').insertAfter('.apple');\n      expect($grape.cheerio).toBeTruthy();\n      expect($grape.each).toBeTruthy();\n      expect($grape.length).toBe(1);\n      expect($grape.hasClass('grape')).toBe(true);\n    });\n\n    it('(single) should return the new elements for chaining', () => {\n      const $purple = $(\n        '<li class=\"grape\">Grape</li><li class=\"plum\">Plum</li>',\n      ).insertAfter('.apple');\n      expect($purple.cheerio).toBeTruthy();\n      expect($purple.each).toBeTruthy();\n      expect($purple.length).toBe(2);\n      expect($purple.eq(0).hasClass('grape')).toBe(true);\n      expect($purple.eq(1).hasClass('plum')).toBe(true);\n    });\n\n    it('(multiple) should return the new elements for chaining', () => {\n      const $purple = $(\n        '<li class=\"grape\">Grape</li><li class=\"plum\">Plum</li>',\n      ).insertAfter('.apple, .pear');\n      expect($purple.cheerio).toBeTruthy();\n      expect($purple.each).toBeTruthy();\n      expect($purple.length).toBe(4);\n      expect($purple.eq(0).hasClass('grape')).toBe(true);\n      expect($purple.eq(1).hasClass('plum')).toBe(true);\n      expect($purple.eq(2).hasClass('grape')).toBe(true);\n      expect($purple.eq(3).hasClass('plum')).toBe(true);\n    });\n\n    it('(single) should return the existing element for chaining', () => {\n      const $pear = $('.pear').insertAfter('.apple');\n      expect($pear.cheerio).toBeTruthy();\n      expect($pear.each).toBeTruthy();\n      expect($pear.length).toBe(1);\n      expect($pear.hasClass('pear')).toBe(true);\n    });\n\n    it('(single) should return the existing elements for chaining', () => {\n      const $things = $('.orange, .apple').insertAfter('.pear');\n      expect($things.cheerio).toBeTruthy();\n      expect($things.each).toBeTruthy();\n      expect($things.length).toBe(2);\n      expect($things.eq(0).hasClass('apple')).toBe(true);\n      expect($things.eq(1).hasClass('orange')).toBe(true);\n    });\n\n    it('(multiple) should return the existing elements for chaining', () => {\n      $('<li class=\"grape\">Grape</li>').insertAfter('.apple');\n      const $things = $('.orange, .apple').insertAfter('.pear, .grape');\n      expect($things.cheerio).toBeTruthy();\n      expect($things.each).toBeTruthy();\n      expect($things.length).toBe(4);\n      expect($things.eq(0).hasClass('apple')).toBe(true);\n      expect($things.eq(1).hasClass('orange')).toBe(true);\n      expect($things.eq(2).hasClass('apple')).toBe(true);\n      expect($things.eq(3).hasClass('orange')).toBe(true);\n    });\n  });\n\n  describe('.before', () => {\n    it('() : should do nothing', () => {\n      expect($('#fruits').before()[0].tagName).toBe('ul');\n    });\n\n    it('(html) : should add element as previous sibling', () => {\n      const grape = '<li class=\"grape\">Grape</li>';\n      $('.apple').before(grape);\n      expect($('.apple').prev().hasClass('grape')).toBe(true);\n    });\n\n    it('($(...)) : should add element as previous sibling', () => {\n      const $plum = $('<li class=\"plum\">Plum</li>');\n      $('.apple').before($plum);\n      expect($('.apple').prev().hasClass('plum')).toBe(true);\n    });\n\n    it('(Node) : should add element as previous sibling', () => {\n      const plum = $('<li class=\"plum\">Plum</li>')[0];\n      $('.apple').before(plum);\n      expect($('.apple').prev().hasClass('plum')).toBe(true);\n    });\n\n    it('(existing Node) : should remove existing nodes from previous locations', () => {\n      const pear = $fruits.children()[2];\n\n      $('.apple').before(pear);\n\n      const $children = $fruits.children();\n      expect($children).toHaveLength(3);\n      expect($children[0]).toBe(pear);\n    });\n\n    it('(existing Node) : should update original direct siblings', () => {\n      $('.apple').before($('.orange'));\n      expect($('.apple').next()[0]).toBe($('.pear')[0]);\n      expect($('.pear').prev()[0]).toBe($('.apple')[0]);\n    });\n\n    it('(existing Node) : should clone all but the last occurrence', () => {\n      const $originalPear = $('.pear');\n      $('.apple, .orange').before($originalPear);\n\n      expect($('.pear')).toHaveLength(2);\n      expect($('.pear').eq(0).prev()).toHaveLength(0);\n      expect($('.pear').eq(0).next()[0]).toBe($('.apple')[0]);\n      expect($('.pear').eq(1).prev()[0]).toBe($('.apple')[0]);\n      expect($('.pear').eq(1).next()[0]).toBe($('.orange')[0]);\n      expect($('.pear')[0]).not.toStrictEqual($originalPear[0]);\n      expect($('.pear')[1]).toStrictEqual($originalPear[0]);\n    });\n\n    it('(elem) : should handle if removed', () => {\n      const $apple = $('.apple');\n      const $plum = $('<li class=\"plum\">Plum</li>');\n\n      $apple.remove();\n      $apple.before($plum);\n      expect($plum.next()).toHaveLength(0);\n    });\n\n    it('(Array) : should add all elements in the array as previous sibling', () => {\n      const more = $(\n        '<li class=\"plum\">Plum</li><li class=\"grape\">Grape</li>',\n      ).get();\n      $('.apple').before(more);\n      expect($fruits.children().eq(0).hasClass('plum')).toBe(true);\n      expect($fruits.children().eq(1).hasClass('grape')).toBe(true);\n    });\n\n    it('($(...), html) : should add multiple elements as previous siblings', () => {\n      const $plum = $('<li class=\"plum\">Plum</li>');\n      const grape = '<li class=\"grape\">Grape</li>';\n      $('.apple').before($plum, grape);\n      expect($('.apple').prev().hasClass('grape')).toBe(true);\n      expect($('.grape').prev().hasClass('plum')).toBe(true);\n    });\n\n    it('(fn) : should invoke the callback with the correct arguments and context', () => {\n      const args: [number, string][] = [];\n      const thisValues: AnyNode[] = [];\n      const $fruits = $('#fruits').children();\n\n      $fruits.before(function (...myArgs) {\n        args.push(myArgs);\n        thisValues.push(this);\n        return this;\n      });\n\n      expect(args).toStrictEqual([\n        [0, 'Apple'],\n        [1, 'Orange'],\n        [2, 'Pear'],\n      ]);\n      expect(thisValues).toStrictEqual([$fruits[0], $fruits[1], $fruits[2]]);\n    });\n\n    it('(fn) : should add returned string as previous sibling', () => {\n      const $fruits = $('#fruits').children();\n\n      $fruits.before(() => '<li class=\"first\">');\n\n      expect($('.first')[0]).toBe($('#fruits').contents()[0]);\n      expect($('.first')[1]).toBe($('#fruits').contents()[2]);\n      expect($('.first')[2]).toBe($('#fruits').contents()[4]);\n    });\n\n    it('(fn) : should add returned Cheerio object as previous sibling', () => {\n      const $fruits = $('#fruits').children();\n\n      $fruits.before(() => $('<li class=\"second\">'));\n\n      expect($('.second')[0]).toBe($('#fruits').contents()[0]);\n      expect($('.second')[1]).toBe($('#fruits').contents()[2]);\n      expect($('.second')[2]).toBe($('#fruits').contents()[4]);\n    });\n\n    it('(fn) : should add returned Node as previous sibling', () => {\n      const $fruits = $('#fruits').children();\n\n      $fruits.before(() => $('<li class=\"third\">')[0]);\n\n      expect($('.third')[0]).toBe($('#fruits').contents()[0]);\n      expect($('.third')[1]).toBe($('#fruits').contents()[2]);\n      expect($('.third')[2]).toBe($('#fruits').contents()[4]);\n    });\n\n    it('(fn) : should support text nodes', () => {\n      const $text = load(mixedText);\n\n      $text($text('body')[0].children).before(\n        (_, content) => `<c>${content}added</c>`,\n      );\n\n      expect($text('body').html()).toBe(\n        '<c>1added</c><a>1</a>TEXT<c>2added</c><b>2</b>',\n      );\n    });\n\n    it('($(...)) : should remove from root element', () => {\n      const $plum = $('<li class=\"plum\">Plum</li>');\n      const root = $plum[0].parent;\n      expect(root?.type).toBe('root');\n\n      $fruits.before($plum);\n      expect($plum[0].parent?.type).not.toBe('root');\n      expect(root?.childNodes).not.toContain($plum[0]);\n    });\n  });\n\n  describe('.insertBefore', () => {\n    it('(selector) : should create element and add as prev sibling', () => {\n      const grape = $('<li class=\"grape\">Grape</li>');\n      grape.insertBefore('.apple');\n      expect($('.apple').prev().hasClass('grape')).toBe(true);\n    });\n\n    it('(selector) : should create element and add as prev sibling of multiple elements', () => {\n      const grape = $('<li class=\"grape\">Grape</li>');\n      grape.insertBefore('.apple, .pear');\n      expect($('.apple').prev().hasClass('grape')).toBe(true);\n      expect($('.pear').prev().hasClass('grape')).toBe(true);\n    });\n\n    it('($(...)) : should create element and add as prev sibling', () => {\n      const grape = $('<li class=\"grape\">Grape</li>');\n      grape.insertBefore($('.apple'));\n      expect($('.apple').prev().hasClass('grape')).toBe(true);\n    });\n\n    it('($(...)) : should create element and add as next sibling of multiple elements', () => {\n      const grape = $('<li class=\"grape\">Grape</li>');\n      grape.insertBefore($('.apple, .pear'));\n      expect($('.apple').prev().hasClass('grape')).toBe(true);\n      expect($('.pear').prev().hasClass('grape')).toBe(true);\n    });\n\n    it('($(...)) : should create all elements in the array and add as prev siblings', () => {\n      const more = $('<li class=\"plum\">Plum</li><li class=\"grape\">Grape</li>');\n      more.insertBefore($('.apple'));\n      expect($fruits.children().eq(0).hasClass('plum')).toBe(true);\n      expect($fruits.children().eq(1).hasClass('grape')).toBe(true);\n      expect($fruits.children().eq(2).hasClass('apple')).toBe(true);\n    });\n\n    it('(existing Node) : should remove existing nodes from previous locations', () => {\n      $('.pear').insertBefore('.apple');\n      expect($fruits.children().eq(2).hasClass('pear')).toBe(false);\n      expect($fruits.children().length).toBe(3);\n      expect($('.pear').length).toBe(1);\n    });\n\n    it('(existing Node) : should update original direct siblings', () => {\n      $('.pear').insertBefore('.apple');\n      expect($('.apple').prev().hasClass('pear')).toBe(true);\n      expect($('.apple').next().hasClass('orange')).toBe(true);\n      expect($('.pear').next().hasClass('apple')).toBe(true);\n      expect($('.pear').prev()).toHaveLength(0);\n    });\n\n    it('(existing Node) : should update original direct siblings of multiple elements', () => {\n      $('.pear').insertBefore('.apple, .orange');\n      expect($('.apple').prev().hasClass('pear')).toBe(true);\n      expect($('.apple').next().hasClass('pear')).toBe(true);\n      expect($('.orange').prev().hasClass('pear')).toBe(true);\n      expect($('.orange').next()).toHaveLength(0);\n      expect($fruits.children().length).toBe(4);\n      const pears = $('.pear');\n      expect(pears.length).toBe(2);\n      expect(pears.eq(0).next().hasClass('apple')).toBe(true);\n      expect(pears.eq(1).next().hasClass('orange')).toBe(true);\n    });\n\n    it('(elem) : should handle if removed', () => {\n      const $apple = $('.apple');\n      const $plum = $('<li class=\"plum\">Plum</li>');\n\n      $apple.remove();\n      $plum.insertBefore($apple);\n      expect($plum.next()).toHaveLength(0);\n    });\n\n    it('(single) should return the new element for chaining', () => {\n      const $grape = $('<li class=\"grape\">Grape</li>').insertBefore('.apple');\n      expect($grape.cheerio).toBeTruthy();\n      expect($grape.each).toBeTruthy();\n      expect($grape.length).toBe(1);\n      expect($grape.hasClass('grape')).toBe(true);\n    });\n\n    it('(single) should return the new elements for chaining', () => {\n      const $purple = $(\n        '<li class=\"grape\">Grape</li><li class=\"plum\">Plum</li>',\n      ).insertBefore('.apple');\n      expect($purple.cheerio).toBeTruthy();\n      expect($purple.each).toBeTruthy();\n      expect($purple.length).toBe(2);\n      expect($purple.eq(0).hasClass('grape')).toBe(true);\n      expect($purple.eq(1).hasClass('plum')).toBe(true);\n    });\n\n    it('(multiple) should return the new elements for chaining', () => {\n      const $purple = $(\n        '<li class=\"grape\">Grape</li><li class=\"plum\">Plum</li>',\n      ).insertBefore('.apple, .pear');\n      expect($purple.cheerio).toBeTruthy();\n      expect($purple.each).toBeTruthy();\n      expect($purple.length).toBe(4);\n      expect($purple.eq(0).hasClass('grape')).toBe(true);\n      expect($purple.eq(1).hasClass('plum')).toBe(true);\n      expect($purple.eq(2).hasClass('grape')).toBe(true);\n      expect($purple.eq(3).hasClass('plum')).toBe(true);\n    });\n\n    it('(single) should return the existing element for chaining', () => {\n      const $orange = $('.orange').insertBefore('.apple');\n      expect($orange.cheerio).toBeTruthy();\n      expect($orange.each).toBeTruthy();\n      expect($orange.length).toBe(1);\n      expect($orange.hasClass('orange')).toBe(true);\n    });\n\n    it('(single) should return the existing elements for chaining', () => {\n      const $things = $('.orange, .pear').insertBefore('.apple');\n      expect($things.cheerio).toBeTruthy();\n      expect($things.each).toBeTruthy();\n      expect($things.length).toBe(2);\n      expect($things.eq(0).hasClass('orange')).toBe(true);\n      expect($things.eq(1).hasClass('pear')).toBe(true);\n    });\n\n    it('(multiple) should return the existing elements for chaining', () => {\n      $('<li class=\"grape\">Grape</li>').insertBefore('.apple');\n      const $things = $('.orange, .apple').insertBefore('.pear, .grape');\n      expect($things.cheerio).toBeTruthy();\n      expect($things.each).toBeTruthy();\n      expect($things.length).toBe(4);\n      expect($things.eq(0).hasClass('apple')).toBe(true);\n      expect($things.eq(1).hasClass('orange')).toBe(true);\n      expect($things.eq(2).hasClass('apple')).toBe(true);\n      expect($things.eq(3).hasClass('orange')).toBe(true);\n    });\n  });\n\n  describe('.remove', () => {\n    it('() : should remove selected elements', () => {\n      $('.apple').remove();\n      expect($fruits.find('.apple')).toHaveLength(0);\n    });\n\n    it('() : should be reentrant', () => {\n      const $apple = $('.apple');\n      $apple.remove();\n      $apple.remove();\n      expect($fruits.find('.apple')).toHaveLength(0);\n    });\n\n    it('(selector) : should remove matching selected elements', () => {\n      $('li').remove('.apple');\n      expect($fruits.find('.apple')).toHaveLength(0);\n    });\n\n    it('($(...)) : should remove from root element', () => {\n      const $plum = $('<li class=\"plum\">Plum</li>');\n      const root = $plum[0].parent;\n      expect(root?.type).toBe('root');\n\n      $plum.remove();\n      expect($plum[0].parent).toBe(null);\n      expect(root?.childNodes).not.toContain($plum[0]);\n    });\n  });\n\n  describe('.replaceWith', () => {\n    it('(elem) : should replace one <li> tag with another', () => {\n      const $plum = $('<li class=\"plum\">Plum</li>');\n      $('.orange').replaceWith($plum);\n      expect($('.apple').next().hasClass('plum')).toBe(true);\n      expect($('.apple').next().html()).toBe('Plum');\n      expect($('.pear').prev().hasClass('plum')).toBe(true);\n      expect($('.pear').prev().html()).toBe('Plum');\n    });\n\n    it('(Array) : should replace one <li> tag with the elements in the array', () => {\n      const more = $(\n        '<li class=\"plum\">Plum</li><li class=\"grape\">Grape</li>',\n      ).get();\n      $('.orange').replaceWith(more);\n\n      expect($fruits.children().eq(1).hasClass('plum')).toBe(true);\n      expect($fruits.children().eq(2).hasClass('grape')).toBe(true);\n      expect($('.apple').next().hasClass('plum')).toBe(true);\n      expect($('.pear').prev().hasClass('grape')).toBe(true);\n      expect($fruits.children()).toHaveLength(4);\n    });\n\n    it('(Node) : should replace the selected element with given node', () => {\n      const $src = $('<h2>hi <span>there</span></h2>');\n      const $new = $('<ul></ul>');\n      const $replaced = $src.find('span').replaceWith($new[0]);\n      expect($new[0].parentNode).toBe($src[0]);\n      expect($replaced[0].parentNode).toBe(null);\n      expect($.html($src)).toBe('<h2>hi <ul></ul></h2>');\n    });\n\n    it('(existing element) : should remove element from its previous location', () => {\n      $('.pear').replaceWith($('.apple'));\n      expect($fruits.children()).toHaveLength(2);\n      expect($fruits.children()[0]).toBe($('.orange')[0]);\n      expect($fruits.children()[1]).toBe($('.apple')[0]);\n    });\n\n    it('(elem) : should NOP if removed', () => {\n      const $pear = $('.pear');\n      const $plum = $('<li class=\"plum\">Plum</li>');\n\n      $pear.remove();\n      $pear.replaceWith($plum);\n      expect($('.orange').next().hasClass('plum')).toBe(false);\n    });\n\n    it('(elem) : should replace the single selected element with given element', () => {\n      const $src = $('<h2>hi <span>there</span></h2>');\n      const $new = $('<div>here</div>');\n      const $replaced = $src.find('span').replaceWith($new);\n      expect($new[0].parentNode).toBe($src[0]);\n      expect($replaced[0].parentNode).toBe(null);\n      expect($.html($src)).toBe('<h2>hi <div>here</div></h2>');\n    });\n\n    it('(self) : should be replaced after replacing it with itself', () => {\n      const $a = load('<a>foo</a>', null, false);\n      const replacement = '<a>bar</a>';\n      $a('a').replaceWith((_, el: AnyNode) => el);\n      $a('a').replaceWith(replacement);\n      expect($a.html()).toBe(replacement);\n    });\n\n    it('(str) : should accept strings', () => {\n      const $src = $('<h2>hi <span>there</span></h2>');\n      const newStr = '<div>here</div>';\n      const $replaced = $src.find('span').replaceWith(newStr);\n      expect($replaced[0].parentNode).toBe(null);\n      expect($.html($src)).toBe('<h2>hi <div>here</div></h2>');\n    });\n\n    it('(str) : should replace all selected elements', () => {\n      const $src = $('<b>a<br>b<br>c<br>d</b>');\n      const $replaced = $src.find('br').replaceWith(' ');\n      expect($replaced[0].parentNode).toBe(null);\n      expect($.html($src)).toBe('<b>a b c d</b>');\n    });\n\n    it('(fn) : should invoke the callback with the correct argument and context', () => {\n      const origChildren = $fruits.children().get();\n      const args: [number, AnyNode][] = [];\n      const thisValues: AnyNode[] = [];\n\n      $fruits.children().replaceWith(function (...myArgs) {\n        args.push(myArgs);\n        thisValues.push(this);\n        return '<li class=\"first\">';\n      });\n\n      expect(args).toStrictEqual([\n        [0, origChildren[0]],\n        [1, origChildren[1]],\n        [2, origChildren[2]],\n      ]);\n      expect(thisValues).toStrictEqual([\n        origChildren[0],\n        origChildren[1],\n        origChildren[2],\n      ]);\n    });\n\n    it('(fn) : should replace the selected element with the returned string', () => {\n      $fruits.children().replaceWith(() => '<li class=\"first\">');\n\n      expect($fruits.find('.first')).toHaveLength(3);\n    });\n\n    it('(fn) : should replace the selected element with the returned Cheerio object', () => {\n      $fruits.children().replaceWith(() => $('<li class=\"second\">'));\n\n      expect($fruits.find('.second')).toHaveLength(3);\n    });\n\n    it('(fn) : should replace the selected element with the returned node', () => {\n      $fruits.children().replaceWith(() => $('<li class=\"third\">')[0]);\n\n      expect($fruits.find('.third')).toHaveLength(3);\n    });\n\n    it('($(...)) : should remove from root element', () => {\n      const $plum = $('<li class=\"plum\">Plum</li>');\n      const root = $plum[0].parent;\n      expect(root?.type).toBe('root');\n\n      $fruits.children().replaceWith($plum);\n      expect($plum[0].parent?.type).not.toBe('root');\n      expect(root?.childNodes).not.toContain($plum[0]);\n    });\n  });\n\n  describe('.empty', () => {\n    it('() : should remove all children from selected elements', () => {\n      expect($fruits.children()).toHaveLength(3);\n\n      $fruits.empty();\n      expect($fruits.children()).toHaveLength(0);\n    });\n\n    it('() : should allow element reinsertion', () => {\n      const $children = $fruits.children();\n\n      $fruits.empty();\n      expect($fruits.children()).toHaveLength(0);\n      expect($children).toHaveLength(3);\n\n      $fruits.append($('<div></div><div></div>'));\n      const $remove = $fruits.children().eq(0);\n\n      $remove.replaceWith($children);\n      expect($fruits.children()).toHaveLength(4);\n    });\n\n    it(\"() : should destroy children's references to the parent\", () => {\n      const $children = $fruits.children();\n\n      $fruits.empty();\n\n      expect($children.eq(0).parent()).toHaveLength(0);\n      expect($children.eq(0).next()).toHaveLength(0);\n      expect($children.eq(0).prev()).toHaveLength(0);\n      expect($children.eq(1).parent()).toHaveLength(0);\n      expect($children.eq(1).next()).toHaveLength(0);\n      expect($children.eq(1).prev()).toHaveLength(0);\n      expect($children.eq(2).parent()).toHaveLength(0);\n      expect($children.eq(2).next()).toHaveLength(0);\n      expect($children.eq(2).prev()).toHaveLength(0);\n    });\n\n    it('() : should skip text nodes', () => {\n      const $text = load(mixedText);\n      const $body = $text($text('body')[0].children);\n\n      $body.empty();\n\n      expect($text('body').html()).toBe('<a></a>TEXT<b></b>');\n    });\n\n    it('() : should skip comment nodes', () => {\n      const $comment = load('<a>1</a><!--Comment-->TEXT<b>2</b>');\n      const $body = $comment($comment('body')[0].children);\n\n      $body.empty();\n\n      expect($comment('body').html()).toBe('<a></a><!--Comment-->TEXT<b></b>');\n    });\n  });\n\n  describe('.html', () => {\n    it('() : should get the innerHTML for an element', () => {\n      expect($fruits.html()).toBe(\n        [\n          '<li class=\"apple\">Apple</li>',\n          '<li class=\"orange\">Orange</li>',\n          '<li class=\"pear\">Pear</li>',\n        ].join(''),\n      );\n    });\n\n    it('() : should get innerHTML even if its just text', () => {\n      expect($('.pear', '<li class=\"pear\">Pear</li>').html()).toBe('Pear');\n    });\n\n    it('() : should return empty string if nothing inside', () => {\n      expect($('li', '<li></li>').html()).toBe('');\n    });\n\n    it('(html) : should set the html for its children', () => {\n      $fruits.html('<li class=\"durian\">Durian</li>');\n      const html = $fruits.html();\n      expect(html).toBe('<li class=\"durian\">Durian</li>');\n    });\n\n    it('(html) : should add new elements for each element in selection', () => {\n      const $fruits = $('li');\n      $fruits.html('<li class=\"durian\">Durian</li>');\n      let tested = 0;\n      $fruits.each(function () {\n        expect($(this).children().parent().get(0)).toBe(this);\n        tested++;\n      });\n      expect(tested).toBe(3);\n    });\n\n    it('(html) : should skip text nodes', () => {\n      const $text = load(mixedText);\n      const $body = $text($text('body')[0].children);\n\n      $body.html('test');\n\n      expect($text('body').html()).toBe('<a>test</a>TEXT<b>test</b>');\n    });\n\n    it('(elem) : should set the html for its children with element', () => {\n      $fruits.html($('<li class=\"durian\">Durian</li>'));\n      const html = $fruits.html();\n      expect(html).toBe('<li class=\"durian\">Durian</li>');\n    });\n\n    it('(elem) : should move the passed element (#940)', () => {\n      $('.apple').html($('.orange'));\n      expect($fruits.html()).toBe(\n        '<li class=\"apple\"><li class=\"orange\">Orange</li></li><li class=\"pear\">Pear</li>',\n      );\n    });\n\n    it('() : should allow element reinsertion', () => {\n      const $children = $fruits.children();\n\n      $fruits.html('<div></div><div></div>');\n      expect($fruits.children()).toHaveLength(2);\n\n      const $remove = $fruits.children().eq(0);\n\n      $remove.replaceWith($children);\n      expect($fruits.children()).toHaveLength(4);\n    });\n\n    it('(script value) : should add content as text', () => {\n      const $data = '<a><b>';\n      const $script = $('<script>').html($data) as Cheerio<Element>;\n\n      expect($script).toHaveLength(1);\n      expect($script[0].type).toBe('script');\n      expect($script[0]).toHaveProperty('name', 'script');\n\n      expect($script[0].children).toHaveLength(1);\n      expect($script[0].children[0].type).toBe('text');\n      expect($script[0].children[0]).toHaveProperty('data', $data);\n    });\n  });\n\n  describe('.toString', () => {\n    it('() : should get the outerHTML for an element', () => {\n      expect($fruits.toString()).toBe(fruits);\n    });\n\n    it('() : should return an html string for a set of elements', () => {\n      expect($fruits.find('li').toString()).toBe(\n        '<li class=\"apple\">Apple</li><li class=\"orange\">Orange</li><li class=\"pear\">Pear</li>',\n      );\n    });\n\n    it('() : should be called implicitly', () => {\n      const string = [$('<foo>'), $('<bar>'), $('<baz>')].join('');\n      expect(string).toBe('<foo></foo><bar></bar><baz></baz>');\n    });\n\n    it('() : should pass options', () => {\n      const dom = load('&', { xml: { decodeEntities: false } });\n      expect(dom.root().toString()).toBe('&');\n    });\n  });\n\n  describe('.text', () => {\n    it('() : gets the text for a single element', () => {\n      expect($('.apple').text()).toBe('Apple');\n    });\n\n    it('() : combines all text from children text nodes', () => {\n      expect($('#fruits').text()).toBe('AppleOrangePear');\n    });\n\n    it('(text) : sets the text for the child node', () => {\n      $('.apple').text('Granny Smith Apple');\n      expect($('.apple')[0].childNodes[0]).toHaveProperty(\n        'data',\n        'Granny Smith Apple',\n      );\n    });\n\n    it('(text) : inserts separate nodes for all children', () => {\n      $('li').text('Fruits');\n      let tested = 0;\n      $('li').each(function () {\n        expect(this.childNodes[0].parentNode).toBe(this);\n        tested++;\n      });\n      expect(tested).toBe(3);\n    });\n\n    it('(text) : should create a Node with the DOM level 1 API', () => {\n      const $apple = $('.apple');\n\n      $apple.text('anything');\n      const textNode = $apple[0].childNodes[0];\n\n      expect(textNode.parentNode).toBe($apple[0]);\n      expect(textNode.nodeType).toBe(3);\n      expect(textNode).toHaveProperty('data', 'anything');\n    });\n\n    it('(html) : should skip text nodes', () => {\n      const $text = load(mixedText);\n      const $body = $text($text('body')[0].children);\n\n      $body.text('test');\n\n      expect($text('body').html()).toBe('<a>test</a>TEXT<b>test</b>');\n    });\n\n    it('should allow functions as arguments', () => {\n      $('.apple').text((idx, content) => {\n        expect(idx).toBe(0);\n        expect(content).toBe('Apple');\n        return 'whatever mate';\n      });\n      expect($('.apple')[0].childNodes[0]).toHaveProperty(\n        'data',\n        'whatever mate',\n      );\n    });\n\n    it('should allow functions as arguments for multiple elements', () => {\n      $('li').text((idx) => `text${idx}`);\n      $('li').each(function (this, idx) {\n        expect(this.childNodes[0]).toHaveProperty('data', `text${idx}`);\n      });\n    });\n\n    it('should decode special chars', () => {\n      const text = $('<p>M&amp;M</p>').text();\n      expect(text).toBe('M&M');\n    });\n\n    it('should work with special chars added as strings', () => {\n      const text = $('<p>M&M</p>').text();\n      expect(text).toBe('M&M');\n    });\n\n    it('should turn passed values to strings', () => {\n      $('.apple').text(1 as never);\n      expect($('.apple')[0].childNodes[0]).toHaveProperty('data', '1');\n    });\n\n    it('( undefined ) : should act as an accessor', () => {\n      const $div = $('<div>test</div>');\n      expect(typeof $div.text(undefined as never)).toBe('string');\n      expect($div.text()).toBe('test');\n    });\n\n    it('( \"\" ) : should convert to string', () => {\n      const $div = $('<div>test</div>');\n      expect($div.text('').text()).toBe('');\n    });\n\n    it('( null ) : should convert to string', () => {\n      expect(\n        $('<div>')\n          .text(null as never)\n          .text(),\n      ).toBe('null');\n    });\n\n    it('( 0 ) : should convert to string', () => {\n      expect(\n        $('<div>')\n          .text(0 as never)\n          .text(),\n      ).toBe('0');\n    });\n\n    it('(str) should encode then decode unsafe characters', () => {\n      const $apple = $('.apple');\n\n      $apple.text('blah <script>alert(\"XSS!\")</script> blah');\n      expect($apple[0].childNodes[0]).toHaveProperty(\n        'data',\n        'blah <script>alert(\"XSS!\")</script> blah',\n      );\n      expect($apple.text()).toBe('blah <script>alert(\"XSS!\")</script> blah');\n\n      $apple.text('blah <script>alert(\"XSS!\")</script> blah');\n      expect($apple.html()).not.toContain('<script>alert(\"XSS!\")</script>');\n    });\n  });\n\n  describe('.clone', () => {\n    it('() : should return a copy', () => {\n      const $src = $(\n        '<div><span>foo</span><span>bar</span><span>baz</span></div>',\n      ).children();\n      const $elem = $src.clone();\n      expect($elem.length).toBe(3);\n      expect($elem.parent()).toHaveLength(0);\n      expect($elem.text()).toBe($src.text());\n      $src.text('rofl');\n      expect($elem.text()).not.toBe($src.text());\n    });\n\n    it('() : should return a copy of document', () => {\n      const $src = load('<html><body><div>foo</div>bar</body></html>')\n        .root()\n        .children();\n      const $elem = $src.clone();\n      expect($elem.length).toBe(1);\n      expect($elem.parent()).toHaveLength(0);\n      expect($elem.text()).toBe($src.text());\n      $src.text('rofl');\n      expect($elem.text()).not.toBe($src.text());\n    });\n\n    it('() : should preserve parsing options', () => {\n      const $ = load('<div>π</div>', { xml: { decodeEntities: false } });\n      const $div = $('div');\n\n      expect($div.text()).toBe($div.clone().text());\n    });\n  });\n});\n"
  },
  {
    "path": "src/api/manipulation.ts",
    "content": "/**\n * Methods for modifying the DOM structure.\n *\n * @module cheerio/manipulation\n */\n\nimport {\n  type AnyNode,\n  cloneNode,\n  Document,\n  type Element,\n  hasChildren,\n  isTag,\n  type ParentNode,\n  Text,\n} from 'domhandler';\nimport { removeElement } from 'domutils';\nimport { ElementType } from 'htmlparser2';\nimport type { Cheerio } from '../cheerio.js';\nimport { update as updateDOM } from '../parse.js';\nimport { text as staticText } from '../static.js';\nimport type { AcceptedElems, BasicAcceptedElems } from '../types.js';\nimport { domEach, isCheerio, isHtml } from '../utils.js';\n\n/**\n * Create an array of nodes, recursing into arrays and parsing strings if\n * necessary.\n *\n * @private\n * @category Manipulation\n * @param elem - Elements to make an array of.\n * @param clone - Optionally clone nodes.\n * @returns The array of nodes.\n */\nexport function _makeDomArray<T extends AnyNode>(\n  this: Cheerio<T>,\n  elem?: BasicAcceptedElems<AnyNode> | BasicAcceptedElems<AnyNode>[],\n  clone?: boolean,\n): AnyNode[] {\n  if (elem == null) {\n    return [];\n  }\n\n  if (typeof elem === 'string') {\n    return [...this._parse(elem, this.options, false, null).children];\n  }\n\n  if ('length' in elem) {\n    if (elem.length === 1) {\n      return this._makeDomArray(elem[0], clone);\n    }\n\n    const result: AnyNode[] = [];\n\n    for (let i = 0; i < elem.length; i++) {\n      const el = elem[i];\n\n      if (typeof el === 'object') {\n        if (el == null) {\n          continue;\n        }\n\n        if (!('length' in el)) {\n          result.push(clone ? cloneNode(el, true) : el);\n          continue;\n        }\n      }\n\n      result.push(...this._makeDomArray(el, clone));\n    }\n\n    return result;\n  }\n\n  return [clone ? cloneNode(elem, true) : elem];\n}\n\nfunction _insert(\n  concatenator: (\n    dom: AnyNode[],\n    children: AnyNode[],\n    parent: ParentNode,\n  ) => void,\n) {\n  return function <T extends AnyNode>(\n    this: Cheerio<T>,\n    ...elems:\n      | [\n          (\n            this: AnyNode,\n            i: number,\n            html: string,\n          ) => BasicAcceptedElems<AnyNode>,\n        ]\n      | BasicAcceptedElems<AnyNode>[]\n  ) {\n    const lastIdx = this.length - 1;\n\n    return domEach(this, (el, i) => {\n      if (!hasChildren(el)) return;\n\n      const domSrc =\n        typeof elems[0] === 'function'\n          ? elems[0].call(el, i, this._render(el.children))\n          : (elems as BasicAcceptedElems<AnyNode>[]);\n\n      const dom = this._makeDomArray(domSrc, i < lastIdx);\n      concatenator(dom, el.children, el);\n    });\n  };\n}\n\n/**\n * Modify an array in-place, removing some number of elements and adding new\n * elements directly following them.\n *\n * @private\n * @category Manipulation\n * @param array - Target array to splice.\n * @param spliceIdx - Index at which to begin changing the array.\n * @param spliceCount - Number of elements to remove from the array.\n * @param newElems - Elements to insert into the array.\n * @param parent - The parent of the node.\n * @returns The spliced array.\n */\nfunction uniqueSplice(\n  array: AnyNode[],\n  spliceIdx: number,\n  spliceCount: number,\n  newElems: AnyNode[],\n  parent: ParentNode,\n): AnyNode[] {\n  const spliceArgs: Parameters<AnyNode[]['splice']> = [\n    spliceIdx,\n    spliceCount,\n    ...newElems,\n  ];\n  const prev = spliceIdx === 0 ? null : array[spliceIdx - 1];\n  const next =\n    spliceIdx + spliceCount >= array.length\n      ? null\n      : array[spliceIdx + spliceCount];\n\n  /*\n   * Before splicing in new elements, ensure they do not already appear in the\n   * current array.\n   */\n  for (let idx = 0; idx < newElems.length; ++idx) {\n    const node = newElems[idx];\n    const oldParent = node.parent;\n\n    if (oldParent) {\n      const oldSiblings: AnyNode[] = oldParent.children;\n      const prevIdx = oldSiblings.indexOf(node);\n\n      if (prevIdx !== -1) {\n        oldParent.children.splice(prevIdx, 1);\n        if (parent === oldParent && spliceIdx > prevIdx) {\n          spliceArgs[0]--;\n        }\n      }\n    }\n\n    node.parent = parent;\n\n    if (node.prev) {\n      node.prev.next = node.next ?? null;\n    }\n\n    if (node.next) {\n      node.next.prev = node.prev ?? null;\n    }\n\n    node.prev = idx === 0 ? prev : newElems[idx - 1];\n    node.next = idx === newElems.length - 1 ? next : newElems[idx + 1];\n  }\n\n  if (prev) {\n    prev.next = newElems[0];\n  }\n  if (next) {\n    next.prev = newElems[newElems.length - 1];\n  }\n  return array.splice(...spliceArgs);\n}\n\n/**\n * Insert every element in the set of matched elements to the end of the target.\n *\n * @category Manipulation\n * @example\n *\n * ```js\n * $('<li class=\"plum\">Plum</li>').appendTo('#fruits');\n * $.html();\n * //=>  <ul id=\"fruits\">\n * //      <li class=\"apple\">Apple</li>\n * //      <li class=\"orange\">Orange</li>\n * //      <li class=\"pear\">Pear</li>\n * //      <li class=\"plum\">Plum</li>\n * //    </ul>\n * ```\n *\n * @param target - Element to append elements to.\n * @returns The instance itself.\n * @see {@link https://api.jquery.com/appendTo/}\n */\nexport function appendTo<T extends AnyNode>(\n  this: Cheerio<T>,\n  target: BasicAcceptedElems<AnyNode>,\n): Cheerio<T> {\n  const appendTarget = isCheerio<T>(target) ? target : this._make(target);\n\n  appendTarget.append(this);\n\n  return this;\n}\n\n/**\n * Insert every element in the set of matched elements to the beginning of the\n * target.\n *\n * @category Manipulation\n * @example\n *\n * ```js\n * $('<li class=\"plum\">Plum</li>').prependTo('#fruits');\n * $.html();\n * //=>  <ul id=\"fruits\">\n * //      <li class=\"plum\">Plum</li>\n * //      <li class=\"apple\">Apple</li>\n * //      <li class=\"orange\">Orange</li>\n * //      <li class=\"pear\">Pear</li>\n * //    </ul>\n * ```\n *\n * @param target - Element to prepend elements to.\n * @returns The instance itself.\n * @see {@link https://api.jquery.com/prependTo/}\n */\nexport function prependTo<T extends AnyNode>(\n  this: Cheerio<T>,\n  target: BasicAcceptedElems<AnyNode>,\n): Cheerio<T> {\n  const prependTarget = isCheerio<T>(target) ? target : this._make(target);\n\n  prependTarget.prepend(this);\n\n  return this;\n}\n\n/**\n * Inserts content as the _last_ child of each of the selected elements.\n *\n * @category Manipulation\n * @example\n *\n * ```js\n * $('ul').append('<li class=\"plum\">Plum</li>');\n * $.html();\n * //=>  <ul id=\"fruits\">\n * //      <li class=\"apple\">Apple</li>\n * //      <li class=\"orange\">Orange</li>\n * //      <li class=\"pear\">Pear</li>\n * //      <li class=\"plum\">Plum</li>\n * //    </ul>\n * ```\n *\n * @see {@link https://api.jquery.com/append/}\n */\nexport const append: <T extends AnyNode>(\n  this: Cheerio<T>,\n  ...elems:\n    | [(this: AnyNode, i: number, html: string) => BasicAcceptedElems<AnyNode>]\n    | BasicAcceptedElems<AnyNode>[]\n) => Cheerio<T> = _insert((dom, children, parent) => {\n  uniqueSplice(children, children.length, 0, dom, parent);\n});\n\n/**\n * Inserts content as the _first_ child of each of the selected elements.\n *\n * @category Manipulation\n * @example\n *\n * ```js\n * $('ul').prepend('<li class=\"plum\">Plum</li>');\n * $.html();\n * //=>  <ul id=\"fruits\">\n * //      <li class=\"plum\">Plum</li>\n * //      <li class=\"apple\">Apple</li>\n * //      <li class=\"orange\">Orange</li>\n * //      <li class=\"pear\">Pear</li>\n * //    </ul>\n * ```\n *\n * @see {@link https://api.jquery.com/prepend/}\n */\nexport const prepend: <T extends AnyNode>(\n  this: Cheerio<T>,\n  ...elems:\n    | [(this: AnyNode, i: number, html: string) => BasicAcceptedElems<AnyNode>]\n    | BasicAcceptedElems<AnyNode>[]\n) => Cheerio<T> = _insert((dom, children, parent) => {\n  uniqueSplice(children, 0, 0, dom, parent);\n});\n\nfunction _wrap(\n  insert: (\n    el: AnyNode,\n    elInsertLocation: ParentNode,\n    wrapperDom: ParentNode[],\n  ) => void,\n) {\n  return function <T extends AnyNode>(\n    this: Cheerio<T>,\n    wrapper: AcceptedElems<AnyNode>,\n  ) {\n    const lastIdx = this.length - 1;\n    const lastParent = this.parents().last();\n\n    for (let i = 0; i < this.length; i++) {\n      const el = this[i];\n\n      const wrap =\n        typeof wrapper === 'function'\n          ? wrapper.call(el, i, el)\n          : typeof wrapper === 'string' && !isHtml(wrapper)\n            ? lastParent.find(wrapper).clone()\n            : wrapper;\n\n      const [wrapperDom] = this._makeDomArray(wrap, i < lastIdx);\n\n      if (!(wrapperDom && hasChildren(wrapperDom))) continue;\n\n      let elInsertLocation = wrapperDom;\n\n      /*\n       * Find the deepest child. Only consider the first tag child of each node\n       * (ignore text); stop if no children are found.\n       */\n      let j = 0;\n\n      while (j < elInsertLocation.children.length) {\n        const child = elInsertLocation.children[j];\n        if (isTag(child)) {\n          elInsertLocation = child;\n          j = 0;\n        } else {\n          j++;\n        }\n      }\n\n      insert(el, elInsertLocation, [wrapperDom]);\n    }\n\n    return this;\n  };\n}\n\n/**\n * The .wrap() function can take any string or object that could be passed to\n * the $() factory function to specify a DOM structure. This structure may be\n * nested several levels deep, but should contain only one inmost element. A\n * copy of this structure will be wrapped around each of the elements in the set\n * of matched elements. This method returns the original set of elements for\n * chaining purposes.\n *\n * @category Manipulation\n * @example\n *\n * ```js\n * const redFruit = $('<div class=\"red-fruit\"></div>');\n * $('.apple').wrap(redFruit);\n *\n * //=> <ul id=\"fruits\">\n * //     <div class=\"red-fruit\">\n * //      <li class=\"apple\">Apple</li>\n * //     </div>\n * //     <li class=\"orange\">Orange</li>\n * //     <li class=\"plum\">Plum</li>\n * //   </ul>\n *\n * const healthy = $('<div class=\"healthy\"></div>');\n * $('li').wrap(healthy);\n *\n * //=> <ul id=\"fruits\">\n * //     <div class=\"healthy\">\n * //       <li class=\"apple\">Apple</li>\n * //     </div>\n * //     <div class=\"healthy\">\n * //       <li class=\"orange\">Orange</li>\n * //     </div>\n * //     <div class=\"healthy\">\n * //        <li class=\"plum\">Plum</li>\n * //     </div>\n * //   </ul>\n * ```\n *\n * @param wrapper - The DOM structure to wrap around each element in the\n *   selection.\n * @see {@link https://api.jquery.com/wrap/}\n */\nexport const wrap: <T extends AnyNode>(\n  this: Cheerio<T>,\n  wrapper: AcceptedElems<AnyNode>,\n) => Cheerio<T> = _wrap((el, elInsertLocation, wrapperDom) => {\n  const { parent } = el;\n\n  if (!parent) return;\n\n  const siblings: AnyNode[] = parent.children;\n  const index = siblings.indexOf(el);\n\n  updateDOM([el], elInsertLocation);\n  /*\n   * The previous operation removed the current element from the `siblings`\n   * array, so the `dom` array can be inserted without removing any\n   * additional elements.\n   */\n  uniqueSplice(siblings, index, 0, wrapperDom, parent);\n});\n\n/**\n * The .wrapInner() function can take any string or object that could be passed\n * to the $() factory function to specify a DOM structure. This structure may be\n * nested several levels deep, but should contain only one inmost element. The\n * structure will be wrapped around the content of each of the elements in the\n * set of matched elements.\n *\n * @category Manipulation\n * @example\n *\n * ```js\n * const redFruit = $('<div class=\"red-fruit\"></div>');\n * $('.apple').wrapInner(redFruit);\n *\n * //=> <ul id=\"fruits\">\n * //     <li class=\"apple\">\n * //       <div class=\"red-fruit\">Apple</div>\n * //     </li>\n * //     <li class=\"orange\">Orange</li>\n * //     <li class=\"pear\">Pear</li>\n * //   </ul>\n *\n * const healthy = $('<div class=\"healthy\"></div>');\n * $('li').wrapInner(healthy);\n *\n * //=> <ul id=\"fruits\">\n * //     <li class=\"apple\">\n * //       <div class=\"healthy\">Apple</div>\n * //     </li>\n * //     <li class=\"orange\">\n * //       <div class=\"healthy\">Orange</div>\n * //     </li>\n * //     <li class=\"pear\">\n * //       <div class=\"healthy\">Pear</div>\n * //     </li>\n * //   </ul>\n * ```\n *\n * @param wrapper - The DOM structure to wrap around the content of each element\n *   in the selection.\n * @returns The instance itself, for chaining.\n * @see {@link https://api.jquery.com/wrapInner/}\n */\nexport const wrapInner: <T extends AnyNode>(\n  this: Cheerio<T>,\n  wrapper: AcceptedElems<AnyNode>,\n) => Cheerio<T> = _wrap((el, elInsertLocation, wrapperDom) => {\n  if (!hasChildren(el)) return;\n  updateDOM(el.children, elInsertLocation);\n  updateDOM(wrapperDom, el);\n});\n\n/**\n * The .unwrap() function, removes the parents of the set of matched elements\n * from the DOM, leaving the matched elements in their place.\n *\n * @category Manipulation\n * @example <caption>without selector</caption>\n *\n * ```js\n * const $ = cheerio.load(\n *   '<div id=test>\\n  <div><p>Hello</p></div>\\n  <div><p>World</p></div>\\n</div>',\n * );\n * $('#test p').unwrap();\n *\n * //=> <div id=test>\n * //     <p>Hello</p>\n * //     <p>World</p>\n * //   </div>\n * ```\n *\n * @example <caption>with selector</caption>\n *\n * ```js\n * const $ = cheerio.load(\n *   '<div id=test>\\n  <p>Hello</p>\\n  <b><p>World</p></b>\\n</div>',\n * );\n * $('#test p').unwrap('b');\n *\n * //=> <div id=test>\n * //     <p>Hello</p>\n * //     <p>World</p>\n * //   </div>\n * ```\n *\n * @param selector - A selector to check the parent element against. If an\n *   element's parent does not match the selector, the element won't be\n *   unwrapped.\n * @returns The instance itself, for chaining.\n * @see {@link https://api.jquery.com/unwrap/}\n */\nexport function unwrap<T extends AnyNode>(\n  this: Cheerio<T>,\n  selector?: string,\n): Cheerio<T> {\n  this.parent(selector)\n    .not('body')\n    .each((_, el) => {\n      this._make(el).replaceWith(el.children);\n    });\n  return this;\n}\n\n/**\n * The .wrapAll() function can take any string or object that could be passed to\n * the $() function to specify a DOM structure. This structure may be nested\n * several levels deep, but should contain only one inmost element. The\n * structure will be wrapped around all of the elements in the set of matched\n * elements, as a single group.\n *\n * @category Manipulation\n * @example <caption>With markup passed to `wrapAll`</caption>\n *\n * ```js\n * const $ = cheerio.load(\n *   '<div class=\"container\"><div class=\"inner\">First</div><div class=\"inner\">Second</div></div>',\n * );\n * $('.inner').wrapAll(\"<div class='new'></div>\");\n *\n * //=> <div class=\"container\">\n * //     <div class='new'>\n * //       <div class=\"inner\">First</div>\n * //       <div class=\"inner\">Second</div>\n * //     </div>\n * //   </div>\n * ```\n *\n * @example <caption>With an existing cheerio instance</caption>\n *\n * ```js\n * const $ = cheerio.load(\n *   '<span>Span 1</span><strong>Strong</strong><span>Span 2</span>',\n * );\n * const wrap = $('<div><p><em><b></b></em></p></div>');\n * $('span').wrapAll(wrap);\n *\n * //=> <div>\n * //     <p>\n * //       <em>\n * //         <b>\n * //           <span>Span 1</span>\n * //           <span>Span 2</span>\n * //         </b>\n * //       </em>\n * //     </p>\n * //   </div>\n * //   <strong>Strong</strong>\n * ```\n *\n * @param wrapper - The DOM structure to wrap around all matched elements in the\n *   selection.\n * @returns The instance itself.\n * @see {@link https://api.jquery.com/wrapAll/}\n */\nexport function wrapAll<T extends AnyNode>(\n  this: Cheerio<T>,\n  wrapper: AcceptedElems<T>,\n): Cheerio<T> {\n  const el = this[0];\n  if (el) {\n    const wrap: Cheerio<AnyNode> = this._make(\n      typeof wrapper === 'function' ? wrapper.call(el, 0, el) : wrapper,\n    ).insertBefore(el);\n\n    // If html is given as wrapper, wrap may contain text elements\n    let elInsertLocation: Element | undefined;\n\n    for (let i = 0; i < wrap.length; i++) {\n      if (wrap[i].type === ElementType.Tag) {\n        elInsertLocation = wrap[i] as Element;\n      }\n    }\n\n    let j = 0;\n\n    /*\n     * Find the deepest child. Only consider the first tag child of each node\n     * (ignore text); stop if no children are found.\n     */\n    while (elInsertLocation && j < elInsertLocation.children.length) {\n      const child = elInsertLocation.children[j];\n      if (child.type === ElementType.Tag) {\n        elInsertLocation = child;\n        j = 0;\n      } else {\n        j++;\n      }\n    }\n\n    if (elInsertLocation) this._make(elInsertLocation).append(this);\n  }\n  return this;\n}\n\n/**\n * Insert content next to each element in the set of matched elements.\n *\n * @category Manipulation\n * @example\n *\n * ```js\n * $('.apple').after('<li class=\"plum\">Plum</li>');\n * $.html();\n * //=>  <ul id=\"fruits\">\n * //      <li class=\"apple\">Apple</li>\n * //      <li class=\"plum\">Plum</li>\n * //      <li class=\"orange\">Orange</li>\n * //      <li class=\"pear\">Pear</li>\n * //    </ul>\n * ```\n *\n * @param elems - HTML string, DOM element, array of DOM elements or Cheerio to\n *   insert after each element in the set of matched elements.\n * @returns The instance itself.\n * @see {@link https://api.jquery.com/after/}\n */\nexport function after<T extends AnyNode>(\n  this: Cheerio<T>,\n  ...elems:\n    | [(this: AnyNode, i: number, html: string) => BasicAcceptedElems<AnyNode>]\n    | BasicAcceptedElems<AnyNode>[]\n): Cheerio<T> {\n  const lastIdx = this.length - 1;\n\n  return domEach(this, (el, i) => {\n    if (!(hasChildren(el) && el.parent)) {\n      return;\n    }\n\n    const siblings: AnyNode[] = el.parent.children;\n    const index = siblings.indexOf(el);\n\n    // If not found, move on\n    /* istanbul ignore next */\n    if (index === -1) return;\n\n    const domSrc =\n      typeof elems[0] === 'function'\n        ? elems[0].call(el, i, this._render(el.children))\n        : (elems as BasicAcceptedElems<AnyNode>[]);\n\n    const dom = this._makeDomArray(domSrc, i < lastIdx);\n\n    // Add element after `this` element\n    uniqueSplice(siblings, index + 1, 0, dom, el.parent);\n  });\n}\n\n/**\n * Insert every element in the set of matched elements after the target.\n *\n * @category Manipulation\n * @example\n *\n * ```js\n * $('<li class=\"plum\">Plum</li>').insertAfter('.apple');\n * $.html();\n * //=>  <ul id=\"fruits\">\n * //      <li class=\"apple\">Apple</li>\n * //      <li class=\"plum\">Plum</li>\n * //      <li class=\"orange\">Orange</li>\n * //      <li class=\"pear\">Pear</li>\n * //    </ul>\n * ```\n *\n * @param target - Element to insert elements after.\n * @returns The set of newly inserted elements.\n * @see {@link https://api.jquery.com/insertAfter/}\n */\nexport function insertAfter<T extends AnyNode>(\n  this: Cheerio<T>,\n  target: BasicAcceptedElems<AnyNode>,\n): Cheerio<T> {\n  if (typeof target === 'string') {\n    target = this._make<AnyNode>(target);\n  }\n\n  this.remove();\n\n  const clones: T[] = [];\n\n  for (const el of this._makeDomArray(target)) {\n    const clonedSelf = this.clone().toArray();\n    const { parent } = el;\n    if (!parent) {\n      continue;\n    }\n\n    const siblings: AnyNode[] = parent.children;\n    const index = siblings.indexOf(el);\n\n    // If not found, move on\n    /* istanbul ignore next */\n    if (index === -1) continue;\n\n    // Add cloned `this` element(s) after target element\n    uniqueSplice(siblings, index + 1, 0, clonedSelf, parent);\n    clones.push(...clonedSelf);\n  }\n\n  return this._make(clones);\n}\n\n/**\n * Insert content previous to each element in the set of matched elements.\n *\n * @category Manipulation\n * @example\n *\n * ```js\n * $('.apple').before('<li class=\"plum\">Plum</li>');\n * $.html();\n * //=>  <ul id=\"fruits\">\n * //      <li class=\"plum\">Plum</li>\n * //      <li class=\"apple\">Apple</li>\n * //      <li class=\"orange\">Orange</li>\n * //      <li class=\"pear\">Pear</li>\n * //    </ul>\n * ```\n *\n * @param elems - HTML string, DOM element, array of DOM elements or Cheerio to\n *   insert before each element in the set of matched elements.\n * @returns The instance itself.\n * @see {@link https://api.jquery.com/before/}\n */\nexport function before<T extends AnyNode>(\n  this: Cheerio<T>,\n  ...elems:\n    | [(this: AnyNode, i: number, html: string) => BasicAcceptedElems<AnyNode>]\n    | BasicAcceptedElems<AnyNode>[]\n): Cheerio<T> {\n  const lastIdx = this.length - 1;\n\n  return domEach(this, (el, i) => {\n    if (!(hasChildren(el) && el.parent)) {\n      return;\n    }\n\n    const siblings: AnyNode[] = el.parent.children;\n    const index = siblings.indexOf(el);\n\n    // If not found, move on\n    /* istanbul ignore next */\n    if (index === -1) return;\n\n    const domSrc =\n      typeof elems[0] === 'function'\n        ? elems[0].call(el, i, this._render(el.children))\n        : (elems as BasicAcceptedElems<AnyNode>[]);\n\n    const dom = this._makeDomArray(domSrc, i < lastIdx);\n\n    // Add element before `el` element\n    uniqueSplice(siblings, index, 0, dom, el.parent);\n  });\n}\n\n/**\n * Insert every element in the set of matched elements before the target.\n *\n * @category Manipulation\n * @example\n *\n * ```js\n * $('<li class=\"plum\">Plum</li>').insertBefore('.apple');\n * $.html();\n * //=>  <ul id=\"fruits\">\n * //      <li class=\"plum\">Plum</li>\n * //      <li class=\"apple\">Apple</li>\n * //      <li class=\"orange\">Orange</li>\n * //      <li class=\"pear\">Pear</li>\n * //    </ul>\n * ```\n *\n * @param target - Element to insert elements before.\n * @returns The set of newly inserted elements.\n * @see {@link https://api.jquery.com/insertBefore/}\n */\nexport function insertBefore<T extends AnyNode>(\n  this: Cheerio<T>,\n  target: BasicAcceptedElems<AnyNode>,\n): Cheerio<T> {\n  const targetArr = this._make<AnyNode>(target);\n\n  this.remove();\n\n  const clones: T[] = [];\n\n  domEach(targetArr, (el) => {\n    const clonedSelf = this.clone().toArray();\n    const { parent } = el;\n    if (!parent) {\n      return;\n    }\n\n    const siblings: AnyNode[] = parent.children;\n    const index = siblings.indexOf(el);\n\n    // If not found, move on\n    /* istanbul ignore next */\n    if (index === -1) return;\n\n    // Add cloned `this` element(s) after target element\n    uniqueSplice(siblings, index, 0, clonedSelf, parent);\n    clones.push(...clonedSelf);\n  });\n\n  return this._make(clones);\n}\n\n/**\n * Removes the set of matched elements from the DOM and all their children.\n * `selector` filters the set of matched elements to be removed.\n *\n * @category Manipulation\n * @example\n *\n * ```js\n * $('.pear').remove();\n * $.html();\n * //=>  <ul id=\"fruits\">\n * //      <li class=\"apple\">Apple</li>\n * //      <li class=\"orange\">Orange</li>\n * //    </ul>\n * ```\n *\n * @param selector - Optional selector for elements to remove.\n * @returns The instance itself.\n * @see {@link https://api.jquery.com/remove/}\n */\nexport function remove<T extends AnyNode>(\n  this: Cheerio<T>,\n  selector?: string,\n): Cheerio<T> {\n  // Filter if we have selector\n  const elems = selector ? this.filter(selector) : this;\n\n  domEach(elems, (el) => {\n    removeElement(el);\n    el.prev = el.next = el.parent = null;\n  });\n\n  return this;\n}\n\n/**\n * Replaces matched elements with `content`.\n *\n * @category Manipulation\n * @example\n *\n * ```js\n * const plum = $('<li class=\"plum\">Plum</li>');\n * $('.pear').replaceWith(plum);\n * $.html();\n * //=> <ul id=\"fruits\">\n * //     <li class=\"apple\">Apple</li>\n * //     <li class=\"orange\">Orange</li>\n * //     <li class=\"plum\">Plum</li>\n * //   </ul>\n * ```\n *\n * @param content - Replacement for matched elements.\n * @returns The instance itself.\n * @see {@link https://api.jquery.com/replaceWith/}\n */\nexport function replaceWith<T extends AnyNode>(\n  this: Cheerio<T>,\n  content: AcceptedElems<AnyNode>,\n): Cheerio<T> {\n  return domEach(this, (el, i) => {\n    const { parent } = el;\n    if (!parent) {\n      return;\n    }\n\n    const siblings: AnyNode[] = parent.children;\n    const cont =\n      typeof content === 'function' ? content.call(el, i, el) : content;\n    const dom = this._makeDomArray(cont);\n\n    /*\n     * In the case that `dom` contains nodes that already exist in other\n     * structures, ensure those nodes are properly removed.\n     */\n    updateDOM(dom, null);\n\n    const index = siblings.indexOf(el);\n\n    // Completely remove old element\n    uniqueSplice(siblings, index, 1, dom, parent);\n\n    if (!dom.includes(el)) {\n      el.parent = el.prev = el.next = null;\n    }\n  });\n}\n\n/**\n * Removes all children from each item in the selection. Text nodes and comment\n * nodes are left as is.\n *\n * @category Manipulation\n * @example\n *\n * ```js\n * $('ul').empty();\n * $.html();\n * //=>  <ul id=\"fruits\"></ul>\n * ```\n *\n * @returns The instance itself.\n * @see {@link https://api.jquery.com/empty/}\n */\nexport function empty<T extends AnyNode>(this: Cheerio<T>): Cheerio<T> {\n  return domEach(this, (el) => {\n    if (!hasChildren(el)) return;\n    for (const child of el.children) {\n      child.next = child.prev = child.parent = null;\n    }\n\n    el.children.length = 0;\n  });\n}\n\n/**\n * Gets an HTML content string from the first selected element.\n *\n * @category Manipulation\n * @example\n *\n * ```js\n * $('.orange').html();\n * //=> Orange\n *\n * $('#fruits').html('<li class=\"mango\">Mango</li>').html();\n * //=> <li class=\"mango\">Mango</li>\n * ```\n *\n * @returns The HTML content string.\n * @see {@link https://api.jquery.com/html/}\n */\nexport function html<T extends AnyNode>(this: Cheerio<T>): string | null;\n/**\n * Replaces each selected element's content with the specified content.\n *\n * @category Manipulation\n * @example\n *\n * ```js\n * $('.orange').html('<li class=\"mango\">Mango</li>').html();\n * //=> <li class=\"mango\">Mango</li>\n * ```\n *\n * @param str - The content to replace selection's contents with.\n * @returns The instance itself.\n * @see {@link https://api.jquery.com/html/}\n */\nexport function html<T extends AnyNode>(\n  this: Cheerio<T>,\n  str: string | Cheerio<T>,\n): Cheerio<T>;\nexport function html<T extends AnyNode>(\n  this: Cheerio<T>,\n  str?: string | Cheerio<AnyNode>,\n): Cheerio<T> | string | null {\n  if (str === undefined) {\n    const el = this[0];\n    if (!(el && hasChildren(el))) return null;\n    return this._render(el.children);\n  }\n\n  return domEach(this, (el) => {\n    if (!hasChildren(el)) return;\n    for (const child of el.children) {\n      child.next = child.prev = child.parent = null;\n    }\n\n    const content = isCheerio(str)\n      ? str.toArray()\n      : this._parse(`${str}`, this.options, false, el).children;\n\n    updateDOM(content, el);\n  });\n}\n\n/**\n * Turns the collection to a string. Alias for `.html()`.\n *\n * @category Manipulation\n * @returns The rendered document.\n */\nexport function toString<T extends AnyNode>(this: Cheerio<T>): string {\n  return this._render(this);\n}\n\n/**\n * Get the combined text contents of each element in the set of matched\n * elements, including their descendants.\n *\n * @category Manipulation\n * @example\n *\n * ```js\n * $('.orange').text();\n * //=> Orange\n *\n * $('ul').text();\n * //=>  Apple\n * //    Orange\n * //    Pear\n * ```\n *\n * @returns The text contents of the collection.\n * @see {@link https://api.jquery.com/text/}\n */\nexport function text<T extends AnyNode>(this: Cheerio<T>): string;\n/**\n * Set the content of each element in the set of matched elements to the\n * specified text.\n *\n * @category Manipulation\n * @example\n *\n * ```js\n * $('.orange').text('Orange');\n * //=> <div class=\"orange\">Orange</div>\n * ```\n *\n * @param str - The text to set as the content of each matched element.\n * @returns The instance itself.\n * @see {@link https://api.jquery.com/text/}\n */\nexport function text<T extends AnyNode>(\n  this: Cheerio<T>,\n  str: string | ((this: AnyNode, i: number, text: string) => string),\n): Cheerio<T>;\nexport function text<T extends AnyNode>(\n  this: Cheerio<T>,\n  str?: string | ((this: AnyNode, i: number, text: string) => string),\n): Cheerio<T> | string {\n  // If `str` is undefined, act as a \"getter\"\n  if (str === undefined) {\n    return staticText(this);\n  }\n  if (typeof str === 'function') {\n    // Function support\n    return domEach(this, (el, i) =>\n      this._make(el).text(str.call(el, i, staticText([el]))),\n    );\n  }\n\n  // Append text node to each selected elements\n  return domEach(this, (el) => {\n    if (!hasChildren(el)) return;\n    for (const child of el.children) {\n      child.next = child.prev = child.parent = null;\n    }\n\n    const textNode = new Text(`${str}`);\n\n    updateDOM(textNode, el);\n  });\n}\n\n/**\n * Clone the cheerio object.\n *\n * @category Manipulation\n * @example\n *\n * ```js\n * const moreFruit = $('#fruits').clone();\n * ```\n *\n * @returns The cloned object.\n * @see {@link https://api.jquery.com/clone/}\n */\nexport function clone<T extends AnyNode>(this: Cheerio<T>): Cheerio<T> {\n  const clone = Array.prototype.map.call(\n    this.get(),\n    (el) => cloneNode(el, true) as T,\n  ) as T[];\n\n  // Add a root node around the cloned nodes\n  const root = new Document(clone);\n  for (const node of clone) {\n    node.parent = root;\n  }\n\n  return this._make(clone);\n}\n"
  },
  {
    "path": "src/api/traversing.spec.ts",
    "content": "import { type AnyNode, type Element, isText, type Text } from 'domhandler';\nimport { beforeEach, describe, expect, it } from 'vitest';\nimport {\n  cheerio,\n  drinks,\n  eleven,\n  food,\n  forms,\n  fruits,\n  mixedText,\n  text,\n  vegetables,\n} from '../__fixtures__/fixtures.js';\nimport { Cheerio } from '../cheerio.js';\nimport { type CheerioAPI, load } from '../index.js';\n\nfunction getText(el: Cheerio<Element>) {\n  if (el.length === 0) return;\n  const [firstChild] = el[0].childNodes;\n  return isText(firstChild) ? firstChild.data : undefined;\n}\n\ndescribe('$(...)', () => {\n  let $: CheerioAPI;\n\n  beforeEach(() => {\n    $ = load(fruits);\n  });\n\n  describe('.load', () => {\n    it('should throw a TypeError if given invalid input', () => {\n      expect(() => {\n        // @ts-expect-error Testing invalid input\n        load();\n      }).toThrow('cheerio.load() expects a string');\n    });\n  });\n\n  describe('.find', () => {\n    it('() : should find nothing', () => {\n      expect($('ul').find()).toHaveLength(0);\n    });\n\n    it('(single) : should find one descendant', () => {\n      expect($('#fruits').find('.apple')[0].attribs).toHaveProperty(\n        'class',\n        'apple',\n      );\n    });\n\n    // #1679 - text tags not filtered\n    it('(single) : should filter out text nodes', () => {\n      const $root = $(`<html>\\n${fruits.replace(/></g, '>\\n<')}\\n</html>`);\n      expect($root.find('.apple')[0].attribs).toHaveProperty('class', 'apple');\n    });\n\n    it('(many) : should find all matching descendant', () => {\n      expect($('#fruits').find('li')).toHaveLength(3);\n    });\n\n    it('(many) : should merge all selected elems with matching descendants', () => {\n      expect($('#fruits, #food', food).find('.apple')).toHaveLength(1);\n    });\n\n    it('(invalid single) : should return empty if cant find', () => {\n      expect($('ul').find('blah')).toHaveLength(0);\n    });\n\n    it('(invalid single) : should query descendants only', () => {\n      expect($('#fruits').find('ul')).toHaveLength(0);\n    });\n\n    it('should return empty if search already empty result', () => {\n      expect($('#not-fruits').find('li')).toHaveLength(0);\n    });\n\n    it('should lowercase selectors', () => {\n      expect($('#fruits').find('LI')).toHaveLength(3);\n    });\n\n    it('should query immediate descendant only', () => {\n      const q = load('<foo><bar><bar></bar><bar></bar></bar></foo>');\n      expect(q('foo').find('> bar')).toHaveLength(1);\n    });\n\n    it('should find siblings', () => {\n      const q = load('<p class=a><p class=b></p>');\n      expect(q('.a').find('+.b')).toHaveLength(1);\n      expect(q('.a').find('~.b')).toHaveLength(1);\n      expect(q('.a').find('+.a')).toHaveLength(0);\n      expect(q('.a').find('~.a')).toHaveLength(0);\n    });\n\n    it('should find self', () => {\n      const q = load('<p class=a></p>');\n      expect(q('.a').find(':scope')).toHaveLength(1);\n    });\n\n    it('should query case-sensitively when in xml mode', () => {\n      const q = load('<caseSenSitive allTheWay>', { xml: true });\n      expect(q('caseSenSitive')).toHaveLength(1);\n      expect(q('[allTheWay]')).toHaveLength(1);\n      expect(q('casesensitive')).toHaveLength(0);\n      expect(q('[alltheway]')).toHaveLength(0);\n    });\n\n    it('should throw an Error if given an invalid selector', () => {\n      expect(() => {\n        $('#fruits').find(':bah');\n      }).toThrow('Unknown pseudo-class :bah');\n    });\n\n    it('should respect the `lowerCaseTags` option (#3495)', () => {\n      const q = load(\n        `<parentTag class=\"myClass\">\n          <firstTag> <child> blah </child> </firstTag>\n          <secondTag> <child> blah </child> </secondTag>\n        </parentTag> `,\n        {\n          xml: {\n            xmlMode: true,\n            decodeEntities: false,\n            lowerCaseTags: true,\n            lowerCaseAttributeNames: false,\n            recognizeSelfClosing: true,\n          },\n        },\n      );\n      expect(q('.myClass').find('firstTag > child')).toHaveLength(1);\n    });\n\n    describe('(cheerio object) :', () => {\n      it('returns only those nodes contained within the current selection', () => {\n        const q = load(food);\n        const $selection = q('#fruits').find(q('li'));\n\n        expect($selection).toHaveLength(3);\n        expect($selection[0]).toBe(q('.apple')[0]);\n        expect($selection[1]).toBe(q('.orange')[0]);\n        expect($selection[2]).toBe(q('.pear')[0]);\n      });\n      it('returns only those nodes contained within any element in the current selection', () => {\n        const q = load(food);\n        const $selection = q('.apple, #vegetables').find(q('li'));\n\n        expect($selection).toHaveLength(2);\n        expect($selection[0]).toBe(q('.carrot')[0]);\n        expect($selection[1]).toBe(q('.sweetcorn')[0]);\n      });\n    });\n\n    describe('(node) :', () => {\n      it('returns node when contained within the current selection', () => {\n        const q = load(food);\n        const $selection = q('#fruits').find(q('.apple')[0]);\n\n        expect($selection).toHaveLength(1);\n        expect($selection[0]).toBe(q('.apple')[0]);\n      });\n      it('returns node when contained within any element the current selection', () => {\n        const q = load(food);\n        const $selection = q('#fruits, #vegetables').find(q('.carrot')[0]);\n\n        expect($selection).toHaveLength(1);\n        expect($selection[0]).toBe(q('.carrot')[0]);\n      });\n      it('does not return node that is not contained within the current selection', () => {\n        const q = load(food);\n        const $selection = q('#fruits').find(q('.carrot')[0]);\n\n        expect($selection).toHaveLength(0);\n      });\n    });\n  });\n\n  describe('.children', () => {\n    it('() : should get all children', () => {\n      expect($('ul').children()).toHaveLength(3);\n    });\n\n    it('() : should skip text nodes', () => {\n      expect($(mixedText).children()).toHaveLength(0);\n    });\n\n    it('() : should return children of all matched elements', () => {\n      expect($('ul ul', food).children()).toHaveLength(5);\n    });\n\n    it('(selector) : should return children matching selector', () => {\n      const { attribs } = $('ul').children('.orange')[0];\n      expect(attribs).toHaveProperty('class', 'orange');\n    });\n\n    it('(invalid selector) : should return empty', () => {\n      expect($('ul').children('.lulz')).toHaveLength(0);\n    });\n\n    it('should only match immediate children, not ancestors', () => {\n      expect($(food).children('li')).toHaveLength(0);\n    });\n  });\n\n  describe('.contents', () => {\n    beforeEach(() => {\n      $ = load(text);\n    });\n\n    it('() : should get all contents', () => {\n      expect($('p').contents()).toHaveLength(5);\n    });\n\n    it('() : should skip text nodes', () => {\n      expect($(mixedText).contents()).toHaveLength(2);\n    });\n\n    it('() : should include text nodes', () => {\n      expect($('p').contents().first()[0].type).toBe('text');\n    });\n\n    it('() : should include comment nodes', () => {\n      expect($('p').contents().last()[0].type).toBe('comment');\n    });\n  });\n\n  describe('.next', () => {\n    it('() : should return next element', () => {\n      const { attribs } = $('.orange').next()[0];\n      expect(attribs).toHaveProperty('class', 'pear');\n    });\n\n    it('() : should skip text nodes', () => {\n      expect($(mixedText).next()[0]).toHaveProperty('name', 'b');\n    });\n\n    it('(no next) : should return empty for last child', () => {\n      expect($('.pear').next()).toHaveLength(0);\n    });\n\n    it('(next on empty object) : should return empty', () => {\n      expect($('.banana').next()).toHaveLength(0);\n    });\n\n    it('() : should operate over all elements in the selection', () => {\n      expect($('.apple, .orange', food).next()).toHaveLength(2);\n    });\n\n    it('() : should return elements in order', () => {\n      const result = load(eleven)('.red').next();\n      expect(result).toHaveLength(2);\n      expect(result.eq(0).text()).toBe('Six');\n      expect(result.eq(1).text()).toBe('Ten');\n    });\n\n    it('should reject elements that violate the filter', () => {\n      expect($('.apple').next('.non-existent')).toHaveLength(0);\n    });\n\n    it('should accept elements that satisfy the filter', () => {\n      expect($('.apple').next('.orange')).toHaveLength(1);\n    });\n\n    describe('(selector) :', () => {\n      it('should reject elements that violate the filter', () => {\n        expect($('.apple').next('.non-existent')).toHaveLength(0);\n      });\n\n      it('should accept elements that satisfy the filter', () => {\n        expect($('.apple').next('.orange')).toHaveLength(1);\n      });\n    });\n  });\n\n  describe('.nextAll', () => {\n    it('() : should return all following siblings', () => {\n      const elems = $('.apple').nextAll();\n      expect(elems).toHaveLength(2);\n      expect(elems[0].attribs).toHaveProperty('class', 'orange');\n      expect(elems[1].attribs).toHaveProperty('class', 'pear');\n    });\n\n    it('(no next) : should return empty for last child', () => {\n      expect($('.pear').nextAll()).toHaveLength(0);\n    });\n\n    it('(nextAll on empty object) : should return empty', () => {\n      expect($('.banana').nextAll()).toHaveLength(0);\n    });\n\n    it('() : should operate over all elements in the selection', () => {\n      expect($('.apple, .carrot', food).nextAll()).toHaveLength(3);\n    });\n\n    it('() : should not contain duplicate elements', () => {\n      const elems = $('.apple, .orange', food);\n      expect(elems.nextAll()).toHaveLength(2);\n    });\n\n    it('() : should not contain text elements', () => {\n      const elems = $('.apple', fruits.replace(/></g, '>\\n<'));\n      expect(elems.nextAll()).toHaveLength(2);\n    });\n\n    describe('(selector) :', () => {\n      it('should filter according to the provided selector', () => {\n        expect($('.apple').nextAll('.pear')).toHaveLength(1);\n      });\n\n      it(\"should not consider siblings' contents when filtering\", () => {\n        expect($('#fruits', food).nextAll('li')).toHaveLength(0);\n      });\n    });\n  });\n\n  describe('.nextUntil', () => {\n    it('() : should return all following siblings if no selector specified', () => {\n      const elems = $('.apple', food).nextUntil();\n      expect(elems).toHaveLength(2);\n      expect(elems[0].attribs).toHaveProperty('class', 'orange');\n      expect(elems[1].attribs).toHaveProperty('class', 'pear');\n    });\n\n    it('() : should filter out non-element nodes', () => {\n      const elems = $('<div><div></div><!-- comment -->text<div></div></div>');\n      const div = elems.children().eq(0);\n      expect(div.nextUntil()).toHaveLength(1);\n    });\n\n    it('() : should operate over all elements in the selection', () => {\n      const elems = $('.apple, .carrot', food);\n      expect(elems.nextUntil()).toHaveLength(3);\n    });\n\n    it('() : should not contain duplicate elements', () => {\n      const elems = $('.apple, .orange', food);\n      expect(elems.nextUntil()).toHaveLength(2);\n    });\n\n    it('(selector) : should return all following siblings until selector', () => {\n      const elems = $('.apple', food).nextUntil('.pear');\n      expect(elems).toHaveLength(1);\n      expect(elems[0].attribs).toHaveProperty('class', 'orange');\n    });\n\n    it('(selector) : should support selector matching multiple elements', () => {\n      const elems = $('#disabled', forms).nextUntil('option, #unnamed');\n      expect(elems).toHaveLength(2);\n      expect(elems[0].attribs).toHaveProperty('id', 'submit');\n      expect(elems[1].attribs).toHaveProperty('id', 'select');\n    });\n\n    it('(selector not sibling) : should return all following siblings', () => {\n      const elems = $('.apple').nextUntil('#vegetables');\n      expect(elems).toHaveLength(2);\n    });\n\n    it('(selector, filterString) : should return all following siblings until selector, filtered by filter', () => {\n      const elems = $('.beer', drinks).nextUntil('.water', '.milk');\n      expect(elems).toHaveLength(1);\n      expect(elems[0].attribs).toHaveProperty('class', 'milk');\n    });\n\n    it('(null, filterString) : should return all following siblings until selector, filtered by filter', () => {\n      const elems = $('<ul><li></li><li><p></p></li></ul>');\n      const empty = elems.find('li').eq(0).nextUntil(null, 'p');\n      expect(empty).toHaveLength(0);\n    });\n\n    it('() : should return an empty object for last child', () => {\n      expect($('.pear').nextUntil()).toHaveLength(0);\n    });\n\n    it('() : should return an empty object when called on an empty object', () => {\n      expect($('.banana').nextUntil()).toHaveLength(0);\n    });\n\n    it('(node) : should return all following siblings until the node', () => {\n      const $fruits = $('#fruits').children();\n      const elems = $fruits.eq(0).nextUntil($fruits[2]);\n      expect(elems).toHaveLength(1);\n    });\n\n    it('(cheerio object) : should return all following siblings until any member of the cheerio object', () => {\n      const $drinks = $(drinks).children();\n      const $until = $([$drinks[4], $drinks[3]]);\n      const elems = $drinks.eq(0).nextUntil($until);\n      expect(elems).toHaveLength(2);\n    });\n  });\n\n  describe('.prev', () => {\n    it('() : should return previous element', () => {\n      const { attribs } = $('.orange').prev()[0];\n      expect(attribs).toHaveProperty('class', 'apple');\n    });\n\n    it('() : should skip text nodes', () => {\n      expect($($(mixedText)[2]).prev()[0]).toHaveProperty('name', 'a');\n    });\n\n    it('(no prev) : should return empty for first child', () => {\n      expect($('.apple').prev()).toHaveLength(0);\n    });\n\n    it('(prev on empty object) : should return empty', () => {\n      expect($('.banana').prev()).toHaveLength(0);\n    });\n\n    it('() : should operate over all elements in the selection', () => {\n      expect($('.orange, .pear', food).prev()).toHaveLength(2);\n    });\n\n    it('() : should maintain elements order', () => {\n      const sel = load(eleven)('.sel');\n      expect(sel).toHaveLength(3);\n      expect(sel.eq(0).text()).toBe('Three');\n      expect(sel.eq(1).text()).toBe('Nine');\n      expect(sel.eq(2).text()).toBe('Eleven');\n\n      // Swap last elements\n      const el = sel[2];\n      sel[2] = sel[1];\n      sel[1] = el;\n\n      const result = sel.prev();\n      expect(result).toHaveLength(3);\n      expect(result.eq(0).text()).toBe('Two');\n      expect(result.eq(1).text()).toBe('Ten');\n      expect(result.eq(2).text()).toBe('Eight');\n    });\n\n    describe('(selector) :', () => {\n      it('should reject elements that violate the filter', () => {\n        expect($('.orange').prev('.non-existent')).toHaveLength(0);\n      });\n\n      it('should accept elements that satisfy the filter', () => {\n        expect($('.orange').prev('.apple')).toHaveLength(1);\n      });\n\n      it('(selector) : should reject elements that violate the filter', () => {\n        expect($('.orange').prev('.non-existent')).toHaveLength(0);\n      });\n\n      it('(selector) : should accept elements that satisfy the filter', () => {\n        expect($('.orange').prev('.apple')).toHaveLength(1);\n      });\n    });\n  });\n\n  describe('.prevAll', () => {\n    it('() : should return all preceding siblings', () => {\n      const elems = $('.pear').prevAll();\n      expect(elems).toHaveLength(2);\n      expect(elems[0].attribs).toHaveProperty('class', 'orange');\n      expect(elems[1].attribs).toHaveProperty('class', 'apple');\n    });\n\n    it('() : should not contain text elements', () => {\n      const elems = $('.pear', fruits.replace(/></g, '>\\n<'));\n      expect(elems.prevAll()).toHaveLength(2);\n    });\n\n    it('(no prev) : should return empty for first child', () => {\n      expect($('.apple').prevAll()).toHaveLength(0);\n    });\n\n    it('(prevAll on empty object) : should return empty', () => {\n      expect($('.banana').prevAll()).toHaveLength(0);\n    });\n\n    it('() : should operate over all elements in the selection', () => {\n      expect($('.orange, .sweetcorn', food).prevAll()).toHaveLength(2);\n    });\n\n    it('() : should not contain duplicate elements', () => {\n      const elems = $('.orange, .pear', food);\n      expect(elems.prevAll()).toHaveLength(2);\n    });\n\n    describe('(selector) :', () => {\n      it('should filter returned elements', () => {\n        const elems = $('.pear').prevAll('.apple');\n        expect(elems).toHaveLength(1);\n      });\n\n      it(\"should not consider siblings's descendents\", () => {\n        const elems = $('#vegetables', food).prevAll('li');\n        expect(elems).toHaveLength(0);\n      });\n    });\n  });\n\n  describe('.prevUntil', () => {\n    it('() : should return all preceding siblings if no selector specified', () => {\n      const elems = $('.pear').prevUntil();\n      expect(elems).toHaveLength(2);\n      expect(elems[0].attribs).toHaveProperty('class', 'orange');\n      expect(elems[1].attribs).toHaveProperty('class', 'apple');\n    });\n\n    it('() : should filter out non-element nodes', () => {\n      const elems = $(\n        '<div class=\"1\"><div class=\"2\"></div><!-- comment -->text<div class=\"3\"></div></div>',\n      );\n      const div = elems.children().last();\n      expect(div.prevUntil()).toHaveLength(1);\n    });\n\n    it('() : should operate over all elements in the selection', () => {\n      const elems = $('.pear, .sweetcorn', food);\n      expect(elems.prevUntil()).toHaveLength(3);\n    });\n\n    it('() : should not contain duplicate elements', () => {\n      const elems = $('.orange, .pear', food);\n      expect(elems.prevUntil()).toHaveLength(2);\n    });\n\n    it('(selector) : should return all preceding siblings until selector', () => {\n      const elems = $('.pear').prevUntil('.apple');\n      expect(elems).toHaveLength(1);\n      expect(elems[0].attribs).toHaveProperty('class', 'orange');\n    });\n\n    it('(selector) : should support selector matching multiple elements', () => {\n      const elems = $('#unnamed', forms).prevUntil('option, #disabled');\n      expect(elems).toHaveLength(2);\n      expect(elems[0].attribs).toHaveProperty('id', 'select');\n      expect(elems[1].attribs).toHaveProperty('id', 'submit');\n    });\n\n    it('(selector not sibling) : should return all preceding siblings', () => {\n      const elems = $('.sweetcorn', food).prevUntil('#fruits');\n      expect(elems).toHaveLength(1);\n      expect(elems[0].attribs).toHaveProperty('class', 'carrot');\n    });\n\n    it('(selector, filterString) : should return all preceding siblings until selector, filtered by filter', () => {\n      const elems = $('.cider', drinks).prevUntil('.juice', '.water');\n      expect(elems).toHaveLength(1);\n      expect(elems[0].attribs).toHaveProperty('class', 'water');\n    });\n\n    it('(selector, filterString) : should return all preceding siblings until selector', () => {\n      const elems = $('<ul><li><p></p></li><li></li></ul>');\n      const empty = elems.find('li').eq(1).prevUntil(null, 'p');\n      expect(empty).toHaveLength(0);\n    });\n\n    it('() : should return an empty object for first child', () => {\n      expect($('.apple').prevUntil()).toHaveLength(0);\n    });\n\n    it('() : should return an empty object when called on an empty object', () => {\n      expect($('.banana').prevUntil()).toHaveLength(0);\n    });\n\n    it('(node) : should return all previous siblings until the node', () => {\n      const $fruits = $('#fruits').children();\n      const elems = $fruits.eq(2).prevUntil($fruits[0]);\n      expect(elems).toHaveLength(1);\n    });\n\n    it('(cheerio object) : should return all previous siblings until any member of the cheerio object', () => {\n      const $drinks = $(drinks).children();\n      const $until = $([$drinks[0], $drinks[1]]);\n      const elems = $drinks.eq(4).prevUntil($until);\n      expect(elems).toHaveLength(2);\n    });\n  });\n\n  describe('.siblings', () => {\n    it('() : should get all the siblings', () => {\n      expect($('.orange').siblings()).toHaveLength(2);\n      expect($('#fruits').siblings()).toHaveLength(0);\n      expect($('.apple, .carrot', food).siblings()).toHaveLength(3);\n    });\n\n    it('(selector) : should get all siblings that match the selector', () => {\n      expect($('.orange').siblings('.apple')).toHaveLength(1);\n      expect($('.orange').siblings('.peach')).toHaveLength(0);\n    });\n\n    it('(selector) : should throw an Error if given an invalid selector', () => {\n      expect(() => {\n        $('.orange').siblings(':bah');\n      }).toThrow('Unknown pseudo-class :bah');\n    });\n\n    it('(selector) : does not consider the contents of siblings when filtering (GH-374)', () => {\n      expect($('#fruits', food).siblings('li')).toHaveLength(0);\n    });\n\n    it('() : when two elements are siblings to each other they have to be included', () => {\n      const result = load(eleven)('.sel').siblings();\n      expect(result).toHaveLength(7);\n      expect(result.eq(0).text()).toBe('One');\n      expect(result.eq(1).text()).toBe('Two');\n      expect(result.eq(2).text()).toBe('Four');\n      expect(result.eq(3).text()).toBe('Eight');\n      expect(result.eq(4).text()).toBe('Nine');\n      expect(result.eq(5).text()).toBe('Ten');\n      expect(result.eq(6).text()).toBe('Eleven');\n    });\n\n    it('(selector) : when two elements are siblings to each other they have to be included', () => {\n      const result = load(eleven)('.sel').siblings('.red');\n      expect(result).toHaveLength(2);\n      expect(result.eq(0).text()).toBe('Four');\n      expect(result.eq(1).text()).toBe('Nine');\n    });\n\n    it('(cheerio) : test filtering with cheerio object', () => {\n      const doc = load(eleven);\n      const result = doc('.sel').siblings(doc(':not([class])'));\n      expect(result).toHaveLength(4);\n      expect(result.eq(0).text()).toBe('One');\n      expect(result.eq(1).text()).toBe('Two');\n      expect(result.eq(2).text()).toBe('Eight');\n      expect(result.eq(3).text()).toBe('Ten');\n    });\n  });\n\n  describe('.parents', () => {\n    beforeEach(() => {\n      $ = load(food);\n    });\n\n    it('() : should get all of the parents in logical order', () => {\n      const orange = $('.orange').parents();\n      expect(orange).toHaveLength(4);\n      expect(orange[0].attribs).toHaveProperty('id', 'fruits');\n      expect(orange[1].attribs).toHaveProperty('id', 'food');\n      expect(orange[2].tagName).toBe('body');\n      expect(orange[3].tagName).toBe('html');\n      const fruits = $('#fruits').parents();\n      expect(fruits).toHaveLength(3);\n      expect(fruits[0].attribs).toHaveProperty('id', 'food');\n      expect(fruits[1].tagName).toBe('body');\n      expect(fruits[2].tagName).toBe('html');\n    });\n\n    it('(selector) : should get all of the parents that match the selector in logical order', () => {\n      const fruits = $('.orange').parents('#fruits');\n      expect(fruits).toHaveLength(1);\n      expect(fruits[0].attribs).toHaveProperty('id', 'fruits');\n      const uls = $('.orange').parents('ul');\n      expect(uls).toHaveLength(2);\n      expect(uls[0].attribs).toHaveProperty('id', 'fruits');\n      expect(uls[1].attribs).toHaveProperty('id', 'food');\n    });\n\n    it('() : should not break if the selector does not have any results', () => {\n      const result = $('.saladbar').parents();\n      expect(result).toHaveLength(0);\n    });\n\n    it('() : should return an empty set for top-level elements', () => {\n      const result = $('html').parents();\n      expect(result).toHaveLength(0);\n    });\n\n    it('() : should return the parents of every element in the *reversed* collection, omitting duplicates', () => {\n      const $parents = $('li').parents();\n\n      expect($parents).toHaveLength(5);\n      expect($parents[0]).toBe($('#vegetables')[0]);\n      expect($parents[1]).toBe($('#fruits')[0]);\n      expect($parents[2]).toBe($('#food')[0]);\n      expect($parents[3]).toBe($('body')[0]);\n      expect($parents[4]).toBe($('html')[0]);\n    });\n  });\n\n  describe('.parentsUntil', () => {\n    beforeEach(() => {\n      $ = load(food);\n    });\n\n    it('() : should get all of the parents in logical order', () => {\n      const result = $('.orange').parentsUntil();\n      expect(result).toHaveLength(4);\n      expect(result[0].attribs).toHaveProperty('id', 'fruits');\n      expect(result[1].attribs).toHaveProperty('id', 'food');\n      expect(result[2].tagName).toBe('body');\n      expect(result[3].tagName).toBe('html');\n    });\n\n    it('() : should get all of the parents in reversed order, omitting duplicates', () => {\n      const result = $('.apple, .sweetcorn').parentsUntil();\n      expect(result).toHaveLength(5);\n      expect(result[0]).toBe($('#vegetables')[0]);\n      expect(result[1]).toBe($('#fruits')[0]);\n      expect(result[2]).toBe($('#food')[0]);\n      expect(result[3]).toBe($('body')[0]);\n      expect(result[4]).toBe($('html')[0]);\n    });\n\n    it('(selector) : should get all of the parents until selector', () => {\n      const food = $('.orange').parentsUntil('#food');\n      expect(food).toHaveLength(1);\n      expect(food[0].attribs).toHaveProperty('id', 'fruits');\n      const fruits = $('.orange').parentsUntil('#fruits');\n      expect(fruits).toHaveLength(0);\n    });\n\n    it('(selector) : Less simple parentsUntil check with selector', () => {\n      const result = $('#fruits').parentsUntil('html, body');\n      expect(result.eq(0).attr('id')).toBe('food');\n    });\n\n    it('(selector not parent) : should return all parents', () => {\n      const result = $('.orange').parentsUntil('.apple');\n      expect(result).toHaveLength(4);\n      expect(result[0].attribs).toHaveProperty('id', 'fruits');\n      expect(result[1].attribs).toHaveProperty('id', 'food');\n      expect(result[2].tagName).toBe('body');\n      expect(result[3].tagName).toBe('html');\n    });\n\n    it('(selector, filter) : should get all of the parents that match the filter', () => {\n      const result = $('.apple, .sweetcorn').parentsUntil(\n        '.saladbar',\n        '#vegetables',\n      );\n      expect(result).toHaveLength(1);\n      expect(result[0].attribs).toHaveProperty('id', 'vegetables');\n    });\n\n    it('(selector, filter) : Multiple-filtered parentsUntil check', () => {\n      const result = $('.orange').parentsUntil('html', 'ul,body');\n      expect(result).toHaveLength(3);\n      expect(result.eq(0).attr('id')).toBe('fruits');\n      expect(result.eq(1).attr('id')).toBe('food');\n      expect(result.eq(2).prop('tagName')).toBe('BODY');\n    });\n\n    it('() : should return empty object when called on an empty object', () => {\n      const result = $('.saladbar').parentsUntil();\n      expect(result).toHaveLength(0);\n    });\n\n    it('() : should return an empty set for top-level elements', () => {\n      const result = $('html').parentsUntil();\n      expect(result).toHaveLength(0);\n    });\n\n    it('(cheerio object) : should return all parents until any member of the cheerio object', () => {\n      const $fruits = $('#fruits');\n      const $until = $('#food');\n      const result = $fruits.children().eq(1).parentsUntil($until);\n      expect(result).toHaveLength(1);\n      expect(result[0].attribs).toHaveProperty('id', 'fruits');\n    });\n\n    it('(cheerio object) : should return all parents until body element', () => {\n      const body = $('body')[0];\n      const result = $('.carrot').parentsUntil(body);\n      expect(result).toHaveLength(2);\n      expect(result.eq(0).is('ul#vegetables')).toBe(true);\n    });\n  });\n\n  describe('.parent', () => {\n    it('() : should return the parent of each matched element', () => {\n      let result = $('.orange').parent();\n      expect(result).toHaveLength(1);\n      expect(result[0].attribs).toHaveProperty('id', 'fruits');\n      result = $('li', food).parent();\n      expect(result).toHaveLength(2);\n      expect(result[0].attribs).toHaveProperty('id', 'fruits');\n      expect(result[1].attribs).toHaveProperty('id', 'vegetables');\n    });\n\n    it('(undefined) : should not throw an exception', () => {\n      expect(() => {\n        $('li').parent(undefined);\n      }).not.toThrow();\n    });\n\n    it('() : should return an empty object for top-level elements', () => {\n      const result = $('html').parent();\n      expect(result).toHaveLength(0);\n    });\n\n    it('() : should not contain duplicate elements', () => {\n      const result = $('li').parent();\n      expect(result).toHaveLength(1);\n    });\n\n    it('(selector) : should filter the matched parent elements by the selector', () => {\n      const parents = $('.orange').parent();\n      expect(parents).toHaveLength(1);\n      expect(parents[0].attribs).toHaveProperty('id', 'fruits');\n      const fruits = $('li', food).parent('#fruits');\n      expect(fruits).toHaveLength(1);\n      expect(fruits[0].attribs).toHaveProperty('id', 'fruits');\n    });\n  });\n\n  describe('.closest', () => {\n    it('() : should return an empty array', () => {\n      const result = $('.orange').closest();\n      expect(result).toHaveLength(0);\n      expect(result).toBeInstanceOf(Cheerio);\n    });\n\n    it('(selector) : should find the closest element that matches the selector, searching through its ancestors and itself', () => {\n      expect($('.orange').closest('.apple')).toHaveLength(0);\n      expect(\n        ($('.orange', food).closest('#food')[0] as Element).attribs,\n      ).toHaveProperty('id', 'food');\n      expect(\n        ($('.orange', food).closest('ul')[0] as Element).attribs,\n      ).toHaveProperty('id', 'fruits');\n      expect(\n        ($('.orange', food).closest('li')[0] as Element).attribs,\n      ).toHaveProperty('class', 'orange');\n    });\n\n    it('(selector) : should find the closest element of each item, removing duplicates', () => {\n      const result = $('li', food).closest('ul');\n      expect(result).toHaveLength(2);\n    });\n\n    it('() : should not break if the selector does not have any results', () => {\n      const result = $('.saladbar', food).closest('ul');\n      expect(result).toHaveLength(0);\n    });\n\n    it('(selector) : should find closest element for text nodes', () => {\n      const textNode = $('.apple', food).contents().first();\n      const result = textNode.closest('#food') as Cheerio<Element>;\n      expect(result[0].attribs).toHaveProperty('id', 'food');\n    });\n  });\n\n  describe('.each', () => {\n    it('( (i, elem) -> ) : should loop selected returning fn with (i, elem)', () => {\n      const items: Element[] = [];\n      const classes = ['apple', 'orange', 'pear'];\n      $('li').each(function (idx, elem) {\n        items[idx] = elem;\n        expect(this.attribs).toHaveProperty('class', classes[idx]);\n      });\n      expect(items[0].attribs).toHaveProperty('class', 'apple');\n      expect(items[1].attribs).toHaveProperty('class', 'orange');\n      expect(items[2].attribs).toHaveProperty('class', 'pear');\n    });\n\n    it('( (i, elem) -> ) : should break iteration when the iterator function returns false', () => {\n      let iterationCount = 0;\n      $('li').each((idx) => {\n        iterationCount++;\n        return idx < 1;\n      });\n\n      expect(iterationCount).toBe(2);\n    });\n  });\n\n  if (typeof Symbol !== 'undefined') {\n    describe('[Symbol.iterator]', () => {\n      it('should yield each element', () => {\n        // The equivalent of: for (const element of $('li')) ...\n        const $li = $('li');\n        const iterator = $li[Symbol.iterator]() as Iterator<Element, Element>;\n        expect(iterator.next().value.attribs).toHaveProperty('class', 'apple');\n        expect(iterator.next().value.attribs).toHaveProperty('class', 'orange');\n        expect(iterator.next().value.attribs).toHaveProperty('class', 'pear');\n        expect(iterator.next().done).toBe(true);\n      });\n    });\n  }\n\n  describe('.map', () => {\n    it('(fn) : should be invoked with the correct arguments and context', () => {\n      const $fruits = $('li');\n      const args: [number, AnyNode][] = [];\n      const thisVals: AnyNode[] = [];\n\n      $fruits.map(function (...myArgs) {\n        args.push(myArgs);\n        thisVals.push(this);\n        return null;\n      });\n\n      expect(args).toStrictEqual([\n        [0, $fruits[0]],\n        [1, $fruits[1]],\n        [2, $fruits[2]],\n      ]);\n      expect(thisVals).toStrictEqual([$fruits[0], $fruits[1], $fruits[2]]);\n    });\n\n    it('(fn) : should return an Cheerio object wrapping the returned items', () => {\n      const $fruits = $('li');\n      const $mapped = $fruits.map((i) => $fruits[2 - i]);\n\n      expect($mapped).toHaveLength(3);\n      expect($mapped[0]).toBe($fruits[2]);\n      expect($mapped[1]).toBe($fruits[1]);\n      expect($mapped[2]).toBe($fruits[0]);\n    });\n\n    it('(fn) : should ignore `null` and `undefined` returned by iterator', () => {\n      const $fruits = $('li');\n      const retVals = [null, undefined, $fruits[1]];\n\n      const $mapped = $fruits.map((i) => retVals[i]);\n\n      expect($mapped).toHaveLength(1);\n      expect($mapped[0]).toBe($fruits[1]);\n    });\n\n    it('(fn) : should perform a shallow merge on arrays returned by iterator', () => {\n      const $fruits = $('li');\n\n      const $mapped = $fruits.map(() => [1, [3, 4]]);\n\n      expect($mapped.get()).toStrictEqual([1, [3, 4], 1, [3, 4], 1, [3, 4]]);\n    });\n\n    it('(fn) : should tolerate `null` and `undefined` when flattening arrays returned by iterator', () => {\n      const $fruits = $('li');\n\n      const $mapped = $fruits.map(() => [null, undefined]);\n\n      expect($mapped.get()).toStrictEqual([\n        null,\n        undefined,\n        null,\n        undefined,\n        null,\n        undefined,\n      ]);\n    });\n  });\n\n  describe('.filter', () => {\n    it('(selector) : should reduce the set of matched elements to those that match the selector', () => {\n      const pear = $('li').filter('.pear').text();\n      expect(pear).toBe('Pear');\n    });\n\n    it('(selector) : should not consider nested elements', () => {\n      const lis = $('#fruits').filter('li');\n      expect(lis).toHaveLength(0);\n    });\n\n    it('(selection) : should reduce the set of matched elements to those that are contained in the provided selection', () => {\n      const $fruits = $('li');\n      const $pear = $fruits.filter('.pear, .apple');\n      expect($fruits.filter($pear)).toHaveLength(2);\n    });\n\n    it('(element) : should reduce the set of matched elements to those that specified directly', () => {\n      const $fruits = $('li');\n      const pear = $fruits.filter('.pear')[0];\n      expect($fruits.filter(pear)).toHaveLength(1);\n    });\n\n    it(\"(fn) : should reduce the set of matched elements to those that pass the function's test\", () => {\n      const orange = $('li')\n        .filter(function (i, el) {\n          expect(this).toBe(el);\n          expect(el.tagName).toBe('li');\n          expect(typeof i).toBe('number');\n          return $(this).attr('class') === 'orange';\n        })\n        .text();\n\n      expect(orange).toBe('Orange');\n    });\n\n    it('should also iterate over text nodes (#1867)', () => {\n      const text = $('<a>a</a>b<c></c>').filter((_, el): el is Text =>\n        isText(el),\n      );\n\n      expect(text[0].data).toBe('b');\n    });\n  });\n\n  describe('.not', () => {\n    it('(selector) : should reduce the set of matched elements to those that do not match the selector', () => {\n      const $fruits = $('li');\n\n      const $notPear = $fruits.not('.pear');\n\n      expect($notPear).toHaveLength(2);\n      expect($notPear[0]).toBe($fruits[0]);\n      expect($notPear[1]).toBe($fruits[1]);\n    });\n\n    it('(selector) : should not consider nested elements', () => {\n      const lis = $('#fruits').not('li');\n      expect(lis).toHaveLength(1);\n    });\n\n    it('(selection) : should reduce the set of matched elements to those that are not contained in the provided selection', () => {\n      const $fruits = $('li');\n      const $orange = $('.orange');\n\n      const $notOrange = $fruits.not($orange);\n\n      expect($notOrange).toHaveLength(2);\n      expect($notOrange[0]).toBe($fruits[0]);\n      expect($notOrange[1]).toBe($fruits[2]);\n    });\n\n    it('(element) : should reduce the set of matched elements to those that specified directly', () => {\n      const $fruits = $('li');\n      const apple = $('.apple')[0];\n\n      const $notApple = $fruits.not(apple);\n\n      expect($notApple).toHaveLength(2);\n      expect($notApple[0]).toBe($fruits[1]);\n      expect($notApple[1]).toBe($fruits[2]);\n    });\n\n    it(\"(fn) : should reduce the set of matched elements to those that do not pass the function's test\", () => {\n      const $fruits = $('li');\n\n      const $notOrange = $fruits.not(function (i, el) {\n        expect(this).toBe(el);\n        expect(el).toHaveProperty('name', 'li');\n        expect(typeof i).toBe('number');\n        return $(this).attr('class') === 'orange';\n      });\n\n      expect($notOrange).toHaveLength(2);\n      expect($notOrange[0]).toBe($fruits[0]);\n      expect($notOrange[1]).toBe($fruits[2]);\n    });\n  });\n\n  describe('.has', () => {\n    beforeEach(() => {\n      $ = load(food);\n    });\n\n    it('(selector) : should reduce the set of matched elements to those with descendants that match the selector', () => {\n      const $fruits = $('#fruits,#vegetables').has('.pear');\n      expect($fruits).toHaveLength(1);\n      expect($fruits[0]).toBe($('#fruits')[0]);\n    });\n\n    it('(selector) : should only consider nested elements', () => {\n      const $empty = $('#fruits').has('#fruits');\n      expect($empty).toHaveLength(0);\n    });\n\n    it('(element) : should reduce the set of matched elements to those that are ancestors of the provided element', () => {\n      const $fruits = $('#fruits,#vegetables').has($('.pear')[0]);\n      expect($fruits).toHaveLength(1);\n      expect($fruits[0]).toBe($('#fruits')[0]);\n    });\n\n    it('(element) : should only consider nested elements', () => {\n      const $fruits = $('#fruits');\n      const fruitsEl = $fruits[0];\n      const $empty = $fruits.has(fruitsEl);\n\n      expect($empty).toHaveLength(0);\n    });\n  });\n\n  describe('.first', () => {\n    it('() : should return the first item', () => {\n      const $src = $(\n        '<span>foo</span><span>bar</span><span>baz</span>',\n      ) as Cheerio<Element>;\n      const $elem = $src.first();\n      expect($elem.length).toBe(1);\n      expect($elem[0].childNodes[0]).toHaveProperty('data', 'foo');\n    });\n\n    it('() : should return an empty object for an empty object', () => {\n      const $src = $();\n      const $first = $src.first();\n      expect($first.length).toBe(0);\n      expect($first[0]).toBeUndefined();\n    });\n  });\n\n  describe('.last', () => {\n    it('() : should return the last element', () => {\n      const $src = $(\n        '<span>foo</span><span>bar</span><span>baz</span>',\n      ) as Cheerio<Element>;\n      const $elem = $src.last();\n      expect($elem.length).toBe(1);\n      expect($elem[0].childNodes[0]).toHaveProperty('data', 'baz');\n    });\n\n    it('() : should return an empty object for an empty object', () => {\n      const $src = $();\n      const $last = $src.last();\n      expect($last.length).toBe(0);\n      expect($last[0]).toBeUndefined();\n    });\n  });\n\n  describe('.first & .last', () => {\n    it('() : should return equivalent collections if only one element', () => {\n      const $src = $('<span>bar</span>') as Cheerio<Element>;\n      const $first = $src.first();\n      const $last = $src.last();\n      expect($first.length).toBe(1);\n      expect($first[0].childNodes[0]).toHaveProperty('data', 'bar');\n      expect($last.length).toBe(1);\n      expect($last[0].childNodes[0]).toHaveProperty('data', 'bar');\n      expect($first[0]).toBe($last[0]);\n    });\n  });\n\n  describe('.eq', () => {\n    it('(i) : should return the element at the specified index', () => {\n      expect(getText($('li').eq(0))).toBe('Apple');\n      expect(getText($('li').eq(1))).toBe('Orange');\n      expect(getText($('li').eq(2))).toBe('Pear');\n      expect(getText($('li').eq(3))).toBeUndefined();\n      expect(getText($('li').eq(-1))).toBe('Pear');\n    });\n  });\n\n  describe('.get', () => {\n    it('(i) : should return the element at the specified index', () => {\n      const children = $('#fruits').children();\n      expect(children.get(0)).toBe(children[0]);\n      expect(children.get(1)).toBe(children[1]);\n      expect(children.get(2)).toBe(children[2]);\n    });\n\n    it('(-1) : should return the element indexed from the end of the collection', () => {\n      const children = $('#fruits').children();\n      expect(children.get(-1)).toBe(children[2]);\n      expect(children.get(-2)).toBe(children[1]);\n      expect(children.get(-3)).toBe(children[0]);\n    });\n\n    it('() : should return an array containing all of the collection', () => {\n      const children = $('#fruits').children();\n      const all = children.get();\n      expect(Array.isArray(all)).toBe(true);\n      expect(all).toStrictEqual([children[0], children[1], children[2]]);\n    });\n  });\n\n  describe('.index', () => {\n    describe('() :', () => {\n      it('returns the index of a child amongst its siblings', () => {\n        expect($('.orange').index()).toBe(1);\n      });\n      it('returns -1 when the selection has no parent', () => {\n        expect($('<div/>').index()).toBe(-1);\n      });\n    });\n\n    describe('(selector) :', () => {\n      it('returns the index of the first element in the set matched by `selector`', () => {\n        expect($('.apple').index('#fruits, li')).toBe(1);\n      });\n      it('returns -1 when the item is not present in the set matched by `selector`', () => {\n        expect($('.apple').index('#fuits')).toBe(-1);\n      });\n      it('returns -1 when the first element in the set has no parent', () => {\n        expect($('<div/>').index('*')).toBe(-1);\n      });\n    });\n\n    describe('(node) :', () => {\n      it('returns the index of the given node within the current selection', () => {\n        const $lis = $('li');\n        expect($lis.index($lis.get(1))).toBe(1);\n      });\n      it('returns the index of the given node within the current selection when the current selection has no parent', () => {\n        const $apple = $('.apple').remove();\n\n        expect($apple.index($apple.get(0))).toBe(0);\n      });\n      it('returns -1 when the given node is not present in the current selection', () => {\n        expect($('li').index($('#fruits').get(0))).toBe(-1);\n      });\n      it('returns -1 when the current selection is empty', () => {\n        expect($('.not-fruit').index($('#fruits').get(0))).toBe(-1);\n      });\n    });\n\n    describe('(selection) :', () => {\n      it('returns the index of the first node in the provided selection within the current selection', () => {\n        const $lis = $('li');\n        expect($lis.index($('.orange, .pear'))).toBe(1);\n      });\n      it('returns -1 when the given node is not present in the current selection', () => {\n        expect($('li').index($('#fruits'))).toBe(-1);\n      });\n      it('returns -1 when the current selection is empty', () => {\n        expect($('.not-fruit').index($('#fruits'))).toBe(-1);\n      });\n    });\n  });\n\n  describe('.slice', () => {\n    it('(start) : should return all elements after the given index', () => {\n      const sliced = $('li').slice(1);\n      expect(sliced).toHaveLength(2);\n      expect(getText(sliced.eq(0))).toBe('Orange');\n      expect(getText(sliced.eq(1))).toBe('Pear');\n    });\n\n    it('(start, end) : should return all elements matching the given range', () => {\n      const sliced = $('li').slice(1, 2);\n      expect(sliced).toHaveLength(1);\n      expect(getText(sliced.eq(0))).toBe('Orange');\n    });\n\n    it('(-start) : should return element matching the offset from the end', () => {\n      const sliced = $('li').slice(-1);\n      expect(sliced).toHaveLength(1);\n      expect(getText(sliced.eq(0))).toBe('Pear');\n    });\n  });\n\n  describe('.end() :', () => {\n    let $fruits: Cheerio<Element>;\n\n    beforeEach(() => {\n      $fruits = $('#fruits').children();\n    });\n\n    it('returns an empty object at the end of the chain', () => {\n      expect($fruits.end().end().end()).toBeTruthy();\n      expect($fruits.end().end().end()).toHaveLength(0);\n    });\n    it('find', () => {\n      expect($fruits.find('.apple').end()).toBe($fruits);\n    });\n    it('filter', () => {\n      expect($fruits.filter('.apple').end()).toBe($fruits);\n    });\n    it('map', () => {\n      expect(\n        $fruits\n          .map(function () {\n            return this;\n          })\n          .end(),\n      ).toBe($fruits);\n    });\n    it('contents', () => {\n      expect($fruits.contents().end()).toBe($fruits);\n    });\n    it('eq', () => {\n      expect($fruits.eq(1).end()).toBe($fruits);\n    });\n    it('first', () => {\n      expect($fruits.first().end()).toBe($fruits);\n    });\n    it('last', () => {\n      expect($fruits.last().end()).toBe($fruits);\n    });\n    it('slice', () => {\n      expect($fruits.slice(1).end()).toBe($fruits);\n    });\n    it('children', () => {\n      expect($fruits.children().end()).toBe($fruits);\n    });\n    it('parent', () => {\n      expect($fruits.parent().end()).toBe($fruits);\n    });\n    it('parents', () => {\n      expect($fruits.parents().end()).toBe($fruits);\n    });\n    it('closest', () => {\n      expect($fruits.closest('ul').end()).toBe($fruits);\n    });\n    it('siblings', () => {\n      expect($fruits.siblings().end()).toBe($fruits);\n    });\n    it('next', () => {\n      expect($fruits.next().end()).toBe($fruits);\n    });\n    it('nextAll', () => {\n      expect($fruits.nextAll().end()).toBe($fruits);\n    });\n    it('prev', () => {\n      expect($fruits.prev().end()).toBe($fruits);\n    });\n    it('prevAll', () => {\n      expect($fruits.prevAll().end()).toBe($fruits);\n    });\n    it('clone', () => {\n      expect($fruits.clone().end()).toBe($fruits);\n    });\n  });\n\n  describe('.add()', () => {\n    let $fruits: Cheerio<AnyNode>;\n    let $apple: Cheerio<Element>;\n    let $orange: Cheerio<Element>;\n    let $pear: Cheerio<Element>;\n\n    beforeEach(() => {\n      $ = load(food);\n      $fruits = $('#fruits');\n      $apple = $('.apple');\n      $orange = $('.orange');\n      $pear = $('.pear');\n    });\n\n    describe('(selector) matched element :', () => {\n      it('occurs before current selection', () => {\n        const $selection = $orange.add('.apple');\n\n        expect($selection).toHaveLength(2);\n        expect($selection[0]).toBe($apple[0]);\n        expect($selection[1]).toBe($orange[0]);\n      });\n      it('is identical to the current selection', () => {\n        const $selection = $orange.add('.orange');\n\n        expect($selection).toHaveLength(1);\n        expect($selection[0]).toBe($orange[0]);\n      });\n      it('occurs after current selection', () => {\n        const $selection = $orange.add('.pear');\n\n        expect($selection).toHaveLength(2);\n        expect($selection[0]).toBe($orange[0]);\n        expect($selection[1]).toBe($pear[0]);\n      });\n      it('contains the current selection', () => {\n        const $selection = $orange.add('#fruits');\n\n        expect($selection).toHaveLength(2);\n        expect($selection[0]).toBe($fruits[0]);\n        expect($selection[1]).toBe($orange[0]);\n      });\n      it('is a child of the current selection', () => {\n        const $selection = $fruits.add('.orange');\n\n        expect($selection).toHaveLength(2);\n        expect($selection[0]).toBe($fruits[0]);\n        expect($selection[1]).toBe($orange[0]);\n      });\n      it('is root object preserved', () => {\n        const $selection = $('<div></div>').add('#fruits');\n\n        expect($selection).toHaveLength(2);\n        expect($selection.eq(0).is('div')).toBe(true);\n        expect($selection.eq(1).is($fruits.eq(0))).toBe(true);\n      });\n    });\n    describe('(selector) matched elements :', () => {\n      it('occur before the current selection', () => {\n        const $selection = $pear.add('.apple, .orange');\n\n        expect($selection).toHaveLength(3);\n        expect($selection[0]).toBe($apple[0]);\n        expect($selection[1]).toBe($orange[0]);\n        expect($selection[2]).toBe($pear[0]);\n      });\n      it('include the current selection', () => {\n        const $selection = $pear.add('#fruits li');\n\n        expect($selection).toHaveLength(3);\n        expect($selection[0]).toBe($apple[0]);\n        expect($selection[1]).toBe($orange[0]);\n        expect($selection[2]).toBe($pear[0]);\n      });\n      it('occur after the current selection', () => {\n        const $selection = $apple.add('.orange, .pear');\n\n        expect($selection).toHaveLength(3);\n        expect($selection[0]).toBe($apple[0]);\n        expect($selection[1]).toBe($orange[0]);\n        expect($selection[2]).toBe($pear[0]);\n      });\n      it('occur within the current selection', () => {\n        const $selection = $fruits.add('#fruits li');\n\n        expect($selection).toHaveLength(4);\n        expect($selection[0]).toBe($fruits[0]);\n        expect($selection[1]).toBe($apple[0]);\n        expect($selection[2]).toBe($orange[0]);\n        expect($selection[3]).toBe($pear[0]);\n      });\n    });\n    describe('(selector, context) :', () => {\n      it(', context)', () => {\n        const $selection = $fruits.add('li', '#vegetables');\n        expect($selection).toHaveLength(3);\n        expect($selection[0]).toBe($fruits[0]);\n        expect($selection[1]).toBe($('.carrot')[0]);\n        expect($selection[2]).toBe($('.sweetcorn')[0]);\n      });\n    });\n\n    describe('(element) honors document order when element occurs :', () => {\n      it('before the current selection', () => {\n        const $selection = $orange.add($apple[0]);\n\n        expect($selection).toHaveLength(2);\n        expect($selection[0]).toBe($apple[0]);\n        expect($selection[1]).toBe($orange[0]);\n      });\n      it('after the current selection', () => {\n        const $selection = $orange.add($pear[0]);\n\n        expect($selection).toHaveLength(2);\n        expect($selection[0]).toBe($orange[0]);\n        expect($selection[1]).toBe($pear[0]);\n      });\n      it('within the current selection', () => {\n        const $selection = $fruits.add($orange[0]);\n\n        expect($selection).toHaveLength(2);\n        expect($selection[0]).toBe($fruits[0]);\n        expect($selection[1]).toBe($orange[0]);\n      });\n      it('as an ancestor of the current selection', () => {\n        const $selection = $orange.add($fruits[0]);\n\n        expect($selection).toHaveLength(2);\n        expect($selection[0]).toBe($fruits[0]);\n        expect($selection[1]).toBe($orange[0]);\n      });\n      it('does not insert an element already contained within the current selection', () => {\n        const $selection = $apple.add($apple[0]);\n\n        expect($selection).toHaveLength(1);\n        expect($selection[0]).toBe($apple[0]);\n      });\n    });\n    describe('([elements]) : elements', () => {\n      it('occur before the current selection', () => {\n        const $selection = $pear.add($('.apple, .orange').get());\n\n        expect($selection).toHaveLength(3);\n        expect($selection[0]).toBe($apple[0]);\n        expect($selection[1]).toBe($orange[0]);\n        expect($selection[2]).toBe($pear[0]);\n      });\n      it('include the current selection', () => {\n        const $selection = $pear.add($('#fruits li').get());\n\n        expect($selection).toHaveLength(3);\n        expect($selection[0]).toBe($apple[0]);\n        expect($selection[1]).toBe($orange[0]);\n        expect($selection[2]).toBe($pear[0]);\n      });\n      it('occur after the current selection', () => {\n        const $selection = $apple.add($('.orange, .pear').get());\n\n        expect($selection).toHaveLength(3);\n        expect($selection[0]).toBe($apple[0]);\n        expect($selection[1]).toBe($orange[0]);\n        expect($selection[2]).toBe($pear[0]);\n      });\n      it('occur within the current selection', () => {\n        const $selection = $fruits.add($('#fruits li').get());\n\n        expect($selection).toHaveLength(4);\n        expect($selection[0]).toBe($fruits[0]);\n        expect($selection[1]).toBe($apple[0]);\n        expect($selection[2]).toBe($orange[0]);\n        expect($selection[3]).toBe($pear[0]);\n      });\n    });\n\n    /**\n     * Element order is undefined in this case, so it should not be asserted\n     * here.\n     *\n     * If the collection consists of elements from different documents or ones\n     * not in any document, the sort order is undefined.\n     *\n     * @see {@link https://api.jquery.com/add/}\n     */\n    it('(html) : correctly parses and adds the new elements', () => {\n      const $selection = $apple.add('<li class=\"banana\">banana</li>');\n\n      expect($selection).toHaveLength(2);\n      expect($selection.is('.apple')).toBe(true);\n      expect($selection.is('.banana')).toBe(true);\n    });\n\n    describe('(selection) element in selection :', () => {\n      it('occurs before current selection', () => {\n        const $selection = $orange.add($('.apple'));\n\n        expect($selection).toHaveLength(2);\n        expect($selection[0]).toBe($apple[0]);\n        expect($selection[1]).toBe($orange[0]);\n      });\n      it('is identical to the current selection', () => {\n        const $selection = $orange.add($('.orange'));\n\n        expect($selection).toHaveLength(1);\n        expect($selection[0]).toBe($orange[0]);\n      });\n      it('occurs after current selection', () => {\n        const $selection = $orange.add($('.pear'));\n\n        expect($selection).toHaveLength(2);\n        expect($selection[0]).toBe($orange[0]);\n        expect($selection[1]).toBe($pear[0]);\n      });\n      it('contains the current selection', () => {\n        const $selection = $orange.add($('#fruits'));\n\n        expect($selection).toHaveLength(2);\n        expect($selection[0]).toBe($fruits[0]);\n        expect($selection[1]).toBe($orange[0]);\n      });\n      it('is a child of the current selection', () => {\n        const $selection = $fruits.add($('.orange'));\n\n        expect($selection).toHaveLength(2);\n        expect($selection[0]).toBe($fruits[0]);\n        expect($selection[1]).toBe($orange[0]);\n      });\n    });\n    describe('(selection) elements in the selection :', () => {\n      it('occur before the current selection', () => {\n        const $selection = $pear.add($('.apple, .orange'));\n\n        expect($selection).toHaveLength(3);\n        expect($selection[0]).toBe($apple[0]);\n        expect($selection[1]).toBe($orange[0]);\n        expect($selection[2]).toBe($pear[0]);\n      });\n      it('include the current selection', () => {\n        const $selection = $pear.add($('#fruits li'));\n\n        expect($selection).toHaveLength(3);\n        expect($selection[0]).toBe($apple[0]);\n        expect($selection[1]).toBe($orange[0]);\n        expect($selection[2]).toBe($pear[0]);\n      });\n      it('occur after the current selection', () => {\n        const $selection = $apple.add($('.orange, .pear'));\n\n        expect($selection).toHaveLength(3);\n        expect($selection[0]).toBe($apple[0]);\n        expect($selection[1]).toBe($orange[0]);\n        expect($selection[2]).toBe($pear[0]);\n      });\n      it('occur within the current selection', () => {\n        const $selection = $fruits.add($('#fruits li'));\n\n        expect($selection).toHaveLength(4);\n        expect($selection[0]).toBe($fruits[0]);\n        expect($selection[1]).toBe($apple[0]);\n        expect($selection[2]).toBe($orange[0]);\n        expect($selection[3]).toBe($pear[0]);\n      });\n    });\n\n    describe('(selection) :', () => {\n      it('modifying nested selections should not impact the parent [#834]', () => {\n        const apple_pear = $apple.add($pear);\n\n        // Applies red to apple and pear\n        apple_pear.addClass('red');\n\n        expect($apple.hasClass('red')).toBe(true); // This is true\n        expect($pear.hasClass('red')).toBe(true); // This is true\n\n        // Applies green to pear... AND should not affect apple\n        $pear.addClass('green');\n        expect($pear.hasClass('green')).toBe(true); // Currently this is true\n        expect($apple.hasClass('green')).toBe(false); // And this should be false!\n      });\n    });\n  });\n\n  describe('.addBack', () => {\n    describe('() :', () => {\n      it('includes siblings and self', () => {\n        const $selection = $('.orange').siblings().addBack();\n\n        expect($selection).toHaveLength(3);\n        expect($selection[0]).toBe($('.apple')[0]);\n        expect($selection[1]).toBe($('.orange')[0]);\n        expect($selection[2]).toBe($('.pear')[0]);\n      });\n      it('includes children and self', () => {\n        const $selection = $('#fruits').children().addBack();\n\n        expect($selection).toHaveLength(4);\n        expect($selection[0]).toBe($('#fruits')[0]);\n        expect($selection[1]).toBe($('.apple')[0]);\n        expect($selection[2]).toBe($('.orange')[0]);\n        expect($selection[3]).toBe($('.pear')[0]);\n      });\n      it('includes parent and self', () => {\n        const $selection = $('.apple').parent().addBack();\n\n        expect($selection).toHaveLength(2);\n        expect($selection[0]).toBe($('#fruits')[0]);\n        expect($selection[1]).toBe($('.apple')[0]);\n      });\n      it('includes parents and self', () => {\n        const q = load(food);\n        const $selection = q('.apple').parents().addBack();\n\n        expect($selection).toHaveLength(5);\n        expect($selection[0]).toBe(q('html')[0]);\n        expect($selection[1]).toBe(q('body')[0]);\n        expect($selection[2]).toBe(q('#food')[0]);\n        expect($selection[3]).toBe(q('#fruits')[0]);\n        expect($selection[4]).toBe(q('.apple')[0]);\n      });\n    });\n    it('(filter) : filters the previous selection', () => {\n      const $selection = $('li').eq(1).addBack('.apple');\n\n      expect($selection).toHaveLength(2);\n      expect($selection[0]).toBe($('.apple')[0]);\n      expect($selection[1]).toBe($('.orange')[0]);\n    });\n    it('() : fails gracefully when no args are passed', () => {\n      const $div = cheerio('<div>');\n      expect($div.addBack()).toBe($div);\n    });\n  });\n\n  describe('.is', () => {\n    it('() : should return false', () => {\n      expect($('li.apple').is()).toBe(false);\n    });\n\n    it('(true selector) : should return true', () => {\n      expect(cheerio('#vegetables', vegetables).is('ul')).toBe(true);\n    });\n\n    it('(false selector) : should return false', () => {\n      expect(cheerio('#vegetables', vegetables).is('div')).toBe(false);\n    });\n\n    it('(true selection) : should return true', () => {\n      const $vegetables = cheerio('li', vegetables);\n      expect($vegetables.is($vegetables.eq(1))).toBe(true);\n    });\n\n    it('(false selection) : should return false', () => {\n      const $vegetableList = cheerio(vegetables);\n      const $vegetables = $vegetableList.find('li');\n      expect($vegetables.is($vegetableList)).toBe(false);\n    });\n\n    it('(true element) : should return true', () => {\n      const $vegetables = cheerio('li', vegetables);\n      expect($vegetables.is($vegetables[0])).toBe(true);\n    });\n\n    it('(false element) : should return false', () => {\n      const $vegetableList = cheerio(vegetables);\n      const $vegetables = $vegetableList.find('li');\n      expect($vegetables.is($vegetableList[0])).toBe(false);\n    });\n\n    it('(true predicate) : should return true', () => {\n      const result = $('li').is(function () {\n        return this.tagName === 'li' && $(this).hasClass('pear');\n      });\n      expect(result).toBe(true);\n    });\n\n    it('(false predicate) : should return false', () => {\n      const result = $('li')\n        .last()\n        .is(function () {\n          return this.tagName === 'ul';\n        });\n      expect(result).toBe(false);\n    });\n  });\n});\n"
  },
  {
    "path": "src/api/traversing.ts",
    "content": "/**\n * Methods for traversing the DOM structure.\n *\n * @module cheerio/traversing\n */\n\nimport * as select from 'cheerio-select';\nimport {\n  type AnyNode,\n  type Document,\n  type Element,\n  hasChildren,\n  isDocument,\n  isTag,\n} from 'domhandler';\nimport {\n  getChildren,\n  getSiblings,\n  nextElementSibling,\n  prevElementSibling,\n  uniqueSort,\n} from 'domutils';\nimport type { Cheerio } from '../cheerio.js';\nimport { contains } from '../static.js';\nimport type { AcceptedFilters, FilterFunction } from '../types.js';\nimport { domEach, isCheerio } from '../utils.js';\n\nconst reContextSelector = /^\\s*(?:[+~]|:scope\\b)/;\n\n/**\n * Get the descendants of each element in the current set of matched elements,\n * filtered by a selector, jQuery object, or element.\n *\n * @category Traversing\n * @example\n *\n * ```js\n * $('#fruits').find('li').length;\n * //=> 3\n * $('#fruits').find($('.apple')).length;\n * //=> 1\n * ```\n *\n * @param selectorOrHaystack - Element to look for.\n * @returns The found elements.\n * @see {@link https://api.jquery.com/find/}\n */\nexport function find<T extends AnyNode>(\n  this: Cheerio<T>,\n  selectorOrHaystack?: string | Cheerio<Element> | Element,\n): Cheerio<Element> {\n  if (!selectorOrHaystack) {\n    return this._make([]);\n  }\n\n  if (typeof selectorOrHaystack !== 'string') {\n    const haystack = isCheerio(selectorOrHaystack)\n      ? selectorOrHaystack.toArray()\n      : [selectorOrHaystack];\n\n    const context = this.toArray();\n\n    return this._make(\n      haystack.filter((elem) => context.some((node) => contains(node, elem))),\n    );\n  }\n\n  return this._findBySelector(selectorOrHaystack, Number.POSITIVE_INFINITY);\n}\n\n/**\n * Find elements by a specific selector.\n *\n * @private\n * @category Traversing\n * @param selector - Selector to filter by.\n * @param limit - Maximum number of elements to match.\n * @returns The found elements.\n */\nexport function _findBySelector<T extends AnyNode>(\n  this: Cheerio<T>,\n  selector: string,\n  limit: number,\n): Cheerio<Element> {\n  const context = this.toArray();\n\n  const elems = reContextSelector.test(selector)\n    ? context\n    : this.children().toArray();\n\n  const options = {\n    context,\n    root: this._root?.[0],\n\n    // Pass options that are recognized by `cheerio-select`\n    xmlMode: this.options.xmlMode,\n    lowerCaseTags: this.options.lowerCaseTags,\n    lowerCaseAttributeNames: this.options.lowerCaseAttributeNames,\n    pseudos: this.options.pseudos,\n    quirksMode: this.options.quirksMode,\n  };\n\n  return this._make(select.select(selector, elems, options, limit));\n}\n\n/**\n * Creates a matcher, using a particular mapping function. Matchers provide a\n * function that finds elements using a generating function, supporting\n * filtering.\n *\n * @private\n * @param matchMap - Mapping function.\n * @returns - Function for wrapping generating functions.\n */\nfunction _getMatcher<P>(\n  matchMap: (fn: (elem: AnyNode) => P, elems: Cheerio<AnyNode>) => Element[],\n) {\n  return (\n    fn: (elem: AnyNode) => P,\n    ...postFns: ((elems: Element[]) => Element[])[]\n  ) =>\n    function <T extends AnyNode>(\n      this: Cheerio<T>,\n      selector?: AcceptedFilters<Element>,\n    ): Cheerio<Element> {\n      let matched: Element[] = matchMap(fn, this);\n\n      if (selector) {\n        matched = filterArray(\n          matched,\n          selector,\n          this.options.xmlMode,\n          this._root?.[0],\n        );\n      }\n\n      return this._make(\n        // Post processing is only necessary if there is more than one element.\n        this.length > 1 && matched.length > 1\n          ? postFns.reduce((elems, fn) => fn(elems), matched)\n          : matched,\n      );\n    };\n}\n\n/** Matcher that adds multiple elements for each entry in the input. */\nconst _matcher = _getMatcher((fn: (elem: AnyNode) => Element[], elems) =>\n  elems.toArray().flatMap((elem) => fn(elem)),\n);\n\n/** Matcher that adds at most one element for each entry in the input. */\nconst _singleMatcher = _getMatcher(\n  (fn: (elem: AnyNode) => Element | null, elems) => {\n    const ret: Element[] = [];\n\n    for (let i = 0; i < elems.length; i++) {\n      const value = fn(elems[i]);\n      if (value !== null) {\n        ret.push(value);\n      }\n    }\n    return ret;\n  },\n);\n\n/**\n * Matcher that supports traversing until a condition is met.\n *\n * @param nextElem - Function that returns the next element.\n * @param postFns - Post processing functions.\n * @returns A function usable for `*Until` methods.\n */\nfunction _matchUntil(\n  nextElem: (elem: AnyNode) => Element | null,\n  ...postFns: ((elems: Element[]) => Element[])[]\n) {\n  // We use a variable here that is used from within the matcher.\n  let matches: ((el: Element, i: number) => boolean) | null = null;\n\n  const innerMatcher = _getMatcher(\n    (nextElem: (elem: AnyNode) => Element | null, elems) => {\n      const matched: Element[] = [];\n\n      domEach(elems, (elem) => {\n        for (let next; (next = nextElem(elem)); elem = next) {\n          // FIXME: `matched` might contain duplicates here and the index is too large.\n          if (matches?.(next, matched.length)) break;\n          matched.push(next);\n        }\n      });\n\n      return matched;\n    },\n  )(nextElem, ...postFns);\n\n  return function <T extends AnyNode>(\n    this: Cheerio<T>,\n    selector?: AcceptedFilters<Element> | null,\n    filterSelector?: AcceptedFilters<Element>,\n  ): Cheerio<Element> {\n    // Override `matches` variable with the new target.\n    matches =\n      typeof selector === 'string'\n        ? (elem: Element) => select.is(elem, selector, this.options)\n        : selector\n          ? getFilterFn(selector)\n          : null;\n\n    const ret = innerMatcher.call(this, filterSelector);\n\n    // Set `matches` to `null`, so we don't waste memory.\n    matches = null;\n\n    return ret;\n  };\n}\n\nfunction _removeDuplicates<T extends AnyNode>(elems: T[]): T[] {\n  return elems.length > 1 ? [...new Set<T>(elems)] : elems;\n}\n\n/**\n * Get the parent of each element in the current set of matched elements,\n * optionally filtered by a selector.\n *\n * @category Traversing\n * @example\n *\n * ```js\n * $('.pear').parent().attr('id');\n * //=> fruits\n * ```\n *\n * @param selector - If specified filter for parent.\n * @returns The parents.\n * @see {@link https://api.jquery.com/parent/}\n */\nexport const parent: <T extends AnyNode>(\n  this: Cheerio<T>,\n  selector?: AcceptedFilters<Element>,\n) => Cheerio<Element> = _singleMatcher(\n  ({ parent }) => (parent && !isDocument(parent) ? (parent as Element) : null),\n  _removeDuplicates,\n);\n\n/**\n * Get a set of parents filtered by `selector` of each element in the current\n * set of match elements.\n *\n * @category Traversing\n * @example\n *\n * ```js\n * $('.orange').parents().length;\n * //=> 2\n * $('.orange').parents('#fruits').length;\n * //=> 1\n * ```\n *\n * @param selector - If specified filter for parents.\n * @returns The parents.\n * @see {@link https://api.jquery.com/parents/}\n */\nexport const parents: <T extends AnyNode>(\n  this: Cheerio<T>,\n  selector?: AcceptedFilters<Element>,\n) => Cheerio<Element> = _matcher(\n  (elem) => {\n    const matched: Element[] = [];\n    while (elem.parent && !isDocument(elem.parent)) {\n      matched.push(elem.parent as Element);\n      elem = elem.parent;\n    }\n    return matched;\n  },\n  uniqueSort,\n  // eslint-disable-next-line unicorn/no-array-reverse\n  (elems) => elems.reverse(),\n);\n\n/**\n * Get the ancestors of each element in the current set of matched elements, up\n * to but not including the element matched by the selector, DOM node, or\n * cheerio object.\n *\n * @category Traversing\n * @example\n *\n * ```js\n * $('.orange').parentsUntil('#food').length;\n * //=> 1\n * ```\n *\n * @param selector - Selector for element to stop at.\n * @param filterSelector - Optional filter for parents.\n * @returns The parents.\n * @see {@link https://api.jquery.com/parentsUntil/}\n */\nexport const parentsUntil: <T extends AnyNode>(\n  this: Cheerio<T>,\n  selector?: AcceptedFilters<Element> | null,\n  filterSelector?: AcceptedFilters<Element>,\n) => Cheerio<Element> = _matchUntil(\n  ({ parent }) => (parent && !isDocument(parent) ? (parent as Element) : null),\n  uniqueSort,\n  // eslint-disable-next-line unicorn/no-array-reverse\n  (elems) => elems.reverse(),\n);\n\n/**\n * For each element in the set, get the first element that matches the selector\n * by testing the element itself and traversing up through its ancestors in the\n * DOM tree.\n *\n * @category Traversing\n * @example\n *\n * ```js\n * $('.orange').closest();\n * //=> []\n *\n * $('.orange').closest('.apple');\n * // => []\n *\n * $('.orange').closest('li');\n * //=> [<li class=\"orange\">Orange</li>]\n *\n * $('.orange').closest('#fruits');\n * //=> [<ul id=\"fruits\"> ... </ul>]\n * ```\n *\n * @param selector - Selector for the element to find.\n * @returns The closest nodes.\n * @see {@link https://api.jquery.com/closest/}\n */\nexport function closest<T extends AnyNode>(\n  this: Cheerio<T>,\n  selector?: AcceptedFilters<Element>,\n): Cheerio<AnyNode> {\n  const set: AnyNode[] = [];\n\n  if (!selector) {\n    return this._make(set);\n  }\n\n  const selectOpts = {\n    xmlMode: this.options.xmlMode,\n    root: this._root?.[0],\n  };\n\n  const selectFn =\n    typeof selector === 'string'\n      ? (elem: Element) => select.is(elem, selector, selectOpts)\n      : getFilterFn(selector);\n\n  domEach(this, (elem: AnyNode | null) => {\n    if (elem && !isDocument(elem) && !isTag(elem)) {\n      elem = elem.parent;\n    }\n    while (elem && isTag(elem)) {\n      if (selectFn(elem, 0)) {\n        // Do not add duplicate elements to the set\n        if (!set.includes(elem)) {\n          set.push(elem);\n        }\n        break;\n      }\n      elem = elem.parent;\n    }\n  });\n\n  return this._make(set);\n}\n\n/**\n * Gets the next sibling of each selected element, optionally filtered by a\n * selector.\n *\n * @category Traversing\n * @example\n *\n * ```js\n * $('.apple').next().hasClass('orange');\n * //=> true\n * ```\n *\n * @param selector - If specified filter for sibling.\n * @returns The next nodes.\n * @see {@link https://api.jquery.com/next/}\n */\nexport const next: <T extends AnyNode>(\n  this: Cheerio<T>,\n  selector?: AcceptedFilters<Element>,\n) => Cheerio<Element> = _singleMatcher((elem) => nextElementSibling(elem));\n\n/**\n * Gets all the following siblings of the each selected element, optionally\n * filtered by a selector.\n *\n * @category Traversing\n * @example\n *\n * ```js\n * $('.apple').nextAll();\n * //=> [<li class=\"orange\">Orange</li>, <li class=\"pear\">Pear</li>]\n * $('.apple').nextAll('.orange');\n * //=> [<li class=\"orange\">Orange</li>]\n * ```\n *\n * @param selector - If specified filter for siblings.\n * @returns The next nodes.\n * @see {@link https://api.jquery.com/nextAll/}\n */\nexport const nextAll: <T extends AnyNode>(\n  this: Cheerio<T>,\n  selector?: AcceptedFilters<Element>,\n) => Cheerio<Element> = _matcher((elem) => {\n  const matched = [];\n  while (elem.next) {\n    elem = elem.next;\n    if (isTag(elem)) matched.push(elem);\n  }\n  return matched;\n}, _removeDuplicates);\n\n/**\n * Gets all the following siblings up to but not including the element matched\n * by the selector, optionally filtered by another selector.\n *\n * @category Traversing\n * @example\n *\n * ```js\n * $('.apple').nextUntil('.pear');\n * //=> [<li class=\"orange\">Orange</li>]\n * ```\n *\n * @param selector - Selector for element to stop at.\n * @param filterSelector - If specified filter for siblings.\n * @returns The next nodes.\n * @see {@link https://api.jquery.com/nextUntil/}\n */\nexport const nextUntil: <T extends AnyNode>(\n  this: Cheerio<T>,\n  selector?: AcceptedFilters<Element> | null,\n  filterSelector?: AcceptedFilters<Element>,\n) => Cheerio<Element> = _matchUntil(\n  (el) => nextElementSibling(el),\n  _removeDuplicates,\n);\n\n/**\n * Gets the previous sibling of each selected element optionally filtered by a\n * selector.\n *\n * @category Traversing\n * @example\n *\n * ```js\n * $('.orange').prev().hasClass('apple');\n * //=> true\n * ```\n *\n * @param selector - If specified filter for siblings.\n * @returns The previous nodes.\n * @see {@link https://api.jquery.com/prev/}\n */\nexport const prev: <T extends AnyNode>(\n  this: Cheerio<T>,\n  selector?: AcceptedFilters<Element>,\n) => Cheerio<Element> = _singleMatcher((elem) => prevElementSibling(elem));\n\n/**\n * Gets all the preceding siblings of each selected element, optionally filtered\n * by a selector.\n *\n * @category Traversing\n * @example\n *\n * ```js\n * $('.pear').prevAll();\n * //=> [<li class=\"orange\">Orange</li>, <li class=\"apple\">Apple</li>]\n *\n * $('.pear').prevAll('.orange');\n * //=> [<li class=\"orange\">Orange</li>]\n * ```\n *\n * @param selector - If specified filter for siblings.\n * @returns The previous nodes.\n * @see {@link https://api.jquery.com/prevAll/}\n */\nexport const prevAll: <T extends AnyNode>(\n  this: Cheerio<T>,\n  selector?: AcceptedFilters<Element>,\n) => Cheerio<Element> = _matcher((elem) => {\n  const matched = [];\n  while (elem.prev) {\n    elem = elem.prev;\n    if (isTag(elem)) matched.push(elem);\n  }\n  return matched;\n}, _removeDuplicates);\n\n/**\n * Gets all the preceding siblings up to but not including the element matched\n * by the selector, optionally filtered by another selector.\n *\n * @category Traversing\n * @example\n *\n * ```js\n * $('.pear').prevUntil('.apple');\n * //=> [<li class=\"orange\">Orange</li>]\n * ```\n *\n * @param selector - Selector for element to stop at.\n * @param filterSelector - If specified filter for siblings.\n * @returns The previous nodes.\n * @see {@link https://api.jquery.com/prevUntil/}\n */\nexport const prevUntil: <T extends AnyNode>(\n  this: Cheerio<T>,\n  selector?: AcceptedFilters<Element> | null,\n  filterSelector?: AcceptedFilters<Element>,\n) => Cheerio<Element> = _matchUntil(\n  (el) => prevElementSibling(el),\n  _removeDuplicates,\n);\n\n/**\n * Get the siblings of each element (excluding the element) in the set of\n * matched elements, optionally filtered by a selector.\n *\n * @category Traversing\n * @example\n *\n * ```js\n * $('.pear').siblings().length;\n * //=> 2\n *\n * $('.pear').siblings('.orange').length;\n * //=> 1\n * ```\n *\n * @param selector - If specified filter for siblings.\n * @returns The siblings.\n * @see {@link https://api.jquery.com/siblings/}\n */\nexport const siblings: <T extends AnyNode>(\n  this: Cheerio<T>,\n  selector?: AcceptedFilters<Element>,\n) => Cheerio<Element> = _matcher(\n  (elem) =>\n    getSiblings(elem).filter((el): el is Element => isTag(el) && el !== elem),\n  uniqueSort,\n);\n\n/**\n * Gets the element children of each element in the set of matched elements.\n *\n * @category Traversing\n * @example\n *\n * ```js\n * $('#fruits').children().length;\n * //=> 3\n *\n * $('#fruits').children('.pear').text();\n * //=> Pear\n * ```\n *\n * @param selector - If specified filter for children.\n * @returns The children.\n * @see {@link https://api.jquery.com/children/}\n */\nexport const children: <T extends AnyNode>(\n  this: Cheerio<T>,\n  selector?: AcceptedFilters<Element>,\n) => Cheerio<Element> = _matcher(\n  (elem) => getChildren(elem).filter(isTag),\n  _removeDuplicates,\n);\n\n/**\n * Gets the children of each element in the set of matched elements, including\n * text and comment nodes.\n *\n * @category Traversing\n * @example\n *\n * ```js\n * $('#fruits').contents().length;\n * //=> 3\n * ```\n *\n * @returns The children.\n * @see {@link https://api.jquery.com/contents/}\n */\nexport function contents<T extends AnyNode>(\n  this: Cheerio<T>,\n): Cheerio<AnyNode> {\n  const elems = this.toArray().flatMap((elem) =>\n    hasChildren(elem) ? elem.children : [],\n  );\n  return this._make(elems);\n}\n\n/**\n * Iterates over a cheerio object, executing a function for each matched\n * element. When the callback is fired, the function is fired in the context of\n * the DOM element, so `this` refers to the current element, which is equivalent\n * to the function parameter `element`. To break out of the `each` loop early,\n * return with `false`.\n *\n * @category Traversing\n * @example\n *\n * ```js\n * const fruits = [];\n *\n * $('li').each(function (i, elem) {\n *   fruits[i] = $(this).text();\n * });\n *\n * fruits.join(', ');\n * //=> Apple, Orange, Pear\n * ```\n *\n * @param fn - Function to execute.\n * @returns The instance itself, useful for chaining.\n * @see {@link https://api.jquery.com/each/}\n */\nexport function each<T>(\n  this: Cheerio<T>,\n  fn: (this: T, i: number, el: T) => void | boolean,\n): Cheerio<T> {\n  let i = 0;\n  const len = this.length;\n  while (i < len && fn.call(this[i], i, this[i]) !== false) ++i;\n  return this;\n}\n\n/**\n * Pass each element in the current matched set through a function, producing a\n * new Cheerio object containing the return values. The function can return an\n * individual data item or an array of data items to be inserted into the\n * resulting set. If an array is returned, the elements inside the array are\n * inserted into the set. If the function returns null or undefined, no element\n * will be inserted.\n *\n * @category Traversing\n * @example\n *\n * ```js\n * $('li')\n *   .map(function (i, el) {\n *     // this === el\n *     return $(this).text();\n *   })\n *   .toArray()\n *   .join(' ');\n * //=> \"apple orange pear\"\n * ```\n *\n * @param fn - Function to execute.\n * @returns The mapped elements, wrapped in a Cheerio collection.\n * @see {@link https://api.jquery.com/map/}\n */\nexport function map<T, M>(\n  this: Cheerio<T>,\n  fn: (this: T, i: number, el: T) => M[] | M | null | undefined,\n): Cheerio<M> {\n  let elems: M[] = [];\n  for (let i = 0; i < this.length; i++) {\n    const el = this[i];\n    const val = fn.call(el, i, el);\n    if (val != null) {\n      // eslint-disable-next-line unicorn/prefer-spread\n      elems = elems.concat(val);\n    }\n  }\n  return this._make(elems);\n}\n\n/**\n * Creates a function to test if a filter is matched.\n *\n * @param match - A filter.\n * @returns A function that determines if a filter has been matched.\n */\nfunction getFilterFn<T>(\n  match: FilterFunction<T> | Cheerio<T> | T,\n): (el: T, i: number) => boolean {\n  if (typeof match === 'function') {\n    return (el, i) => (match as FilterFunction<T>).call(el, i, el);\n  }\n  if (isCheerio<T>(match)) {\n    return (el) => Array.prototype.includes.call(match, el);\n  }\n  return (el) => match === el;\n}\n\n/**\n * Iterates over a cheerio object, reducing the set of selector elements to\n * those that match the selector or pass the function's test.\n *\n * This is the definition for using type guards; have a look below for other\n * ways to invoke this method. The function is executed in the context of the\n * selected element, so `this` refers to the current element.\n *\n * @category Traversing\n * @example <caption>Function</caption>\n *\n * ```js\n * $('li')\n *   .filter(function (i, el) {\n *     // this === el\n *     return $(this).attr('class') === 'orange';\n *   })\n *   .attr('class'); //=> orange\n * ```\n *\n * @param match - Value to look for, following the rules above.\n * @returns The filtered collection.\n * @see {@link https://api.jquery.com/filter/}\n */\nexport function filter<T, S extends T>(\n  this: Cheerio<T>,\n  match: (this: T, index: number, value: T) => value is S,\n): Cheerio<S>;\n/**\n * Iterates over a cheerio object, reducing the set of selector elements to\n * those that match the selector or pass the function's test.\n *\n * - When a Cheerio selection is specified, return only the elements contained in\n *   that selection.\n * - When an element is specified, return only that element (if it is contained in\n *   the original selection).\n * - If using the function method, the function is executed in the context of the\n *   selected element, so `this` refers to the current element.\n *\n * @category Traversing\n * @example <caption>Selector</caption>\n *\n * ```js\n * $('li').filter('.orange').attr('class');\n * //=> orange\n * ```\n *\n * @example <caption>Function</caption>\n *\n * ```js\n * $('li')\n *   .filter(function (i, el) {\n *     // this === el\n *     return $(this).attr('class') === 'orange';\n *   })\n *   .attr('class'); //=> orange\n * ```\n *\n * @param match - Value to look for, following the rules above. See\n *   {@link AcceptedFilters}.\n * @returns The filtered collection.\n * @see {@link https://api.jquery.com/filter/}\n */\nexport function filter<T, S extends AcceptedFilters<T>>(\n  this: Cheerio<T>,\n  match: S,\n): Cheerio<S extends string ? Element : T>;\nexport function filter<T>(\n  this: Cheerio<T>,\n  match: AcceptedFilters<T>,\n): Cheerio<unknown> {\n  return this._make<unknown>(\n    filterArray(this.toArray(), match, this.options.xmlMode, this._root?.[0]),\n  );\n}\n\n/**\n * Filter an array of nodes with either a selector or predicate.\n *\n * @param nodes - The nodes to filter.\n * @param match - Selector or predicate used to keep nodes.\n * @param xmlMode - Whether selector matching should use XML mode.\n * @param root - Optional document root used for selector matching.\n */\nexport function filterArray<T>(\n  nodes: T[],\n  match: AcceptedFilters<T>,\n  xmlMode?: boolean,\n  root?: Document,\n): Element[] | T[] {\n  return typeof match === 'string'\n    ? select.filter(match, nodes as unknown as AnyNode[], { xmlMode, root })\n    : nodes.filter(getFilterFn<T>(match));\n}\n\n/**\n * Checks the current list of elements and returns `true` if _any_ of the\n * elements match the selector. If using an element or Cheerio selection,\n * returns `true` if _any_ of the elements match. If using a predicate function,\n * the function is executed in the context of the selected element, so `this`\n * refers to the current element.\n *\n * @category Traversing\n * @param selector - Selector for the selection.\n * @returns Whether or not the selector matches an element of the instance.\n * @see {@link https://api.jquery.com/is/}\n */\nexport function is<T>(\n  this: Cheerio<T>,\n  selector?: AcceptedFilters<T>,\n): boolean {\n  const nodes = this.toArray();\n  return typeof selector === 'string'\n    ? select.some(\n        (nodes as unknown as AnyNode[]).filter(isTag),\n        selector,\n        this.options,\n      )\n    : selector\n      ? nodes.some(getFilterFn<T>(selector))\n      : false;\n}\n\n/**\n * Remove elements from the set of matched elements. Given a Cheerio object that\n * represents a set of DOM elements, the `.not()` method constructs a new\n * Cheerio object from a subset of the matching elements. The supplied selector\n * is tested against each element; the elements that don't match the selector\n * will be included in the result.\n *\n * The `.not()` method can take a function as its argument in the same way that\n * `.filter()` does. Elements for which the function returns `true` are excluded\n * from the filtered set; all other elements are included.\n *\n * @category Traversing\n * @example <caption>Selector</caption>\n *\n * ```js\n * $('li').not('.apple').length;\n * //=> 2\n * ```\n *\n * @example <caption>Function</caption>\n *\n * ```js\n * $('li').not(function (i, el) {\n *   // this === el\n *   return $(this).attr('class') === 'orange';\n * }).length; //=> 2\n * ```\n *\n * @param match - Value to look for, following the rules above.\n * @returns The filtered collection.\n * @see {@link https://api.jquery.com/not/}\n */\nexport function not<T extends AnyNode>(\n  this: Cheerio<T>,\n  match: AcceptedFilters<T>,\n): Cheerio<T> {\n  let nodes = this.toArray();\n\n  if (typeof match === 'string') {\n    const matches = new Set<AnyNode>(select.filter(match, nodes, this.options));\n    nodes = nodes.filter((el) => !matches.has(el));\n  } else {\n    const filterFn = getFilterFn(match);\n    nodes = nodes.filter((el, i) => !filterFn(el, i));\n  }\n\n  return this._make(nodes);\n}\n\n/**\n * Filters the set of matched elements to only those which have the given DOM\n * element as a descendant or which have a descendant that matches the given\n * selector. Equivalent to `.filter(':has(selector)')`.\n *\n * @category Traversing\n * @example <caption>Selector</caption>\n *\n * ```js\n * $('ul').has('.pear').attr('id');\n * //=> fruits\n * ```\n *\n * @example <caption>Element</caption>\n *\n * ```js\n * $('ul').has($('.pear')[0]).attr('id');\n * //=> fruits\n * ```\n *\n * @param selectorOrHaystack - Element to look for.\n * @returns The filtered collection.\n * @see {@link https://api.jquery.com/has/}\n */\nexport function has(\n  this: Cheerio<AnyNode | Element>,\n  selectorOrHaystack: string | Cheerio<Element> | Element,\n): Cheerio<AnyNode | Element> {\n  return this.filter(\n    typeof selectorOrHaystack === 'string'\n      ? // Using the `:has` selector here short-circuits searches.\n        `:has(${selectorOrHaystack})`\n      : (_, el) => this._make(el).find(selectorOrHaystack).length > 0,\n  );\n}\n\n/**\n * Will select the first element of a cheerio object.\n *\n * @category Traversing\n * @example\n *\n * ```js\n * $('#fruits').children().first().text();\n * //=> Apple\n * ```\n *\n * @returns The first element.\n * @see {@link https://api.jquery.com/first/}\n */\nexport function first<T extends AnyNode>(this: Cheerio<T>): Cheerio<T> {\n  return this.length > 1 ? this._make(this[0]) : this;\n}\n\n/**\n * Will select the last element of a cheerio object.\n *\n * @category Traversing\n * @example\n *\n * ```js\n * $('#fruits').children().last().text();\n * //=> Pear\n * ```\n *\n * @returns The last element.\n * @see {@link https://api.jquery.com/last/}\n */\nexport function last<T>(this: Cheerio<T>): Cheerio<T> {\n  return this.length > 0 ? this._make(this[this.length - 1]) : this;\n}\n\n/**\n * Reduce the set of matched elements to the one at the specified index. Use\n * `.eq(-i)` to count backwards from the last selected element.\n *\n * @category Traversing\n * @example\n *\n * ```js\n * $('li').eq(0).text();\n * //=> Apple\n *\n * $('li').eq(-1).text();\n * //=> Pear\n * ```\n *\n * @param i - Index of the element to select.\n * @returns The element at the `i`th position.\n * @see {@link https://api.jquery.com/eq/}\n */\nexport function eq<T>(this: Cheerio<T>, i: number): Cheerio<T> {\n  i = +i;\n\n  // Use the first identity optimization if possible\n  if (i === 0 && this.length <= 1) return this;\n\n  if (i < 0) i = this.length + i;\n  return this._make(this[i] ?? []);\n}\n\n/**\n * Retrieve one of the elements matched by the Cheerio object, at the `i`th\n * position.\n *\n * @category Traversing\n * @example\n *\n * ```js\n * $('li').get(0).tagName;\n * //=> li\n * ```\n *\n * @param i - Element to retrieve.\n * @returns The element at the `i`th position.\n * @see {@link https://api.jquery.com/get/}\n */\nexport function get<T>(this: Cheerio<T>, i: number): T | undefined;\n/**\n * Retrieve all elements matched by the Cheerio object, as an array.\n *\n * @category Traversing\n * @example\n *\n * ```js\n * $('li').get().length;\n * //=> 3\n * ```\n *\n * @returns All elements matched by the Cheerio object.\n * @see {@link https://api.jquery.com/get/}\n */\nexport function get<T>(this: Cheerio<T>): T[];\nexport function get<T>(this: Cheerio<T>, i?: number): T | T[] {\n  if (i == null) {\n    return this.toArray();\n  }\n  return this[i < 0 ? this.length + i : i];\n}\n\n/**\n * Retrieve all the DOM elements contained in the jQuery set as an array.\n *\n * @example\n *\n * ```js\n * $('li').toArray();\n * //=> [ {...}, {...}, {...} ]\n * ```\n *\n * @returns The contained items.\n */\nexport function toArray<T>(this: Cheerio<T>): T[] {\n  return (Array.prototype as T[]).slice.call(this);\n}\n\n/**\n * Search for a given element from among the matched elements.\n *\n * @category Traversing\n * @example\n *\n * ```js\n * $('.pear').index();\n * //=> 2 $('.orange').index('li');\n * //=> 1\n * $('.apple').index($('#fruit, li'));\n * //=> 1\n * ```\n *\n * @param selectorOrNeedle - Element to look for.\n * @returns The index of the element.\n * @see {@link https://api.jquery.com/index/}\n */\nexport function index<T extends AnyNode>(\n  this: Cheerio<T>,\n  selectorOrNeedle?: string | Cheerio<AnyNode> | AnyNode,\n): number {\n  let $haystack: Cheerio<AnyNode>;\n  let needle: AnyNode;\n\n  if (selectorOrNeedle == null) {\n    $haystack = this.parent().children();\n    needle = this[0];\n  } else if (typeof selectorOrNeedle === 'string') {\n    $haystack = this._make<AnyNode>(selectorOrNeedle);\n    needle = this[0];\n  } else {\n    // eslint-disable-next-line unicorn/no-this-assignment\n    $haystack = this;\n    needle = isCheerio(selectorOrNeedle)\n      ? selectorOrNeedle[0]\n      : selectorOrNeedle;\n  }\n\n  return Array.prototype.indexOf.call($haystack, needle);\n}\n\n/**\n * Gets the elements matching the specified range (0-based position).\n *\n * @category Traversing\n * @example\n *\n * ```js\n * $('li').slice(1).eq(0).text();\n * //=> 'Orange'\n *\n * $('li').slice(1, 2).length;\n * //=> 1\n * ```\n *\n * @param start - A position at which the elements begin to be selected. If\n *   negative, it indicates an offset from the end of the set.\n * @param end - A position at which the elements stop being selected. If\n *   negative, it indicates an offset from the end of the set. If omitted, the\n *   range continues until the end of the set.\n * @returns The elements matching the specified range.\n * @see {@link https://api.jquery.com/slice/}\n */\nexport function slice<T>(\n  this: Cheerio<T>,\n  start?: number,\n  end?: number,\n): Cheerio<T> {\n  return this._make<T>(Array.prototype.slice.call(this, start, end));\n}\n\n/**\n * End the most recent filtering operation in the current chain and return the\n * set of matched elements to its previous state.\n *\n * @category Traversing\n * @example\n *\n * ```js\n * $('li').eq(0).end().length;\n * //=> 3\n * ```\n *\n * @returns The previous state of the set of matched elements.\n * @see {@link https://api.jquery.com/end/}\n */\nexport function end<T>(this: Cheerio<T>): Cheerio<AnyNode> {\n  return (this.prevObject as Cheerio<AnyNode> | null) ?? this._make([]);\n}\n\n/**\n * Add elements to the set of matched elements.\n *\n * @category Traversing\n * @example\n *\n * ```js\n * $('.apple').add('.orange').length;\n * //=> 2\n * ```\n *\n * @param other - Elements to add.\n * @param context - Optionally the context of the new selection.\n * @returns The combined set.\n * @see {@link https://api.jquery.com/add/}\n */\nexport function add<S extends AnyNode, T extends AnyNode>(\n  this: Cheerio<T>,\n  other: string | Cheerio<S> | S | S[],\n  context?: Cheerio<S> | string,\n): Cheerio<S | T> {\n  const selection = this._make(other, context);\n  const contents = uniqueSort([...this.get(), ...selection.get()]);\n  return this._make(contents);\n}\n\n/**\n * Add the previous set of elements on the stack to the current set, optionally\n * filtered by a selector.\n *\n * @category Traversing\n * @example\n *\n * ```js\n * $('li').eq(0).addBack('.orange').length;\n * //=> 2\n * ```\n *\n * @param selector - Selector for the elements to add.\n * @returns The combined set.\n * @see {@link https://api.jquery.com/addBack/}\n */\nexport function addBack<T extends AnyNode>(\n  this: Cheerio<T>,\n  selector?: string,\n): Cheerio<AnyNode> {\n  return this.prevObject\n    ? this.add<AnyNode, T>(\n        selector ? this.prevObject.filter(selector) : this.prevObject,\n      )\n    : this;\n}\n"
  },
  {
    "path": "src/cheerio.spec.ts",
    "content": "import type { AnyNode, Element } from 'domhandler';\nimport { parseDOM } from 'htmlparser2';\nimport { describe, expect, it } from 'vitest';\nimport { cheerio, food, fruits, noscript } from './__fixtures__/fixtures.js';\nimport type { Cheerio } from './index.js';\n\ndeclare module './index.js' {\n  interface Cheerio<T> {\n    myPlugin(...args: unknown[]): {\n      context: Cheerio<T>;\n      args: unknown[];\n    };\n    foo(this: void): void;\n  }\n}\n\nfunction testAppleSelect($apple: ArrayLike<Element>) {\n  expect($apple).toHaveLength(1);\n  const apple = $apple[0];\n  expect(apple.parentNode).toHaveProperty('tagName', 'ul');\n  expect(apple.prev).toBe(null);\n  expect((apple.next as Element).attribs).toHaveProperty('class', 'orange');\n  expect(apple.childNodes).toHaveLength(1);\n  expect(apple.childNodes[0]).toHaveProperty('data', 'Apple');\n}\n\ndescribe('cheerio', () => {\n  it('cheerio(null) should be empty', () => {\n    expect(cheerio(null as never)).toHaveLength(0);\n  });\n\n  it('cheerio(undefined) should be empty', () => {\n    expect(cheerio(undefined)).toHaveLength(0);\n  });\n\n  it(\"cheerio('') should be empty\", () => {\n    expect(cheerio('')).toHaveLength(0);\n  });\n\n  it('cheerio(selector) with no context or root should be empty', () => {\n    expect(cheerio('.h2')).toHaveLength(0);\n    expect(cheerio('#fruits')).toHaveLength(0);\n  });\n\n  it('cheerio(node) : should override previously-loaded nodes', () => {\n    const $ = cheerio.load('<div><span></span></div>');\n    const spanNode = $('span')[0];\n    const $span = $(spanNode);\n    expect($span[0]).toBe(spanNode);\n  });\n\n  it('should be able to create html without a root or context', () => {\n    const $h2 = cheerio('<h2>');\n    expect($h2).not.toHaveLength(0);\n    expect($h2).toHaveLength(1);\n    expect($h2[0]).toHaveProperty('tagName', 'h2');\n  });\n\n  it('should be able to create complicated html', () => {\n    const $script = cheerio(\n      '<script src=\"script.js\" type=\"text/javascript\"></script>',\n    ) as Cheerio<Element>;\n    expect($script).not.toHaveLength(0);\n    expect($script).toHaveLength(1);\n    expect($script[0].attribs).toHaveProperty('src', 'script.js');\n    expect($script[0].attribs).toHaveProperty('type', 'text/javascript');\n    expect($script[0].childNodes).toHaveLength(0);\n  });\n\n  // eslint-disable-next-line vitest/expect-expect\n  it('should be able to select .apple with only a context', () => {\n    const $apple = cheerio('.apple', fruits);\n    testAppleSelect($apple);\n  });\n\n  // eslint-disable-next-line vitest/expect-expect\n  it('should be able to select .apple with a node as context', () => {\n    const $apple = cheerio('.apple', cheerio(fruits)[0]);\n    testAppleSelect($apple);\n  });\n\n  // eslint-disable-next-line vitest/expect-expect\n  it('should be able to select .apple with only a root', () => {\n    const $apple = cheerio('.apple', null, fruits);\n    testAppleSelect($apple);\n  });\n\n  it('should be able to select an id', () => {\n    const $fruits = cheerio('#fruits', null, fruits);\n    expect($fruits).toHaveLength(1);\n    expect($fruits[0].attribs).toHaveProperty('id', 'fruits');\n  });\n\n  it('should be able to select a tag', () => {\n    const $ul = cheerio('ul', fruits);\n    expect($ul).toHaveLength(1);\n    expect($ul[0].tagName).toBe('ul');\n  });\n\n  it('should accept a node reference as a context', () => {\n    const $elems = cheerio('<div><span></span></div>');\n    expect(cheerio('span', $elems[0])).toHaveLength(1);\n  });\n\n  it('should accept an array of node references as a context', () => {\n    const $elems = cheerio('<div><span></span></div>');\n    expect(cheerio('span', $elems.toArray())).toHaveLength(1);\n  });\n\n  it('should select only elements inside given context (Issue #193)', () => {\n    const $ = cheerio.load(food);\n    const $fruits = $('#fruits');\n    const fruitElements = $('li', $fruits);\n\n    expect(fruitElements).toHaveLength(3);\n  });\n\n  it('should be able to select multiple tags', () => {\n    const $fruits = cheerio('li', null, fruits);\n    expect($fruits).toHaveLength(3);\n    const classes = ['apple', 'orange', 'pear'];\n    $fruits.each((idx, $fruit) => {\n      expect($fruit.attribs).toHaveProperty('class', classes[idx]);\n    });\n  });\n\n  // eslint-disable-next-line vitest/expect-expect\n  it('should be able to do: cheerio(\"#fruits .apple\")', () => {\n    const $apple = cheerio('#fruits .apple', fruits);\n    testAppleSelect($apple);\n  });\n\n  // eslint-disable-next-line vitest/expect-expect\n  it('should be able to do: cheerio(\"li.apple\")', () => {\n    const $apple = cheerio('li.apple', fruits);\n    testAppleSelect($apple);\n  });\n\n  // eslint-disable-next-line vitest/expect-expect\n  it('should be able to select by attributes', () => {\n    const $apple = cheerio('li[class=apple]', fruits);\n    testAppleSelect($apple);\n  });\n\n  it('should be able to select multiple classes: cheerio(\".btn.primary\")', () => {\n    const $a = cheerio(\n      '.btn.primary',\n      '<p><a class=\"btn primary\" href=\"#\">Save</a></p>',\n    );\n    expect($a).toHaveLength(1);\n    expect($a[0].childNodes[0]).toHaveProperty('data', 'Save');\n  });\n\n  it('should not create a top-level node', () => {\n    const $elem = cheerio('* div', '<div>');\n    expect($elem).toHaveLength(0);\n  });\n\n  it('should be able to select multiple elements: cheerio(\".apple, #fruits\")', () => {\n    const $elems = cheerio('.apple, #fruits', fruits);\n    expect($elems).toHaveLength(2);\n\n    const $apple = $elems\n      .toArray()\n      .filter((elem) => elem.attribs['class'] === 'apple');\n    const $fruit = $elems\n      .toArray()\n      .find((elem) => elem.attribs['id'] === 'fruits');\n    testAppleSelect($apple);\n    expect($fruit?.attribs).toHaveProperty('id', 'fruits');\n  });\n\n  it('should select first element cheerio(:first)', () => {\n    const $elem = cheerio('li:first', fruits);\n    expect($elem.attr('class')).toBe('apple');\n\n    const $filtered = cheerio('li', fruits).filter(':even');\n    expect($filtered).toHaveLength(2);\n  });\n\n  it('should be able to select immediate children: cheerio(\"#fruits > .pear\")', () => {\n    const $food = cheerio(food);\n    cheerio('.pear', $food).append('<li class=\"pear\">Another Pear!</li>');\n    expect(cheerio('#fruits .pear', $food)).toHaveLength(2);\n    const $elem = cheerio('#fruits > .pear', $food);\n    expect($elem).toHaveLength(1);\n    expect($elem.attr('class')).toBe('pear');\n  });\n\n  it('should be able to select immediate children: cheerio(\".apple + .pear\")', () => {\n    expect(cheerio('.apple + li', fruits)).toHaveLength(1);\n    expect(cheerio('.apple + .pear', fruits)).toHaveLength(0);\n    const $elem = cheerio('.apple + .orange', fruits);\n    expect($elem).toHaveLength(1);\n    expect($elem.attr('class')).toBe('orange');\n  });\n\n  it('should be able to select immediate children: cheerio(\".apple ~ .pear\")', () => {\n    expect(cheerio('.apple ~ li', fruits)).toHaveLength(2);\n    expect(cheerio('.apple ~ .pear', fruits).attr('class')).toBe('pear');\n  });\n\n  it('should handle wildcards on attributes: cheerio(\"li[class*=r]\")', () => {\n    const $elem = cheerio('li[class*=r]', fruits);\n    expect($elem).toHaveLength(2);\n    expect($elem.eq(0).attr('class')).toBe('orange');\n    expect($elem.eq(1).attr('class')).toBe('pear');\n  });\n\n  it('should handle beginning of attr selectors: cheerio(\"li[class^=o]\")', () => {\n    const $elem = cheerio('li[class^=o]', fruits);\n    expect($elem).toHaveLength(1);\n    expect($elem.eq(0).attr('class')).toBe('orange');\n  });\n\n  it('should handle beginning of attr selectors: cheerio(\"li[class$=e]\")', () => {\n    const $elem = cheerio('li[class$=e]', fruits);\n    expect($elem).toHaveLength(2);\n    expect($elem.eq(0).attr('class')).toBe('apple');\n    expect($elem.eq(1).attr('class')).toBe('orange');\n  });\n\n  it('(extended Array) should not interfere with prototype methods (issue #119)', () => {\n    const extended: AnyNode[] = [];\n    // @ts-expect-error - Ignore for testing\n    extended.find =\n      // @ts-expect-error - Ignore for testing\n      extended.children =\n      // @ts-expect-error - Ignore for testing\n      extended.each =\n        () => {\n          /* Ignore */\n        };\n    const $empty = cheerio(extended);\n\n    // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n    expect($empty.find).toBe(cheerio.prototype.find);\n    // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n    expect($empty.children).toBe(cheerio.prototype.children);\n    // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n    expect($empty.each).toBe(cheerio.prototype.each);\n  });\n\n  it('cheerio.html(null) should return a \"\" string', () => {\n    expect(cheerio.html(null as never)).toBe('');\n  });\n\n  it('should set html(number) as a string', () => {\n    const $elem = cheerio('<div>');\n    $elem.html(123 as never);\n    expect(typeof $elem.text()).toBe('string');\n  });\n\n  it('should set text(number) as a string', () => {\n    const $elem = cheerio('<div>');\n    $elem.text(123 as never);\n    expect(typeof $elem.text()).toBe('string');\n  });\n\n  describe('.load', () => {\n    it('should generate selections as proper instances', () => {\n      const $ = cheerio.load(fruits);\n\n      expect($('.apple')).toBeInstanceOf($);\n    });\n\n    // Issue #1092\n    it('should handle a character `)` in `:contains` selector', () => {\n      const result =\n        cheerio.load('<p>)aaa</p>')(String.raw`:contains('\\)aaa')`);\n      expect(result).toHaveLength(3);\n      expect(result.first().prop('tagName')).toBe('HTML');\n      expect(result.eq(1).prop('tagName')).toBe('BODY');\n      expect(result.last().prop('tagName')).toBe('P');\n    });\n\n    it('should be able to filter down using the context', () => {\n      const $ = cheerio.load(fruits);\n      const apple = $('.apple', 'ul');\n      const lis = $('li', 'ul');\n\n      expect(apple).toHaveLength(1);\n      expect(lis).toHaveLength(3);\n    });\n\n    it('should preserve root content', () => {\n      const $ = cheerio.load(fruits);\n      // Root should not be overwritten\n      const el = $('<div></div>');\n      expect(Object.is(el, el._root)).toBe(false);\n      // Query has to have results\n      expect($('li', 'ul')).toHaveLength(3);\n    });\n\n    it('should allow loading a pre-parsed DOM', () => {\n      const dom = parseDOM(food);\n      const $ = cheerio.load(dom);\n\n      expect($('ul')).toHaveLength(3);\n    });\n\n    it('should allow loading a single element', () => {\n      const el = parseDOM(food)[0];\n      const $ = cheerio.load(el);\n\n      expect($('ul')).toHaveLength(3);\n    });\n\n    it('should render xml in html() when options.xml = true', () => {\n      const str = '<MixedCaseTag UPPERCASEATTRIBUTE=\"\"></MixedCaseTag>';\n      const expected = '<MixedCaseTag UPPERCASEATTRIBUTE=\"\"/>';\n      const $ = cheerio.load(str, { xml: true });\n\n      expect($('MixedCaseTag').get(0)).toHaveProperty(\n        'tagName',\n        'MixedCaseTag',\n      );\n      expect($.html()).toBe(expected);\n    });\n\n    it('should render xml in html() when options.xml = true passed to html()', () => {\n      const str = '<MixedCaseTag UPPERCASEATTRIBUTE=\"\"></MixedCaseTag>';\n      // Since parsing done without xml flag, all tags converted to lowercase\n      const expectedXml =\n        '<html><head/><body><mixedcasetag uppercaseattribute=\"\"/></body></html>';\n      const expectedNoXml =\n        '<html><head></head><body><mixedcasetag uppercaseattribute=\"\"></mixedcasetag></body></html>';\n      const $ = cheerio.load(str);\n\n      expect($('MixedCaseTag').get(0)).toHaveProperty(\n        'tagName',\n        'mixedcasetag',\n      );\n      expect($.html()).toBe(expectedNoXml);\n      expect($.html({ xml: true })).toBe(expectedXml);\n    });\n\n    it('should respect options on the element level', () => {\n      const str =\n        '<!doctype html><html><head><title>Some test</title></head><body><footer><p>Copyright &copy; 2003-2014</p></footer></body></html>';\n      const expectedHtml = '<p>Copyright &copy; 2003-2014</p>';\n      const expectedXml = '<p>Copyright © 2003-2014</p>';\n      const domNotEncoded = cheerio.load(str, {\n        xml: { decodeEntities: false },\n      });\n      const domEncoded = cheerio.load(str);\n\n      expect(domNotEncoded('footer').html()).toBe(expectedHtml);\n      expect(domEncoded('footer').html()).toBe(expectedXml);\n    });\n\n    it('should use htmlparser2 if xml option is used', () => {\n      const str = '<div></div>';\n      const dom = cheerio.load(str, null, false);\n      expect(dom.html()).toBe(str);\n    });\n\n    it('should return a fully-qualified Function', () => {\n      const $ = cheerio.load('<div>');\n\n      expect($).toBeInstanceOf(Function);\n    });\n\n    describe('prototype extensions', () => {\n      it('should honor extensions defined on `prototype` property', () => {\n        const $ = cheerio.load('<div>');\n\n        ($.prototype as Cheerio<AnyNode>).myPlugin = function (\n          ...args: unknown[]\n        ) {\n          return {\n            context: this,\n            args,\n          };\n        };\n\n        const $div = $('div');\n\n        expect(typeof $div.myPlugin).toBe('function');\n        expect($div.myPlugin().context).toBe($div);\n        expect($div.myPlugin(1, 2, 3).args).toStrictEqual([1, 2, 3]);\n      });\n\n      it('should honor extensions defined on `fn` property', () => {\n        const $ = cheerio.load('<div>');\n        $.fn.myPlugin = function (...args: unknown[]) {\n          return {\n            context: this,\n            args,\n          };\n        };\n\n        const $div = $('div');\n\n        expect(typeof $div.myPlugin).toBe('function');\n        expect($div.myPlugin().context).toBe($div);\n        expect($div.myPlugin(1, 2, 3).args).toStrictEqual([1, 2, 3]);\n      });\n\n      it('should isolate extensions between loaded functions', () => {\n        const $a = cheerio.load('<div>');\n        const $b = cheerio.load('<div>');\n\n        ($a.prototype as Cheerio<AnyNode>).foo = () => {\n          /* Ignore */\n        };\n\n        expect($b('div').foo).toBeUndefined();\n      });\n    });\n  });\n\n  describe('parse5 options', () => {\n    // Should parse noscript tags only with false option value\n    it('{scriptingEnabled: ???}', () => {\n      // [default] `scriptingEnabled: true` - tag contains one text element\n      const withScripts = cheerio.load(noscript)('noscript');\n      expect(withScripts).toHaveLength(1);\n      expect(withScripts[0].children).toHaveLength(1);\n      expect(withScripts[0].children[0].type).toBe('text');\n\n      // `scriptingEnabled: false` - content of noscript will parsed\n      const noScripts = cheerio.load(noscript, { scriptingEnabled: false })(\n        'noscript',\n      );\n      expect(noScripts).toHaveLength(1);\n      expect(noScripts[0].children).toHaveLength(2);\n      expect(noScripts[0].children[0].type).toBe('comment');\n      expect(noScripts[0].children[1].type).toBe('tag');\n      expect(noScripts[0].children[1]).toHaveProperty('name', 'a');\n\n      // `scriptingEnabled: ???` - should acts as true\n      for (const val of [undefined, null, 0, '']) {\n        const options = { scriptingEnabled: val as never };\n        const result = cheerio.load(noscript, options)('noscript');\n        expect(result).toHaveLength(1);\n        expect(result[0].children).toHaveLength(1);\n        expect(result[0].children[0].type).toBe('text');\n      }\n    });\n\n    // Should contain location data only with truthful option value\n    it('{sourceCodeLocationInfo: ???}', () => {\n      // Location data should not be present\n      for (const val of [undefined, null, 0, false, '']) {\n        const options = { sourceCodeLocationInfo: val as never };\n        const result = cheerio.load(noscript, options)('noscript');\n        expect(result).toHaveLength(1);\n        expect(result[0]).not.toHaveProperty('sourceCodeLocation');\n      }\n\n      // Location data should be present\n      for (const val of [true, 1, 'test']) {\n        const options = { sourceCodeLocationInfo: val as never };\n        const result = cheerio.load(noscript, options)('noscript');\n        expect(result).toHaveLength(1);\n        expect(result[0]).toHaveProperty('sourceCodeLocation');\n        expect(typeof result[0].sourceCodeLocation).toBe('object');\n      }\n    });\n  });\n});\n"
  },
  {
    "path": "src/cheerio.ts",
    "content": "import type { AnyNode, Document, ParentNode } from 'domhandler';\nimport * as Attributes from './api/attributes.js';\nimport * as Css from './api/css.js';\nimport * as Extract from './api/extract.js';\nimport * as Forms from './api/forms.js';\nimport * as Manipulation from './api/manipulation.js';\nimport * as Traversing from './api/traversing.js';\nimport type { InternalOptions } from './options.js';\nimport type { BasicAcceptedElems } from './types.js';\n\ntype MethodsType = typeof Attributes &\n  typeof Traversing &\n  typeof Manipulation &\n  typeof Css &\n  typeof Forms &\n  typeof Extract;\n\n/**\n * The cheerio class is the central class of the library. It wraps a set of\n * elements and provides an API for traversing, modifying, and interacting with\n * the set.\n *\n * Loading a document will return the Cheerio class bound to the root element of\n * the document. The class will be instantiated when querying the document (when\n * calling `$('selector')`).\n *\n * @example This is the HTML markup we will be using in all of the API examples:\n *\n * ```html\n * <ul id=\"fruits\">\n *   <li class=\"apple\">Apple</li>\n *   <li class=\"orange\">Orange</li>\n *   <li class=\"pear\">Pear</li>\n * </ul>\n * ```\n */\nexport abstract class Cheerio<T> implements ArrayLike<T> {\n  length = 0;\n  [index: number]: T;\n\n  options: InternalOptions;\n  /**\n   * The root of the document. Can be set by using the `root` argument of the\n   * constructor.\n   *\n   * @private\n   */\n  _root: Cheerio<Document> | null;\n\n  /**\n   * Instance of cheerio. Methods are specified in the modules. Usage of this\n   * constructor is not recommended. Please use `$.load` instead.\n   *\n   * @private\n   * @param elements - The new selection.\n   * @param root - Sets the root node.\n   * @param options - Options for the instance.\n   */\n  constructor(\n    elements: ArrayLike<T> | undefined,\n    root: Cheerio<Document> | null,\n    options: InternalOptions,\n  ) {\n    this.options = options;\n    this._root = root;\n\n    if (elements) {\n      for (let idx = 0; idx < elements.length; idx++) {\n        this[idx] = elements[idx];\n      }\n      this.length = elements.length;\n    }\n  }\n\n  prevObject: Cheerio<any> | undefined;\n  /**\n   * Make a cheerio object.\n   *\n   * @private\n   * @param dom - The contents of the new object.\n   * @param context - The context of the new object.\n   * @returns The new cheerio object.\n   */\n  abstract _make<T>(\n    dom: ArrayLike<T> | T | string,\n    context?: BasicAcceptedElems<AnyNode>,\n  ): Cheerio<T>;\n\n  /**\n   * Parses some content.\n   *\n   * @private\n   * @param content - Content to parse.\n   * @param options - Options for parsing.\n   * @param isDocument - Allows parser to be switched to fragment mode.\n   * @returns A document containing the `content`.\n   */\n  abstract _parse(\n    content: string | Document | AnyNode | AnyNode[] | Buffer,\n    options: InternalOptions,\n    isDocument: boolean,\n    context: ParentNode | null,\n  ): Document;\n\n  /**\n   * Render an element or a set of elements.\n   *\n   * @private\n   * @param dom - DOM to render.\n   * @returns The rendered DOM.\n   */\n  abstract _render(dom: AnyNode | ArrayLike<AnyNode>): string;\n}\n\n/** Public Cheerio collection interface exposed by the module. */\nexport interface Cheerio<T> extends MethodsType, Iterable<T> {\n  cheerio: '[cheerio object]';\n\n  splice: typeof Array.prototype.splice;\n}\n\n/** Set a signature of the object. */\nCheerio.prototype.cheerio = '[cheerio object]';\n\n/*\n * Make cheerio an array-like object\n */\nCheerio.prototype.splice = Array.prototype.splice;\n\n// Support for (const element of $(...)) iteration:\nCheerio.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];\n\n// Plug in the API\nObject.assign(\n  Cheerio.prototype,\n  Attributes,\n  Traversing,\n  Manipulation,\n  Css,\n  Forms,\n  Extract,\n);\n"
  },
  {
    "path": "src/index-browser.mts",
    "content": "export * from './load-parse.js';\nexport type {\n  AnyNode,\n  Cheerio,\n  CheerioAPI,\n  CheerioOptions,\n  Document,\n  Element,\n  HTMLParser2Options,\n  ParentNode,\n} from './slim.js';\nexport { contains, merge } from './static.js';\nexport type * from './types.js';\n"
  },
  {
    "path": "src/index.spec.ts",
    "content": "import { createServer, type RequestListener, type Server } from 'node:http';\nimport { Writable } from 'node:stream';\nimport { afterEach, describe, expect, it } from 'vitest';\nimport * as cheerio from './index.js';\n\nfunction noop() {\n  // Ignore\n}\n\n// Returns a promise and a resolve function\nfunction getPromise() {\n  let cb!: (error: Error | null | undefined, $: cheerio.CheerioAPI) => void;\n  const promise = new Promise<cheerio.CheerioAPI>((resolve, reject) => {\n    cb = (error, $) => (error ? reject(error) : resolve($));\n  });\n\n  return { promise, cb };\n}\n\nconst TEST_HTML = '<h1>Hello World</h1><a href=\"link\">Example</a>';\nconst TEST_HTML_UTF16 = Buffer.from(TEST_HTML, 'utf16le');\nconst TEST_HTML_UTF16_BOM = Buffer.from([\n  // UTF16-LE BOM\n  0xff,\n  0xfe,\n  ...TEST_HTML_UTF16,\n]);\n\ndescribe('loadBuffer', () => {\n  it('should parse UTF-8 HTML', () => {\n    const $ = cheerio.loadBuffer(Buffer.from(TEST_HTML));\n\n    expect($.html()).toBe(\n      `<html><head></head><body>${TEST_HTML}</body></html>`,\n    );\n  });\n\n  it('should parse UTF-16 HTML', () => {\n    const $ = cheerio.loadBuffer(TEST_HTML_UTF16_BOM);\n\n    expect($.html()).toBe(\n      `<html><head></head><body>${TEST_HTML}</body></html>`,\n    );\n  });\n});\n\ndescribe('stringStream', () => {\n  it('should use parse5 by default', async () => {\n    const { promise, cb } = getPromise();\n    const stream = cheerio.stringStream({}, cb);\n\n    expect(stream).toBeInstanceOf(Writable);\n\n    stream.end(TEST_HTML);\n\n    const $ = await promise;\n\n    expect($.html()).toBe(\n      `<html><head></head><body>${TEST_HTML}</body></html>`,\n    );\n  });\n\n  it('should error from parse5 on buffer', () => {\n    const stream = cheerio.stringStream({}, noop);\n    expect(stream).toBeInstanceOf(Writable);\n\n    expect(() => stream.write(Buffer.from(TEST_HTML))).toThrow(\n      'Parser can work only with string streams.',\n    );\n  });\n\n  it('should use htmlparser2 for XML', async () => {\n    const { promise, cb } = getPromise();\n    const stream = cheerio.stringStream({ xmlMode: true }, cb);\n\n    expect(stream).toBeInstanceOf(Writable);\n\n    stream.end(TEST_HTML);\n\n    const $ = await promise;\n\n    expect($.html()).toBe(TEST_HTML);\n  });\n});\n\ndescribe('decodeStream', () => {\n  it('should use parse5 by default', async () => {\n    const { promise, cb } = getPromise();\n    const stream = cheerio.decodeStream({}, cb);\n\n    expect(stream).toBeInstanceOf(Writable);\n\n    stream.end(TEST_HTML_UTF16_BOM);\n\n    const $ = await promise;\n\n    expect($.html()).toBe(\n      `<html><head></head><body>${TEST_HTML}</body></html>`,\n    );\n    expect($('a').prop('href')).toBe('link');\n  });\n\n  it('should use htmlparser2 for XML', async () => {\n    const { promise, cb } = getPromise();\n    const stream = cheerio.decodeStream({ xmlMode: true }, cb);\n\n    expect(stream).toBeInstanceOf(Writable);\n\n    stream.end(TEST_HTML_UTF16_BOM);\n\n    const $ = await promise;\n\n    expect($.html()).toBe(TEST_HTML);\n  });\n});\n\ndescribe('fromURL', () => {\n  let server: Server | undefined;\n\n  function createTestServer(\n    contentType: string,\n    body: string | Buffer,\n    handler: RequestListener = (_req, res) => {\n      res.writeHead(200, { 'Content-Type': contentType });\n      res.end(body);\n    },\n  ): Promise<number> {\n    return new Promise((resolve, reject) => {\n      server = createServer(handler);\n\n      server.listen(0, () => {\n        const address = server?.address();\n\n        if (typeof address === 'string' || address == null) {\n          reject(new Error('Failed to get port'));\n        } else {\n          resolve(address.port);\n        }\n      });\n    });\n  }\n\n  afterEach(\n    async () =>\n      new Promise<void>((resolve, reject) => {\n        if (server) {\n          server.close((err) => (err ? reject(err) : resolve()));\n          server = undefined;\n        } else {\n          resolve();\n        }\n      }),\n  );\n\n  it('should fetch UTF-8 HTML', async () => {\n    const port = await createTestServer('text/html', TEST_HTML);\n    const $ = await cheerio.fromURL(`http://localhost:${port}`);\n\n    expect($.html()).toBe(\n      `<html><head></head><body>${TEST_HTML}</body></html>`,\n    );\n  });\n\n  it('should fetch UTF-16 HTML', async () => {\n    const port = await createTestServer(\n      'text/html; charset=utf-16le',\n      TEST_HTML_UTF16,\n    );\n    const $ = await cheerio.fromURL(`http://localhost:${port}`);\n\n    expect($.html()).toBe(\n      `<html><head></head><body>${TEST_HTML}</body></html>`,\n    );\n  });\n\n  it('should parse XML based on Content-Type', async () => {\n    const port = await createTestServer('text/xml', TEST_HTML);\n    const $ = await cheerio.fromURL(`http://localhost:${port}`);\n\n    expect($.html()).toBe(TEST_HTML);\n  });\n\n  it('should throw on non-HTML/XML Content-Type', async () => {\n    const port = await createTestServer('text/plain', TEST_HTML);\n    await expect(cheerio.fromURL(`http://localhost:${port}`)).rejects.toThrow(\n      'The content-type \"text/plain\" is neither HTML nor XML.',\n    );\n  });\n\n  it('should throw on non-2xx responses', async () => {\n    const port = await createTestServer('text/html', TEST_HTML, (_, res) => {\n      res.writeHead(500);\n      res.end();\n    });\n\n    await expect(cheerio.fromURL(`http://localhost:${port}`)).rejects.toThrow(\n      'Response Error',\n    );\n  });\n\n  it('should follow redirects', async () => {\n    let firstRequestUrl: string | undefined;\n    let secondRequestUrl: string | undefined;\n    const port = await createTestServer('text/html', TEST_HTML, (req, res) => {\n      if (firstRequestUrl === undefined) {\n        firstRequestUrl = req.url;\n        res.writeHead(302, { Location: `http://localhost:${port}/final/path` });\n        res.end();\n      } else {\n        secondRequestUrl = req.url;\n        res.writeHead(200, { 'Content-Type': 'text/html' });\n        res.end(TEST_HTML);\n      }\n    });\n\n    const $ = await cheerio.fromURL(`http://localhost:${port}/first`);\n    expect(firstRequestUrl).toBe('/first');\n    expect(secondRequestUrl).toBe('/final/path');\n    expect($.html()).toBe(\n      `<html><head></head><body>${TEST_HTML}</body></html>`,\n    );\n    expect($('a').prop('href')).toBe(`http://localhost:${port}/final/link`);\n  });\n});\n"
  },
  {
    "path": "src/index.ts",
    "content": "/**\n * @file Batteries-included version of Cheerio. This module includes several\n *   convenience methods for loading documents from various sources.\n */\n\nexport * from './load-parse.js';\nexport type {\n  AnyNode,\n  Cheerio,\n  CheerioAPI,\n  CheerioOptions,\n  Document,\n  Element,\n  HTMLParser2Options,\n  ParentNode,\n} from './slim.js';\nexport { contains, merge } from './static.js';\nexport type * from './types.js';\n\nimport { finished, Writable } from 'node:stream';\nimport {\n  DecodeStream,\n  decodeBuffer,\n  type SnifferOptions,\n} from 'encoding-sniffer';\nimport * as htmlparser2 from 'htmlparser2';\nimport { adapter as htmlparser2Adapter } from 'parse5-htmlparser2-tree-adapter';\nimport { ParserStream as Parse5Stream } from 'parse5-parser-stream';\nimport * as undici from 'undici';\nimport MIMEType from 'whatwg-mimetype';\nimport type { CheerioAPI } from './load.js';\nimport { load } from './load-parse.js';\nimport {\n  type CheerioOptions,\n  flattenOptions,\n  type InternalOptions,\n} from './options.js';\n\n/**\n * Sniffs the encoding of a buffer, then creates a querying function bound to a\n * document created from the buffer.\n *\n * @category Loading\n * @example\n *\n * ```js\n * import * as cheerio from 'cheerio';\n *\n * const buffer = fs.readFileSync('index.html');\n * const $ = cheerio.loadBuffer(buffer);\n * ```\n *\n * @param buffer - The buffer to sniff the encoding of.\n * @param options - The options to pass to Cheerio.\n * @returns The loaded document.\n */\nexport function loadBuffer(\n  buffer: Buffer,\n  options: DecodeStreamOptions = {},\n): CheerioAPI {\n  const opts = flattenOptions(options);\n  const str = decodeBuffer(buffer, {\n    defaultEncoding: opts?.xmlMode ? 'utf8' : 'windows-1252',\n    ...options.encoding,\n  });\n\n  return load(str, opts);\n}\n\nfunction _stringStream(\n  options: InternalOptions | undefined,\n  cb: (err: Error | null | undefined, $: CheerioAPI) => void,\n): Writable {\n  if (options?._useHtmlParser2) {\n    const parser = htmlparser2.createDocumentStream(\n      (err, document) => cb(err, load(document, options)),\n      options,\n    );\n\n    return new Writable({\n      decodeStrings: false,\n      write(chunk, _encoding, callback) {\n        if (typeof chunk !== 'string') {\n          throw new TypeError('Expected a string');\n        }\n\n        parser.write(chunk);\n        callback();\n      },\n      final(callback) {\n        parser.end();\n        callback();\n      },\n    });\n  }\n\n  options ??= {};\n  options.treeAdapter ??= htmlparser2Adapter;\n\n  if (options.scriptingEnabled !== false) {\n    options.scriptingEnabled = true;\n  }\n\n  const stream = new Parse5Stream(options);\n\n  finished(stream, (err) => cb(err, load(stream.document, options)));\n\n  return stream;\n}\n\n/**\n * Creates a stream that parses a sequence of strings into a document.\n *\n * The stream is a `Writable` stream that accepts strings. When the stream is\n * finished, the callback is called with the loaded document.\n *\n * @category Loading\n * @example\n *\n * ```js\n * import * as cheerio from 'cheerio';\n * import * as fs from 'fs';\n *\n * const writeStream = cheerio.stringStream({}, (err, $) => {\n *   if (err) {\n *     // Handle error\n *   }\n *\n *   console.log($('h1').text());\n *   // Output: Hello, world!\n * });\n *\n * fs.createReadStream('my-document.html', { encoding: 'utf8' }).pipe(\n *   writeStream,\n * );\n * ```\n *\n * @param options - The options to pass to Cheerio.\n * @param cb - The callback to call when the stream is finished.\n * @returns The writable stream.\n */\nexport function stringStream(\n  options: CheerioOptions,\n  cb: (err: Error | null | undefined, $: CheerioAPI) => void,\n): Writable {\n  return _stringStream(flattenOptions(options), cb);\n}\n\n/** Options used by {@link decodeStream} for decoding incoming buffers. */\nexport interface DecodeStreamOptions extends CheerioOptions {\n  encoding?: SnifferOptions;\n}\n\n/**\n * Parses a stream of buffers into a document.\n *\n * The stream is a `Writable` stream that accepts buffers. When the stream is\n * finished, the callback is called with the loaded document.\n *\n * @category Loading\n * @param options - The options to pass to Cheerio.\n * @param cb - The callback to call when the stream is finished.\n * @returns The writable stream.\n */\nexport function decodeStream(\n  options: DecodeStreamOptions,\n  cb: (err: Error | null | undefined, $: CheerioAPI) => void,\n): Writable {\n  const { encoding = {}, ...cheerioOptions } = options;\n  const opts = flattenOptions(cheerioOptions);\n\n  // Set the default encoding to UTF-8 for XML mode\n  encoding.defaultEncoding ??= opts?.xmlMode ? 'utf8' : 'windows-1252';\n\n  const decodeStream = new DecodeStream(encoding);\n  const loadStream = _stringStream(opts, cb);\n\n  decodeStream.pipe(loadStream);\n\n  return decodeStream;\n}\n\ntype UndiciStreamOptions = Omit<\n  undici.Dispatcher.RequestOptions<unknown>,\n  'path'\n>;\n\n/** Options accepted by {@link fromURL}. */\nexport interface CheerioRequestOptions extends DecodeStreamOptions {\n  /** The options passed to `undici`'s `stream` method. */\n  requestOptions?: UndiciStreamOptions;\n}\n\nconst defaultRequestOptions: UndiciStreamOptions = {\n  method: 'GET',\n  // Set an Accept header\n  headers: {\n    accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',\n  },\n};\n\n/**\n * `fromURL` loads a document from a URL.\n *\n * By default, redirects are allowed and non-2xx responses are rejected.\n *\n * @category Loading\n * @example\n *\n * ```js\n * import * as cheerio from 'cheerio';\n *\n * const $ = await cheerio.fromURL('https://example.com');\n * ```\n *\n * @param url - The URL to load the document from.\n * @param options - The options to pass to Cheerio.\n * @returns The loaded document.\n */\nexport async function fromURL(\n  url: string | URL,\n  options: CheerioRequestOptions = {},\n): Promise<CheerioAPI> {\n  const {\n    requestOptions = defaultRequestOptions,\n    encoding = {},\n    ...cheerioOptions\n  } = options;\n  let undiciStream: Promise<undici.Dispatcher.StreamData<unknown>> | undefined;\n\n  // Add headers if none were supplied.\n  const urlObject = typeof url === 'string' ? new URL(url) : url;\n  const streamOptions = {\n    headers: defaultRequestOptions.headers,\n    path: urlObject.pathname + urlObject.search,\n    ...requestOptions,\n  };\n\n  const promise = new Promise<CheerioAPI>((resolve, reject) => {\n    undiciStream = new undici.Client(urlObject.origin)\n      .compose(undici.interceptors.redirect({ maxRedirections: 5 }))\n      .stream(streamOptions, (res) => {\n        if (res.statusCode < 200 || res.statusCode >= 300) {\n          throw new undici.errors.ResponseError(\n            'Response Error',\n            res.statusCode,\n            {\n              headers: res.headers,\n            },\n          );\n        }\n\n        const contentTypeHeader = res.headers['content-type'] ?? 'text/html';\n        const mimeType = new MIMEType(\n          Array.isArray(contentTypeHeader)\n            ? contentTypeHeader[0]\n            : contentTypeHeader,\n        );\n\n        if (!(mimeType.isHTML() || mimeType.isXML())) {\n          throw new RangeError(\n            `The content-type \"${mimeType.essence}\" is neither HTML nor XML.`,\n          );\n        }\n\n        // Forward the charset from the header to the decodeStream.\n        encoding.transportLayerEncodingLabel =\n          mimeType.parameters.get('charset');\n\n        /*\n         * If we allow redirects, we will have entries in the history.\n         * The last entry will be the final URL.\n         */\n        const history = (\n          res.context as\n            | {\n                history?: URL[];\n              }\n            | undefined\n        )?.history;\n        // Set the `baseURI` to the final URL.\n        const baseURI = history?.at(-1) ?? urlObject;\n\n        const opts: DecodeStreamOptions = {\n          encoding,\n          // Set XML mode based on the MIME type.\n          xmlMode: mimeType.isXML(),\n          baseURI,\n          ...cheerioOptions,\n        };\n\n        return decodeStream(opts, (err, $) => (err ? reject(err) : resolve($)));\n      });\n  });\n\n  // Let's make sure the request is completed before returning the promise.\n  await undiciStream;\n\n  return promise;\n}\n"
  },
  {
    "path": "src/load-parse.ts",
    "content": "import renderWithHtmlparser2 from 'dom-serializer';\nimport type { AnyNode } from 'domhandler';\nimport { parseDocument as parseWithHtmlparser2 } from 'htmlparser2';\nimport { type CheerioAPI, getLoad } from './load.js';\nimport type { CheerioOptions } from './options.js';\nimport { getParse } from './parse.js';\nimport { parseWithParse5, renderWithParse5 } from './parsers/parse5-adapter.js';\n\nconst parse = getParse((content, options, isDocument, context) =>\n  options._useHtmlParser2\n    ? parseWithHtmlparser2(content, options)\n    : parseWithParse5(content, options, isDocument, context),\n);\n\n// Duplicate docs due to https://github.com/TypeStrong/typedoc/issues/1616\n/**\n * Create a querying function, bound to a document created from the provided\n * markup.\n *\n * Note that similar to web browser contexts, this operation may introduce\n * `<html>`, `<head>`, and `<body>` elements; set `isDocument` to `false` to\n * switch to fragment mode and disable this.\n *\n * @category Loading\n * @param content - Markup to be loaded.\n * @param options - Options for the created instance.\n * @param isDocument - Allows parser to be switched to fragment mode.\n * @returns The loaded document.\n * @see {@link https://cheerio.js.org/docs/basics/loading#load} for additional usage information.\n */\nexport const load: (\n  content: string | AnyNode | AnyNode[] | Buffer,\n  options?: CheerioOptions | null,\n  isDocument?: boolean,\n) => CheerioAPI = getLoad(parse, (dom, options) =>\n  options._useHtmlParser2\n    ? renderWithHtmlparser2(dom, options)\n    : renderWithParse5(dom),\n);\n"
  },
  {
    "path": "src/load.spec.ts",
    "content": "import { describe, expect, it } from 'vitest';\nimport { load } from './index.js';\n\ndescribe('.load', () => {\n  it('(html) : should retain original root after creating a new node', () => {\n    const $ = load('<body><ul id=\"fruits\"></ul></body>');\n    expect($('body')).toHaveLength(1);\n    $('<script>');\n    expect($('body')).toHaveLength(1);\n  });\n\n  it('(html) : should handle lowercase tag options', () => {\n    const $ = load('<BODY><ul id=\"fruits\"></ul></BODY>', {\n      xml: { lowerCaseTags: true },\n    });\n    expect($.html()).toBe('<body><ul id=\"fruits\"/></body>');\n  });\n\n  it('(html) : should handle xml tag option', () => {\n    const $ = load('<body><script><foo></script></body>', {\n      xml: true,\n    });\n    expect($('script')[0].children[0].type).toBe('tag');\n  });\n\n  it('(buffer) : should accept a buffer', () => {\n    const html = '<html><head></head><body>foo</body></html>';\n    const $html = load(Buffer.from(html));\n    expect($html.html()).toBe(html);\n  });\n});\n"
  },
  {
    "path": "src/load.ts",
    "content": "import type { AnyNode, Document, Element, ParentNode } from 'domhandler';\nimport { ElementType } from 'htmlparser2';\nimport { Cheerio } from './cheerio.js';\nimport {\n  type CheerioOptions,\n  flattenOptions,\n  type InternalOptions,\n} from './options.js';\nimport * as staticMethods from './static.js';\nimport type { BasicAcceptedElems, SelectorType } from './types.js';\nimport { isCheerio, isHtml } from './utils.js';\n\nexport type { AnyNode, Document, Element, ParentNode } from 'domhandler';\n\ntype StaticType = typeof staticMethods;\n\n/**\n * A querying function, bound to a document created from the provided markup.\n *\n * Also provides several helper methods for dealing with the document as a\n * whole.\n */\nexport interface CheerioAPI extends StaticType {\n  /**\n   * This selector method is the starting point for traversing and manipulating\n   * the document. Like jQuery, it's the primary method for selecting elements\n   * in the document.\n   *\n   * `selector` searches within the `context` scope, which searches within the\n   * `root` scope.\n   *\n   * @example\n   *\n   * ```js\n   * $('ul .pear').attr('class');\n   * //=> pear\n   *\n   * $('li[class=orange]').html();\n   * //=> Orange\n   *\n   * $('.apple', '#fruits').text();\n   * //=> Apple\n   * ```\n   *\n   * Optionally, you can also load HTML by passing the string as the selector:\n   *\n   * ```js\n   * $('<ul id=\"fruits\">...</ul>');\n   * ```\n   *\n   * Or the context:\n   *\n   * ```js\n   * $('ul', '<ul id=\"fruits\">...</ul>');\n   * ```\n   *\n   * Or as the root:\n   *\n   * ```js\n   * $('li', 'ul', '<ul id=\"fruits\">...</ul>');\n   * ```\n   *\n   * @param selector - Either a selector to look for within the document, or the\n   *   contents of a new Cheerio instance.\n   * @param context - Either a selector to look for within the root, or the\n   *   contents of the document to query.\n   * @param root - Optional HTML document string.\n   */\n  <T extends AnyNode, S extends string>(\n    selector?: S | BasicAcceptedElems<T>,\n    context?: BasicAcceptedElems<AnyNode> | null,\n    root?: BasicAcceptedElems<Document>,\n    options?: CheerioOptions,\n  ): Cheerio<S extends SelectorType ? Element : T>;\n\n  /**\n   * The root the document was originally loaded with.\n   *\n   * @private\n   */\n  _root: Document;\n\n  /**\n   * The options the document was originally loaded with.\n   *\n   * @private\n   */\n  _options: InternalOptions;\n\n  /** Mimic jQuery's prototype alias for plugin authors. */\n  fn: typeof Cheerio.prototype;\n\n  /**\n   * The `.load` static method defined on the \"loaded\" Cheerio factory function\n   * is deprecated. Users are encouraged to instead use the `load` function\n   * exported by the Cheerio module.\n   *\n   * @deprecated Use the `load` function exported by the Cheerio module.\n   * @category Deprecated\n   * @example\n   *\n   * ```js\n   * const $ = cheerio.load('<h1>Hello, <span>world</span>.</h1>');\n   * ```\n   */\n  load: ReturnType<typeof getLoad>;\n}\n\n/**\n * Create a loader factory from parser and renderer implementations.\n *\n * @param parse - Parser used to convert input into a document.\n * @param render - Renderer used to serialize nodes back to markup.\n */\nexport function getLoad(\n  parse: Cheerio<AnyNode>['_parse'],\n  render: (\n    dom: AnyNode | ArrayLike<AnyNode>,\n    options: InternalOptions,\n  ) => string,\n) {\n  /**\n   * Create a querying function, bound to a document created from the provided\n   * markup.\n   *\n   * Note that similar to web browser contexts, this operation may introduce\n   * `<html>`, `<head>`, and `<body>` elements; set `isDocument` to `false` to\n   * switch to fragment mode and disable this.\n   *\n   * @param content - Markup to be loaded.\n   * @param options - Options for the created instance.\n   * @param isDocument - Allows parser to be switched to fragment mode.\n   * @returns The loaded document.\n   * @see {@link https://cheerio.js.org/docs/basics/loading#load} for additional usage information.\n   */\n  return function load(\n    content: string | AnyNode | AnyNode[] | Buffer,\n    options?: CheerioOptions | null,\n    isDocument = true,\n  ): CheerioAPI {\n    if ((content as string | null) == null) {\n      throw new Error('cheerio.load() expects a string');\n    }\n\n    const internalOpts = flattenOptions(options);\n    const initialRoot = parse(content, internalOpts, isDocument, null);\n\n    /**\n     * Create an extended class here, so that extensions only live on one\n     * instance.\n     */\n    class LoadedCheerio<T> extends Cheerio<T> {\n      _make<T>(\n        selector?: ArrayLike<T> | T | string,\n        context?: BasicAcceptedElems<AnyNode> | null,\n      ): Cheerio<T> {\n        const cheerio = initialize(selector, context);\n        cheerio.prevObject = this;\n\n        return cheerio;\n      }\n\n      _parse(\n        content: string | Document | AnyNode | AnyNode[] | Buffer,\n        options: InternalOptions,\n        isDocument: boolean,\n        context: ParentNode | null,\n      ) {\n        return parse(content, options, isDocument, context);\n      }\n\n      _render(dom: AnyNode | ArrayLike<AnyNode>): string {\n        return render(dom, this.options);\n      }\n    }\n\n    function initialize<T = AnyNode, S extends string = string>(\n      selector?: ArrayLike<T> | T | S,\n      context?: BasicAcceptedElems<AnyNode> | null,\n      root: BasicAcceptedElems<Document> = initialRoot,\n      opts?: CheerioOptions,\n    ): Cheerio<S extends SelectorType ? Element : T> {\n      type Result = S extends SelectorType ? Element : T;\n\n      // $($)\n      if (selector && isCheerio<Result>(selector)) return selector;\n\n      const options = flattenOptions(opts, internalOpts);\n      const r =\n        typeof root === 'string'\n          ? [parse(root, options, false, null)]\n          : 'length' in root\n            ? root\n            : [root];\n      const rootInstance = isCheerio<Document>(r)\n        ? r\n        : new LoadedCheerio<Document>(r, null, options);\n      // Add a cyclic reference, so that calling methods on `_root` never fails.\n      rootInstance._root = rootInstance;\n\n      // $(), $(null), $(undefined), $(false)\n      if (!selector) {\n        return new LoadedCheerio<Result>(undefined, rootInstance, options);\n      }\n\n      const elements: AnyNode[] | undefined =\n        typeof selector === 'string' && isHtml(selector)\n          ? // $(<html>)\n            parse(selector, options, false, null).children\n          : isNode(selector)\n            ? // $(dom)\n              [selector]\n            : Array.isArray(selector)\n              ? // $([dom])\n                selector\n              : undefined;\n\n      const instance = new LoadedCheerio(elements, rootInstance, options);\n\n      if (elements) {\n        return instance as Cheerio<Result>;\n      }\n\n      if (typeof selector !== 'string') {\n        throw new TypeError('Unexpected type of selector');\n      }\n\n      // We know that our selector is a string now.\n      let search = selector;\n\n      const searchContext: Cheerio<AnyNode> | undefined = context\n        ? // If we don't have a context, maybe we have a root, from loading\n          typeof context === 'string'\n          ? isHtml(context)\n            ? // $('li', '<ul>...</ul>')\n              new LoadedCheerio<Document>(\n                [parse(context, options, false, null)],\n                rootInstance,\n                options,\n              )\n            : // $('li', 'ul')\n              ((search = `${context} ${search}` as S), rootInstance)\n          : isCheerio<AnyNode>(context)\n            ? // $('li', $)\n              context\n            : // $('li', node), $('li', [nodes])\n              new LoadedCheerio<AnyNode>(\n                Array.isArray(context) ? context : [context],\n                rootInstance,\n                options,\n              )\n        : rootInstance;\n\n      // If we still don't have a context, return\n      if (!searchContext) return instance as Cheerio<Result>;\n\n      /*\n       * #id, .class, tag\n       */\n      return searchContext.find(search) as Cheerio<Result>;\n    }\n\n    // Add in static methods & properties\n    Object.assign(initialize, staticMethods, {\n      load,\n      // `_root` and `_options` are used in static methods.\n      _root: initialRoot,\n      _options: internalOpts,\n      // Add `fn` for plugins\n      fn: LoadedCheerio.prototype,\n      // Add the prototype here to maintain `instanceof` behavior.\n      prototype: LoadedCheerio.prototype,\n    });\n\n    return initialize as CheerioAPI;\n  };\n}\n\nfunction isNode(obj: unknown): obj is AnyNode {\n  return (\n    // @ts-expect-error: TS doesn't know about the `name` property.\n    !!obj.name ||\n    // @ts-expect-error: TS doesn't know about the `type` property.\n    obj.type === ElementType.Root ||\n    // @ts-expect-error: TS doesn't know about the `type` property.\n    obj.type === ElementType.Text ||\n    // @ts-expect-error: TS doesn't know about the `type` property.\n    obj.type === ElementType.Comment\n  );\n}\n"
  },
  {
    "path": "src/options.ts",
    "content": "import type { Options as SelectOptions } from 'cheerio-select';\nimport type { DomSerializerOptions } from 'dom-serializer';\nimport type { DomHandlerOptions } from 'domhandler';\nimport type { ParserOptions as HTMLParser2ParserOptions } from 'htmlparser2';\nimport type { ParserOptions as Parse5ParserOptions } from 'parse5';\nimport type { Htmlparser2TreeAdapterMap } from 'parse5-htmlparser2-tree-adapter';\n\n/**\n * Options accepted by htmlparser2, the default parser for XML.\n *\n * @see https://github.com/fb55/htmlparser2/wiki/Parser-options\n */\nexport interface HTMLParser2Options\n  extends DomHandlerOptions,\n    DomSerializerOptions,\n    HTMLParser2ParserOptions {\n  /** Treat the input as an XML document. */\n  xmlMode?: boolean;\n}\n\n/**\n * Options accepted by Cheerio.\n *\n * Please note that parser-specific options are _only recognized_ if the\n * relevant parser is used.\n */\nexport interface CheerioOptions\n  extends Parse5ParserOptions<Htmlparser2TreeAdapterMap> {\n  /**\n   * Recommended way of configuring htmlparser2 when wanting to parse XML.\n   *\n   * This will switch Cheerio to use htmlparser2.\n   *\n   * @default false\n   */\n  xml?: HTMLParser2Options | boolean;\n\n  /**\n   * Enable xml mode, which will switch Cheerio to use htmlparser2.\n   *\n   * @deprecated Please use the `xml` option instead.\n   * @default false\n   */\n  xmlMode?: boolean;\n\n  /** The base URI for the document. Used to resolve the `href` and `src` props. */\n  baseURI?: string | URL;\n\n  /**\n   * Is the document in quirks mode?\n   *\n   * This will lead to `.className` and `#id` being case-insensitive.\n   *\n   * @default false\n   */\n  quirksMode?: SelectOptions['quirksMode'];\n  /**\n   * Extension point for pseudo-classes.\n   *\n   * Maps from names to either strings of functions.\n   *\n   * - A string value is a selector that the element must match to be selected.\n   * - A function is called with the element as its first argument, and optional\n   *   parameters second. If it returns true, the element is selected.\n   *\n   * @example\n   *\n   * ```js\n   * const $ = cheerio.load(\n   *   '<div class=\"foo\"></div><div data-bar=\"boo\"></div>',\n   *   {\n   *     pseudos: {\n   *       // `:foo` is an alias for `div.foo`\n   *       foo: 'div.foo',\n   *       // `:bar(val)` is equivalent to `[data-bar=val s]`\n   *       bar: (el, val) => el.attribs['data-bar'] === val,\n   *     },\n   *   },\n   * );\n   *\n   * $(':foo').length; // 1\n   * $('div:bar(boo)').length; // 1\n   * $('div:bar(baz)').length; // 0\n   * ```\n   */\n  pseudos?: SelectOptions['pseudos'];\n}\n\n/** Internal options for Cheerio. */\nexport interface InternalOptions\n  extends HTMLParser2Options,\n    Omit<CheerioOptions, 'xml'> {\n  /**\n   * Whether to use htmlparser2.\n   *\n   * This is set to true if `xml` is set to true.\n   */\n  _useHtmlParser2?: boolean;\n}\n\nconst defaultOpts: InternalOptions = {\n  _useHtmlParser2: false,\n};\n\n/**\n * Flatten the options for Cheerio.\n *\n * This will set `_useHtmlParser2` to true if `xml` is set to true.\n *\n * @param options - The options to flatten.\n * @param baseOptions - The base options to use.\n * @returns The flattened options.\n */\nexport function flattenOptions(\n  options?: CheerioOptions | null,\n  baseOptions?: InternalOptions,\n): InternalOptions {\n  if (!options) {\n    return baseOptions ?? defaultOpts;\n  }\n\n  const opts: InternalOptions = {\n    _useHtmlParser2: !!options.xmlMode,\n    ...baseOptions,\n    ...options,\n  };\n\n  if (options.xml) {\n    opts._useHtmlParser2 = true;\n    opts.xmlMode = true;\n\n    if (options.xml !== true) {\n      Object.assign(opts, options.xml);\n    }\n  } else if (options.xmlMode) {\n    opts._useHtmlParser2 = true;\n  }\n\n  return opts;\n}\n"
  },
  {
    "path": "src/parse.spec.ts",
    "content": "import type { Document, Element } from 'domhandler';\nimport { parseDocument as parseWithHtmlparser2 } from 'htmlparser2';\nimport { describe, expect, it } from 'vitest';\nimport { getParse } from './parse.js';\nimport { parseWithParse5 } from './parsers/parse5-adapter.js';\n\nconst defaultOpts = { _useHtmlParser2: false };\n\nconst parse = getParse((content, options, isDocument, context) =>\n  options._useHtmlParser2\n    ? parseWithHtmlparser2(content, options)\n    : parseWithParse5(content, options, isDocument, context),\n);\n\n// Tags\nconst basic = '<html></html>';\nconst siblings = '<h2></h2><p></p>';\n\n// Single Tags\nconst single = '<br/>';\nconst singleWrong = '<br>';\n\n// Children\nconst children = '<html><br/></html>';\nconst li = '<li class=\"durian\">Durian</li>';\n\n// Attributes\nconst attributes = '<img src=\"hello.png\" alt=\"man waving\">';\nconst noValueAttribute = '<textarea disabled></textarea>';\n\n// Comments\nconst comment = '<!-- sexy -->';\nconst conditional =\n  '<!--[if IE 8]><html class=\"no-js ie8\" lang=\"en\"><![endif]-->';\n\n// Text\nconst text = 'lorem ipsum';\n\n// Script\nconst script = '<script type=\"text/javascript\">alert(\"hi world!\");</script>';\nconst scriptEmpty = '<script></script>';\n\n// Style\nconst style = '<style type=\"text/css\"> h2 { color:blue; } </style>';\nconst styleEmpty = '<style></style>';\n\n// Directives\nconst directive = '<!doctype html>';\n\nfunction rootTest(root: Document) {\n  expect(root).toHaveProperty('type', 'root');\n\n  expect(root.nextSibling).toBe(null);\n  expect(root.previousSibling).toBe(null);\n  expect(root.parentNode).toBe(null);\n\n  const child = root.childNodes[0];\n  expect(child.parentNode).toBe(root);\n}\n\ndescribe('parse', () => {\n  describe('evaluate', () => {\n    it(`should parse basic empty tags: ${basic}`, () => {\n      const [tag] = parse(basic, defaultOpts, true, null).children as Element[];\n      expect(tag.type).toBe('tag');\n      expect(tag.tagName).toBe('html');\n      expect(tag.childNodes).toHaveLength(2);\n    });\n\n    it(`should handle sibling tags: ${siblings}`, () => {\n      const dom = parse(siblings, defaultOpts, false, null)\n        .children as Element[];\n      const [h2, p] = dom;\n\n      expect(dom).toHaveLength(2);\n      expect(h2.tagName).toBe('h2');\n      expect(p.tagName).toBe('p');\n    });\n\n    it(`should handle single tags: ${single}`, () => {\n      const [tag] = parse(single, defaultOpts, false, null)\n        .children as Element[];\n      expect(tag.type).toBe('tag');\n      expect(tag.tagName).toBe('br');\n      expect(tag.childNodes).toHaveLength(0);\n    });\n\n    it(`should handle malformatted single tags: ${singleWrong}`, () => {\n      const [tag] = parse(singleWrong, defaultOpts, false, null)\n        .children as Element[];\n      expect(tag.type).toBe('tag');\n      expect(tag.tagName).toBe('br');\n      expect(tag.childNodes).toHaveLength(0);\n    });\n\n    it(`should handle tags with children: ${children}`, () => {\n      const [tag] = parse(children, defaultOpts, true, null)\n        .children as Element[];\n      expect(tag.type).toBe('tag');\n      expect(tag.tagName).toBe('html');\n      expect(tag.childNodes).toBeTruthy();\n      expect(tag.childNodes[1]).toHaveProperty('tagName', 'body');\n      expect((tag.childNodes[1] as Element).childNodes).toHaveLength(1);\n    });\n\n    it(`should handle tags with children: ${li}`, () => {\n      const [tag] = parse(li, defaultOpts, false, null).children as Element[];\n      expect(tag.childNodes).toHaveLength(1);\n      expect(tag.childNodes[0]).toHaveProperty('data', 'Durian');\n    });\n\n    it(`should handle tags with attributes: ${attributes}`, () => {\n      const attrs = parse(attributes, defaultOpts, false, null)\n        .children[0] as Element;\n      expect(attrs.attribs).toBeTruthy();\n      expect(attrs.attribs).toHaveProperty('src', 'hello.png');\n      expect(attrs.attribs).toHaveProperty('alt', 'man waving');\n    });\n\n    it(`should handle value-less attributes: ${noValueAttribute}`, () => {\n      const attrs = parse(noValueAttribute, defaultOpts, false, null)\n        .children[0] as Element;\n      expect(attrs.attribs).toBeTruthy();\n      expect(attrs.attribs).toHaveProperty('disabled', '');\n    });\n\n    it(`should handle comments: ${comment}`, () => {\n      const elem = parse(comment, defaultOpts, false, null).children[0];\n      expect(elem.type).toBe('comment');\n      expect(elem).toHaveProperty('data', ' sexy ');\n    });\n\n    it(`should handle conditional comments: ${conditional}`, () => {\n      const elem = parse(conditional, defaultOpts, false, null).children[0];\n      expect(elem.type).toBe('comment');\n      expect(elem).toHaveProperty(\n        'data',\n        conditional.replace('<!--', '').replace('-->', ''),\n      );\n    });\n\n    it(`should handle text: ${text}`, () => {\n      const text_ = parse(text, defaultOpts, false, null).children[0];\n      expect(text_.type).toBe('text');\n      expect(text_).toHaveProperty('data', 'lorem ipsum');\n    });\n\n    it(`should handle script tags: ${script}`, () => {\n      const script_ = parse(script, defaultOpts, false, null)\n        .children[0] as Element;\n      expect(script_.type).toBe('script');\n      expect(script_.tagName).toBe('script');\n      expect(script_.attribs).toHaveProperty('type', 'text/javascript');\n      expect(script_.childNodes).toHaveLength(1);\n      expect(script_.childNodes[0].type).toBe('text');\n      expect(script_.childNodes[0]).toHaveProperty(\n        'data',\n        'alert(\"hi world!\");',\n      );\n    });\n\n    it(`should handle style tags: ${style}`, () => {\n      const style_ = parse(style, defaultOpts, false, null)\n        .children[0] as Element;\n      expect(style_.type).toBe('style');\n      expect(style_.tagName).toBe('style');\n      expect(style_.attribs).toHaveProperty('type', 'text/css');\n      expect(style_.childNodes).toHaveLength(1);\n      expect(style_.childNodes[0].type).toBe('text');\n      expect(style_.childNodes[0]).toHaveProperty(\n        'data',\n        ' h2 { color:blue; } ',\n      );\n    });\n\n    it(`should handle directives: ${directive}`, () => {\n      const elem = parse(directive, defaultOpts, true, null).children[0];\n      expect(elem.type).toBe('directive');\n      expect(elem).toHaveProperty('data', '!DOCTYPE html');\n      expect(elem).toHaveProperty('name', '!doctype');\n    });\n  });\n\n  describe('.parse', () => {\n    // Root test utility\n\n    it(`should add root to: ${basic}`, () => {\n      const root = parse(basic, defaultOpts, true, null);\n      rootTest(root);\n      expect(root.childNodes).toHaveLength(1);\n      expect(root.childNodes[0]).toHaveProperty('tagName', 'html');\n    });\n\n    it(`should add root to: ${siblings}`, () => {\n      const root = parse(siblings, defaultOpts, false, null);\n      rootTest(root);\n      expect(root.childNodes).toHaveLength(2);\n      expect(root.childNodes[0]).toHaveProperty('tagName', 'h2');\n      expect(root.childNodes[1]).toHaveProperty('tagName', 'p');\n      expect(root.childNodes[1].parent).toBe(root);\n    });\n\n    it(`should add root to: ${comment}`, () => {\n      const root = parse(comment, defaultOpts, false, null);\n      rootTest(root);\n      expect(root.childNodes).toHaveLength(1);\n      expect(root.childNodes[0].type).toBe('comment');\n    });\n\n    it(`should add root to: ${text}`, () => {\n      const root = parse(text, defaultOpts, false, null);\n      rootTest(root);\n      expect(root.childNodes).toHaveLength(1);\n      expect(root.childNodes[0].type).toBe('text');\n    });\n\n    it(`should add root to: ${scriptEmpty}`, () => {\n      const root = parse(scriptEmpty, defaultOpts, false, null);\n      rootTest(root);\n      expect(root.childNodes).toHaveLength(1);\n      expect(root.childNodes[0].type).toBe('script');\n    });\n\n    it(`should add root to: ${styleEmpty}`, () => {\n      const root = parse(styleEmpty, defaultOpts, false, null);\n      rootTest(root);\n      expect(root.childNodes).toHaveLength(1);\n      expect(root.childNodes[0].type).toBe('style');\n    });\n\n    it(`should add root to: ${directive}`, () => {\n      const root = parse(directive, defaultOpts, true, null);\n      rootTest(root);\n      expect(root.childNodes).toHaveLength(2);\n      expect(root.childNodes[0].type).toBe('directive');\n    });\n\n    it('should simply return root', () => {\n      const oldroot = parse(basic, defaultOpts, true, null);\n      const root = parse(oldroot, defaultOpts, true, null);\n      expect(root).toBe(oldroot);\n      rootTest(root);\n      expect(root.childNodes).toHaveLength(1);\n      expect(root.childNodes[0]).toHaveProperty('tagName', 'html');\n    });\n\n    it('should expose the DOM level 1 API', () => {\n      const root = parse(\n        '<div><a></a><span></span><p></p></div>',\n        defaultOpts,\n        false,\n        null,\n      ).childNodes[0] as Element;\n      const childNodes = root.childNodes as Element[];\n\n      expect(childNodes).toHaveLength(3);\n\n      expect(root.tagName).toBe('div');\n      expect(root.firstChild).toBe(childNodes[0]);\n      expect(root.lastChild).toBe(childNodes[2]);\n\n      expect(childNodes[0].tagName).toBe('a');\n      expect(childNodes[0].previousSibling).toBe(null);\n      expect(childNodes[0].nextSibling).toBe(childNodes[1]);\n      expect(childNodes[0].parentNode).toBe(root);\n      expect(childNodes[0].childNodes).toHaveLength(0);\n      expect(childNodes[0].firstChild).toBe(null);\n      expect(childNodes[0].lastChild).toBe(null);\n\n      expect(childNodes[1].tagName).toBe('span');\n      expect(childNodes[1].previousSibling).toBe(childNodes[0]);\n      expect(childNodes[1].nextSibling).toBe(childNodes[2]);\n      expect(childNodes[1].parentNode).toBe(root);\n      expect(childNodes[1].childNodes).toHaveLength(0);\n      expect(childNodes[1].firstChild).toBe(null);\n      expect(childNodes[1].lastChild).toBe(null);\n\n      expect(childNodes[2].tagName).toBe('p');\n      expect(childNodes[2].previousSibling).toBe(childNodes[1]);\n      expect(childNodes[2].nextSibling).toBe(null);\n      expect(childNodes[2].parentNode).toBe(root);\n      expect(childNodes[2].childNodes).toHaveLength(0);\n      expect(childNodes[2].firstChild).toBe(null);\n      expect(childNodes[2].lastChild).toBe(null);\n    });\n\n    it('Should parse less than or equal sign sign', () => {\n      const root = parse('<i>A</i><=<i>B</i>', defaultOpts, false, null);\n      const { childNodes } = root;\n\n      expect(childNodes[0]).toHaveProperty('tagName', 'i');\n      expect((childNodes[0] as Element).childNodes[0]).toHaveProperty(\n        'data',\n        'A',\n      );\n      expect(childNodes[1]).toHaveProperty('data', '<=');\n      expect(childNodes[2]).toHaveProperty('tagName', 'i');\n      expect((childNodes[2] as Element).childNodes[0]).toHaveProperty(\n        'data',\n        'B',\n      );\n    });\n\n    it('Should ignore unclosed CDATA', () => {\n      const root = parse(\n        '<a></a><script>foo //<![CDATA[ bar</script><b></b>',\n        defaultOpts,\n        false,\n        null,\n      );\n      const childNodes = root.childNodes as Element[];\n\n      expect(childNodes[0].tagName).toBe('a');\n      expect(childNodes[1].tagName).toBe('script');\n      expect(childNodes[1].childNodes[0]).toHaveProperty(\n        'data',\n        'foo //<![CDATA[ bar',\n      );\n      expect(childNodes[2].tagName).toBe('b');\n    });\n\n    it('Should add <head> to documents', () => {\n      const root = parse('<html></html>', defaultOpts, true, null);\n      const childNodes = root.childNodes as Element[];\n\n      expect(childNodes[0].tagName).toBe('html');\n      expect(childNodes[0].childNodes[0]).toHaveProperty('tagName', 'head');\n    });\n\n    it('Should implicitly create <tr> around <td>', () => {\n      const root = parse(\n        '<table><td>bar</td></tr></table>',\n        defaultOpts,\n        false,\n        null,\n      );\n\n      const table = root.childNodes[0] as Element;\n      expect(table.tagName).toBe('table');\n      expect(table.childNodes.length).toBe(1);\n      const tbody = table.childNodes[0] as Element;\n      expect(table.childNodes[0]).toHaveProperty('tagName', 'tbody');\n      const tr = tbody.childNodes[0] as Element;\n      expect(tr).toHaveProperty('tagName', 'tr');\n      const td = tr.childNodes[0] as Element;\n      expect(td).toHaveProperty('tagName', 'td');\n      expect(td.childNodes[0]).toHaveProperty('data', 'bar');\n    });\n\n    it('Should parse custom tag <line>', () => {\n      const root = parse('<line>test</line>', defaultOpts, false, null);\n      const childNodes = root.childNodes as Element[];\n\n      expect(childNodes.length).toBe(1);\n      expect(childNodes[0].tagName).toBe('line');\n      expect(childNodes[0].childNodes[0]).toHaveProperty('data', 'test');\n    });\n\n    it('Should properly parse misnested table tags', () => {\n      const root = parse(\n        '<tr><td>i1</td></tr><tr><td>i2</td></td></tr><tr><td>i3</td></td></tr>',\n        defaultOpts,\n        false,\n        null,\n      );\n      const childNodes = root.childNodes as Element[];\n\n      expect(childNodes.length).toBe(3);\n\n      for (let i = 0; i < childNodes.length; i++) {\n        const child = childNodes[i];\n        expect(child.tagName).toBe('tr');\n        expect(child.childNodes[0]).toHaveProperty('tagName', 'td');\n        expect((child.childNodes[0] as Element).childNodes[0]).toHaveProperty(\n          'data',\n          `i${i + 1}`,\n        );\n      }\n    });\n\n    it('Should correctly parse data url attributes', () => {\n      const html =\n        '<div style=\\'font-family:\"butcherman-caps\"; src:url(data:font/opentype;base64,AAEA...);\\'></div>';\n      const expectedAttr =\n        'font-family:\"butcherman-caps\"; src:url(data:font/opentype;base64,AAEA...);';\n      const root = parse(html, defaultOpts, false, null);\n      const childNodes = root.childNodes as Element[];\n\n      expect(childNodes[0].attribs).toHaveProperty('style', expectedAttr);\n    });\n\n    it('Should treat <xmp> tag content as text', () => {\n      const root = parse('<xmp><h2></xmp>', defaultOpts, false, null);\n      const childNodes = root.childNodes as Element[];\n\n      expect(childNodes[0].childNodes[0]).toHaveProperty('data', '<h2>');\n    });\n\n    it('Should correctly parse malformed numbered entities', () => {\n      const root = parse('<p>z&#</p>', defaultOpts, false, null);\n      const childNodes = root.childNodes as Element[];\n\n      expect(childNodes[0].childNodes[0]).toHaveProperty('data', 'z&#');\n    });\n\n    it('Should correctly parse mismatched headings', () => {\n      const root = parse('<h2>Test</h3><div></div>', defaultOpts, false, null);\n      const { childNodes } = root;\n\n      expect(childNodes.length).toBe(2);\n      expect(childNodes[0]).toHaveProperty('tagName', 'h2');\n      expect(childNodes[1]).toHaveProperty('tagName', 'div');\n    });\n\n    it('Should correctly parse tricky <pre> content', () => {\n      const root = parse(\n        '<pre>\\nA <- factor(A, levels = c(\"c\",\"a\",\"b\"))\\n</pre>',\n        defaultOpts,\n        false,\n        null,\n      );\n      const childNodes = root.childNodes as Element[];\n\n      expect(childNodes.length).toBe(1);\n      expect(childNodes[0].tagName).toBe('pre');\n      expect(childNodes[0].childNodes[0]).toHaveProperty(\n        'data',\n        'A <- factor(A, levels = c(\"c\",\"a\",\"b\"))\\n',\n      );\n    });\n\n    it('should pass the options for including the location info to parse5', () => {\n      const root = parse(\n        '<p>Hello</p>',\n        { ...defaultOpts, sourceCodeLocationInfo: true },\n        false,\n        null,\n      );\n      const location = root.children[0].sourceCodeLocation;\n\n      expect(typeof location).toBe('object');\n      expect(location?.endOffset).toBe(12);\n    });\n  });\n});\n"
  },
  {
    "path": "src/parse.ts",
    "content": "import {\n  type AnyNode,\n  isDocument as checkIsDocument,\n  Document,\n  type ParentNode,\n} from 'domhandler';\nimport { removeElement } from 'domutils';\nimport type { InternalOptions } from './options.js';\n\n/**\n * Get the parse function with options.\n *\n * @param parser - The parser function.\n * @returns The parse function with options.\n */\nexport function getParse(\n  parser: (\n    content: string,\n    options: InternalOptions,\n    isDocument: boolean,\n    context: ParentNode | null,\n  ) => Document,\n) {\n  /**\n   * Parse a HTML string or a node.\n   *\n   * @param content - The HTML string or node.\n   * @param options - The parser options.\n   * @param isDocument - If `content` is a document.\n   * @param context - The context node in the DOM tree.\n   * @returns The parsed document node.\n   */\n  return function parse(\n    content: string | Document | AnyNode | AnyNode[] | Buffer,\n    options: InternalOptions,\n    isDocument: boolean,\n    context: ParentNode | null,\n  ): Document {\n    if (typeof Buffer !== 'undefined' && Buffer.isBuffer(content)) {\n      content = content.toString();\n    }\n\n    if (typeof content === 'string') {\n      return parser(content, options, isDocument, context);\n    }\n\n    const doc = content as AnyNode | AnyNode[] | Document;\n\n    if (!Array.isArray(doc) && checkIsDocument(doc)) {\n      // If `doc` is already a root, just return it\n      return doc;\n    }\n\n    // Add content to new root element\n    const root = new Document([]);\n\n    // Update the DOM using the root\n    update(doc, root);\n\n    return root;\n  };\n}\n\n/**\n * Update the dom structure, for one changed layer.\n *\n * @param newChilds - The new children.\n * @param parent - The new parent.\n * @returns The parent node.\n */\nexport function update(\n  newChilds: AnyNode[] | AnyNode,\n  parent: ParentNode | null,\n): ParentNode | null {\n  // Normalize\n  const arr = Array.isArray(newChilds) ? newChilds : [newChilds];\n\n  // Update parent\n  if (parent) {\n    parent.children = arr;\n  } else {\n    parent = null;\n  }\n\n  // Update neighbors\n  for (let i = 0; i < arr.length; i++) {\n    const node = arr[i];\n\n    // Cleanly remove existing nodes from their previous structures.\n    if (node.parent && node.parent.children !== arr) {\n      removeElement(node);\n    }\n\n    if (parent) {\n      node.prev = arr[i - 1] || null;\n      node.next = arr[i + 1] || null;\n    } else {\n      node.prev = node.next = null;\n    }\n\n    node.parent = parent;\n  }\n\n  return parent;\n}\n"
  },
  {
    "path": "src/parsers/parse5-adapter.ts",
    "content": "import {\n  type AnyNode,\n  type Document,\n  isDocument,\n  type ParentNode,\n} from 'domhandler';\nimport { parse as parseDocument, parseFragment, serializeOuter } from 'parse5';\nimport { adapter as htmlparser2Adapter } from 'parse5-htmlparser2-tree-adapter';\nimport type { InternalOptions } from '../options.js';\n\n/**\n * Parse the content with `parse5` in the context of the given `ParentNode`.\n *\n * @param content - The content to parse.\n * @param options - A set of options to use to parse.\n * @param isDocument - Whether to parse the content as a full HTML document.\n * @param context - The context in which to parse the content.\n * @returns The parsed content.\n */\nexport function parseWithParse5(\n  content: string,\n  options: InternalOptions,\n  isDocument: boolean,\n  context: ParentNode | null,\n): Document {\n  options.treeAdapter ??= htmlparser2Adapter;\n\n  if (options.scriptingEnabled !== false) {\n    options.scriptingEnabled = true;\n  }\n\n  return isDocument\n    ? parseDocument(content, options)\n    : parseFragment(context, content, options);\n}\n\nconst renderOpts = { treeAdapter: htmlparser2Adapter };\n\n/**\n * Renders the given DOM tree with `parse5` and returns the result as a string.\n *\n * @param dom - The DOM tree to render.\n * @returns The rendered document.\n */\nexport function renderWithParse5(dom: AnyNode | ArrayLike<AnyNode>): string {\n  /*\n   * `dom-serializer` passes over the special \"root\" node and renders the\n   * node's children in its place. To mimic this behavior with `parse5`, an\n   * equivalent operation must be applied to the input array.\n   */\n  const nodes = 'length' in dom ? dom : [dom];\n  for (let index = 0; index < nodes.length; index += 1) {\n    const node = nodes[index];\n    if (isDocument(node)) {\n      Array.prototype.splice.call(nodes, index, 1, ...node.children);\n    }\n  }\n\n  let result = '';\n  for (let index = 0; index < nodes.length; index += 1) {\n    const node = nodes[index];\n    result += serializeOuter(node, renderOpts);\n  }\n\n  return result;\n}\n"
  },
  {
    "path": "src/slim.ts",
    "content": "/**\n * @file Alternative entry point for Cheerio that always uses htmlparser2. This\n *   way, parse5 won't be loaded, saving some memory.\n */\n\nimport render from 'dom-serializer';\nimport type { AnyNode } from 'domhandler';\nimport { parseDocument } from 'htmlparser2';\nimport { type CheerioAPI, getLoad } from './load.js';\nimport type { CheerioOptions } from './options.js';\nimport { getParse } from './parse.js';\n\nexport type { Cheerio } from './cheerio.js';\nexport type {\n  AnyNode,\n  CheerioAPI,\n  Document,\n  Element,\n  ParentNode,\n} from './load.js';\nexport type { CheerioOptions, HTMLParser2Options } from './options.js';\nexport { contains, merge } from './static.js';\nexport type * from './types.js';\n\n/**\n * Create a querying function, bound to a document created from the provided\n * markup.\n *\n * @param content - Markup to be loaded.\n * @param options - Options for the created instance.\n * @param isDocument - Always `false` here, as we are always using\n *   `htmlparser2`.\n * @returns The loaded document.\n * @see {@link https://cheerio.js.org#loading} for additional usage information.\n */\nexport const load: (\n  content: string | AnyNode | AnyNode[] | Buffer,\n  options?: CheerioOptions | null,\n  isDocument?: boolean,\n) => CheerioAPI = getLoad(getParse(parseDocument), render);\n"
  },
  {
    "path": "src/static.spec.ts",
    "content": "import { beforeEach, describe, expect, it } from 'vitest';\nimport { cheerio, eleven, food } from './__fixtures__/fixtures.js';\nimport type { CheerioAPI } from './index.js';\n\ndescribe('cheerio', () => {\n  describe('.html', () => {\n    it('() : should return innerHTML; $.html(obj) should return outerHTML', () => {\n      const $div = cheerio(\n        'div',\n        '<div><span>foo</span><span>bar</span></div>',\n      );\n      const span = $div.children()[1];\n      expect(cheerio(span).html()).toBe('bar');\n      expect(cheerio.html(span)).toBe('<span>bar</span>');\n    });\n\n    it('(<obj>) : should accept an object, an array, or a cheerio object', () => {\n      const $span = cheerio('<span>foo</span>');\n      expect(cheerio.html($span[0])).toBe('<span>foo</span>');\n      expect(cheerio.html($span)).toBe('<span>foo</span>');\n    });\n\n    it('(<value>) : should be able to set to an empty string', () => {\n      const $elem = cheerio('<span>foo</span>').html('');\n      expect(cheerio.html($elem)).toBe('<span></span>');\n    });\n\n    it('(<root>) : does not render the root element', () => {\n      const $ = cheerio.load('');\n      expect(cheerio.html($.root())).toBe(\n        '<html><head></head><body></body></html>',\n      );\n    });\n\n    it('(<elem>, <root>, <elem>) : does not render the root element', () => {\n      const $ = cheerio.load('<div>a div</div><span>a span</span>');\n      const $collection = $('div').add($.root()).add('span');\n      const expected =\n        '<html><head></head><body><div>a div</div><span>a span</span></body></html><div>a div</div><span>a span</span>';\n      expect(cheerio.html($collection)).toBe(expected);\n    });\n\n    it('() : does not crash with `null` as `this` value', () => {\n      const { html } = cheerio;\n      expect(html.call(null as never)).toBe('');\n      expect(html.call(null as never, '#nothing')).toBe('');\n    });\n  });\n\n  describe('.text', () => {\n    it('(cheerio object) : should return the text contents of the specified elements', () => {\n      const $ = cheerio.load('<a>This is <em>content</em>.</a>');\n      expect(cheerio.text($('a'))).toBe('This is content.');\n    });\n\n    it('(cheerio object) : should omit comment nodes', () => {\n      const $ = cheerio.load(\n        '<a>This is <!-- a comment --> not a comment.</a>',\n      );\n      expect(cheerio.text($('a'))).toBe('This is  not a comment.');\n    });\n\n    it('(cheerio object) : should include text contents of children recursively', () => {\n      const $ = cheerio.load(\n        '<a>This is <div>a child with <span>another child and <!-- a comment --> not a comment</span> followed by <em>one last child</em> and some final</div> text.</a>',\n      );\n      expect(cheerio.text($('a'))).toBe(\n        'This is a child with another child and  not a comment followed by one last child and some final text.',\n      );\n    });\n\n    it('() : should return the rendered text content of the root', () => {\n      const $ = cheerio.load(\n        '<a>This is <div>a child with <span>another child and <!-- a comment --> not a comment</span> followed by <em>one last child</em> and some final</div> text.</a>',\n      );\n      expect(cheerio.text($.root())).toBe(\n        'This is a child with another child and  not a comment followed by one last child and some final text.',\n      );\n    });\n\n    it('(cheerio object) : should not omit script tags', () => {\n      const $ = cheerio.load('<script>console.log(\"test\")</script>');\n      expect(cheerio.text($.root())).toBe('console.log(\"test\")');\n    });\n\n    it('(cheerio object) : should omit style tags', () => {\n      const $ = cheerio.load(\n        '<style type=\"text/css\">.cf-hidden { display: none; }</style>',\n      );\n      expect($.text()).toBe('.cf-hidden { display: none; }');\n    });\n\n    it('() : does not crash with `null` as `this` value', () => {\n      const { text } = cheerio;\n      expect(text.call(null as never)).toBe('');\n    });\n  });\n\n  describe('.parseHTML', () => {\n    const $ = cheerio.load('');\n\n    it('() : returns null', () => {\n      expect($.parseHTML()).toBe(null);\n    });\n\n    it('(null) : returns null', () => {\n      expect($.parseHTML(null)).toBe(null);\n    });\n\n    it('(\"\") : returns null', () => {\n      expect($.parseHTML('')).toBe(null);\n    });\n\n    it('(largeHtmlString) : parses large HTML strings', () => {\n      const html = '<div></div>'.repeat(10);\n      const nodes = $.parseHTML(html);\n\n      expect(nodes.length).toBe(10);\n      expect(nodes).toBeInstanceOf(Array);\n    });\n\n    it('(\"<script>\") : ignores scripts by default', () => {\n      const html = '<script>undefined()</script>';\n      expect($.parseHTML(html)).toHaveLength(0);\n    });\n\n    it('(\"<script>\", true) : preserves scripts when requested', () => {\n      const html = '<script>undefined()</script>';\n      expect($.parseHTML(html, true)[0]).toHaveProperty('tagName', 'script');\n    });\n\n    it('(\"scriptAndNonScript) : preserves non-script nodes', () => {\n      const html = '<script>undefined()</script><div></div>';\n      expect($.parseHTML(html)[0]).toHaveProperty('tagName', 'div');\n    });\n\n    it('(scriptAndNonScript, true) : Preserves script position', () => {\n      const html = '<script>undefined()</script><div></div>';\n      expect($.parseHTML(html, true)[0]).toHaveProperty('tagName', 'script');\n    });\n\n    it('(text) : returns a text node', () => {\n      expect($.parseHTML('text')[0].type).toBe('text');\n    });\n\n    it('(<tab>>text) : preserves leading whitespace', () => {\n      expect($.parseHTML('\\t<div></div>')[0]).toHaveProperty('data', '\\t');\n    });\n\n    it('( text) : Leading spaces are treated as text nodes', () => {\n      expect($.parseHTML(' <div/> ')[0].type).toBe('text');\n    });\n\n    it('(html) : should preserve content', () => {\n      const html = '<div>test div</div>';\n      expect(cheerio($.parseHTML(html)[0]).html()).toBe('test div');\n    });\n\n    it('(malformedHtml) : should not break', () => {\n      expect($.parseHTML('<span><span>')).toHaveLength(1);\n    });\n\n    it('(garbageInput) : should not cause an error', () => {\n      expect(\n        $.parseHTML('<#if><tr><p>This is a test.</p></tr><#/if>'),\n      ).toBeTruthy();\n    });\n\n    it('(text) : should return an array that is not effected by DOM manipulation methods', () => {\n      const $div = cheerio.load('<div>');\n      const elems = $div.parseHTML('<b></b><i></i>');\n\n      $div('div').append(elems);\n\n      expect(elems).toHaveLength(2);\n    });\n\n    it('(html, context) : should ignore context argument', () => {\n      const $div = cheerio.load('<div>');\n      const elems = $div.parseHTML('<script>foo</script><a>', { foo: 123 });\n\n      $div('div').append(elems);\n\n      expect(elems).toHaveLength(1);\n    });\n\n    it('(html, context, keepScripts) : should ignore context argument', () => {\n      const $div = cheerio.load('<div>');\n      const elems = $div.parseHTML(\n        '<script>foo</script><a>',\n        { foo: 123 },\n        true,\n      );\n\n      $div('div').append(elems);\n\n      expect(elems).toHaveLength(2);\n    });\n  });\n\n  describe('.merge', () => {\n    const $ = cheerio.load('');\n\n    it('should be a function', () => {\n      expect(typeof $.merge).toBe('function');\n    });\n\n    it('(arraylike, arraylike) : should modify the first array, but not the second', () => {\n      const arr1 = [1, 2, 3];\n      const arr2 = [4, 5, 6];\n\n      const ret = $.merge(arr1, arr2);\n      expect(typeof ret).toBe('object');\n      expect(Array.isArray(ret)).toBe(true);\n      expect(ret).toBe(arr1);\n      expect(arr1).toHaveLength(6);\n      expect(arr2).toHaveLength(3);\n    });\n\n    it('(arraylike, arraylike) : should handle objects that arent arrays, but are arraylike', () => {\n      const arr1: ArrayLike<string> = {\n        length: 3,\n        0: 'a',\n        1: 'b',\n        2: 'c',\n      };\n      const arr2 = {\n        length: 3,\n        0: 'd',\n        1: 'e',\n        2: 'f',\n      };\n\n      $.merge(arr1, arr2);\n      expect(arr1).toHaveLength(6);\n      expect(arr1[3]).toBe('d');\n      expect(arr1[4]).toBe('e');\n      expect(arr1[5]).toBe('f');\n      expect(arr2).toHaveLength(3);\n    });\n\n    it('(?, ?) : should gracefully reject invalid inputs', () => {\n      expect($.merge([4], 3 as never)).toBeFalsy();\n      expect($.merge({} as never, {} as never)).toBeFalsy();\n      expect($.merge([], {} as never)).toBeFalsy();\n      expect($.merge({} as never, [])).toBeFalsy();\n      const fakeArray1 = { length: 3, 0: 'a', 1: 'b', 3: 'd' };\n      expect($.merge(fakeArray1, [])).toBeFalsy();\n      expect($.merge([], fakeArray1)).toBeFalsy();\n      expect($.merge({ length: '7' } as never, [])).toBeFalsy();\n      expect($.merge({ length: -1 }, [])).toBeFalsy();\n    });\n\n    it('(?, ?) : should no-op on invalid inputs', () => {\n      const fakeArray1 = { length: 3, 0: 'a', 1: 'b', 3: 'd' };\n      $.merge(fakeArray1, []);\n      expect(fakeArray1).toHaveLength(3);\n      expect(fakeArray1[0]).toBe('a');\n      expect(fakeArray1[1]).toBe('b');\n      expect(fakeArray1[3]).toBe('d');\n      $.merge([], fakeArray1);\n      expect(fakeArray1).toHaveLength(3);\n      expect(fakeArray1[0]).toBe('a');\n      expect(fakeArray1[1]).toBe('b');\n      expect(fakeArray1[3]).toBe('d');\n    });\n  });\n\n  describe('.contains', () => {\n    let $: CheerioAPI;\n\n    beforeEach(() => {\n      $ = cheerio.load(food);\n    });\n\n    it('(container, contained) : should correctly detect the provided element', () => {\n      const $food = $('#food');\n      const $fruits = $('#fruits');\n      const $apple = $('.apple');\n\n      expect($.contains($food[0], $fruits[0])).toBe(true);\n      expect($.contains($food[0], $apple[0])).toBe(true);\n    });\n\n    it('(container, other) : should not detect elements that are not contained', () => {\n      const $fruits = $('#fruits');\n      const $vegetables = $('#vegetables');\n      const $apple = $('.apple');\n\n      expect($.contains($vegetables[0], $apple[0])).toBe(false);\n      expect($.contains($fruits[0], $vegetables[0])).toBe(false);\n      expect($.contains($vegetables[0], $fruits[0])).toBe(false);\n      expect($.contains($fruits[0], $fruits[0])).toBe(false);\n      expect($.contains($vegetables[0], $vegetables[0])).toBe(false);\n    });\n  });\n\n  describe('.root', () => {\n    it('() : should return a cheerio-wrapped root object', () => {\n      const $ = cheerio.load('<html><head></head><body>foo</body></html>');\n      $.root().append('<div id=\"test\"></div>');\n      expect($.html()).toBe(\n        '<html><head></head><body>foo</body></html><div id=\"test\"></div>',\n      );\n    });\n  });\n\n  describe('.extract', () => {\n    it('() : should extract values for selectors', () => {\n      const $ = cheerio.load(eleven);\n\n      expect(\n        $.extract({\n          red: [{ selector: '.red', value: 'outerHTML' }],\n        }),\n      ).toStrictEqual({\n        red: [\n          '<li class=\"red\">Four</li>',\n          '<li class=\"red\">Five</li>',\n          '<li class=\"red sel\">Nine</li>',\n        ],\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "src/static.ts",
    "content": "import type { AnyNode, Document } from 'domhandler';\nimport { textContent } from 'domutils';\nimport type { ExtractedMap, ExtractMap } from './api/extract.js';\nimport type { Cheerio } from './cheerio.js';\nimport type { CheerioAPI } from './load.js';\nimport {\n  type CheerioOptions,\n  flattenOptions,\n  type InternalOptions,\n} from './options.js';\nimport type { BasicAcceptedElems } from './types.js';\n\n/**\n * Helper function to render a DOM.\n *\n * @param that - Cheerio instance to render.\n * @param dom - The DOM to render. Defaults to `that`'s root.\n * @param options - Options for rendering.\n * @returns The rendered document.\n */\nfunction render(\n  that: CheerioAPI,\n  dom: BasicAcceptedElems<AnyNode> | undefined,\n  options: InternalOptions,\n): string {\n  if (!that) return '';\n\n  return that(dom ?? that._root.children, null, undefined, options).toString();\n}\n\n/**\n * Checks if a passed object is an options object.\n *\n * @param dom - Object to check if it is an options object.\n * @param options - Options object.\n * @returns Whether the object is an options object.\n */\nfunction isOptions(\n  dom?: BasicAcceptedElems<AnyNode> | CheerioOptions | null,\n  options?: CheerioOptions,\n): dom is CheerioOptions {\n  return (\n    !options &&\n    typeof dom === 'object' &&\n    dom != null &&\n    !('length' in dom) &&\n    !('type' in dom)\n  );\n}\n\n/**\n * Renders the document.\n *\n * @category Static\n * @param options - Options for the renderer.\n * @returns The rendered document.\n */\nexport function html(this: CheerioAPI, options?: CheerioOptions): string;\n/**\n * Renders the document.\n *\n * @category Static\n * @param dom - Element to render.\n * @param options - Options for the renderer.\n * @returns The rendered document.\n */\nexport function html(\n  this: CheerioAPI,\n  dom?: BasicAcceptedElems<AnyNode>,\n  options?: CheerioOptions,\n): string;\nexport function html(\n  this: CheerioAPI,\n  dom?: BasicAcceptedElems<AnyNode> | CheerioOptions,\n  options?: CheerioOptions,\n): string {\n  /*\n   * Be flexible about parameters, sometimes we call html(),\n   * with options as only parameter\n   * check dom argument for dom element specific properties\n   * assume there is no 'length' or 'type' properties in the options object\n   */\n  const toRender = isOptions(dom) ? ((options = dom), undefined) : dom;\n\n  /*\n   * Sometimes `$.html()` is used without preloading html,\n   * so fallback non-existing options to the default ones.\n   */\n  const opts = {\n    ...this?._options,\n    ...flattenOptions(options),\n  };\n\n  return render(this, toRender, opts);\n}\n\n/**\n * Render the document as XML.\n *\n * @category Static\n * @param dom - Element to render.\n * @returns THe rendered document.\n */\nexport function xml(\n  this: CheerioAPI,\n  dom?: BasicAcceptedElems<AnyNode>,\n): string {\n  const options = { ...this._options, xmlMode: true };\n\n  return render(this, dom, options);\n}\n\n/**\n * Render the document as text.\n *\n * This returns the `textContent` of the passed elements. The result will\n * include the contents of `<script>` and `<style>` elements. To avoid this, use\n * `.prop('innerText')` instead.\n *\n * @category Static\n * @param elements - Elements to render.\n * @returns The rendered document.\n */\nexport function text(\n  this: CheerioAPI | void,\n  elements?: ArrayLike<AnyNode>,\n): string {\n  const elems = elements ?? (this ? this.root() : []);\n\n  let ret = '';\n\n  for (let i = 0; i < elems.length; i++) {\n    ret += textContent(elems[i]);\n  }\n\n  return ret;\n}\n\n/**\n * Parses a string into an array of DOM nodes. The `context` argument has no\n * meaning for Cheerio, but it is maintained for API compatibility with jQuery.\n *\n * @category Static\n * @param data - Markup that will be parsed.\n * @param context - Will be ignored. If it is a boolean it will be used as the\n *   value of `keepScripts`.\n * @param keepScripts - If false all scripts will be removed.\n * @returns The parsed DOM.\n * @alias Cheerio.parseHTML\n * @see {@link https://api.jquery.com/jQuery.parseHTML/}\n */\nexport function parseHTML(\n  this: CheerioAPI,\n  data: string,\n  context?: unknown,\n  keepScripts?: boolean,\n): AnyNode[];\nexport function parseHTML(this: CheerioAPI, data?: '' | null): null;\nexport function parseHTML(\n  this: CheerioAPI,\n  data?: string | null,\n  context?: unknown,\n  keepScripts = typeof context === 'boolean' ? context : false,\n): AnyNode[] | null {\n  if (!data || typeof data !== 'string') {\n    return null;\n  }\n\n  if (typeof context === 'boolean') {\n    keepScripts = context;\n  }\n\n  const parsed = this.load(data, this._options, false);\n  if (!keepScripts) {\n    parsed('script').remove();\n  }\n\n  /*\n   * The `children` array is used by Cheerio internally to group elements that\n   * share the same parents. When nodes created through `parseHTML` are\n   * inserted into previously-existing DOM structures, they will be removed\n   * from the `children` array. The results of `parseHTML` should remain\n   * constant across these operations, so a shallow copy should be returned.\n   */\n  return [...parsed.root()[0].children];\n}\n\n/**\n * Sometimes you need to work with the top-level root element. To query it, you\n * can use `$.root()`.\n *\n * @category Static\n * @example\n *\n * ```js\n * $.root().append('<ul id=\"vegetables\"></ul>').html();\n * //=> <ul id=\"fruits\">...</ul><ul id=\"vegetables\"></ul>\n * ```\n *\n * @returns Cheerio instance wrapping the root node.\n * @alias Cheerio.root\n */\nexport function root(this: CheerioAPI): Cheerio<Document> {\n  return this(this._root);\n}\n\n/**\n * Checks to see if the `contained` DOM element is a descendant of the\n * `container` DOM element.\n *\n * @category Static\n * @param container - Potential parent node.\n * @param contained - Potential child node.\n * @returns Indicates if the nodes contain one another.\n * @alias Cheerio.contains\n * @see {@link https://api.jquery.com/jQuery.contains/}\n */\nexport function contains(container: AnyNode, contained: AnyNode): boolean {\n  // According to the jQuery API, an element does not \"contain\" itself\n  if (contained === container) {\n    return false;\n  }\n\n  /*\n   * Step up the descendants, stopping when the root element is reached\n   * (signaled by `.parent` returning a reference to the same object)\n   */\n  let next: AnyNode | null = contained;\n  while (next && next !== next.parent) {\n    next = next.parent;\n    if (next === container) {\n      return true;\n    }\n  }\n\n  return false;\n}\n\n/**\n * Extract multiple values from a document, and store them in an object.\n *\n * @category Static\n * @param map - An object containing key-value pairs. The keys are the names of\n *   the properties to be created on the object, and the values are the\n *   selectors to be used to extract the values.\n * @returns An object containing the extracted values.\n */\nexport function extract<M extends ExtractMap>(\n  this: CheerioAPI,\n  map: M,\n): ExtractedMap<M> {\n  return this.root().extract(map);\n}\n\ntype Writable<T> = { -readonly [P in keyof T]: T[P] };\n\n/**\n * $.merge().\n *\n * @category Static\n * @param arr1 - First array.\n * @param arr2 - Second array.\n * @returns `arr1`, with elements of `arr2` inserted.\n * @alias Cheerio.merge\n * @see {@link https://api.jquery.com/jQuery.merge/}\n */\nexport function merge<T>(\n  arr1: Writable<ArrayLike<T>>,\n  arr2: ArrayLike<T>,\n): ArrayLike<T> | undefined {\n  if (!(isArrayLike(arr1) && isArrayLike(arr2))) {\n    return;\n  }\n  let newLength = arr1.length;\n  const len = +arr2.length;\n\n  for (let i = 0; i < len; i++) {\n    arr1[newLength++] = arr2[i];\n  }\n  arr1.length = newLength;\n  return arr1;\n}\n\n/**\n * Checks if an object is array-like.\n *\n * @category Static\n * @param item - Item to check.\n * @returns Indicates if the item is array-like.\n */\nfunction isArrayLike(item: unknown): item is ArrayLike<unknown> {\n  if (Array.isArray(item)) {\n    return true;\n  }\n\n  if (\n    typeof item !== 'object' ||\n    item === null ||\n    !('length' in item) ||\n    typeof item.length !== 'number' ||\n    item.length < 0\n  ) {\n    return false;\n  }\n\n  for (let i = 0; i < item.length; i++) {\n    if (!(i in item)) {\n      return false;\n    }\n  }\n  return true;\n}\n"
  },
  {
    "path": "src/types.ts",
    "content": "/** @file Types used in signatures of Cheerio methods. */\n\ntype LowercaseLetters =\n  | 'a'\n  | 'b'\n  | 'c'\n  | 'd'\n  | 'e'\n  | 'f'\n  | 'g'\n  | 'h'\n  | 'i'\n  | 'j'\n  | 'k'\n  | 'l'\n  | 'm'\n  | 'n'\n  | 'o'\n  | 'p'\n  | 'q'\n  | 'r'\n  | 's'\n  | 't'\n  | 'u'\n  | 'v'\n  | 'w'\n  | 'x'\n  | 'y'\n  | 'z';\n\ntype AlphaNumeric =\n  | LowercaseLetters\n  | Uppercase<LowercaseLetters>\n  | `${number}`;\n\ntype SelectorSpecial = '.' | '#' | ':' | '|' | '>' | '+' | '~' | '[';\n/**\n * Type for identifying selectors. Allows us to \"upgrade\" queries using\n * selectors to return `Element`s.\n */\nexport type SelectorType =\n  | `${SelectorSpecial}${AlphaNumeric}${string}`\n  | `${AlphaNumeric}${string}`;\n\nimport type { AnyNode } from 'domhandler';\nimport type { Cheerio } from './cheerio.js';\n\n/** Elements that can be passed to manipulation methods. */\nexport type BasicAcceptedElems<T extends AnyNode> = ArrayLike<T> | T | string;\n/** Elements that can be passed to manipulation methods, including functions. */\nexport type AcceptedElems<T extends AnyNode> =\n  | BasicAcceptedElems<T>\n  | ((this: T, i: number, el: T) => BasicAcceptedElems<T>);\n\n/** Function signature, for traversal methods. */\nexport type FilterFunction<T> = (this: T, i: number, el: T) => boolean;\n/** Supported filter types, for traversal methods. */\nexport type AcceptedFilters<T> = string | FilterFunction<T> | T | Cheerio<T>;\n"
  },
  {
    "path": "src/utils.spec.ts",
    "content": "import { describe, expect, it } from 'vitest';\nimport * as utils from './utils.js';\n\ndescribe('util functions', () => {\n  it('camelCase function test', () => {\n    expect(utils.camelCase('cheerio.js')).toBe('cheerioJs');\n    expect(utils.camelCase('camel-case-')).toBe('camelCase');\n    expect(utils.camelCase('__directory__')).toBe('_directory_');\n    expect(utils.camelCase('_one-two.three')).toBe('OneTwoThree');\n  });\n\n  it('cssCase function test', () => {\n    expect(utils.cssCase('camelCase')).toBe('camel-case');\n    expect(utils.cssCase('jQuery')).toBe('j-query');\n    expect(utils.cssCase('neverSayNever')).toBe('never-say-never');\n    expect(utils.cssCase('CSSCase')).toBe('-c-s-s-case');\n  });\n\n  it('isHtml function test', () => {\n    expect(utils.isHtml('<html>')).toBe(true);\n    expect(utils.isHtml('\\n<html>\\n')).toBe(true);\n    expect(utils.isHtml('#main')).toBe(false);\n    expect(utils.isHtml('\\n<p>foo<p>bar\\n')).toBe(true);\n    expect(utils.isHtml('dog<p>fox<p>cat')).toBe(true);\n    expect(utils.isHtml('<p>fox<p>cat')).toBe(true);\n    expect(utils.isHtml('\\n<p>fox<p>cat\\n')).toBe(true);\n    expect(utils.isHtml('#<p>fox<p>cat#')).toBe(true);\n    expect(utils.isHtml('<!-- comment -->')).toBe(true);\n    expect(utils.isHtml('<!doctype html>')).toBe(true);\n    expect(utils.isHtml('<123>')).toBe(false);\n  });\n});\n"
  },
  {
    "path": "src/utils.ts",
    "content": "import type { AnyNode } from 'domhandler';\nimport type { Cheerio } from './cheerio.js';\n\n/**\n * Checks if an object is a Cheerio instance.\n *\n * @category Utils\n * @param maybeCheerio - The object to check.\n * @returns Whether the object is a Cheerio instance.\n */\nexport function isCheerio<T>(\n  maybeCheerio: unknown,\n): maybeCheerio is Cheerio<T> {\n  return (maybeCheerio as Cheerio<T>).cheerio != null;\n}\n\n/**\n * Convert a string to camel case notation.\n *\n * @private\n * @category Utils\n * @param str - The string to be converted.\n * @returns String in camel case notation.\n */\nexport function camelCase(str: string): string {\n  return str.replace(/[._-](\\w|$)/g, (_, x) => (x as string).toUpperCase());\n}\n\n/**\n * Convert a string from camel case to \"CSS case\", where word boundaries are\n * described by hyphens (\"-\") and all characters are lower-case.\n *\n * @private\n * @category Utils\n * @param str - The string to be converted.\n * @returns String in \"CSS case\".\n */\nexport function cssCase(str: string): string {\n  return str.replace(/[A-Z]/g, '-$&').toLowerCase();\n}\n\n/**\n * Iterate over each DOM element without creating intermediary Cheerio\n * instances.\n *\n * This is indented for use internally to avoid otherwise unnecessary memory\n * pressure introduced by _make.\n *\n * @category Utils\n * @param array - The array to iterate over.\n * @param fn - Function to call.\n * @returns The original instance.\n */\nexport function domEach<\n  T extends AnyNode,\n  Arr extends ArrayLike<T> = Cheerio<T>,\n>(array: Arr, fn: (elem: T, index: number) => void): Arr {\n  const len = array.length;\n  for (let i = 0; i < len; i++) fn(array[i], i);\n  return array;\n}\n\nconst enum CharacterCode {\n  LowerA = 97,\n  LowerZ = 122,\n  UpperA = 65,\n  UpperZ = 90,\n  Exclamation = 33,\n}\n\n/**\n * Check if string is HTML.\n *\n * Tests for a `<` within a string, immediate followed by a letter and\n * eventually followed by a `>`.\n *\n * @private\n * @category Utils\n * @param str - The string to check.\n * @returns Indicates if `str` is HTML.\n */\nexport function isHtml(str: string): boolean {\n  if (typeof str !== 'string') {\n    return false;\n  }\n\n  const tagStart = str.indexOf('<');\n\n  if (tagStart === -1 || tagStart > str.length - 3) return false;\n\n  const tagChar = str.charCodeAt(tagStart + 1) as CharacterCode;\n\n  return (\n    ((tagChar >= CharacterCode.LowerA && tagChar <= CharacterCode.LowerZ) ||\n      (tagChar >= CharacterCode.UpperA && tagChar <= CharacterCode.UpperZ) ||\n      tagChar === CharacterCode.Exclamation) &&\n    str.includes('>', tagStart + 2)\n  );\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    /* Basic Options */\n    \"target\": \"es2019\",\n    \"module\": \"node16\",\n    \"moduleResolution\": \"node16\",\n    \"lib\": [\"ES2022\"],\n    \"declaration\": true,\n    \"declarationMap\": true,\n    \"sourceMap\": true,\n    \"outDir\": \"lib\",\n\n    /* Strict Type-Checking Options */\n    \"strict\": true,\n\n    /* Additional Checks */\n    \"isolatedDeclarations\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"isolatedModules\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"noImplicitOverride\": true,\n    \"noImplicitReturns\": true,\n    \"noPropertyAccessFromIndexSignature\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true\n  },\n  \"exclude\": [\"website/\"] /* The website has its own tsconfig. */\n}\n"
  },
  {
    "path": "tsconfig.typedoc.json",
    "content": "{\n  \"extends\": \"./tsconfig.json\",\n  \"exclude\": [\"*.config.ts\", \"*.spec.ts\", \"scripts/*\", \"website/*\"]\n}\n"
  },
  {
    "path": "vitest.config.ts",
    "content": "import { defineConfig, type ViteUserConfig } from 'vitest/config';\n\nconst config: ViteUserConfig = defineConfig({\n  test: {\n    coverage: {\n      exclude: [\n        'benchmark/**',\n        'scripts/**',\n        'website/**',\n        'dist/**',\n        '*.config.ts',\n      ],\n    },\n    typecheck: {\n      enabled: true,\n      include: ['src/api/extract.spec.ts'],\n    },\n  },\n});\n\nexport default config;\n"
  },
  {
    "path": "website/README.md",
    "content": "# Website\n\nThis website is built using [Docusaurus 2](https://docusaurus.io/), a modern\nstatic website generator.\n\n### Installation\n\n```\n$ yarn\n```\n\n### Local Development\n\n```\n$ yarn start\n```\n\nThis command starts a local development server and opens up a browser window.\nMost changes are reflected live without having to restart the server.\n\n### Build\n\n```\n$ yarn build\n```\n\nThis command generates static content into the `build` directory and can be\nserved using any static contents hosting service.\n"
  },
  {
    "path": "website/astro.config.mjs",
    "content": "import mdx from '@astrojs/mdx';\nimport react from '@astrojs/react';\nimport sitemap from '@astrojs/sitemap';\nimport tailwindcss from '@tailwindcss/vite';\nimport { defineConfig } from 'astro/config';\nimport remarkDirective from 'remark-directive';\nimport { rehypeExternalLinks } from './src/plugins/rehype-external-links.ts';\nimport { remarkAdmonitions } from './src/plugins/remark-admonitions.ts';\nimport { remarkFixTypedocLinks } from './src/plugins/remark-fix-typedoc-links.ts';\nimport { remarkLiveCode } from './src/plugins/remark-live-code.ts';\n\nexport default defineConfig({\n  site: 'https://cheerio.js.org',\n  integrations: [mdx(), react(), sitemap()],\n  image: {\n    remotePatterns: [\n      { protocol: 'https', hostname: 'github.com' },\n      { protocol: 'https', hostname: '*.github.com' },\n      { protocol: 'https', hostname: 'images.opencollective.com' },\n      { protocol: 'https', hostname: 'hasdata.com' },\n    ],\n  },\n  vite: {\n    plugins: [tailwindcss()],\n  },\n  markdown: {\n    remarkPlugins: [\n      remarkDirective,\n      remarkAdmonitions,\n      remarkFixTypedocLinks,\n      remarkLiveCode,\n    ],\n    rehypePlugins: [rehypeExternalLinks],\n  },\n});\n"
  },
  {
    "path": "website/package.json",
    "content": "{\n  \"name\": \"@cheerio/website\",\n  \"description\": \"Documentation website for cheerio\",\n  \"type\": \"module\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"astro\": \"astro\",\n    \"build\": \"npm run build:api && astro build\",\n    \"build:api\": \"typedoc\",\n    \"dev\": \"astro dev\",\n    \"preview\": \"astro preview\",\n    \"start\": \"astro dev\"\n  },\n  \"dependencies\": {\n    \"@astrojs/mdx\": \"^4.3.0\",\n    \"@astrojs/react\": \"^5.0.1\",\n    \"@astrojs/rss\": \"^4.0.17\",\n    \"@astrojs/sitemap\": \"^3.7.1\",\n    \"@codesandbox/sandpack-react\": \"^2.19.0\",\n    \"@docsearch/css\": \"^4.6.0\",\n    \"@docsearch/js\": \"^4.6.0\",\n    \"@lucide/astro\": \"^0.577.0\",\n    \"@tailwindcss/vite\": \"^4.2.2\",\n    \"astro\": \"^5.17.2\",\n    \"hastscript\": \"^9.0.1\",\n    \"marked\": \"^17.0.4\",\n    \"react\": \"^19.1.0\",\n    \"react-dom\": \"^19.2.4\",\n    \"remark-directive\": \"^4.0.0\",\n    \"tailwindcss\": \"^4.2.2\",\n    \"unist-util-visit\": \"^5.1.0\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^19.2.14\",\n    \"@types/react-dom\": \"^19.1.0\",\n    \"typedoc\": \"^0.28.17\",\n    \"typedoc-plugin-markdown\": \"^4.11.0\",\n    \"typedoc-plugin-mdn-links\": \"^5.1.1\",\n    \"typescript\": \"^5.9.3\"\n  },\n  \"engines\": {\n    \"node\": \">=20.18.1\"\n  }\n}\n"
  },
  {
    "path": "website/sponsors.json",
    "content": "{\n  \"headliner\": [\n    {\n      \"createdAt\": \"2022-06-24\",\n      \"name\": \"Github\",\n      \"image\": \"https://github.com/github.png\",\n      \"url\": \"https://github.com/\",\n      \"type\": \"ORGANIZATION\",\n      \"monthlyDonation\": 0,\n      \"totalDonations\": 0,\n      \"source\": \"manual\",\n      \"tier\": \"headliner\"\n    },\n    {\n      \"createdAt\": \"2018-05-02\",\n      \"name\": \"AirBnB\",\n      \"image\": \"https://github.com/airbnb.png\",\n      \"url\": \"https://www.airbnb.com/\",\n      \"type\": \"ORGANIZATION\",\n      \"monthlyDonation\": 0,\n      \"totalDonations\": 0,\n      \"source\": \"manual\",\n      \"tier\": \"headliner\"\n    },\n    {\n      \"createdAt\": \"2026-01-21\",\n      \"name\": \"HasData\",\n      \"image\": \"https://hasdata.com/favicon.svg\",\n      \"url\": \"https://hasdata.com\",\n      \"type\": \"ORGANIZATION\",\n      \"monthlyDonation\": 0,\n      \"totalDonations\": 0,\n      \"source\": \"manual\",\n      \"tier\": \"headliner\"\n    },\n    {\n      \"createdAt\": \"2026-01-28\",\n      \"name\": \"brand.dev\",\n      \"image\": \"https://github.com/brand-dot-dev.png\",\n      \"url\": \"https://brand.dev/\",\n      \"type\": \"ORGANIZATION\",\n      \"monthlyDonation\": 0,\n      \"totalDonations\": 0,\n      \"source\": \"manual\",\n      \"tier\": \"headliner\"\n    }\n  ],\n  \"sponsor\": [\n    {\n      \"createdAt\": \"2023-12-05T16:44:08.370Z\",\n      \"name\": \"OnlineCasinosSpelen\",\n      \"url\": \"https://onlinecasinosspelen.com\",\n      \"image\": \"https://images.opencollective.com/onlinecasinosspelen/99ac6a2/logo.png\",\n      \"type\": \"ORGANIZATION\",\n      \"monthlyDonation\": 0,\n      \"totalDonations\": 0,\n      \"source\": \"opencollective\",\n      \"tier\": \"sponsor\"\n    },\n    {\n      \"createdAt\": \"2023-12-07T14:07:39.203Z\",\n      \"name\": \"Nieuwe-Casinos.net\",\n      \"url\": \"https://Nieuwe-Casinos.net\",\n      \"image\": \"https://images.opencollective.com/nieuwecasinos/c67d423/logo.png\",\n      \"type\": \"ORGANIZATION\",\n      \"monthlyDonation\": 0,\n      \"totalDonations\": 0,\n      \"source\": \"opencollective\",\n      \"tier\": \"sponsor\"\n    }\n  ],\n  \"professional\": [\n    {\n      \"createdAt\": \"2022-01-26T15:21:25.562Z\",\n      \"name\": \"Vasy Kafidoff\",\n      \"url\": \"https://kafidoff.com\",\n      \"image\": \"https://images.opencollective.com/kafidoff-vasy/d7ff85c/avatar.png\",\n      \"type\": \"INDIVIDUAL\",\n      \"monthlyDonation\": 0,\n      \"totalDonations\": 0,\n      \"source\": \"opencollective\",\n      \"tier\": \"professional\"\n    }\n  ],\n  \"backer\": []\n}\n"
  },
  {
    "path": "website/src/components/Features.astro",
    "content": "---\nimport { CodeXml, Globe, Zap } from '@lucide/astro';\n\nconst features = [\n  {\n    title: 'Proven syntax',\n    description:\n      'Cheerio implements a subset of core jQuery. Cheerio removes all the DOM inconsistencies and browser cruft from the jQuery library, revealing its truly gorgeous API.',\n    accent: 'from-rose-500/10 to-pink-500/10',\n    border: 'hover:border-rose-500/30',\n    iconColor: 'text-rose-500',\n    Icon: CodeXml,\n  },\n  {\n    title: 'Blazingly fast',\n    description:\n      'Cheerio works with a very simple, consistent DOM model. As a result parsing, manipulating, and rendering are incredibly efficient.',\n    accent: 'from-amber-500/10 to-yellow-500/10',\n    border: 'hover:border-amber-500/30',\n    iconColor: 'text-amber-500',\n    Icon: Zap,\n  },\n  {\n    title: 'Incredibly flexible',\n    description:\n      'Cheerio can parse nearly any HTML or XML document. Cheerio works in both browser and server environments.',\n    accent: 'from-blue-500/10 to-cyan-500/10',\n    border: 'hover:border-blue-500/30',\n    iconColor: 'text-blue-500',\n    Icon: Globe,\n  },\n];\n---\n\n<section class=\"relative bg-white py-24 dark:bg-slate-900\">\n  <!-- Subtle top divider -->\n  <div class=\"absolute top-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-slate-200 to-transparent dark:via-slate-800\"></div>\n\n  <div class=\"mx-auto max-w-6xl px-6\">\n    <div class=\"animate-fade-up mb-16 text-center\">\n      <p class=\"mb-3 text-sm font-semibold uppercase tracking-widest text-primary\">Why Cheerio?</p>\n      <h2 class=\"font-display text-4xl text-slate-900 md:text-5xl dark:text-white\">\n        Built for developers who ship\n      </h2>\n    </div>\n\n    <div class=\"grid grid-cols-1 gap-8 md:grid-cols-3\">\n      {\n        features.map((feature, i) => (\n          <div\n            class:list={[\n              'animate-fade-up group relative rounded-2xl border border-slate-200 bg-gradient-to-br p-8 transition-all duration-300 dark:border-slate-800',\n              feature.accent,\n              feature.border,\n              `animation-delay-${(i + 1) * 200}`,\n            ]}\n          >\n            <div class:list={['mb-6 inline-flex rounded-xl bg-white p-3 shadow-sm dark:bg-slate-800', feature.iconColor]}>\n              <feature.Icon class=\"h-8 w-8\" stroke-width={1.5} />\n            </div>\n            <h3 class=\"mb-3 text-xl font-semibold text-slate-900 dark:text-white\">\n              {feature.title}\n            </h3>\n            <p class=\"leading-relaxed text-slate-600 dark:text-slate-400\">\n              {feature.description}\n            </p>\n          </div>\n        ))\n      }\n    </div>\n  </div>\n</section>\n"
  },
  {
    "path": "website/src/components/Footer.astro",
    "content": "---\nimport { Image } from 'astro:assets';\nimport logo from '@/assets/orange-c.svg';\n\nconst footerLinks = {\n  Docs: [\n    { label: 'Learn', href: '/docs/intro' },\n    { label: 'API', href: '/docs/api' },\n  ],\n  Community: [\n    {\n      label: 'Stack Overflow',\n      href: 'https://stackoverflow.com/questions/tagged/cheerio',\n    },\n    { label: 'GitHub', href: 'https://github.com/cheeriojs/cheerio' },\n  ],\n  More: [{ label: 'Blog', href: '/blog' }],\n};\n\nconst currentYear = new Date().getFullYear();\n---\n\n<footer class=\"border-t border-slate-200 bg-slate-950 py-16 text-slate-400 dark:border-slate-800\">\n  <div class=\"mx-auto max-w-7xl px-6\">\n    <div class=\"grid grid-cols-2 gap-8 md:grid-cols-4\">\n      <!-- Brand column -->\n      <div class=\"col-span-2 mb-4 md:col-span-1 md:mb-0\">\n        <a href=\"/\" class=\"mb-4 flex items-center gap-2.5\">\n          <Image src={logo} alt=\"Cheerio\" class=\"h-7 w-7\" loading=\"lazy\" />\n          <span class=\"text-lg font-semibold text-white\">Cheerio</span>\n        </a>\n        <p class=\"mt-3 max-w-xs text-sm leading-relaxed text-slate-500\">\n          The fast, flexible & elegant library for parsing and manipulating HTML and XML.\n        </p>\n      </div>\n\n      {\n        Object.entries(footerLinks).map(([title, links]) => (\n          <div>\n            <h3 class=\"mb-4 text-xs font-semibold uppercase tracking-widest text-slate-500\">\n              {title}\n            </h3>\n            <ul class=\"space-y-3\">\n              {links.map((link) => (\n                <li>\n                  <a\n                    href={link.href}\n                    class=\"text-sm transition-colors hover:text-primary-light\"\n                    target={link.href.startsWith('http') ? '_blank' : undefined}\n                    rel={\n                      link.href.startsWith('http')\n                        ? 'noopener noreferrer'\n                        : undefined\n                    }\n                  >\n                    {link.label}\n                  </a>\n                </li>\n              ))}\n            </ul>\n          </div>\n        ))\n      }\n    </div>\n\n    <div class=\"mt-12 flex flex-col items-center justify-between gap-4 border-t border-slate-800/60 pt-8 md:flex-row\">\n      <div class=\"text-xs text-slate-600\">\n        Copyright &copy; {currentYear} The Cheerio contributors\n      </div>\n      <div class=\"flex items-center gap-4\">\n        <a\n          href=\"https://github.com/cheeriojs/cheerio\"\n          target=\"_blank\"\n          rel=\"noopener noreferrer\"\n          class=\"text-slate-600 transition-colors hover:text-slate-400\"\n          aria-label=\"GitHub\"\n        >\n          <svg viewBox=\"0 0 24 24\" class=\"h-5 w-5\" fill=\"currentColor\">\n            <path d=\"M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z\"/>\n          </svg>\n        </a>\n      </div>\n    </div>\n  </div>\n</footer>\n"
  },
  {
    "path": "website/src/components/Hero.astro",
    "content": "---\n\nconst title = 'cheerio';\nconst tagline =\n  'The fast, flexible & elegant library for parsing and manipulating HTML and XML.';\n\n// Fetch GitHub stars and npm downloads at build time\nfunction formatCount(n: number): string {\n  if (n >= 1_000_000)\n    return `${(n / 1_000_000).toFixed(1).replace(/\\.0$/, '')}M`;\n  if (n >= 1000) return `${(n / 1000).toFixed(1).replace(/\\.0$/, '')}k`;\n  return n.toString();\n}\n\nlet stars = '';\nlet downloads = '';\n\ntry {\n  const [ghRes, npmRes] = await Promise.all([\n    fetch('https://api.github.com/repos/cheeriojs/cheerio', {\n      headers: {\n        Accept: 'application/vnd.github.v3+json',\n        'User-Agent': 'cheerio-website',\n      },\n    }),\n    fetch('https://api.npmjs.org/downloads/point/last-month/cheerio'),\n  ]);\n\n  if (ghRes.ok) {\n    const gh = await ghRes.json();\n    stars = formatCount(gh.stargazers_count);\n  }\n  if (npmRes.ok) {\n    const npm = await npmRes.json();\n    downloads = formatCount(npm.downloads);\n  }\n} catch {\n  // Silently fail — badges just won't render\n}\n---\n\n<header class=\"relative overflow-hidden bg-slate-950 py-24 md:py-32 lg:py-40\">\n  <!-- Background gradient layers -->\n  <div class=\"absolute inset-0 bg-gradient-to-br from-amber-950/40 via-slate-950 to-slate-900\"></div>\n  <div class=\"absolute inset-0 bg-[radial-gradient(ellipse_80%_60%_at_50%_-20%,rgba(232,140,31,0.2),transparent)]\"></div>\n  <div class=\"absolute bottom-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-primary/40 to-transparent\"></div>\n\n  <!-- Grain overlay -->\n  <div class=\"grain absolute inset-0 overflow-hidden opacity-50\"></div>\n\n  <!-- Floating decorative elements -->\n  <div class=\"absolute left-[10%] top-[15%] h-64 w-64 rounded-full bg-primary/5 blur-3xl animate-float\"></div>\n  <div class=\"absolute right-[15%] bottom-[20%] h-48 w-48 rounded-full bg-primary/8 blur-3xl animate-float animation-delay-300\"></div>\n\n  <div class=\"relative z-10 mx-auto max-w-7xl px-6\">\n    <div class=\"grid items-center gap-12 lg:grid-cols-2 lg:gap-16\">\n      <!-- Left column: Text content -->\n      <div class=\"text-center lg:text-left\">\n        <div class=\"animate-fade-up mb-6 flex flex-wrap items-center justify-center gap-3 lg:justify-start\">\n          {stars && (\n            <a\n              href=\"https://github.com/cheeriojs/cheerio/stargazers\"\n              target=\"_blank\"\n              rel=\"noopener noreferrer\"\n              class=\"inline-flex items-center gap-1.5 rounded-full border border-slate-700/60 bg-slate-800/60 px-3 py-1.5 text-sm text-slate-300 transition-colors hover:border-slate-600 hover:text-white\"\n            >\n              <svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"currentColor\">\n                <path d=\"M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z\"/>\n              </svg>\n              <span class=\"font-medium text-primary-light\">{stars} stars</span>\n            </a>\n          )}\n          {downloads && (\n            <a\n              href=\"https://www.npmjs.com/package/cheerio\"\n              target=\"_blank\"\n              rel=\"noopener noreferrer\"\n              class=\"inline-flex items-center gap-1.5 rounded-full border border-slate-700/60 bg-slate-800/60 px-3 py-1.5 text-sm text-slate-300 transition-colors hover:border-slate-600 hover:text-white\"\n            >\n              <svg viewBox=\"0 0 256 256\" class=\"h-4 w-4\" fill=\"currentColor\">\n                <path d=\"M0 256V0h256v256z\"/>\n                <path d=\"M48 48h160v160h-32V80h-32v128H48z\" fill=\"var(--color-slate-800, #1e293b)\"/>\n              </svg>\n              <span class=\"font-medium text-primary-light\">{downloads} downloads/month</span>\n            </a>\n          )}\n        </div>\n\n        <h1 class=\"animate-fade-up animation-delay-100 mb-6 font-display text-6xl tracking-tight text-white md:text-7xl lg:text-8xl\">\n          {title}\n        </h1>\n\n        <p class=\"animate-fade-up animation-delay-200 mb-10 max-w-xl text-lg leading-relaxed text-slate-400 md:text-xl lg:mx-0 mx-auto\">\n          {tagline}\n        </p>\n\n        <div class=\"animate-fade-up animation-delay-300 flex flex-col items-center gap-4 sm:flex-row lg:justify-start justify-center\">\n          <a\n            href=\"/docs/intro\"\n            class=\"group relative inline-flex items-center gap-2 rounded-xl bg-primary px-8 py-3.5 font-semibold text-white transition-all duration-300 hover:bg-primary-dark hover:shadow-lg hover:shadow-primary/25\"\n          >\n            Get Started\n            <svg class=\"h-4 w-4 transition-transform duration-300 group-hover:translate-x-0.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2.5\">\n              <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13.5 4.5L21 12m0 0l-7.5 7.5M21 12H3\" />\n            </svg>\n          </a>\n          <a\n            href=\"https://github.com/cheeriojs/cheerio\"\n            target=\"_blank\"\n            rel=\"noopener noreferrer\"\n            class=\"inline-flex items-center gap-2 rounded-xl border border-slate-700 px-8 py-3.5 font-semibold text-slate-300 transition-all duration-300 hover:border-slate-500 hover:text-white hover:bg-slate-800/50\"\n          >\n            <svg viewBox=\"0 0 24 24\" class=\"h-5 w-5\" fill=\"currentColor\">\n              <path d=\"M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z\"/>\n            </svg>\n            View on GitHub\n          </a>\n        </div>\n\n        <!-- Install snippet -->\n        <div class=\"animate-fade-up animation-delay-400 mt-8 flex items-center justify-center lg:justify-start\">\n          <code class=\"flex items-center gap-3 rounded-lg border border-slate-800 bg-slate-900/80 px-5 py-2.5 font-mono text-sm text-slate-400\">\n            <span class=\"select-none text-primary/60\">$</span>\n            <span>npm install cheerio</span>\n            <button\n              id=\"copy-install\"\n              class=\"ml-2 text-slate-600 transition-colors hover:text-slate-300\"\n              aria-label=\"Copy to clipboard\"\n            >\n              <svg class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n                <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9.75a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184\" />\n              </svg>\n            </button>\n          </code>\n        </div>\n      </div>\n\n      <!-- Right column: Code preview -->\n      <div class=\"animate-slide-in-right animation-delay-400 hidden lg:block\">\n        <div class=\"relative\">\n          <!-- Glow behind the code block -->\n          <div class=\"absolute -inset-4 rounded-2xl bg-gradient-to-br from-primary/10 via-transparent to-primary/5 blur-xl\"></div>\n\n          <div class=\"relative rounded-2xl border border-slate-800 bg-slate-900/90 shadow-2xl shadow-black/40 backdrop-blur-sm\">\n            <!-- Window chrome -->\n            <div class=\"flex items-center gap-2 border-b border-slate-800 px-4 py-3\">\n              <div class=\"h-3 w-3 rounded-full bg-slate-700\"></div>\n              <div class=\"h-3 w-3 rounded-full bg-slate-700\"></div>\n              <div class=\"h-3 w-3 rounded-full bg-slate-700\"></div>\n              <span class=\"ml-3 text-xs text-slate-600 font-mono\">example.js</span>\n            </div>\n            <!-- Code content -->\n            <pre class=\"overflow-x-auto p-6 text-sm leading-relaxed\"><code class=\"font-mono\"><span class=\"text-purple-400\">import</span> <span class=\"text-slate-300\">*</span> <span class=\"text-purple-400\">as</span> <span class=\"text-amber-300\">cheerio</span> <span class=\"text-purple-400\">from</span> <span class=\"text-green-400\">'cheerio'</span><span class=\"text-slate-500\">;</span>\n\n<span class=\"text-purple-400\">const</span> <span class=\"text-amber-300\">$</span> <span class=\"text-slate-500\">=</span> <span class=\"text-slate-300\">cheerio.</span><span class=\"text-blue-400\">load</span><span class=\"text-slate-500\">(</span><span class=\"text-green-400\">'&lt;h2 class=\"title\"&gt;Hello world&lt;/h2&gt;'</span><span class=\"text-slate-500\">);</span>\n\n<span class=\"text-amber-300\">$</span><span class=\"text-slate-500\">(</span><span class=\"text-green-400\">'h2.title'</span><span class=\"text-slate-500\">).</span><span class=\"text-blue-400\">text</span><span class=\"text-slate-500\">(</span><span class=\"text-green-400\">'Hello there!'</span><span class=\"text-slate-500\">);</span>\n<span class=\"text-amber-300\">$</span><span class=\"text-slate-500\">(</span><span class=\"text-green-400\">'h2'</span><span class=\"text-slate-500\">).</span><span class=\"text-blue-400\">addClass</span><span class=\"text-slate-500\">(</span><span class=\"text-green-400\">'welcome'</span><span class=\"text-slate-500\">);</span>\n\n<span class=\"text-amber-300\">$</span><span class=\"text-slate-500\">.</span><span class=\"text-blue-400\">html</span><span class=\"text-slate-500\">();</span>\n<span class=\"text-slate-600\">{\"//=> <html><head></head><body>\"}</span>\n<span class=\"text-slate-600\">{\"//=>   <h2 class=\\\"title welcome\\\">Hello there!</h2>\"}</span>\n<span class=\"text-slate-600\">{\"//=> </body></html>\"}</span></code></pre>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n</header>\n\n<script>\n  function initCopyInstall() {\n    const copyButton = document.getElementById('copy-install');\n    if (!copyButton) return;\n\n    copyButton.onclick = () => {\n      navigator.clipboard.writeText('npm install cheerio');\n      const svg = copyButton.querySelector('svg');\n      if (svg) {\n        const originalPath = svg.innerHTML;\n        svg.innerHTML = '<path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M4.5 12.75l6 6 9-13.5\" />';\n        setTimeout(() => { svg.innerHTML = originalPath; }, 2000);\n      }\n    };\n  }\n\n  initCopyInstall();\n  if (!(window as any).__cheerio_copy_install_swap) {\n    (window as any).__cheerio_copy_install_swap = true;\n    document.addEventListener('astro:after-swap', initCopyInstall);\n  }\n</script>\n"
  },
  {
    "path": "website/src/components/LiveEditor.astro",
    "content": "---\n/**\n * Astro component that wraps LiveCode for use in markdown.\n * Usage in markdown: <LiveEditor>...</LiveEditor>\n */\nimport { LiveCode } from './live-code';\n---\n\n<div class=\"live-editor-wrapper\">\n  <LiveCode client:load code={await Astro.slots.render('default')} />\n</div>\n"
  },
  {
    "path": "website/src/components/Navbar.astro",
    "content": "---\nimport { Image } from 'astro:assets';\nimport logo from '@/assets/orange-c.svg';\n\nconst navItems = [\n  {\n    label: 'Learn',\n    href: '/docs/intro',\n    match: (p: string) => p.startsWith('/docs/') && !p.startsWith('/docs/api'),\n  },\n  {\n    label: 'API',\n    href: '/docs/api',\n    match: (p: string) => p.startsWith('/docs/api'),\n  },\n  { label: 'Blog', href: '/blog', match: (p: string) => p.startsWith('/blog') },\n];\n\nconst currentPath = Astro.url.pathname;\n---\n\n<nav class=\"sticky top-0 z-50 border-b border-slate-200/80 bg-white/80 backdrop-blur-xl dark:border-slate-800/80 dark:bg-slate-900/80\">\n  <div class=\"mx-auto flex max-w-7xl items-center justify-between px-6 py-3.5\">\n    <div class=\"flex items-center gap-10\">\n      <a href=\"/\" class=\"group flex items-center gap-2.5\">\n        <Image src={logo} alt=\"Cheerio Logo\" class=\"h-8 w-8 transition-transform duration-300 group-hover:rotate-[-8deg]\" />\n        <span class=\"text-lg font-semibold tracking-tight text-slate-900 dark:text-white\">Cheerio</span>\n      </a>\n\n      <div class=\"hidden items-center gap-1 md:flex\">\n        {\n          navItems.map((item) => (\n            <a\n              href={item.href}\n              class:list={[\n                'rounded-lg px-3.5 py-2 text-sm font-medium transition-colors',\n                item.match(currentPath)\n                  ? 'bg-primary/10 text-primary'\n                  : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900 dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-white',\n              ]}\n            >\n              {item.label}\n            </a>\n          ))\n        }\n      </div>\n    </div>\n\n    <div class=\"flex items-center gap-3\">\n      <!-- DocSearch container -->\n      <div id=\"docsearch\"></div>\n\n      <a\n        href=\"https://github.com/cheeriojs/cheerio\"\n        target=\"_blank\"\n        rel=\"noopener noreferrer\"\n        class=\"rounded-lg p-2 text-slate-500 transition-colors hover:bg-slate-100 hover:text-slate-900 dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-white\"\n        aria-label=\"GitHub\"\n      >\n        <svg viewBox=\"0 0 24 24\" class=\"h-5 w-5\" fill=\"currentColor\">\n          <path d=\"M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z\"/>\n        </svg>\n      </a>\n\n      <!-- Mobile menu button -->\n      <button\n        type=\"button\"\n        class=\"inline-flex items-center justify-center rounded-lg p-2 text-slate-500 hover:bg-slate-100 hover:text-slate-900 md:hidden dark:text-slate-400 dark:hover:bg-slate-800\"\n        aria-label=\"Open menu\"\n        id=\"mobile-menu-button\"\n      >\n        <svg class=\"h-5 w-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n          <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M3.75 9h16.5m-16.5 6.75h16.5\" />\n        </svg>\n      </button>\n    </div>\n  </div>\n\n  <!-- Mobile menu -->\n  <div id=\"mobile-menu\" class=\"hidden border-t border-slate-200 bg-white md:hidden dark:border-slate-800 dark:bg-slate-900\">\n    <div class=\"space-y-1 px-4 pb-4 pt-2\">\n      {\n        navItems.map((item) => (\n          <a\n            href={item.href}\n            class:list={[\n              'block rounded-lg px-3 py-2.5 text-sm font-medium',\n              item.match(currentPath)\n                ? 'bg-primary/10 text-primary'\n                : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900 dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-white',\n            ]}\n          >\n            {item.label}\n          </a>\n        ))\n      }\n    </div>\n  </div>\n</nav>\n\n<script>\n  // Mobile menu toggle — re-bind after each View Transition navigation.\n  // Use a global guard to ensure the swap listener is registered only once,\n  // avoiding duplicate handlers when the script re-executes on navigations.\n  function initMobileMenu() {\n    const menuButton = document.getElementById('mobile-menu-button');\n    const mobileMenu = document.getElementById('mobile-menu');\n\n    if (menuButton && mobileMenu) {\n      // Clone and replace to remove any previously attached listeners\n      const freshButton = menuButton.cloneNode(true) as HTMLElement;\n      menuButton.replaceWith(freshButton);\n      freshButton.addEventListener('click', () => {\n        mobileMenu.classList.toggle('hidden');\n      });\n    }\n  }\n\n  initMobileMenu();\n  if (!(window as any).__cheerio_mobile_menu_swap) {\n    (window as any).__cheerio_mobile_menu_swap = true;\n    document.addEventListener('astro:after-swap', initMobileMenu);\n  }\n</script>\n\n<script>\n  // Initialize DocSearch — re-run after each View Transition navigation.\n  // Use a global guard to ensure the swap listener is registered only once.\n  import docsearch from '@docsearch/js';\n  import '@docsearch/css';\n\n  function initDocSearch() {\n    const container = document.getElementById('docsearch');\n    if (container) {\n      // Clear any previous instance\n      container.innerHTML = '';\n      docsearch({\n        appId: 'NRR2XU4QSP',\n        apiKey: '9d30ee79d65ccc63b95e693124e05405',\n        indexName: 'crawler_cheerio',\n        container: '#docsearch',\n      });\n    }\n  }\n\n  initDocSearch();\n  if (!(window as any).__cheerio_docsearch_swap) {\n    (window as any).__cheerio_docsearch_swap = true;\n    document.addEventListener('astro:after-swap', initDocSearch);\n  }\n</script>\n"
  },
  {
    "path": "website/src/components/Sidebar.astro",
    "content": "---\nimport { getCollection } from 'astro:content';\n\ninterface SidebarItem {\n  label: string;\n  href: string;\n}\n\ninterface SidebarSection {\n  title: string;\n  items: SidebarItem[];\n}\n\ninterface ApiGroup {\n  title: string;\n  items: SidebarItem[];\n}\n\ninterface Props {\n  currentPath: string;\n}\n\nconst { currentPath } = Astro.props;\n\nconst sidebar: SidebarSection[] = [\n  {\n    title: 'Getting Started',\n    items: [{ label: 'Introduction', href: '/docs/intro' }],\n  },\n  {\n    title: 'Basics',\n    items: [\n      { label: 'Loading Documents', href: '/docs/basics/loading' },\n      { label: 'Selecting Elements', href: '/docs/basics/selecting' },\n      { label: 'Traversing the DOM', href: '/docs/basics/traversing' },\n      { label: 'Manipulating Elements', href: '/docs/basics/manipulation' },\n    ],\n  },\n  {\n    title: 'Advanced',\n    items: [\n      {\n        label: 'Configuring Cheerio',\n        href: '/docs/advanced/configuring-cheerio',\n      },\n      { label: 'Extending Cheerio', href: '/docs/advanced/extending-cheerio' },\n      { label: 'Extracting Data', href: '/docs/advanced/extract' },\n    ],\n  },\n];\n\n// Dynamically build API sub-pages from the content collection\nconst allDocs = await getCollection('docs');\nconst apiDocs = allDocs.filter(\n  (doc) =>\n    doc.id.startsWith('api/') &&\n    doc.id !== 'api/index.md' &&\n    doc.id !== 'api/index',\n);\n\n// Group by kind (directory name)\nconst kindLabels: Record<string, string> = {\n  classes: 'Classes',\n  interfaces: 'Interfaces',\n  functions: 'Functions',\n  types: 'Types',\n  variables: 'Variables',\n};\nconst kindOrder = ['classes', 'variables', 'functions', 'interfaces', 'types'];\n\nconst apiGroups: ApiGroup[] = [];\nconst groupedByKind = new Map<string, SidebarItem[]>();\n\nfor (const doc of apiDocs) {\n  // id looks like \"api/classes/Cheerio.md\" or \"api/functions/contains\"\n  const parts = doc.id\n    .replace(/^api\\//, '')\n    .replace(/\\.mdx?$/, '')\n    .split('/');\n  if (parts.length !== 2) continue;\n  const [kind, name] = parts;\n  if (!groupedByKind.has(kind)) groupedByKind.set(kind, []);\n  groupedByKind.get(kind)!.push({\n    label: name,\n    href: `/docs/api/${kind}/${name}`,\n  });\n}\n\n// Sort items within each group alphabetically\nfor (const items of groupedByKind.values()) {\n  items.sort((a, b) => a.label.localeCompare(b.label));\n}\n\n// Build ordered groups\nfor (const kind of kindOrder) {\n  const items = groupedByKind.get(kind);\n  if (items && items.length > 0) {\n    apiGroups.push({ title: kindLabels[kind] || kind, items });\n  }\n}\n\nconst isApiPage = currentPath.startsWith('/docs/api');\nconst normalizedPath = currentPath.replace(/\\/$/, '');\n\nexport { sidebar };\nexport type { SidebarItem, SidebarSection };\n---\n\n{/* ── Desktop sidebar ── */}\n<aside\n  class=\"hidden w-64 shrink-0 border-r border-slate-200 dark:border-slate-700 lg:block\"\n>\n  <nav class=\"sticky top-14 h-[calc(100vh-3.5rem)] overflow-y-auto px-6 py-8\">\n    {\n      sidebar.map((section) => (\n        <div class=\"mb-8\">\n          <h3 class=\"mb-3 text-xs font-bold uppercase tracking-wider text-slate-400 dark:text-slate-500\">\n            {section.title}\n          </h3>\n          <ul class=\"space-y-1\">\n            {section.items.map((item) => (\n              <li>\n                <a\n                  href={item.href}\n                  class:list={[\n                    'block rounded-lg px-3 py-2 text-sm transition-all duration-150',\n                    normalizedPath === item.href\n                      ? 'bg-primary/10 font-semibold text-primary border-l-2 border-primary'\n                      : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900 dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-slate-100',\n                  ]}\n                >\n                  {item.label}\n                </a>\n              </li>\n            ))}\n          </ul>\n        </div>\n      ))\n    }\n\n    {/* Reference / API section */}\n    <div class=\"mb-8\">\n      <h3 class=\"mb-3 text-xs font-bold uppercase tracking-wider text-slate-400 dark:text-slate-500\">\n        Reference\n      </h3>\n      <ul class=\"space-y-1\">\n        <li>\n          <a\n            href=\"/docs/api\"\n            class:list={[\n              'block rounded-lg px-3 py-2 text-sm transition-all duration-150',\n              normalizedPath === '/docs/api'\n                ? 'bg-primary/10 font-semibold text-primary border-l-2 border-primary'\n                : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900 dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-slate-100',\n            ]}\n          >\n            API Documentation\n          </a>\n        </li>\n      </ul>\n      {apiGroups.map((group) => (\n        <details class=\"group/api mt-2\" open={isApiPage}>\n          <summary class=\"flex cursor-pointer items-center gap-1.5 rounded-lg px-3 py-1.5 text-xs font-semibold uppercase tracking-wider text-slate-400 hover:text-slate-600 dark:text-slate-500 dark:hover:text-slate-300\">\n            <svg class=\"h-3 w-3 shrink-0 transition-transform group-open/api:rotate-90\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2.5\">\n              <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M8.25 4.5l7.5 7.5-7.5 7.5\" />\n            </svg>\n            {group.title}\n          </summary>\n          <ul class=\"mt-1 space-y-0.5 pl-2\">\n            {group.items.map((item) => (\n              <li>\n                <a\n                  href={item.href}\n                  class:list={[\n                    'block rounded-lg px-3 py-1.5 text-sm transition-all duration-150',\n                    normalizedPath === item.href\n                      ? 'bg-primary/10 font-semibold text-primary border-l-2 border-primary'\n                      : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900 dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-slate-100',\n                  ]}\n                >\n                  {item.label}\n                </a>\n              </li>\n            ))}\n          </ul>\n        </details>\n      ))}\n    </div>\n  </nav>\n</aside>\n\n{/* ── Mobile sidebar drawer ── */}\n<div\n  id=\"sidebar-overlay\"\n  class=\"fixed inset-0 z-40 hidden bg-black/50 backdrop-blur-sm lg:hidden\"\n>\n</div>\n<aside\n  id=\"sidebar-drawer\"\n  class=\"fixed left-0 top-0 z-50 hidden h-full w-72 -translate-x-full transform bg-white shadow-2xl transition-transform duration-300 ease-in-out lg:hidden dark:bg-slate-900\"\n>\n  <div class=\"flex items-center justify-between border-b border-slate-200 px-6 py-4 dark:border-slate-700\">\n    <span class=\"text-sm font-semibold uppercase tracking-wider text-slate-400 dark:text-slate-500\">Documentation</span>\n    <button\n      type=\"button\"\n      id=\"sidebar-close\"\n      class=\"rounded-lg p-1.5 text-slate-400 hover:bg-slate-100 hover:text-slate-600 dark:hover:bg-slate-800 dark:hover:text-slate-300\"\n      aria-label=\"Close sidebar\"\n    >\n      <svg class=\"h-5 w-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n      </svg>\n    </button>\n  </div>\n  <nav class=\"overflow-y-auto px-6 py-6\" style=\"max-height: calc(100% - 57px);\">\n    {\n      sidebar.map((section) => (\n        <div class=\"mb-8\">\n          <h3 class=\"mb-3 text-xs font-bold uppercase tracking-wider text-slate-400 dark:text-slate-500\">\n            {section.title}\n          </h3>\n          <ul class=\"space-y-1\">\n            {section.items.map((item) => (\n              <li>\n                <a\n                  href={item.href}\n                  class:list={[\n                    'block rounded-lg px-3 py-2 text-sm transition-all duration-150',\n                    normalizedPath === item.href\n                      ? 'bg-primary/10 font-semibold text-primary border-l-2 border-primary'\n                      : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900 dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-slate-100',\n                  ]}\n                >\n                  {item.label}\n                </a>\n              </li>\n            ))}\n          </ul>\n        </div>\n      ))\n    }\n\n    {/* Reference / API section */}\n    <div class=\"mb-8\">\n      <h3 class=\"mb-3 text-xs font-bold uppercase tracking-wider text-slate-400 dark:text-slate-500\">\n        Reference\n      </h3>\n      <ul class=\"space-y-1\">\n        <li>\n          <a\n            href=\"/docs/api\"\n            class:list={[\n              'block rounded-lg px-3 py-2 text-sm transition-all duration-150',\n              normalizedPath === '/docs/api'\n                ? 'bg-primary/10 font-semibold text-primary border-l-2 border-primary'\n                : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900 dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-slate-100',\n            ]}\n          >\n            API Documentation\n          </a>\n        </li>\n      </ul>\n      {apiGroups.map((group) => (\n        <details class=\"group/api mt-2\" open={isApiPage}>\n          <summary class=\"flex cursor-pointer items-center gap-1.5 rounded-lg px-3 py-1.5 text-xs font-semibold uppercase tracking-wider text-slate-400 hover:text-slate-600 dark:text-slate-500 dark:hover:text-slate-300\">\n            <svg class=\"h-3 w-3 shrink-0 transition-transform group-open/api:rotate-90\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2.5\">\n              <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M8.25 4.5l7.5 7.5-7.5 7.5\" />\n            </svg>\n            {group.title}\n          </summary>\n          <ul class=\"mt-1 space-y-0.5 pl-2\">\n            {group.items.map((item) => (\n              <li>\n                <a\n                  href={item.href}\n                  class:list={[\n                    'block rounded-lg px-3 py-1.5 text-sm transition-all duration-150',\n                    normalizedPath === item.href\n                      ? 'bg-primary/10 font-semibold text-primary border-l-2 border-primary'\n                      : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900 dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-slate-100',\n                  ]}\n                >\n                  {item.label}\n                </a>\n              </li>\n            ))}\n          </ul>\n        </details>\n      ))}\n    </div>\n  </nav>\n</aside>\n\n<script>\n  // Sidebar drawer — re-bind after each View Transition navigation.\n  // Use a global guard to ensure the swap listener is registered only once.\n  // Use .onclick assignment instead of addEventListener to de-dupe handlers.\n  function initSidebarDrawer() {\n    const overlay = document.getElementById('sidebar-overlay');\n    const drawer = document.getElementById('sidebar-drawer');\n    const closeBtn = document.getElementById('sidebar-close');\n    const openBtn = document.getElementById('sidebar-open');\n\n    function openDrawer() {\n      if (!overlay || !drawer) return;\n      overlay.classList.remove('hidden');\n      drawer.classList.remove('hidden');\n      // Force reflow before adding transform\n      drawer.offsetHeight;\n      drawer.classList.remove('-translate-x-full');\n      drawer.classList.add('translate-x-0');\n      document.body.style.overflow = 'hidden';\n    }\n\n    function closeDrawer() {\n      if (!overlay || !drawer) return;\n      drawer.classList.remove('translate-x-0');\n      drawer.classList.add('-translate-x-full');\n      document.body.style.overflow = '';\n      // Wait for transition to finish before hiding\n      setTimeout(() => {\n        overlay.classList.add('hidden');\n        drawer.classList.add('hidden');\n      }, 300);\n    }\n\n    // Use .onclick to replace (not accumulate) handlers on each init\n    if (openBtn) openBtn.onclick = openDrawer;\n    if (closeBtn) closeBtn.onclick = closeDrawer;\n    if (overlay) overlay.onclick = closeDrawer;\n  }\n\n  initSidebarDrawer();\n  if (!(window as any).__cheerio_sidebar_swap) {\n    (window as any).__cheerio_sidebar_swap = true;\n    document.addEventListener('astro:after-swap', initSidebarDrawer);\n  }\n</script>\n"
  },
  {
    "path": "website/src/components/Sponsors.astro",
    "content": "---\nimport { Image } from 'astro:assets';\nimport { Heart } from '@lucide/astro';\nimport sponsorsData from '../../sponsors.json';\n\ninterface Sponsor {\n  name: string;\n  image: string;\n  url: string;\n}\n\nconst headliners = sponsorsData.headliner as Sponsor[];\n---\n\n<section class=\"relative overflow-hidden bg-slate-50 py-20 dark:bg-slate-800/50\">\n  <!-- Decorative background -->\n  <div class=\"absolute inset-0 bg-[radial-gradient(ellipse_at_center,rgba(232,140,31,0.05),transparent_70%)]\"></div>\n\n  <div class=\"relative mx-auto max-w-5xl px-6\">\n    <div class=\"animate-fade-up mb-12 text-center\">\n      <p class=\"mb-3 text-sm font-semibold uppercase tracking-widest text-primary\">Trusted by the best</p>\n      <h2 class=\"font-display text-4xl text-slate-900 dark:text-white\">\n        Supported and Backed by\n      </h2>\n    </div>\n\n    <div class=\"animate-fade-up animation-delay-200 flex flex-wrap items-center justify-center gap-6\">\n      {\n        headliners.map((sponsor) => (\n          <a\n            href={sponsor.url}\n            target=\"_blank\"\n            rel=\"noopener noreferrer\"\n            class=\"group flex items-center gap-3 rounded-xl border border-slate-200 bg-white px-6 py-4 transition-all duration-300 hover:border-primary/30 hover:shadow-lg hover:shadow-primary/5 dark:border-slate-700 dark:bg-slate-800 dark:hover:border-primary/40\"\n          >\n            {sponsor.image.endsWith('.svg') ? (\n              <img\n                src={sponsor.image}\n                alt={`${sponsor.name} logo`}\n                class=\"h-10 w-10 rounded-lg\"\n                loading=\"lazy\"\n              />\n            ) : (\n              <Image\n                src={sponsor.image}\n                alt={`${sponsor.name} logo`}\n                width={40}\n                height={40}\n                class=\"h-10 w-10 rounded-lg\"\n                loading=\"lazy\"\n              />\n            )}\n            <span class=\"font-medium text-slate-700 transition-colors group-hover:text-slate-900 dark:text-slate-300 dark:group-hover:text-white\">\n              {sponsor.name}\n            </span>\n          </a>\n        ))\n      }\n    </div>\n\n    <div class=\"animate-fade-up animation-delay-400 mt-10 text-center\">\n      <a\n        href=\"https://github.com/sponsors/cheeriojs\"\n        target=\"_blank\"\n        rel=\"noopener noreferrer\"\n        class=\"inline-flex items-center gap-2 rounded-full border border-primary/30 bg-primary/5 px-6 py-2.5 text-sm font-medium text-primary transition-all duration-300 hover:bg-primary/10 hover:border-primary/50 dark:text-primary-light\"\n      >\n        <Heart class=\"h-5 w-5\" fill=\"currentColor\" stroke-width={0} />\n        Become a sponsor\n      </a>\n    </div>\n  </div>\n</section>\n"
  },
  {
    "path": "website/src/components/TableOfContents.astro",
    "content": "---\ninterface Props {\n  headings: Array<{\n    depth: number;\n    slug: string;\n    text: string;\n  }>;\n}\n\nconst { headings } = Astro.props;\n\n// Filter to only h2 and h3 headings\nconst toc = headings.filter((h) => h.depth >= 2 && h.depth <= 3);\n---\n\n{toc.length > 0 && (\n  <nav class=\"hidden xl:block w-56 shrink-0 pl-6 pt-4\">\n    <div class=\"sticky top-20\">\n      <h4 class=\"mb-3 text-xs font-semibold uppercase tracking-wider text-slate-400 dark:text-slate-500\">\n        On this page\n      </h4>\n      <ul class=\"space-y-2 text-xs\">\n        {toc.map((heading) => (\n          <li>\n            <a\n              href={`#${heading.slug}`}\n              class:list={[\n                'block text-slate-500 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 transition-colors leading-snug',\n                heading.depth === 2 ? 'font-medium text-slate-600 dark:text-slate-300' : 'pl-3 text-slate-500',\n              ]}\n            >\n              {heading.text}\n            </a>\n          </li>\n        ))}\n      </ul>\n    </div>\n  </nav>\n)}\n"
  },
  {
    "path": "website/src/components/Testimonials.astro",
    "content": "---\nimport { Image } from 'astro:assets';\n\ninterface Tweet {\n  id: string;\n  name: string;\n  user: string;\n  github: string;\n  tweet: string;\n  tagline: string;\n  url?: string;\n}\n\nconst tweets: Tweet[] = [\n  {\n    id: '628016191928446977',\n    name: 'Axel Rauschmayer',\n    user: 'rauschma',\n    github: 'rauschma',\n    tweet:\n      \"For transforming HTML via Node.js scripts, @mattmueller's cheerio works really well.\",\n    tagline: \"Author of 'Exploring JavaScript', educator at 2ality.com\",\n  },\n  {\n    id: '1616150822932385792',\n    name: 'Valeri Karpov',\n    user: 'code_barbarian',\n    github: 'vkarpov15',\n    tweet:\n      'Cheerio is a weird npm module: most devs have never heard of it, but I rarely build an app without it. So much utility for quick and easy HTML transformations.',\n    tagline: 'Creator of Mongoose ODM',\n  },\n  {\n    id: '1186972238190403587',\n    name: 'Matthew Phillips',\n    user: 'matthewcp',\n    github: 'matthewp',\n    tweet:\n      'Cheerio is (still) such a useful tool for manipulating HTML. Shout to @MattMueller for saving me an untold amount of time over the years.',\n    tagline: 'Co-Creator of Astro',\n  },\n  {\n    id: '1403139379757977602',\n    name: 'Mike Pennisi',\n    user: 'JugglinMike',\n    github: 'jugglinmike',\n    tweet:\n      \"Thank you @fb55 for tirelessly pushing Cheerio to version 1.0. That library helps so many developers expand their horizons beyond the browser, and you've been making it possible for a decade!\",\n    tagline: 'Open source developer at Bocoup',\n  },\n  {\n    id: '264033999272439809',\n    name: 'Thomas Steiner',\n    user: 'tomayac',\n    github: 'tomayac',\n    tweet:\n      \"npm install cheerio. That's the #jQuery DOM API for #nodeJS essentially. Thanks, @MattMueller\",\n    tagline: 'Developer Relations Engineer at Google Chrome',\n    url: 'https://tomayac.com/tweets/264033999272439809/',\n  },\n  {\n    id: '1545481085865320449',\n    name: 'Thomas Boutell',\n    user: 'boutell',\n    github: 'boutell',\n    tweet:\n      'If you\\'re great at jQuery, you\\'re going to be really popular on server-side projects that need web scraping or HTML transformation. \"npm install cheerio\" ahoy!',\n    tagline: 'Co-creator of the PNG format, CEO of ApostropheCMS',\n  },\n];\n\n// Split tweets into 3 columns for masonry-like layout\nconst columns: Tweet[][] = [[], [], []];\ntweets.forEach((tweet, i) => {\n  columns[i % 3].push(tweet);\n});\n---\n\n<section class=\"relative bg-white py-24 dark:bg-slate-900\">\n  <div class=\"absolute top-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-slate-200 to-transparent dark:via-slate-800\"></div>\n\n  <div class=\"mx-auto max-w-6xl px-6\">\n    <div class=\"animate-fade-up mb-16 text-center\">\n      <p class=\"mb-3 text-sm font-semibold uppercase tracking-widest text-primary\">Community</p>\n      <h2 class=\"font-display text-4xl text-slate-900 md:text-5xl dark:text-white\">\n        What developers are saying\n      </h2>\n    </div>\n\n    <!-- Masonry-like 3-column layout -->\n    <div class=\"hidden gap-6 md:grid md:grid-cols-3\">\n      {\n        columns.map((column, colIndex) => (\n          <div class=\"flex flex-col gap-6\">\n            {column.map((tweet, i) => (\n              <a\n                href={tweet.url ?? `https://twitter.com/${tweet.user}/status/${tweet.id}`}\n                target=\"_blank\"\n                rel=\"noopener noreferrer\"\n                class=\"animate-fade-up group block rounded-2xl border border-slate-200 bg-slate-50/50 p-6 transition-all duration-300 hover:border-primary/20 hover:shadow-lg hover:shadow-primary/5 dark:border-slate-800 dark:bg-slate-800/50 dark:hover:border-primary/30\"\n                style={`animation-delay: ${(colIndex * 100) + (i * 200)}ms`}\n              >\n                <div class=\"mb-4 flex items-start gap-3\">\n                  <Image\n                    src={`https://github.com/${tweet.github}.png?s=88`}\n                    alt={`${tweet.name}'s avatar`}\n                    width={44}\n                    height={44}\n                    class=\"mt-0.5 h-11 w-11 shrink-0 rounded-full ring-2 ring-slate-100 dark:ring-slate-700\"\n                    loading=\"lazy\"\n                  />\n                  <div class=\"min-w-0 flex-1\">\n                    <div class=\"font-semibold text-slate-900 dark:text-white\">\n                      {tweet.name}\n                    </div>\n                    <div class=\"text-sm text-slate-500 dark:text-slate-400\">\n                      {tweet.tagline}\n                    </div>\n                  </div>\n                </div>\n                <p class=\"leading-relaxed text-slate-700 dark:text-slate-300\">\n                  {tweet.tweet}\n                </p>\n              </a>\n            ))}\n          </div>\n        ))\n      }\n    </div>\n\n    <!-- Mobile: single column -->\n    <div class=\"flex flex-col gap-6 md:hidden\">\n      {\n        tweets.map((tweet, i) => (\n          <a\n            href={tweet.url ?? `https://twitter.com/${tweet.user}/status/${tweet.id}`}\n            target=\"_blank\"\n            rel=\"noopener noreferrer\"\n            class=\"animate-fade-up group block rounded-2xl border border-slate-200 bg-slate-50/50 p-6 transition-all duration-300 hover:border-primary/20 hover:shadow-lg dark:border-slate-800 dark:bg-slate-800/50\"\n            style={`animation-delay: ${i * 100}ms`}\n          >\n            <div class=\"mb-4 flex items-start gap-3\">\n              <Image\n                src={`https://github.com/${tweet.github}.png?s=88`}\n                alt={`${tweet.name}'s avatar`}\n                width={44}\n                height={44}\n                class=\"mt-0.5 h-11 w-11 shrink-0 rounded-full ring-2 ring-slate-100 dark:ring-slate-700\"\n                loading=\"lazy\"\n              />\n              <div class=\"min-w-0 flex-1\">\n                <div class=\"font-semibold text-slate-900 dark:text-white\">\n                  {tweet.name}\n                </div>\n                <div class=\"text-sm text-slate-500 dark:text-slate-400\">\n                  {tweet.tagline}\n                </div>\n              </div>\n            </div>\n            <p class=\"leading-relaxed text-slate-700 dark:text-slate-300\">\n              {tweet.tweet}\n            </p>\n          </a>\n        ))\n      }\n    </div>\n  </div>\n</section>\n"
  },
  {
    "path": "website/src/components/live-code.tsx",
    "content": "import {\n  SandpackCodeEditor,\n  SandpackConsole,\n  SandpackProvider,\n  useSandpack,\n} from '@codesandbox/sandpack-react';\nimport { useCallback } from 'react';\n\ninterface LiveCodeProps {\n  code: string;\n}\n\nfunction ResetButton() {\n  const { sandpack } = useSandpack();\n\n  const handleReset = useCallback(() => sandpack.resetAllFiles(), [sandpack]);\n\n  return (\n    <button\n      type=\"button\"\n      onClick={handleReset}\n      className=\"px-2 py-1 text-xs font-medium text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-slate-100 hover:bg-slate-200 dark:hover:bg-slate-700 rounded transition-colors\"\n      title=\"Reset code and re-run\"\n    >\n      Reset\n    </button>\n  );\n}\n\nfunction RunButton() {\n  const { sandpack } = useSandpack();\n\n  const handleRun = () => {\n    const { code } = sandpack.files['/index.js'];\n    sandpack.updateFile('/index.js', code, true);\n  };\n\n  return (\n    <button\n      type=\"button\"\n      onClick={handleRun}\n      className=\"px-2 py-1 text-xs font-medium text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-slate-100 hover:bg-slate-200 dark:hover:bg-slate-700 rounded transition-colors\"\n      title=\"Run code\"\n    >\n      Run\n    </button>\n  );\n}\n\nfunction Toolbar() {\n  return (\n    <div className=\"flex items-center justify-between px-3 py-2 bg-slate-100 dark:bg-slate-800 border-b border-slate-200 dark:border-slate-700\">\n      <span className=\"text-xs font-medium text-slate-600 dark:text-slate-400 uppercase tracking-wide\">\n        Live Editor\n      </span>\n      <div className=\"flex items-center gap-2\">\n        <RunButton />\n        <ResetButton />\n      </div>\n    </div>\n  );\n}\n\nexport function LiveCode({ code }: LiveCodeProps) {\n  // Wrap user code to run immediately and output via console.log\n  const wrappedCode = `import * as cheerio from 'cheerio';\n\n${code}\n`;\n\n  return (\n    <div className=\"my-4 overflow-hidden rounded-lg border border-slate-200 dark:border-slate-700 not-prose\">\n      <SandpackProvider\n        template=\"vanilla\"\n        theme=\"auto\"\n        files={{\n          '/index.js': wrappedCode,\n        }}\n        customSetup={{\n          dependencies: {\n            cheerio: 'latest',\n          },\n        }}\n      >\n        <Toolbar />\n        <SandpackCodeEditor showLineNumbers style={{ height: '200px' }} />\n        <SandpackConsole\n          style={{ height: '150px' }}\n          standalone\n          showHeader\n          showResetConsoleButton\n        />\n      </SandpackProvider>\n    </div>\n  );\n}\n"
  },
  {
    "path": "website/src/content/blog/2023-02-13-new-website.md",
    "content": "---\nslug: new-website\ntitle: New Website, Who Dis?\nauthors: fb55\ntags: [website, intro]\n---\n\nCheerio has a new website, and it's looking pretty good! This has been in the\nworks for a while, and we're excited to finally share it with you. Let's walk\nthrough all that's new.\n\n:::note\n\nCheerio's website is still a work-in-progress, and covers Cheerio's next release\nthat isn't available yet.\n[We would love your help in making this website better!](https://github.com/cheeriojs/cheerio/discussions/3002)\n\n:::\n\n<!--truncate-->\n\n## Guides\n\nThe Cheerio website now features a set of guides, helping you get started with\nthe project. Even if you've used the project for a while, you might still learn\nsomething new!\n\nMost of these guides were written with the help of\n[ChatGPT](https://chat.openai.com/), which made it possible to generate\nhigh-quality content in a reasonable amount of time. While contents have been\nchecked for accuracy, we are happy to accept pull requests to improve the\nguides.\n\nGet started with the [intro guide](/docs/intro).\n\n## API Docs\n\nAPI docs, the old focus of the website, have been moved to a subdirectory. They\nare available at [docs/api](/docs/api).\n\n## Blog\n\nWe're also starting a blog, where we'll share updates about the project. Feel\nfree to subscribe [to the RSS feed](/blog/rss.xml)!\n"
  },
  {
    "path": "website/src/content/blog/2024-08-07-version-1.md",
    "content": "---\nslug: cheerio-1.0\ntitle: Cheerio 1.0 Released, Batteries Included 🔋\nauthors: fb55\ntags: [release, announcement]\n---\n\nCheerio 1.0 is out! After 12 release candidates and just a short seven years\nafter the initial 1.0 release candidate, it is finally time to call Cheerio 1.0\ncomplete. The theme for this release is \"batteries included\", with common use\ncases now supported out of the box.\n\nSo grab a pair of double-As, and read below for what's new, what's changed, and\nhow to upgrade!\n\n<!--truncate-->\n\n## New Website and Documentation\n\nSince the last release, we've published a new website and documentation for\nCheerio. The new site features detailed guides and API documentation to get the\nmost from Cheerio. Check it out at [cheerio.js.org](https://cheerio.js.org/).\n\n## A new way to load documents\n\nLoading documents into Cheerio has been revamped. Cheerio now supports multiple\nloading methods, each tailored to different use cases:\n\n- `load`: The classic method for parsing HTML or XML strings.\n- `loadBuffer`: Works with binary data, automatically detecting the document\n  encoding.\n- `stringStream` and `decodeStream`: Parse HTML directly from streams.\n- `fromURL`: Fetch and parse HTML from a URL in one go.\n\nDive deeper into these methods in the [Loading Documents](/docs/basics/loading)\ntutorial.\n\n## Simplified Data Extraction\n\nThe new `extract` method allows you to extract data from an HTML document and\nstore it in an object. To fetch the latest release of Cheerio from GitHub and\nextract the release date and the release notes from the release page is now as\nsimple as:\n\n```ts\nimport * as cheerio from 'cheerio';\n\nconst $ = await cheerio.fromURL(\n  'https://github.com/cheeriojs/cheerio/releases',\n);\nconst data = $.extract({\n  releases: [\n    {\n      // First, we select individual release sections.\n      selector: 'section',\n      // Then, we extract the release date, name, and notes from each section.\n      value: {\n        // Selectors are executed within the context of the selected element.\n        name: 'h2',\n        date: {\n          selector: 'relative-time',\n          // The actual release date is stored in the `datetime` attribute.\n          value: 'datetime',\n        },\n        notes: {\n          selector: '.markdown-body',\n          // We are looking for the HTML content of the element.\n          value: 'innerHTML',\n        },\n      },\n    },\n  ],\n});\n```\n\nRead more about all of the available options in the\n[Extracting Data](/docs/advanced/extract) guide.\n\n## Breaking Changes and Upgrade Guide\n\nCheerio 1.0 introduces several breaking changes, most notably:\n\n- The minimum NodeJS version is now 18.17 or higher.\n- Import paths were simplified. For example, use `cheerio/slim` instead of\n  `cheerio/lib/slim`.\n- The deprecated default Cheerio instance and static methods were removed.\n\n  Before, it was possible to write code like this:\n\n  ```ts\n  import cheerio, { html } from 'cheerio';\n\n  html(cheerio('<test></test>')); // ~ '<test></test>' -- NO LONGER WORKS\n  ```\n\n  Make sure to always load documents first:\n\n  ```ts\n  import * as cheerio from 'cheerio';\n\n  cheerio.load('<test></test>').html();\n  ```\n\n- htmlparser2 options now reside exclusively under the `xml` key:\n\n  ```ts\n  const $ = cheerio.load('<html>', {\n    xml: {\n      withStartIndices: true,\n    },\n  });\n  ```\n\n- Node types previously re-exported by Cheerio must now be imported directly\n  from [`domhandler`](https://github.com/fb55/domhandler).\n\nFor a comprehensive list of changes, please consult\n[the changelog](https://github.com/cheeriojs/cheerio/releases).\n\n## Upgrading to Cheerio 1.0\n\nTo upgrade to Cheerio 1.0, just run:\n\n```bash npm2yarn\nnpm install cheerio@latest\n```\n\n## Get Involved\n\nExplore the new features and let us know what you think! Encounter an issue?\nReport it on our\n[GitHub issue tracker](https://github.com/cheeriojs/cheerio/issues). Have an\nidea for an improvement? Pull requests welcome :)\n\n## Thank You\n\nThanks to [@jugglinmike](https://github.com/jugglinmike) for kick-starting\nCheerio 1.0, and to all the contributors who have helped shape this release. We\ncouldn't have done it without you.\n\nThanks to our\n[sponsors and backers](https://github.com/cheeriojs/cheerio?sponsor) for\nsupporting Cheerio's development. If you use Cheerio at work, consider asking\nyour company to support us!\n\nAnd finally, thank you for using Cheerio 🙇🙇‍♀️\n"
  },
  {
    "path": "website/src/content/config.ts",
    "content": "import { defineCollection, z } from 'astro:content';\n\nconst docs = defineCollection({\n  type: 'content',\n  schema: z.object({\n    title: z.string().optional(),\n    description: z.string().optional(),\n    sidebar_position: z.number().optional(),\n    sidebar_label: z.string().optional(),\n  }),\n});\n\nconst blog = defineCollection({\n  type: 'content',\n  schema: z.object({\n    title: z.string(),\n    slug: z.string().optional(),\n    authors: z.union([z.string(), z.array(z.string())]).optional(),\n    tags: z.array(z.string()).optional(),\n    date: z.date().optional(),\n  }),\n});\n\nexport const collections = { docs, blog };\n"
  },
  {
    "path": "website/src/content/docs/advanced/configuring-cheerio.md",
    "content": "---\nsidebar_position: 2\ndescription: Configure Cheerio to work with different documents.\n---\n\n# Configuring Cheerio\n\nIn this guide, we'll cover how to configure Cheerio to work with different types\nof documents, and how to use and configure the different parsers that ship with\nthe library.\n\n## Parsing HTML with parse5\n\nBy default, Cheerio uses the [`parse5`](https://parse5.js.org/) parser for HTML\ndocuments. `parse5` is an excellent project that rigorously conforms to the HTML\nstandard. However, if you need to modify parsing options for HTML input, you may\npass an extra object to `.load()`:\n\n```js\nconst cheerio = require('cheerio');\nconst $ = cheerio.load('<noscript><h1>Nested Tag!</h1></noscript>', {\n  scriptingEnabled: false,\n});\n```\n\nFor example, if you want the contents of `<noscript>` tags to be parsed as HTML,\nyou can set the `scriptingEnabled` option to false.\n\nFor a full list of options and their effects, have a look at\n[the API documentation](/docs/api/interfaces/CheerioOptions).\n\n### Fragment Mode\n\nBy default, `parse5` treats documents it receives as full HTML documents and\nwill structure content in an `<html>` document element with nested `<head>` and\n`<body>` tags.\n\n```js\nconst $ = cheerio.load('<li>Apple</li><li>Banana</li>');\n\n$.html(); // => '<html><head></head><body><li>Apple</li><li>Banana</li></body></html>'\n```\n\n`parse5` also supports a \"fragment mode\" that allows you to parse HTML\nfragments, rather than complete documents. To use this mode, pass a boolean\nindicating whether you are parsing a full document to the `.load()` method:\n\n```js\n// Note that we are passing `false`, as we are not parsing a full document.\nconst $ = cheerio.load('<li>Apple</li><li>Banana</li>', {}, false);\n\n$.html(); // => '<li>Apple</li><li>Banana</li>'\n```\n\nThis will parse the HTML fragment as a standalone document, rather than treating\nit as a part of a larger document.\n\n## Parsing XML with htmlparser2\n\nBy default, Cheerio uses `htmlparser2` for XML documents. `htmlparser2` is a\nfast and memory-efficient parser that can handle both HTML and XML. To parse\nXML, pass the `xml` option to `.load()`:\n\n```js\nconst $ = cheerio.load('<ul id=\"fruits\">...</ul>', {\n  xml: true,\n});\n```\n\nIf you need to customize the parsing options for XML input, you may pass an\nobject as the `xml` option to `.load()`, with the options you want to change:\n\n```js\nconst $ = cheerio.load('<ul id=\"fruits\">...</ul>', {\n  xml: {\n    withStartIndices: true,\n  },\n});\n```\n\nWhen `xml` is set, the default options are:\n\n```js\n{\n    xmlMode: true, // Enable htmlparser2's XML mode.\n    decodeEntities: true, // Decode HTML entities.\n    withStartIndices: false, // Add a `startIndex` property to nodes.\n    withEndIndices: false, // Add an `endIndex` property to nodes.\n}\n```\n\nThe options in the xml object are taken directly from htmlparser2, therefore any\noptions that can be used in htmlparser2 are valid in cheerio as well.\n\nFor a full list of options and their effects, see\n[the API documentation](/docs/api/interfaces/HTMLParser2Options).\n\n### Using `htmlparser2` for HTML\n\nSome users may wish to parse markup with the `htmlparser2` library, and traverse\nand manipulate the resulting structure with Cheerio. This may be the case for\nthose upgrading from pre-1.0 releases of Cheerio (which relied on\n`htmlparser2`), for those dealing with invalid markup (because `htmlparser2` is\nmore forgiving[^1]), or for those operating in performance-critical situations\n(because `htmlparser2` is often faster and the resulting DOM consumes less\nmemory).\n\n[^1]:\n    Note that \"more forgiving\" means `htmlparser2` has error-correcting\n    mechanisms that aren't always a match for the standards observed by web\n    browsers. This behavior may be useful when parsing non-HTML content.\n\nTo support these cases, you can simply disable `xmlMode` inside of the `xml`\noption:\n\n```js\nconst $ = cheerio.load('<ul id=\"fruits\">...</ul>', {\n  xml: {\n    // Disable `xmlMode` to parse HTML with htmlparser2.\n    xmlMode: false,\n  },\n});\n```\n\n`.load()` also accepts a `htmlparser2`-compatible data structure as its first\nargument. Users may install `htmlparser2`, use it to parse input, and pass the\nresult to `.load()`:\n\n```js\nimport * as htmlparser2 from 'htmlparser2';\nconst dom = htmlparser2.parseDocument(document, options);\n\nconst $ = cheerio.load(dom);\n```\n\nThe caveat of this method is that this will still use `parse5`'s serializer, so\nthe resulting output will be HTML, not XML, and not respect any of the supplied\noptions. Disabling `xmlMode`, as shown above, is therefore the recommended\napproach.\n\n:::tip\n\nYou can also use Cheerio's _slim_ export, which always uses `htmlparser2`. This\navoids loading `parse5`, which saves some bytes eg. in browser environments:\n\n```js\nimport * as cheerio from 'cheerio/slim';\n```\n\n:::\n\n## Conclusion\n\nIn this guide, we explored how to configure Cheerio for parsing HTML and XML\ndocuments using `parse5` and `htmlparser2` respectively. We also discussed how\nto modify parsing options and use `htmlparser2` directly.\n"
  },
  {
    "path": "website/src/content/docs/advanced/extending-cheerio.md",
    "content": "---\nsidebar_position: 999\ndescription: Create custom pseudo-classes and plugins.\n---\n\n# Extending Cheerio\n\nCheerio already provides many ways of working with documents, but sometimes you\nmay want to add custom functionality. This guide will cover two approaches:\nadding custom CSS pseudo elements and writing plugins for Cheerio.\n\n## Adding Custom CSS Pseudo-Classes\n\nThe `pseudos` option is the extension point for adding pseudo-classes. It is a\nmap from names to either strings of functions.\n\n- A string value is a selector that the element must match to be selected.\n- A function is called with the element as its first argument, and optional\n  parameters as the second. If it returns true, the element is selected.\n\nHere is an example of using the pseudos option:\n\n```js\nconst $ = cheerio.load('<div class=\"foo\"></div><div data-bar=\"boo\"></div>', {\n  pseudos: {\n    // `:foo` is an alias for `div.foo`\n    foo: 'div.foo',\n    // `:bar(val)` is equivalent to `[data-bar=val s]`\n    bar: (el, val) => el.attribs['data-bar'] === val,\n  },\n});\n\n$(':foo').length; // 1\n$('div:bar(boo)').length; // 1\n$('div:bar(baz)').length; // 0\n```\n\n## Writing Plugins for Cheerio\n\nOnce you have loaded a document, you may extend the prototype or the equivalent\n`fn` property with custom plugin methods. Here is an example:\n\n```js\nconst $ = cheerio.load('<html><body>Hello, <b>world</b>!</body></html>');\n$.prototype.logHtml = function () {\n  console.log(this.html());\n};\n\n$('body').logHtml(); // logs \"Hello, <b>world</b>!\" to the console\n```\n\nIf you're using TypeScript, you should add a type definition for your new\nmethod:\n\n```ts\ndeclare module 'cheerio' {\n  interface Cheerio<T> {\n    logHtml(this: Cheerio<T>): void;\n  }\n}\n```\n"
  },
  {
    "path": "website/src/content/docs/advanced/extract.md",
    "content": "---\nsidebar_label: The `extract` method\nsidebar_position: 1\ndescription: Extract multiple values at once.\n---\n\n# Extracting Data with the `extract` Method\n\nThe `extract` method allows you to extract data from an HTML document and store\nit in an object. The method takes a `map` object as a parameter, where the keys\nare the names of the properties to be created on the object, and the values are\nthe selectors or descriptors to be used to extract the values.\n\nTo use the `extract` method, you first need to import the library and load an\nHTML document. For example:\n\n```js\nimport * as cheerio from 'cheerio';\n\nconst $ = cheerio.load(`\n  <ul>\n    <li>One</li>\n    <li>Two</li>\n    <li class=\"blue sel\">Three</li>\n    <li class=\"red\">Four</li>\n  </ul>\n`);\n```\n\nOnce you have loaded the document, you can use the `extract` method on the\nloaded object to extract data from the document.\n\nHere are some examples of how to use the `extract` method:\n\n```js\n// Extract the text content of the first .red element\nconst data = $.extract({\n  red: '.red',\n});\n```\n\nThis will return an object with a `red` property, whose value is the text\ncontent of the first `.red` element.\n\nTo extract the text content of all `.red` elements, you can wrap the selector in\nan array:\n\n```js\n// Extract the text content of all .red elements\nconst data = $.extract({\n  red: ['.red'],\n});\n```\n\nThis will return an object with a `red` property, whose value is an array of the\ntext content of all `.red` elements.\n\nTo be more specific about what you'd like to extract, you can pass an object\nwith a `selector` and a `value` property. For example, to extract the text\ncontent of the first `.red` element and the `href` attribute of the first `a`\nelement:\n\n```js\nconst data = $.extract({\n  red: '.red',\n  links: {\n    selector: 'a',\n    value: 'href',\n  },\n});\n```\n\nThe `value` property can be used to specify the name of the property to extract\nfrom the selected elements. In this case, we are extracting the `href` attribute\nfrom the `<a>` elements. This uses Cheerio's\n[`prop` method](/docs/api/classes/Cheerio#prop) under the hood.\n\n`value` defaults to `textContent`, which extracts the text content of the\nelement.\n\nAs an attribute with special logic inside the `prop` method, `href`s will be\nresolved relative to the document's URL. The document's URL will be set\nautomatically when using `fromURL` to load the document. Otherwise, use the\n`baseURI` option to specify the documents URL.\n\nThere are many props available here; have a look at the\n[`prop` method](/docs/api/classes/Cheerio#prop) for details. For example, to\nextract the `outerHTML` of all `.red` elements:\n\n```js\nconst data = $.extract({\n  red: [\n    {\n      selector: '.red',\n      value: 'outerHTML',\n    },\n  ],\n});\n```\n\nYou can also extract data from multiple nested elements by specifying an object\nas the `value`. For example, to extract the text content of all `.red` elements\nand the first `.blue` element in the first `<ul>` element, and the text content\nof all `.sel` elements in the second `<ul>` element:\n\n```js\nconst data = $.extract({\n  ul1: {\n    selector: 'ul:first',\n    value: {\n      red: ['.red'],\n      blue: '.blue',\n    },\n  },\n  ul2: {\n    selector: 'ul:eq(2)',\n    value: {\n      sel: ['.sel'],\n    },\n  },\n});\n```\n\nThis will return an object with `ul1` and `ul2` properties. The `ul1` property\nwill be an object with a `red` property, whose value is an array of the text\ncontent of all `.red` elements in the first ul element, and a `blue` property.\nThe `ul2` property will be an object with a `sel` property, whose value is an\narray of the text content of all `.sel` elements in the second `<ul>` element.\n\nFinally, you can pass a function as the `value` property. The function will be\ncalled with each of the selected elements, and the `key` of the property:\n\n```js\nconst data = $.extract({\n  links: [\n    {\n      selector: 'a',\n      value: (el, key) => {\n        const href = $(el).attr('href');\n        return `${key}=${href}`;\n      },\n    },\n  ],\n});\n```\n\nThis will extract the `href` attribute of all `<a>` elements and return a string\nin the form `links=href_value` for each element, where `href_value` is the value\nof the `href` attribute. The returned object will have a `links` property whose\nvalue is an array of these strings.\n\n## Putting it all together\n\nLet's fetch the latest release of Cheerio from GitHub and extract the release\ndate and the release notes from the release page:\n\n```js\nimport * as cheerio from 'cheerio';\n\nconst $ = await cheerio.fromURL(\n  'https://github.com/cheeriojs/cheerio/releases',\n);\n\nconst data = $.extract({\n  releases: [\n    {\n      // First, we select individual release sections.\n      selector: 'section',\n      // Then, we extract the release date, name, and notes from each section.\n      value: {\n        // Selectors are executed within the context of the selected element.\n        name: 'h2',\n        date: {\n          selector: 'relative-time',\n          // The actual release date is stored in the `datetime` attribute.\n          value: 'datetime',\n        },\n        notes: {\n          selector: '.markdown-body',\n          // We are looking for the HTML content of the element.\n          value: 'innerHTML',\n        },\n      },\n    },\n  ],\n});\n```\n"
  },
  {
    "path": "website/src/content/docs/basics/loading.md",
    "content": "---\nsidebar_position: 2\ndescription: A walkthrough of different loading methods.\n---\n\n# Loading Documents\n\nIn this guide, we'll take a look at how to load documents with Cheerio and when\nto use the different loading methods.\n\n:::tip\n\nIf you're familiar with jQuery, then this step will be new to you. jQuery\noperates on the one, baked-in DOM. With Cheerio, we need to pass in the HTML\ndocument.\n\n:::\n\n:::danger[Availability of methods]\n\nThe `loadBuffer`, `stringStream`, `decodeStream`, and `fromURL` methods are not\navailable in the browser environment. Instead, use the `load` method to parse\nHTML strings.\n\n:::\n\n## `load`\n\nThe load method is the most basic way to parse an HTML or XML document with\nCheerio. It takes a string containing the document as its argument and returns a\nCheerio object that you can use to traverse and manipulate the document.\n\nHere's an example of how to use the load method:\n\n```js\nimport * as cheerio from 'cheerio';\n\nconst $ = cheerio.load('<h1>Hello, world!</h1>');\n\nconsole.log($('h1').text());\n// Output: Hello, world!\n```\n\n:::tip\n\nSimilar to web browser contexts, `load` will introduce `<html>`, `<head>`, and\n`<body>` elements if they are not already present. You can set `load`'s third\nargument to `false` to disable this.\n\n```js\nconst $ = cheerio.load('<ul id=\"fruits\">...</ul>', null, false);\n\n$.html();\n//=> '<ul id=\"fruits\">...</ul>'\n```\n\n:::\n\nLearn more about the `load` method in the\n[API documentation](/docs/api/variables/load).\n\n## `loadBuffer`\n\nThe `loadBuffer` method is similar to the `load` method, but it takes a buffer\ncontaining the document as its argument instead of a string. Cheerio will run\nthe HTML encoding sniffing algorithm to determine the encoding of the document.\nThis is useful when you have the document in binary form, such as when you're\nreading it from a file or receiving it over a network connection.\n\nHere's an example of how to use the `loadBuffer` method:\n\n```js\nimport * as cheerio from 'cheerio';\nimport * as fs from 'fs';\n\nconst buffer = fs.readFileSync('document.html');\n\nconst $ = cheerio.loadBuffer(buffer);\n\nconsole.log($('title').text());\n// Output: Hello, world!\n```\n\nLearn more about the `loadBuffer` method in the\n[API documentation](/docs/api/functions/loadBuffer).\n\n## `stringStream`\n\nWhen loading an HTML document from a stream and the encoding is known, you can\nuse the `stringStream` method to parse it into a Cheerio object.\n\n```js\nimport * as cheerio from 'cheerio';\nimport * as fs from 'fs';\n\nconst writeStream = cheerio.stringStream({}, (err, $) => {\n  if (err) {\n    // Handle error\n  }\n\n  console.log($('title').text());\n  // Output: Hello, world!\n});\n\nfs.createReadStream('document.html', { encoding: 'utf8' }).pipe(writeStream);\n```\n\nLearn more about the `stringStream` method in the\n[API documentation](/docs/api/functions/stringStream).\n\n## `decodeStream`\n\nWhen loading an HTML document from a stream and the encoding is not known, you\ncan use the `decodeStream` method to parse it into a Cheerio object. This method\nruns the HTML encoding sniffing algorithm to determine the encoding of the\ndocument.\n\nHere's an example of how to use the `decodeStream` method:\n\n```js\nimport * as cheerio from 'cheerio';\nimport * as fs from 'fs';\n\nconst writeStream = cheerio.decodeStream({}, (err, $) => {\n  if (err) {\n    // Handle error\n  }\n\n  console.log($('title').text());\n  // Output: Hello, world!\n});\n\nfs.createReadStream('document.html').pipe(writeStream);\n```\n\nLearn more about the `decodeStream` method in the\n[API documentation](/docs/api/functions/decodeStream).\n\n## `fromURL`\n\nThe `fromURL` method allows you to load a document from a URL. This method is\nasynchronous, so you need to use `await` (or a `then` block) to access the\nresulting Cheerio object.\n\n```js\nimport * as cheerio from 'cheerio';\n\nconst $ = await cheerio.fromURL('https://example.com');\n```\n\nLearn more about the `fromURL` method in the\n[API documentation](/docs/api/functions/fromURL).\n\n## Conclusion\n\nCheerio provides several methods for loading HTML documents and parsing them\ninto a DOM structure. These methods are useful for different scenarios,\ndepending on the type and source of the HTML data. Users are encouraged to read\nthrough each of these methods and pick the one that best suits their needs.\n\n<!-- Based on ChatGPT with the prompt: Write a guide in Markdown for loading documents with Cheerio, explaining when to use `load`, `loadBuffer`, `stringStream`, `decodeStream`, and `fromURL`. Methods that deal with binary data run the HTML encoding sniffing algorithm and are recommended when the encoding is not known. The guide should be ready to be published on Cheerio's website. Use modern JavaScript with imports in the examples. -->\n"
  },
  {
    "path": "website/src/content/docs/basics/manipulation.md",
    "content": "---\nsidebar_position: 5\ndescription: Methods to manipulate elements within a document.\n---\n\n# Manipulating the DOM\n\nNow that you have learned the basics of using Cheerio and have gained some\nexperience with loading and traversing documents, it's time to dive deeper into\nmanipulating elements. Whether you want to modify element attributes and\nproperties, add and remove classes, modify text and HTML content, or insert and\nremove elements, Cheerio provides a range of methods to help you do so.\n\nIn this guide, we will focus specifically on manipulating elements within a\ndocument using Cheerio. We will cover methods for modifying element attributes\nand properties, adding and removing classes, modifying text and HTML content,\ninserting and removing elements, and handling errors and debugging. By the end\nof this guide, you will have a good understanding of how to use these methods to\nmanipulate elements within a document using Cheerio.\n\n## Modifying Element Attributes and Properties\n\nTo modify the attributes and properties of a single element, you can use the\n[`attr()`](/docs/api/classes/Cheerio#attr) and\n[`prop()`](/docs/api/classes/Cheerio#prop) methods, respectively. Both methods\ntake a key and a value as arguments, and allow you to get and set the attribute\nor property. When setting, they apply to all elements in the selection; when\ngetting, they return a single value corresponding to the first element in the\nselection.\n\n```js\n// Set the 'src' attribute of an image element\n$('img').attr('src', 'https://example.com/image.jpg');\n\n// Set the 'checked' property of a checkbox element\n$('input[type=\"checkbox\"]').prop('checked', true);\n\n// Get the 'href' attribute of a link element\nconst href = $('a').attr('href');\n\n// Get the 'disabled' property of a button element\nconst isDisabled = $('button').prop('disabled');\n```\n\n`prop()` is not limited to simple values like strings and booleans. You can also\nuse it to get complex properties like the `style` object, or to resolve `href`\nor `src` URLs of supported elements. You can also use it to get the `tagName`,\n`innerHTML`, `outerHTML`, `textContent`, and `innerText` properties of a single\nelement.\n\n```js\n// Get the `style` object of an element\nconst style = $('div').prop('style');\n\n// Get the resolved `src` URL of an image element\n$('img').prop('src');\n\n// Get the outerHTML of an element\nconst outerHTML = $('div').prop('outerHTML');\n\n// Get the innerText of an element\nconst innerText = $('div').prop('innerText');\n```\n\n## Adding and Removing Classes\n\nTo add or remove classes from an element, you can use the\n[`addClass()`](/docs/api/classes/Cheerio#addclass),\n[`removeClass()`](/docs/api/classes/Cheerio#removeclass), and\n[`toggleClass()`](/docs/api/classes/Cheerio#toggleclass) methods. All three\nmethods take a class name or a space-separated list of class names as an\nargument. They modify all elements in the selection.\n\n```js\n// Add a class to an element\n$('div').addClass('new-class');\n\n// Add multiple classes to an element\n$('div').addClass('new-class another-class');\n\n// Remove a class from an element\n$('div').removeClass('old-class');\n\n// Remove multiple classes from an element\n$('div').removeClass('old-class another-class');\n\n// Toggle a class on an element (add if it doesn't exist, remove if it does)\n$('div').toggleClass('active');\n```\n\n## Modifying the Text Content of an Element\n\nTo query or modify the text content of an element, you can use the\n[`text()`](/docs/api/classes/Cheerio#text) method. Given a string as an\nargument, it sets the text content of every element in the selection to the\ngiven string. Without arguments, it returns the text content of every element\n(including its descendants) in the selection, concatenated together.\n\n```js\n// Set the text content of an element\n$('h1').text('Hello, World!');\n\n// Get the text content of an element\nconst text = $('p').text();\n```\n\n:::tip[Note]\n\n`text()` returns the `textContent` of all passed elements. The result will\ninclude the contents of `<script>` and `<style>` elements. To avoid this, use\n`.prop('innerText')` instead.\n\n:::\n\n## Modifying the HTML Content of an Element\n\nTo query or modify the HTML content of an element, you can use the\n[`html()`](/docs/api/classes/Cheerio#html) method. Given an HTML string as an\nargument, it sets the inner HTML of every element in the selection to the given\nstring. Without arguments, it returns the inner HTML of the _first_ element in\nthe selection.\n\n```js\n// Set the inner HTML of an element\n$('div').html('<p>Hello, World!</p>');\n\n// Get the inner HTML of an element\nconst html = $('div').html();\n```\n\n## Inserting New Elements\n\nTo insert new elements into a document, you can use the\n[`append()`](/docs/api/classes/Cheerio#append),\n[`prepend()`](/docs/api/classes/Cheerio#prepend),\n[`before()`](/docs/api/classes/Cheerio#before), and\n[`after()`](/docs/api/classes/Cheerio#after) methods. These modify every element\nin the selection.\n\n```js\n// Append an element to the end of a parent element\n$('ul').append('<li>Item</li>');\n\n// Prepend an element to the beginning of a parent element\n$('ul').prepend('<li>Item</li>');\n\n// Insert an element before a target element\n$('li').before('<li>Item</li>');\n\n// Insert an element after a target element\n$('li').after('<li>Item</li>');\n```\n\nThe [`insertAfter()`](/docs/api/classes/Cheerio#insertafter) and\n[`insertBefore()`](/docs/api/classes/Cheerio#insertbefore) methods allow you to\ninsert an element before or after a target element, respectively. Both methods\ntake a string or a Cheerio object as an argument and insert the given element\nbefore or after the target element.\n\n```js\nconst $ = require('cheerio');\n\n// Insert an element after a target element\n$('<p>Inserted element</p>').insertAfter('h1');\n\n// Insert an element before a target element\n$('<p>Inserted element</p>').insertBefore('h1');\n```\n\nThe [`prependTo()`](/docs/api/classes/Cheerio#prependto) and\n[`appendTo()`](/docs/api/classes/Cheerio#appendto) methods allow you to prepend\nor append an element to a parent element, respectively. Both methods take a\nstring or a Cheerio object as an argument and insert the element at the\nbeginning or end of the given parent element.\n\n```js\nconst $ = require('cheerio');\n\n// Prepend an element to a parent element\n$('<p>Inserted element</p>').prependTo('div');\n\n// Append an element to a parent element\n$('<p>Inserted element</p>').appendTo('div');\n```\n\n## Wrapping and Unwrapping Elements\n\nSometimes you may want to wrap an element in another element, or remove the\nelement's parent element while keeping its children. To do this, you can use the\n`wrap()`, `wrapInner()`, and `unwrap()` methods.\n\nThe [`wrap()`](/docs/api/classes/Cheerio#wrap) method takes a string or a\nCheerio object as an argument and wraps the element in the given element.\n\n```js\n// Wrap an element in a div\n$('p').wrap('<div></div>');\n```\n\nThe [`wrapInner()`](/docs/api/classes/Cheerio#wrapinner) method works similar to\nwrap(), but instead of wrapping the element itself, it wraps the element's inner\nHTML in the given element.\n\n```js\n// Wrap the inner HTML of an element in a div\n$('div').wrapInner('<div></div>');\n```\n\nThe [`unwrap()`](/docs/api/classes/Cheerio#unwrap) method removes the element's\nparent element, while keeping the element and its children.\n\n```js\n// Unwrap an element\n$('p').unwrap();\n```\n\n## Replacing Elements\n\nTo replace an element with another element, you can use the\n[`replaceWith()`](/docs/api/classes/Cheerio#replacewith) method. It takes a\nstring or a Cheerio object as an argument and replaces each element in the\nselection with the given element.\n\n```js\n// Replace an element with another element\n$('li').replaceWith('<li>Item</li>');\n```\n\nNote that the `replaceWith()` method removes the element from the document and\nreplaces it with the given element or HTML string. If you want to keep the\nelement and modify its contents, you can use the `html()` or `text()` methods\ninstead.\n\n## Removing Elements\n\nTo remove an element from a document, you can use the\n[`remove()`](/docs/api/classes/Cheerio#remove) method. It removes each element\nin the selection, and all of their children, from the document.\n\n```js\n// Remove an element from the document\n$('li').remove();\n```\n\nAlternatively, you can remove the children of an element from the document,\nwithout removing the element itself, using the\n[`empty()`](/docs/api/classes/Cheerio#empty) method. It removes the children\n(but not text nodes or comments) of each element in the selection from the\ndocument.\n\n```js\n// Remove an element's children from the document\n$('li').empty();\n```\n\n## Conclusion\n\nWe learned how to manipulate elements within a document using Cheerio. We can\nmodify element attributes and properties, add and remove classes, modify text\nand HTML content, insert and remove elements, and handle errors and debug our\ncode. With these tools, you can easily manipulate a document to suit your needs.\n"
  },
  {
    "path": "website/src/content/docs/basics/selecting.md",
    "content": "---\nsidebar_position: 3\ndescription: An introduction to CSS selectors.\n---\n\n# Selecting Elements\n\nCheerio allows users to select elements from an HTML document using\n[CSS selectors](https://developer.mozilla.org/en-US/docs/Glossary/CSS_Selector).\nThis allows you to select elements based on criteria such as their tag name,\nclass name, and attribute values. This guide provides an overview of how to use\nCSS selectors to retrieve elements.\n\nTo select elements with Cheerio, you first need to import the library and load a\ndocument. For example:\n\n```js\nimport * as cheerio from 'cheerio';\n\n// Load the document using any of the methods described in the \"Loading Documents\" section.\nconst $ = cheerio.load('<html>...</html>');\n```\n\nOnce you have loaded the document, you can use the `$` function to select\nelements. The `$` function works just like the `$` function in jQuery, and\nallows you to select elements based on their tag name, class name, and attribute\nvalues.\n\nHere are some examples of how to use the `$` function to select elements:\n\n- To select all the `<p>` elements in the document:\n\n```js\nconst $p = $('p');\n```\n\n:::tip\n\nThe convention in Cheerio is to prefix the variable name with a $ to indicate\nthat it contains a Cheerio object. This is not required, but it is a good\npractice to follow.\n\n:::\n\n- To select elements with a specific class name:\n\n```js\nconst $selected = $('.selected');\n```\n\n- To select elements with a specific attribute value:\n\n```js\nconst $selected = $('[data-selected=true]');\n```\n\n:::tip[XML Namespaces]\n\nYou can select with XML Namespaces but\n[due to the CSS specification](https://www.w3.org/TR/2011/REC-css3-selectors-20110929/#attribute-selectors),\nthe colon (`:`) needs to be escaped for the selector to be valid.\n\n```js\n$('[xml\\\\:id=\"main\"');\n```\n\n:::\n\n- Selectors can be combined to select elements that match multiple criteria. For\n  example, to select all `<p>` elements with the class `selected`:\n\n```js\nconst $selected = $('p.selected');\n```\n\n- Further, you can use spaces (` `) to select elements that are descendants of\n  other elements. For example, to select all `<p>` elements that are descendants\n  of `<div>` elements:\n\n```js\nconst $p = $('div p');\n```\n\n- You can also use the `>` character to select elements that are direct\n  descendants of other elements. For example, to select all `<p>` elements that\n  are direct descendants of `<div>` elements:\n\n```js\nconst $p = $('div > p');\n```\n\nPlease have a look at the documentation of Cheerio's underlying CSS selector\nlibrary, `css-select`, for\n[a list of all supported selectors](https://github.com/fb55/css-select/blob/master/README.md#supported-selectors).\nFor example, to select `<p>` elements containing the word \"hello\":\n\n```js\nconst $p = $('p:contains(\"hello\")');\n```\n\nCheerio also supports several jQuery-specific extensions that allow you to\nselect elements based on their position in the document. For example, to select\nthe first `<p>` element in the document:\n\n```js\nconst $p = $('p:first');\n```\n\nHave a look at\n[cheerio-select](https://github.com/cheeriojs/cheerio-select/blob/master/README.md),\nthe library that implements these extensions, to see what is available.\n\nFor further information, please also have a look at jQuery's guide on\n[selecting elements](https://learn.jquery.com/using-jquery-core/selecting-elements/),\nas well as\n[MDN](https://developer.mozilla.org/en-US/docs/Glossary/CSS_Selector).\n\nFinally, to add custom CSS pseudo-classes, have a look at the\n[Extending Cheerio guide](/docs/advanced/extending-cheerio#adding-custom-css-pseudo-classes).\n"
  },
  {
    "path": "website/src/content/docs/basics/traversing.mdx",
    "content": "---\nsidebar_position: 4\ndescription: Traverse the DOM tree and filter elements.\n---\n\nimport { LiveCode } from '@/components/live-code';\n\n# Traversing the DOM\n\nTraversing a document with Cheerio allows you to select and manipulate specific\nelements within the document. Whether you want to move up and down the DOM tree,\nmove sideways within the tree, or filter elements based on certain criteria,\nCheerio provides a range of methods to help you do so.\n\nIn this guide, we will go through the various methods available in Cheerio for\ntraversing and filtering elements. We will cover methods for moving down the DOM\ntree, moving up the DOM tree, moving sideways within the tree, and filtering\nelements. By the end of this guide, you will have a good understanding of how to\nuse these methods to select and manipulate elements within a document using\nCheerio.\n\n:::tip\n\nThis guide is intended to give you an overview of the various methods available\nin Cheerio for traversing and filtering elements. For a more detailed reference\nof these methods, see the [API documentation](/docs/api/classes/Cheerio).\n\n:::\n\n## Moving Down the DOM Tree\n\nCheerio provides several methods for moving down the DOM tree and selecting\nelements that are children or descendants of the current selection.\n\n### `find`\n\nThe [`find` method](/docs/api/classes/Cheerio#find) allows you to locate\nspecific elements within a selection. It takes a CSS selector as an argument and\nreturns a new selection containing all elements that match the selector within\nthe current selection.\n\nHere's an example of using `find` to select all `<li>` elements within a `<ul>`\nelement:\n\n```js live\nconst $ = cheerio.load(\n  `<ul>\n    <li>Item 1</li>\n    <li>Item 2</li>\n  </ul>`,\n);\n\nconst listItems = $('ul').find('li');\nconsole.log(`List item count: ${listItems.length}`);\n```\n\n### `children`\n\nThe [`children` method](/docs/api/classes/Cheerio#children) allows you to select\nthe direct children of an element. It returns a new selection containing all\ndirect children of the current selection.\n\nHere's an example of using `children` to select all `<li>` elements within a\n`<ul>` element:\n\n```js live\nconst $ = cheerio.load(\n  `<ul>\n    <li>Item 1</li>\n    <li>Item 2</li>\n  </ul>`,\n);\n\nconst listItems = $('ul').children('li');\nconsole.log(`List item count: ${listItems.length}`);\n```\n\n### `contents`\n\nThe [`contents` method](/docs/api/classes/Cheerio#contents) allows you to select\nall children of an element, including text and comment nodes. It returns a new\nselection containing all children of the current selection.\n\nHere's an example of using `contents` to select all children of a `<div>`\nelement:\n\n```js live\nconst $ = cheerio.load(\n  `<div>\n    Text <p>Paragraph</p>\n  </div>`,\n);\n\nconst contents = $('div').contents();\nconsole.log(`Contents count: ${contents.length}`);\n```\n\n## Moving Up the DOM Tree\n\nCheerio provides several methods for moving up the DOM tree and selecting\nelements that are ancestors of the current selection.\n\n### `parent`\n\nThe [`parent` method](/docs/api/classes/Cheerio#parent) allows you to select the\nparent element of a selection. It returns a new selection containing the parent\nelement of each element in the current selection.\n\nHere's an example of using `parent` to select the parent `<ul>` element of a\n`<li>` element:\n\n```js live\nconst $ = cheerio.load(\n  `<ul>\n    <li>Item 1</li>\n  </ul>`,\n);\n\nconst list = $('li').parent();\nconsole.log(list.prop('tagName'));\n```\n\n### `parents` and `parentsUntil`\n\nThe [`parents` method](/docs/api/classes/Cheerio#parents) allows you to select\nall ancestor elements of a selection, up to the root element. It returns a new\nselection containing all ancestor elements of the current selection.\n\nThe [`parentsUntil` method](/docs/api/classes/Cheerio#parentsuntil) is similar\nto `parents`, but allows you to specify an ancestor element as a stop point. It\nreturns a new selection containing all ancestor elements of the current\nselection up to (but not including) the specified ancestor.\n\nHere's an example of using `parents` and `parentsUntil` to select ancestor\nelements of a `<li>` element:\n\n```js live\nconst $ = cheerio.load(\n  `<div>\n    <ul>\n      <li>Item 1</li>\n    </ul>\n  </div>`,\n);\n\nconst ancestors = $('li').parents();\nconst ancestorsUntil = $('li').parentsUntil('div');\n\nconsole.log(`Ancestor count (includes body and html): ${ancestors.length}`);\nconsole.log(`Ancestor count (until div): ${ancestorsUntil.length}`);\n```\n\n### `closest`\n\nThe [`closest` method](/docs/api/classes/Cheerio#closest) allows you to select\nthe closest ancestor matching a given selector. It returns a new selection\ncontaining the closest ancestor element that matches the selector. If no\nmatching ancestor is found, the method returns an empty selection.\n\nHere's an example of using `closest` to select the closest ancestor `<ul>`\nelement of a `<li>` element:\n\n```js live\nconst $ = cheerio.load(\n  `<div>\n    <ul>\n      <li>Item 1</li>\n    </ul>\n  </div>`,\n);\n\nconst list = $('li').closest('ul');\nconsole.log(list.prop('tagName'));\n```\n\n## Moving Sideways Within the DOM Tree\n\nCheerio provides several methods for moving sideways within the DOM tree and\nselecting elements that are siblings of the current selection.\n\n### `next` and `prev`\n\nThe [`next` method](/docs/api/classes/Cheerio#next) allows you to select the\nnext sibling element of a selection. It returns a new selection containing the\nnext sibling element (if there is one). If the given selection contains multiple\nelements, `next` includes the next sibling for each one.\n\nThe [`prev` method](/docs/api/classes/Cheerio#prev) is similar to `next`, but\nallows you to select the previous sibling element. It returns a new selection\ncontaining the previous sibling element for each element in the given selection.\n\nHere's an example of using `next` and `prev` to select sibling elements of a\n`<li>` element:\n\n```js live\nconst $ = cheerio.load(\n  `<ul>\n    <li>Item 1</li>\n    <li>Item 2</li>\n  </ul>`,\n);\n\nconst nextItem = $('li:first').next();\nconst prevItem = $('li:eq(1)').prev();\n\nconsole.log(`Next: ${nextItem.text()}`);\nconsole.log(`Prev: ${prevItem.text()}`);\n```\n\n## `nextAll`, `prevAll`, and `siblings`\n\nThe [`nextAll` method](/docs/api/classes/Cheerio#nextall) allows you to select\nall siblings after the current element. It returns a new selection containing\nall sibling elements after each element in the current selection.\n\nThe [`prevAll` method](/docs/api/classes/Cheerio#prevall) is similar to nextAll,\nbut allows you to select all siblings before the current element. It returns a\nnew selection containing all sibling elements before each element in the current\nselection.\n\nThe [`siblings` method](/docs/api/classes/Cheerio#siblings) allows you to select\nall siblings of a selection. It returns a new selection containing all sibling\nelements of each element in the current selection.\n\nHere's an example of using `nextAll`, `prevAll`, and `siblings` to select\nsibling elements of a `<li>` element:\n\n```js live\nconst $ = cheerio.load(\n  `<ul>\n    <li>[1]</li>\n    <li>[2]</li>\n    <li>[3]</li>\n  </ul>`,\n);\n\nconst nextAll = $('li:first').nextAll();\nconst prevAll = $('li:last').prevAll();\nconst siblings = $('li:eq(1)').siblings();\n\nconsole.log(`Next All: ${nextAll.text()}`);\nconsole.log(`Prev All: ${prevAll.text()}`);\nconsole.log(`Siblings: ${siblings.text()}`);\n```\n\n### `nextUntil` and `prevUntil`\n\nThe [`nextUntil` method](/docs/api/classes/Cheerio#nextuntil) allows you to\nselect all siblings after the current element up to a specified sibling. It\ntakes a selector or a sibling element as an argument and returns a new selection\ncontaining all sibling elements after the current element up to (but not\nincluding) the specified element.\n\nThe [`prevUntil` method](/docs/api/classes/Cheerio#prevuntil) is similar to\n`nextUntil`, but allows you to select all siblings before the current element up\nto a specified sibling. It takes a selector or a sibling element as an argument\nand returns a new selection containing all sibling elements before the current\nelement up to (but not including) the specified element.\n\nHere's an example of using `nextUntil` and `prevUntil` to select sibling\nelements of a `<li>` element:\n\n```js live\nconst $ = cheerio.load(\n  `<ul>\n    <li>Item 1</li>\n    <li>Item 2</li>\n    <li>Item 3</li>\n  </ul>`,\n);\n\nconst nextUntil = $('li:first').nextUntil('li:last-child');\nconst prevUntil = $('li:last').prevUntil('li:first-child');\n\nconsole.log(`Next: ${nextUntil.text()}`);\nconsole.log(`Prev: ${prevUntil.text()}`);\n```\n\n## Filtering elements\n\nCheerio provides several methods for filtering elements within a selection.\n\n:::tip\n\nMost of these filters also exist as selectors. For example, the `first` method\nis available as the `:first` selector. Users are encouraged to use the selector\nsyntax when possible, as it is more performant.\n\n:::\n\n### `eq`\n\nThe [`eq` method](/docs/api/classes/Cheerio#eq) allows you to select an element\nat a specified index within a selection. It takes an index as an argument and\nreturns a new selection containing the element at the specified index.\n\nHere's an example of using `eq` to select the second `<li>` element within a\n`<ul>` element:\n\n```js live\nconst $ = cheerio.load(\n  `<ul>\n    <li>Item 1</li>\n    <li>Item 2</li>\n  </ul>`,\n);\n\nconst secondItem = $('li').eq(1);\nconsole.log(secondItem.text());\n```\n\n### `filter` and `not`\n\nThe [`filter` method](/docs/api/classes/Cheerio#filter) allows you to select\nelements that match a given selector. It takes a selector as an argument and\nreturns a new selection containing only those elements that match the selector.\n\nThe [`not` method](/docs/api/classes/Cheerio#not) is similar to `filter`, but\nallows you to select elements that do not match a given selector. It takes a\nselector as an argument and returns a new selection containing only those\nelements that do not match the selector.\n\nHere's an example of using `filter` and `not` to select `<li>` elements within a\n`<ul>` element:\n\n```js live\nconst $ = cheerio.load(\n  `<ul>\n    <li class=\"item\">Item 1</li>\n    <li>Item 2</li>\n  </ul>`,\n);\n\nconst matchingItems = $('li').filter('.item');\nconst nonMatchingItems = $('li').not('.item');\n\nconsole.log(`Matching: ${matchingItems.text()}`);\nconsole.log(`Non-matching: ${nonMatchingItems.text()}`);\n```\n\n### `has`\n\nThe [`has` method](/docs/api/classes/Cheerio#has) allows you to select elements\nthat contain an element matching a given selector. It takes a selector as an\nargument and returns a new selection containing only those elements that contain\nan element matching the selector.\n\nHere's an example of using `has` to select `<li>` elements within a `<ul>`\nelement that contain a `<strong>` element:\n\n```js live\nconst $ = cheerio.load(\n  `<ul>\n    <li>Item 1</li>\n    <li>\n      <strong>Item 2</strong>\n    </li>\n  </ul>`,\n);\n\nconst matchingItems = $('li').has('strong');\nconsole.log(matchingItems.length);\n```\n\n### `first` and `last`\n\nThe [`first` method](/docs/api/classes/Cheerio#first) allows you to select the\nfirst element in a selection. It returns a new selection containing the first\nelement.\n\nThe [`last` method](/docs/api/classes/Cheerio#last) is similar to `first`, but\nallows you to select the last element in a selection. It returns a new selection\ncontaining the last element.\n\nHere's an example of using `first` and `last` to select elements within a `<ul>`\nelement:\n\n```js live\nconst $ = cheerio.load(\n  `<ul>\n    <li>Item 1</li>\n    <li>Item 2</li>\n  </ul>`,\n);\n\nconst firstItem = $('li').first();\nconst lastItem = $('li').last();\n\nconsole.log(`First: ${firstItem.text()}`);\nconsole.log(`Last: ${lastItem.text()}`);\n```\n\n## Conclusion\n\nCheerio provides a range of methods for traversing and filtering elements within\na document. These methods allow you to move up and down the DOM tree, move\nsideways within the tree, and filter elements based on various criteria. By\nusing these methods, you can easily select and manipulate elements within a\ndocument using Cheerio.\n"
  },
  {
    "path": "website/src/content/docs/intro.md",
    "content": "---\nsidebar_position: 1\nsidebar_label: Introduction\n---\n\n# Welcome to Cheerio!\n\nLet's get a quick overview of **Cheerio in less than 5 minutes**.\n\n## Getting Started\n\nLet's install Cheerio and its dependencies.\n\n### Setting up Node.js\n\nTo install Cheerio, you will need to have Node.js installed on your system.\n\n- Download the latest version of [Node.js](https://nodejs.org/en/download/):\n  - When installing Node.js, you are recommended to check all checkboxes related\n    to dependencies.\n\n### Installing Cheerio\n\nOnce you have set up Node.js, you can use the following command to install\nCheerio:\n\n```bash npm2yarn\nnpm install cheerio\n```\n\n### Importing Cheerio\n\nOnce Cheerio is installed, you can import it into your JavaScript code using the\n`import` statement:\n\n```js\nimport * as cheerio from 'cheerio';\n```\n\nIf you are on an older environment (or prefer using CommonJS), you can use the\n`require` function:\n\n```js\nconst cheerio = require('cheerio');\n```\n\n## Using Cheerio\n\nAfter importing Cheerio, you can start using it to manipulate and scrape web\npage data.\n\n### Loading a Document\n\nThe easiest way of loading HTML is to use the `load` function:\n\n```js\nconst $ = cheerio.load('<h2 class=\"title\">Hello world</h2>');\n```\n\nThis will load the HTML string into Cheerio and return a `Cheerio` object. You\ncan then use this object to traverse the DOM and manipulate the data.\n\nLearn more about [loading documents](/docs/basics/loading).\n\n:::note\n\n**Cheerio is not a web browser.** Cheerio parses markup and provides an API for\ntraversing/manipulating the resulting data structure. It does not interpret the\nresult as a web browser does. Specifically, it does _not_ produce a visual\nrendering, apply CSS, load external resources, or execute JavaScript which is\ncommon for a SPA (single page application). This makes Cheerio **much, much\nfaster than other solutions**. If your use case requires any of this\nfunctionality, you should consider browser automation software like\n[Puppeteer](https://github.com/puppeteer/puppeteer) and\n[Playwright](https://github.com/microsoft/playwright) or DOM emulation projects\nlike [JSDom](https://github.com/jsdom/jsdom).\n\n:::\n\n### Selecting Elements\n\nOnce you have loaded a document, you can use the returned function to select\nelements from the document.\n\nHere, we will select the `h2` element with the class `title`, and then get the\ntext from it:\n\n```js\n$('h2.title').text(); // \"Hello world\"\n```\n\nLearn more about [selecting elements](/docs/basics/selecting).\n\n### Traversing the DOM\n\nThe `$` function returns a `Cheerio` object, which is similar to an array of DOM\nelements. It is possible to use this object as a starting point to further\ntraverse the DOM. For example, you can use the `find` function to select\nelements within the selected elements:\n\n```js\n$('h2.title').find('.subtitle').text();\n```\n\nThere are many other functions that can be used to traverse the DOM. Learn more\nabout [traversing the DOM](/docs/basics/traversing).\n\n### Manipulating Elements\n\nOnce you have selected an element, you can use the `Cheerio` object to\nmanipulate the element.\n\nHere, we will select the `h2` element with the class `title`, and then change\nthe text inside it. We also add a new `h3` element to the document:\n\n```js\n$('h2.title').text('Hello there!');\n\n$('h2').after('<h3>How are you?</h3>');\n```\n\nLearn more about [manipulating elements](/docs/basics/manipulation).\n"
  },
  {
    "path": "website/src/env.d.ts",
    "content": "/* eslint-disable @typescript-eslint/triple-slash-reference */\n/* eslint-disable spaced-comment */\n/* eslint-disable multiline-comment-style */\n/// <reference path=\"../.astro/types.d.ts\" />\n/// <reference types=\"astro/client\" />\n"
  },
  {
    "path": "website/src/layouts/BaseLayout.astro",
    "content": "---\nimport '@/styles/global.css';\nimport { ViewTransitions } from 'astro:transitions';\nimport Footer from '@/components/Footer.astro';\nimport Navbar from '@/components/Navbar.astro';\n\ninterface Props {\n  title: string;\n  description?: string;\n}\n\nconst {\n  title,\n  description = 'The fast, flexible & elegant library for parsing and manipulating HTML and XML.',\n} = Astro.props;\n---\n\n<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <meta name=\"description\" content={description} />\n    <meta\n      name=\"keywords\"\n      content=\"htmlparser, jquery, selector, scraper, parser, dom, xml, html, nodejs\"\n    />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"/img/favicon.ico\" />\n    <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\" />\n    <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin />\n    <link href=\"https://fonts.googleapis.com/css2?family=DM+Serif+Display&family=JetBrains+Mono:wght@400;500&display=swap\" rel=\"stylesheet\" />\n    <title>{title} | Cheerio</title>\n    <ViewTransitions />\n    <script is:inline async src=\"https://www.googletagmanager.com/gtag/js?id=G-PZHRH775FB\"></script>\n    <script is:inline>\n      window.dataLayer = window.dataLayer || [];\n      function gtag() {\n        dataLayer.push(arguments);\n      }\n      gtag('js', new Date());\n      gtag('config', 'G-PZHRH775FB');\n    </script>\n  </head>\n  <body\n    class=\"flex min-h-screen flex-col bg-white text-slate-900 dark:bg-slate-900 dark:text-slate-100\"\n  >\n    <Navbar />\n    <main class=\"flex-1\">\n      <slot />\n    </main>\n    <Footer />\n  </body>\n</html>\n"
  },
  {
    "path": "website/src/layouts/BlogLayout.astro",
    "content": "---\nimport BaseLayout from '@/layouts/BaseLayout.astro';\n\ninterface Props {\n  title: string;\n  description?: string;\n  date?: Date;\n  author?: string;\n}\n\nconst {\n  title,\n  description = 'The fast, flexible & elegant library for parsing and manipulating HTML and XML.',\n  date,\n  author,\n} = Astro.props;\n\nconst formattedDate = date\n  ? date.toLocaleDateString('en-US', {\n      year: 'numeric',\n      month: 'long',\n      day: 'numeric',\n    })\n  : null;\n---\n\n<BaseLayout title={title} description={description}>\n  <article class=\"mx-auto max-w-3xl px-6 py-12\">\n    {/* Editorial header */}\n    <header class=\"mb-10 border-b border-slate-200 pb-8 dark:border-slate-700/50\">\n      <a\n        href=\"/blog\"\n        class=\"mb-4 inline-flex items-center gap-1.5 text-sm font-medium text-slate-400 transition-colors hover:text-primary dark:text-slate-500 dark:hover:text-primary\"\n      >\n        <svg class=\"h-3.5 w-3.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2.5\">\n          <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18\" />\n        </svg>\n        Back to Blog\n      </a>\n      <h1 class=\"font-display text-3xl text-slate-900 lg:text-4xl dark:text-white\">{title}</h1>\n      {\n        (formattedDate || author) && (\n          <div class=\"mt-4 flex items-center gap-3 text-sm text-slate-500 dark:text-slate-400\">\n            {formattedDate && (\n              <time class=\"font-medium\">{formattedDate}</time>\n            )}\n            {formattedDate && author && (\n              <span class=\"text-slate-300 dark:text-slate-600\">&middot;</span>\n            )}\n            {author && <span>by {author}</span>}\n          </div>\n        )\n      }\n    </header>\n    <div class=\"prose\">\n      <slot />\n    </div>\n  </article>\n</BaseLayout>\n"
  },
  {
    "path": "website/src/layouts/DocsLayout.astro",
    "content": "---\nimport '@/styles/global.css';\nimport { ViewTransitions } from 'astro:transitions';\nimport Footer from '@/components/Footer.astro';\nimport Navbar from '@/components/Navbar.astro';\nimport Sidebar from '@/components/Sidebar.astro';\nimport TableOfContents from '@/components/TableOfContents.astro';\n\ninterface Props {\n  title: string;\n  description?: string;\n  sourceFile?: string;\n  wide?: boolean;\n  headings?: Array<{\n    depth: number;\n    slug: string;\n    text: string;\n  }>;\n}\n\nconst {\n  title,\n  description = 'The fast, flexible & elegant library for parsing and manipulating HTML and XML.',\n  sourceFile,\n  wide = false,\n  headings = [],\n} = Astro.props;\n\nconst editUrl = sourceFile\n  ? `https://github.com/cheeriojs/cheerio/edit/main/website/src/content/docs/${sourceFile}`\n  : null;\n\nconst currentPath = Astro.url.pathname;\n\n// Flat ordered list of all doc pages for prev/next navigation\nconst allPages = [\n  { label: 'Introduction', href: '/docs/intro' },\n  { label: 'Loading Documents', href: '/docs/basics/loading' },\n  { label: 'Selecting Elements', href: '/docs/basics/selecting' },\n  { label: 'Traversing the DOM', href: '/docs/basics/traversing' },\n  { label: 'Manipulating Elements', href: '/docs/basics/manipulation' },\n  { label: 'Configuring Cheerio', href: '/docs/advanced/configuring-cheerio' },\n  { label: 'Extending Cheerio', href: '/docs/advanced/extending-cheerio' },\n  { label: 'Extracting Data', href: '/docs/advanced/extract' },\n  { label: 'API Documentation', href: '/docs/api' },\n];\n\nconst currentIndex = allPages.findIndex(\n  (p) => p.href === currentPath.replace(/\\/$/, ''),\n);\nconst prevPage = currentIndex > 0 ? allPages[currentIndex - 1] : null;\nconst nextPage =\n  currentIndex >= 0 && currentIndex < allPages.length - 1\n    ? allPages[currentIndex + 1]\n    : null;\n---\n\n<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <meta name=\"description\" content={description} />\n    <meta\n      name=\"keywords\"\n      content=\"htmlparser, jquery, selector, scraper, parser, dom, xml, html, nodejs\"\n    />\n    <link rel=\"icon\" type=\"image/x-icon\" href=\"/img/favicon.ico\" />\n    <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\" />\n    <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin />\n    <link href=\"https://fonts.googleapis.com/css2?family=DM+Serif+Display&family=JetBrains+Mono:wght@400;500&display=swap\" rel=\"stylesheet\" />\n    <title>{title} | Cheerio</title>\n    <ViewTransitions />\n  </head>\n  <body\n    class=\"flex min-h-screen flex-col bg-white text-slate-900 dark:bg-slate-900 dark:text-slate-100\"\n  >\n    <Navbar />\n    <div class=\"mx-auto flex w-full max-w-screen-2xl flex-1\">\n      <Sidebar currentPath={currentPath} />\n      <main class=\"flex-1 min-w-0 px-6 py-10 lg:px-10\">\n        {/* Mobile sidebar trigger */}\n        <button\n          type=\"button\"\n          id=\"sidebar-open\"\n          class=\"mb-6 inline-flex items-center gap-2 rounded-lg border border-slate-200 px-3 py-2 text-sm font-medium text-slate-600 hover:bg-slate-100 lg:hidden dark:border-slate-700 dark:text-slate-400 dark:hover:bg-slate-800\"\n          aria-label=\"Open navigation\"\n        >\n          <svg class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n            <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5\" />\n          </svg>\n          Menu\n        </button>\n\n        {/* Editorial header */}\n        <header class=\"mb-8 border-b border-slate-200 pb-6 dark:border-slate-700/50\">\n          <h1 class=\"font-display text-3xl text-slate-900 lg:text-4xl dark:text-white\">{title}</h1>\n        </header>\n\n        <article class:list={['prose docs-content', { 'prose-wide': wide }]}>\n          <slot />\n        </article>\n\n        {/* Edit on GitHub */}\n        {editUrl && (\n          <div class=\"mt-10 pt-5\">\n            <a\n              href={editUrl}\n              target=\"_blank\"\n              rel=\"noopener noreferrer\"\n              class=\"inline-flex items-center gap-1.5 text-sm text-slate-400 transition-colors hover:text-primary dark:text-slate-500 dark:hover:text-primary\"\n            >\n              <svg class=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n                <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 0 1 1.13-1.897L16.863 4.487Zm0 0L19.5 7.125\" />\n              </svg>\n              Edit this page on GitHub\n            </a>\n          </div>\n        )}\n\n        {/* Prev/Next navigation */}\n        {(prevPage || nextPage) && (\n          <nav class=\"mt-12 flex items-stretch gap-4 border-t border-slate-200 pt-6 dark:border-slate-700/50\" aria-label=\"Page navigation\">\n            {prevPage ? (\n              <a\n                href={prevPage.href}\n                class=\"group flex flex-1 flex-col items-start rounded-xl border border-slate-200 px-5 py-4 transition-all hover:border-primary/40 hover:shadow-sm dark:border-slate-700 dark:hover:border-primary/40\"\n              >\n                <span class=\"mb-1 text-xs font-medium uppercase tracking-wider text-slate-400 dark:text-slate-500\">Previous</span>\n                <span class=\"flex items-center gap-1.5 text-sm font-semibold text-slate-700 transition-colors group-hover:text-primary dark:text-slate-200 dark:group-hover:text-primary\">\n                  <svg class=\"h-3.5 w-3.5 transition-transform group-hover:-translate-x-0.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2.5\">\n                    <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18\" />\n                  </svg>\n                  {prevPage.label}\n                </span>\n              </a>\n            ) : <div class=\"flex-1\" />}\n            {nextPage ? (\n              <a\n                href={nextPage.href}\n                class=\"group flex flex-1 flex-col items-end rounded-xl border border-slate-200 px-5 py-4 transition-all hover:border-primary/40 hover:shadow-sm dark:border-slate-700 dark:hover:border-primary/40\"\n              >\n                <span class=\"mb-1 text-xs font-medium uppercase tracking-wider text-slate-400 dark:text-slate-500\">Next</span>\n                <span class=\"flex items-center gap-1.5 text-sm font-semibold text-slate-700 transition-colors group-hover:text-primary dark:text-slate-200 dark:group-hover:text-primary\">\n                  {nextPage.label}\n                  <svg class=\"h-3.5 w-3.5 transition-transform group-hover:translate-x-0.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2.5\">\n                    <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13.5 4.5L21 12m0 0l-7.5 7.5M21 12H3\" />\n                  </svg>\n                </span>\n              </a>\n            ) : <div class=\"flex-1\" />}\n          </nav>\n        )}\n      </main>\n      <TableOfContents headings={headings} />\n    </div>\n    <Footer />\n  </body>\n</html>\n"
  },
  {
    "path": "website/src/pages/blog/[slug].astro",
    "content": "---\nimport { getCollection, render } from 'astro:content';\nimport BlogLayout from '@/layouts/BlogLayout.astro';\n\nexport async function getStaticPaths() {\n  const posts = await getCollection('blog');\n  return posts.map((post) => ({\n    params: { slug: post.id.replace('.md', '').replace('.mdx', '') },\n    props: { post },\n  }));\n}\n\nconst { post } = Astro.props;\nconst { Content } = await render(post);\n\n// Extract date from filename (e.g., \"2024-08-07-version-1.md\")\nconst dateParts = post.id.split('-').slice(0, 3);\nconst date = new Date(dateParts.join('-'));\n\n// Get author name (simplified - in a real app you'd look this up)\nconst authorMap: Record<string, string> = {\n  fb55: 'Felix Boehm',\n};\nconst authorName = post.data.authors\n  ? Array.isArray(post.data.authors)\n    ? post.data.authors.map((a: string) => authorMap[a] || a).join(', ')\n    : authorMap[post.data.authors] || post.data.authors\n  : undefined;\n---\n\n<BlogLayout title={post.data.title} date={date} author={authorName}>\n  <Content />\n</BlogLayout>\n"
  },
  {
    "path": "website/src/pages/blog/index.astro",
    "content": "---\nimport { getCollection } from 'astro:content';\nimport BaseLayout from '@/layouts/BaseLayout.astro';\n\nconst posts = await getCollection('blog');\n\n// Sort posts by date (newest first)\nconst sortedPosts = posts.sort((a, b) => {\n  const dateA = new Date(a.id.split('-').slice(0, 3).join('-'));\n  const dateB = new Date(b.id.split('-').slice(0, 3).join('-'));\n  return dateB.getTime() - dateA.getTime();\n});\n\nfunction getPostDate(id: string): string {\n  const datePart = id.split('-').slice(0, 3).join('-');\n  return new Date(datePart).toLocaleDateString('en-US', {\n    year: 'numeric',\n    month: 'long',\n    day: 'numeric',\n  });\n}\n\nfunction getPostSlug(id: string): string {\n  return id.replace('.md', '').replace('.mdx', '');\n}\n\n// Extract excerpt from raw markdown content (content before <!--truncate-->)\nfunction getExcerpt(rawContent: string): string | null {\n  const truncateIndex = rawContent.indexOf('<!--truncate-->');\n  if (truncateIndex === -1) return null;\n\n  let excerpt = rawContent.slice(0, truncateIndex).trim();\n\n  // Remove frontmatter\n  const frontmatterEnd = excerpt.indexOf('---', 3);\n  if (excerpt.startsWith('---') && frontmatterEnd !== -1) {\n    excerpt = excerpt.slice(frontmatterEnd + 3).trim();\n  }\n\n  // Remove the first heading (usually the title, which duplicates frontmatter title)\n  excerpt = excerpt.replace(/^#\\s+.+\\n+/, '').trim();\n\n  // Remove admonition blocks (:::note, :::tip, etc.)\n  excerpt = excerpt.replace(/^:::\\w+(\\[.*?\\])?\\n[\\s\\S]*?^:::\\n*/gm, '').trim();\n\n  // Strip markdown formatting to plain text\n  excerpt = excerpt\n    .replace(/!\\[([^\\]]*)\\]\\([^)]+\\)/g, '') // ![alt](url) → remove\n    .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1') // [text](url) → text\n    .replace(/(\\*\\*|__)(.*?)\\1/g, '$2') // **bold** → bold\n    .replace(/(\\*|_)(.*?)\\1/g, '$2') // *italic* → italic\n    .replace(/`([^`]+)`/g, '$1') // `code` → code\n    .replace(/^#{1,6}\\s+/gm, '') // headings → text\n    .replace(/^[-*+]\\s+/gm, '') // list items\n    .replace(/^\\d+\\.\\s+/gm, '') // ordered list items\n    .replace(/\\n{2,}/g, ' ') // collapse newlines\n    .replace(/\\n/g, ' ') // remaining newlines\n    .trim();\n\n  return excerpt || null;\n}\n\n// Pre-render excerpts for each post\nconst postsWithExcerpts = sortedPosts.map((post) => {\n  const excerpt = getExcerpt(post.body || '');\n  return { post, excerpt };\n});\n---\n\n<BaseLayout title=\"Blog\">\n  {/* Hero header */}\n  <section class=\"relative overflow-hidden border-b border-slate-200 bg-gradient-to-b from-amber-50/60 to-white dark:border-slate-800 dark:from-slate-900 dark:to-slate-900\">\n    <div class=\"mx-auto max-w-4xl px-6 py-16 text-center\">\n      <h1 class=\"font-display text-4xl text-slate-900 lg:text-5xl dark:text-white\">Blog</h1>\n      <p class=\"mt-4 text-lg text-slate-500 dark:text-slate-400\">\n        Updates and announcements from the Cheerio team.\n      </p>\n      <a\n        href=\"/blog/rss.xml\"\n        class=\"mt-6 inline-flex items-center gap-2 rounded-full border border-slate-200 bg-white px-4 py-2 text-sm font-medium text-slate-600 shadow-sm transition-all hover:border-primary/40 hover:text-primary dark:border-slate-700 dark:bg-slate-800 dark:text-slate-400 dark:hover:border-primary/40 dark:hover:text-primary\"\n      >\n        <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\" class=\"h-4 w-4\">\n          <path d=\"M3.75 3a.75.75 0 0 0-.75.75v.5c0 .414.336.75.75.75H4c6.075 0 11 4.925 11 11v.25c0 .414.336.75.75.75h.5a.75.75 0 0 0 .75-.75V16C17 8.82 11.18 3 4 3h-.25Z\" />\n          <path d=\"M3 8.75A.75.75 0 0 1 3.75 8H4a8 8 0 0 1 8 8v.25a.75.75 0 0 1-.75.75h-.5a.75.75 0 0 1-.75-.75V16a6 6 0 0 0-6-6h-.25A.75.75 0 0 1 3 9.25v-.5ZM7 15a2 2 0 1 1-4 0 2 2 0 0 1 4 0Z\" />\n        </svg>\n        Subscribe via RSS\n      </a>\n    </div>\n  </section>\n\n  {/* Posts grid */}\n  <div class=\"mx-auto max-w-4xl px-6 py-12\">\n    <div class=\"space-y-8\">\n      {\n        postsWithExcerpts.map(({ post, excerpt }) => (\n          <article class=\"group rounded-2xl border border-slate-200 bg-white p-6 shadow-sm transition-all duration-200 hover:border-primary/30 hover:shadow-lg dark:border-slate-700/80 dark:bg-slate-800/50 dark:hover:border-primary/30\">\n            <div class=\"mb-3 flex items-center gap-3 text-sm text-slate-500 dark:text-slate-400\">\n              <time class=\"font-medium\">{getPostDate(post.id)}</time>\n              {post.data.tags && (\n                <div class=\"flex gap-2\">\n                  {post.data.tags.map((tag: string) => (\n                    <span class=\"rounded-full bg-primary/10 px-2.5 py-0.5 text-xs font-medium text-primary\">\n                      {tag}\n                    </span>\n                  ))}\n                </div>\n              )}\n            </div>\n            <h2 class=\"mb-2 font-heading text-xl font-semibold lg:text-2xl\">\n              <a\n                href={`/blog/${getPostSlug(post.id)}`}\n                class=\"text-slate-900 transition-colors hover:text-primary dark:text-white dark:hover:text-primary\"\n              >\n                {post.data.title}\n              </a>\n            </h2>\n            {excerpt && (\n              <p class=\"mb-4 line-clamp-3 text-slate-600 dark:text-slate-400\">{excerpt}</p>\n            )}\n            <a\n              href={`/blog/${getPostSlug(post.id)}`}\n              class=\"inline-flex items-center gap-1 text-sm font-medium text-primary transition-colors hover:text-primary-dark\"\n            >\n              Read more\n              <svg class=\"h-3.5 w-3.5 transition-transform group-hover:translate-x-0.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2.5\">\n                <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13.5 4.5L21 12m0 0l-7.5 7.5M21 12H3\" />\n              </svg>\n            </a>\n          </article>\n        ))\n      }\n    </div>\n  </div>\n</BaseLayout>\n"
  },
  {
    "path": "website/src/pages/blog/rss.xml.ts",
    "content": "import { getCollection } from 'astro:content';\nimport rss from '@astrojs/rss';\nimport type { APIContext } from 'astro';\nimport { marked } from 'marked';\n\nexport async function GET(context: APIContext) {\n  const posts = await getCollection('blog');\n\n  // Sort posts by date (newest first)\n  const sortedPosts = posts.toSorted((a, b) => {\n    const dateA = new Date(a.id.split('-').slice(0, 3).join('-'));\n    const dateB = new Date(b.id.split('-').slice(0, 3).join('-'));\n    return dateB.getTime() - dateA.getTime();\n  });\n\n  if (!context.site) {\n    throw new Error('Site URL is required for RSS feed generation');\n  }\n\n  return rss({\n    title: 'Cheerio Blog',\n    description: 'Updates and announcements from the Cheerio team.',\n    site: context.site,\n    items: sortedPosts.map((post) => {\n      const datePart = post.id.split('-').slice(0, 3).join('-');\n      const slug = post.id.replace('.md', '').replace('.mdx', '');\n\n      // Render markdown body to HTML\n      const content = post.body ? marked.parse(post.body) : '';\n\n      return {\n        title: post.data.title,\n        pubDate: new Date(datePart),\n        link: `/blog/${slug}/`,\n        content: content as string,\n      };\n    }),\n  });\n}\n"
  },
  {
    "path": "website/src/pages/docs/[slug].astro",
    "content": "---\nimport { getCollection, render } from 'astro:content';\nimport DocsLayout from '@/layouts/DocsLayout.astro';\n\nexport async function getStaticPaths() {\n  const docs = await getCollection('docs');\n  return docs\n    .filter((doc) => !doc.id.includes('/'))\n    .map((doc) => ({\n      params: { slug: doc.id.replace(/\\.mdx?$/, '') },\n      props: { doc },\n    }));\n}\n\nconst { doc } = Astro.props;\nconst { Content, headings } = await render(doc);\nconst h1 = headings.find((h) => h.depth === 1);\nconst title = doc.data.title || h1?.text || doc.data.sidebar_label || doc.id;\n---\n\n<DocsLayout title={title} description={doc.data.description} headings={headings} sourceFile={doc.id}>\n  <Content />\n</DocsLayout>\n"
  },
  {
    "path": "website/src/pages/docs/advanced/[slug].astro",
    "content": "---\nimport { getCollection, render } from 'astro:content';\nimport DocsLayout from '@/layouts/DocsLayout.astro';\n\nexport async function getStaticPaths() {\n  const docs = await getCollection('docs');\n  return docs\n    .filter((doc) => doc.id.startsWith('advanced/'))\n    .map((doc) => ({\n      params: { slug: doc.id.replace('advanced/', '').replace(/\\.mdx?$/, '') },\n      props: { doc },\n    }));\n}\n\nconst { doc } = Astro.props;\nconst { Content, headings } = await render(doc);\nconst h1 = headings.find((h) => h.depth === 1);\nconst title = doc.data.title || h1?.text || doc.data.sidebar_label || doc.id;\n---\n\n<DocsLayout title={title} description={doc.data.description} headings={headings} sourceFile={doc.id}>\n  <Content />\n</DocsLayout>\n"
  },
  {
    "path": "website/src/pages/docs/api/[...slug].astro",
    "content": "---\nimport { getCollection, render } from 'astro:content';\nimport DocsLayout from '@/layouts/DocsLayout.astro';\n\nexport async function getStaticPaths() {\n  const docs = await getCollection('docs');\n  return docs\n    .filter((doc) => doc.id.startsWith('api/'))\n    .map((doc) => {\n      // Remove 'api/' prefix and file extension\n      const rawSlug = doc.id.replace('api/', '').replace(/\\.mdx?$/, '');\n      return {\n        // Use undefined slug for index page (catch-all [...slug] root)\n        params: { slug: rawSlug === 'index' ? undefined : rawSlug },\n        props: { doc },\n      };\n    });\n}\n\nconst { doc } = Astro.props;\nconst { Content, headings } = await render(doc);\nconst h1 = headings.find((h) => h.depth === 1);\nconst title =\n  doc.data.title || h1?.text || doc.data.sidebar_label || 'API Documentation';\n---\n\n<DocsLayout title={title} description={doc.data.description} headings={headings} sourceFile={doc.id} wide>\n  <Content />\n</DocsLayout>\n"
  },
  {
    "path": "website/src/pages/docs/basics/[slug].astro",
    "content": "---\nimport { getCollection, render } from 'astro:content';\nimport DocsLayout from '@/layouts/DocsLayout.astro';\n\nexport async function getStaticPaths() {\n  const docs = await getCollection('docs');\n  return docs\n    .filter((doc) => doc.id.startsWith('basics/'))\n    .map((doc) => ({\n      params: { slug: doc.id.replace('basics/', '').replace(/\\.mdx?$/, '') },\n      props: { doc },\n    }));\n}\n\nconst { doc } = Astro.props;\nconst { Content, headings } = await render(doc);\nconst h1 = headings.find((h) => h.depth === 1);\nconst title = doc.data.title || h1?.text || doc.data.sidebar_label || doc.id;\n---\n\n<DocsLayout title={title} description={doc.data.description} headings={headings} sourceFile={doc.id}>\n  <Content />\n</DocsLayout>\n"
  },
  {
    "path": "website/src/pages/index.astro",
    "content": "---\nimport Features from '@/components/Features.astro';\nimport Hero from '@/components/Hero.astro';\nimport Sponsors from '@/components/Sponsors.astro';\nimport Testimonials from '@/components/Testimonials.astro';\nimport BaseLayout from '@/layouts/BaseLayout.astro';\n---\n\n<BaseLayout title=\"The industry standard for working with HTML in JavaScript\">\n  <Hero />\n  <Features />\n  <Sponsors />\n  <Testimonials />\n\n  <!-- Final CTA Section -->\n  <section class=\"relative overflow-hidden bg-slate-950 py-24\">\n    <div class=\"absolute inset-0 bg-[radial-gradient(ellipse_at_center,rgba(232,140,31,0.12),transparent_70%)]\"></div>\n    <div class=\"grain absolute inset-0 overflow-hidden opacity-40\"></div>\n    <div class=\"absolute top-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-primary/30 to-transparent\"></div>\n\n    <div class=\"relative z-10 mx-auto max-w-3xl px-6 text-center\">\n      <h2 class=\"animate-fade-up font-display text-4xl text-white md:text-5xl\">\n        Ready to get started?\n      </h2>\n      <p class=\"animate-fade-up animation-delay-100 mt-6 text-lg leading-relaxed text-slate-400\">\n        Join thousands of developers who use Cheerio to parse, manipulate, and render HTML with ease.\n      </p>\n      <div class=\"animate-fade-up animation-delay-200 mt-10 flex flex-col items-center justify-center gap-4 sm:flex-row\">\n        <a\n          href=\"/docs/intro\"\n          class=\"group inline-flex items-center gap-2 rounded-xl bg-primary px-8 py-3.5 font-semibold text-white transition-all duration-300 hover:bg-primary-dark hover:shadow-lg hover:shadow-primary/25\"\n        >\n          Read the docs\n          <svg class=\"h-4 w-4 transition-transform duration-300 group-hover:translate-x-0.5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2.5\">\n            <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13.5 4.5L21 12m0 0l-7.5 7.5M21 12H3\" />\n          </svg>\n        </a>\n        <a\n          href=\"https://github.com/cheeriojs/cheerio\"\n          target=\"_blank\"\n          rel=\"noopener noreferrer\"\n          class=\"inline-flex items-center gap-2 rounded-xl border border-slate-700 px-8 py-3.5 font-semibold text-slate-300 transition-all duration-300 hover:border-slate-500 hover:text-white hover:bg-slate-800/50\"\n        >\n          Star on GitHub\n        </a>\n      </div>\n    </div>\n  </section>\n</BaseLayout>\n"
  },
  {
    "path": "website/src/plugins/rehype-external-links.ts",
    "content": "import type { Element, Root } from 'hast';\nimport { visit } from 'unist-util-visit';\n\nfunction visitExternalLink(node: Element): void {\n  if (\n    node.tagName === 'a' &&\n    node.properties?.href &&\n    typeof node.properties.href === 'string'\n  ) {\n    const { href } = node.properties;\n    // Check if it's an external link (starts with http:// or https://)\n    if (href.startsWith('http://') || href.startsWith('https://')) {\n      node.properties.target = '_blank';\n      node.properties.rel = 'noopener noreferrer';\n    }\n  }\n}\n\nfunction transformer(tree: Root): void {\n  visit(tree, 'element', visitExternalLink);\n}\n\n/**\n * Rehype plugin to make external links open in a new tab. Adds target=\"_blank\"\n * and rel=\"noopener noreferrer\" to external links.\n *\n * @returns A transformer function.\n */\nexport function rehypeExternalLinks() {\n  return transformer;\n}\n"
  },
  {
    "path": "website/src/plugins/remark-admonitions.ts",
    "content": "import type { Root } from 'mdast';\nimport { visit } from 'unist-util-visit';\n\ninterface DirectiveData {\n  hName?: string;\n  hProperties?: Record<string, string>;\n  directiveLabel?: boolean;\n}\n\ninterface DirectiveNode {\n  type: string;\n  name: string;\n  data?: DirectiveData;\n  children: DirectiveChild[];\n}\n\ninterface DirectiveChild {\n  type: string;\n  value?: string;\n  data?: DirectiveData;\n  children?: DirectiveChild[];\n}\n\nconst ADMONITION_TYPES = ['note', 'tip', 'warning', 'danger', 'info'] as const;\n\nfunction visitAdmonition(node: unknown): void {\n  const directive = node as DirectiveNode;\n  if (directive.type === 'containerDirective') {\n    if (\n      !ADMONITION_TYPES.includes(\n        directive.name as (typeof ADMONITION_TYPES)[number],\n      )\n    ) {\n      return;\n    }\n\n    directive.data ??= {};\n    const { data } = directive;\n\n    /*\n     * Get title from the directive label or use default\n     * e.g., :::tip Title Here or just :::tip\n     */\n    let title =\n      directive.name.charAt(0).toUpperCase() + directive.name.slice(1);\n\n    // Check if there's a custom title in the first text\n    const firstChild = directive.children[0];\n    if (firstChild?.data?.directiveLabel) {\n      title = firstChild.children?.[0]?.value ?? title;\n      // Remove the label paragraph from children\n      directive.children.shift();\n    }\n\n    data.hName = 'div';\n    data.hProperties = {\n      class: `admonition admonition-${directive.name}`,\n      'data-type': directive.name,\n    };\n\n    // Prepend a title element\n    directive.children.unshift({\n      type: 'paragraph',\n      data: {\n        hName: 'p',\n        hProperties: { class: 'admonition-title' },\n      },\n      children: [{ type: 'text', value: title }],\n    });\n  }\n}\n\nfunction transformer(tree: Root): void {\n  visit(tree, visitAdmonition);\n}\n\n/**\n * Remark plugin to transform Docusaurus-style admonitions (:::note, :::tip,\n * etc.) into custom HTML elements that can be styled with Tailwind.\n *\n * @returns A transformer function.\n */\nexport function remarkAdmonitions() {\n  return transformer;\n}\n"
  },
  {
    "path": "website/src/plugins/remark-fix-typedoc-links.ts",
    "content": "import type { Link, Root } from 'mdast';\nimport { visit } from 'unist-util-visit';\n\nconst markdownExtensionRe = /\\.md$/;\n\nfunction visitTypedocLink(node: Link): void {\n  if (typeof node.url === 'string' && node.url.startsWith('/docs/api/')) {\n    // Remove .md extension from API doc links\n    node.url = node.url.replace(markdownExtensionRe, '');\n  }\n}\n\nfunction transformer(tree: Root): void {\n  visit(tree, 'link', visitTypedocLink);\n}\n\n/**\n * Remark plugin to fix typedoc-generated links. Removes .md extension from\n * internal API doc links.\n *\n * @returns A transformer function.\n */\nexport function remarkFixTypedocLinks() {\n  return transformer;\n}\n"
  },
  {
    "path": "website/src/plugins/remark-live-code.ts",
    "content": "import type { Code, Parent, Root } from 'mdast';\nimport { visit } from 'unist-util-visit';\n\ninterface MdxJsxAttribute {\n  type: 'mdxJsxAttribute';\n  name: string;\n  value: string | null;\n}\n\ninterface MdxJsxFlowElement {\n  type: 'mdxJsxFlowElement';\n  name: string;\n  attributes: MdxJsxAttribute[];\n  children: unknown[];\n}\n\nfunction visitLiveCode(\n  node: Code,\n  index: number | undefined,\n  parent: Parent | undefined,\n): void {\n  // Check if the code block has 'live' in its meta\n  if (node.meta?.includes('live') && index !== undefined && parent) {\n    // Transform the code node into an MDX JSX element\n    const code = node.value;\n\n    /*\n     * Create an mdxJsxFlowElement node for the LiveCode component\n     * with client:visible for lazy hydration\n     */\n    const jsxNode: MdxJsxFlowElement = {\n      type: 'mdxJsxFlowElement',\n      name: 'LiveCode',\n      attributes: [\n        {\n          type: 'mdxJsxAttribute',\n          name: 'code',\n          value: code,\n        },\n        {\n          type: 'mdxJsxAttribute',\n          name: 'client:visible',\n          value: null,\n        },\n      ],\n      children: [],\n    };\n\n    // Replace the code node with the JSX node\n    parent.children.splice(index, 1, jsxNode as unknown as Code);\n  }\n}\n\nfunction transformer(tree: Root): void {\n  visit(tree, 'code', visitLiveCode);\n}\n\n/**\n * Remark plugin to transform code blocks with 'live' meta into LiveCode\n * components.\n *\n * Usage in markdown:\n *\n * ```js\n * const $ = cheerio.load('<h1>Hello</h1>');\n * return <>{$('h1').text()}</>;\n * ```\n *\n * @returns A transformer function.\n */\nexport function remarkLiveCode() {\n  return transformer;\n}\n"
  },
  {
    "path": "website/src/styles/global.css",
    "content": "@import \"tailwindcss\";\n\n@font-face {\n  font-family: \"Inter\";\n  font-style: normal;\n  font-weight: 600;\n  src: url(\"/fonts/inter.woff\") format(\"woff\");\n  unicode-range:\n    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,\n    U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,\n    U+FFFD;\n}\n\n@font-face {\n  font-family: \"Rubik\";\n  font-style: normal;\n  font-weight: 400;\n  src: url(\"/fonts/rubik.woff\") format(\"woff\");\n  unicode-range:\n    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,\n    U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,\n    U+FFFD;\n}\n\n@theme {\n  --color-primary: #e88c1f;\n  --color-primary-dark: #d07418;\n  --color-primary-light: #f0a040;\n  --color-primary-lightest: #f5c376;\n  --color-amber-50: #fffbeb;\n  --color-amber-100: #fef3c7;\n  --color-amber-900: #78350f;\n  --color-amber-950: #451a03;\n\n  --font-sans:\n    \"Rubik\", system-ui, -apple-system, \"Segoe UI\", Roboto, Ubuntu, Cantarell,\n    \"Noto Sans\", sans-serif;\n  --font-heading: \"Inter\", var(--font-sans);\n  --font-display: \"DM Serif Display\", Georgia, \"Times New Roman\", serif;\n  --font-mono:\n    \"JetBrains Mono\", \"Fira Code\", \"Cascadia Code\", Consolas, monospace;\n}\n\nhtml {\n  scroll-behavior: smooth;\n}\n\nbody {\n  font-family: var(--font-sans);\n}\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n  font-family: var(--font-heading);\n  font-weight: 600;\n}\n\n/* ─── Landing page animations ─── */\n\n@keyframes fade-up {\n  from {\n    opacity: 0;\n    transform: translateY(24px);\n  }\n  to {\n    opacity: 1;\n    transform: translateY(0);\n  }\n}\n\n@keyframes fade-in {\n  from {\n    opacity: 0;\n  }\n  to {\n    opacity: 1;\n  }\n}\n\n@keyframes slide-in-right {\n  from {\n    opacity: 0;\n    transform: translateX(32px);\n  }\n  to {\n    opacity: 1;\n    transform: translateX(0);\n  }\n}\n\n@keyframes float {\n  0%,\n  100% {\n    transform: translateY(0);\n  }\n  50% {\n    transform: translateY(-8px);\n  }\n}\n\n@keyframes pulse-glow {\n  0%,\n  100% {\n    box-shadow: 0 0 20px rgba(232, 140, 31, 0.15);\n  }\n  50% {\n    box-shadow: 0 0 40px rgba(232, 140, 31, 0.3);\n  }\n}\n\n@keyframes grain {\n  0%,\n  100% {\n    transform: translate(0, 0);\n  }\n  10% {\n    transform: translate(-5%, -10%);\n  }\n  30% {\n    transform: translate(3%, -15%);\n  }\n  50% {\n    transform: translate(12%, 9%);\n  }\n  70% {\n    transform: translate(9%, 4%);\n  }\n  90% {\n    transform: translate(-1%, 7%);\n  }\n}\n\n.animate-fade-up {\n  animation: fade-up 0.7s cubic-bezier(0.22, 1, 0.36, 1) both;\n}\n\n.animate-fade-in {\n  animation: fade-in 0.6s ease both;\n}\n\n.animate-slide-in-right {\n  animation: slide-in-right 0.7s cubic-bezier(0.22, 1, 0.36, 1) both;\n}\n\n.animate-float {\n  animation: float 6s ease-in-out infinite;\n}\n\n.animation-delay-100 {\n  animation-delay: 100ms;\n}\n.animation-delay-200 {\n  animation-delay: 200ms;\n}\n.animation-delay-300 {\n  animation-delay: 300ms;\n}\n.animation-delay-400 {\n  animation-delay: 400ms;\n}\n.animation-delay-500 {\n  animation-delay: 500ms;\n}\n.animation-delay-600 {\n  animation-delay: 600ms;\n}\n.animation-delay-700 {\n  animation-delay: 700ms;\n}\n.animation-delay-800 {\n  animation-delay: 800ms;\n}\n\n/* Grain texture overlay */\n.grain::before {\n  content: \"\";\n  position: absolute;\n  inset: -50%;\n  width: 200%;\n  height: 200%;\n  background-image: url(\"data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.04'/%3E%3C/svg%3E\");\n  background-size: 128px 128px;\n  pointer-events: none;\n  animation: grain 8s steps(10) infinite;\n  z-index: 1;\n}\n\n/* ─── Prose styles for documentation ─── */\n.prose {\n  max-width: 65ch;\n}\n\n/* API docs need more room for tables */\n.prose-wide {\n  max-width: 90ch;\n}\n\n/* Hide the first h1 in docs content since the editorial header already shows it */\n.docs-content > h1:first-child {\n  display: none;\n}\n\n.prose h1,\n.prose h2,\n.prose h3,\n.prose h4 {\n  font-family: var(--font-heading);\n  font-weight: 600;\n  margin-top: 1.5em;\n  margin-bottom: 0.5em;\n}\n\n.prose h1 {\n  font-size: 2.25rem;\n}\n\n.prose h2 {\n  font-size: 1.75rem;\n}\n\n.prose h3 {\n  font-size: 1.375rem;\n}\n\n.prose p {\n  margin-top: 1em;\n  margin-bottom: 1em;\n}\n\n.prose a {\n  color: var(--color-primary);\n  text-decoration: underline;\n}\n\n.prose a:hover {\n  color: var(--color-primary-dark);\n}\n\n.prose code {\n  background-color: #f1f5f9;\n  padding: 0.125rem 0.25rem;\n  border-radius: 0.25rem;\n  font-size: 0.875em;\n}\n\n.prose pre {\n  background-color: #1e293b;\n  color: #e2e8f0;\n  padding: 1rem;\n  border-radius: 0.5rem;\n  overflow-x: auto;\n  margin: 1rem 0;\n}\n\n.prose pre code {\n  background-color: transparent;\n  padding: 0;\n  font-size: 0.875rem;\n}\n\n.prose ul,\n.prose ol {\n  margin-top: 1em;\n  margin-bottom: 1em;\n  padding-left: 1.5em;\n}\n\n.prose ul {\n  list-style-type: disc;\n}\n\n.prose ol {\n  list-style-type: decimal;\n}\n\n.prose li {\n  margin-top: 0.25em;\n  margin-bottom: 0.25em;\n}\n\n.prose blockquote {\n  border-left: 4px solid var(--color-primary);\n  padding-left: 1rem;\n  margin: 1rem 0;\n  color: #64748b;\n  font-style: italic;\n}\n\n/* Table styles */\n.prose table {\n  width: 100%;\n  border-collapse: collapse;\n  margin: 1.5rem 0;\n  font-size: 0.875rem;\n  line-height: 1.5;\n  overflow: hidden;\n  border-radius: 0.5rem;\n  border: 1px solid #e2e8f0;\n}\n\n.prose thead {\n  background-color: #f8fafc;\n  border-bottom: 2px solid #e2e8f0;\n}\n\n.prose th {\n  font-family: var(--font-heading);\n  font-weight: 600;\n  font-size: 0.75rem;\n  letter-spacing: 0.05em;\n  text-transform: uppercase;\n  color: #64748b;\n  text-align: left;\n  padding: 0.75rem 1rem;\n}\n\n.prose td {\n  padding: 0.75rem 1rem;\n  border-top: 1px solid #f1f5f9;\n  vertical-align: top;\n  color: #475569;\n}\n\n.prose tbody tr:hover {\n  background-color: #f8fafc;\n}\n\n.prose td:first-child {\n  font-weight: 500;\n  white-space: nowrap;\n}\n\n/* Table link styling */\n.prose td a {\n  text-decoration: none;\n  font-weight: 500;\n}\n\n.prose td a:hover {\n  text-decoration: underline;\n}\n\n/* Inline code within tables — smaller */\n.prose td code,\n.prose th code {\n  font-size: 0.8125rem;\n  padding: 0.1rem 0.3rem;\n}\n\n/* Admonition styles */\n.admonition {\n  padding: 1rem;\n  border-radius: 0.5rem;\n  margin: 1.5rem 0;\n}\n\n.admonition-title {\n  font-weight: 600;\n  /* biome-ignore lint/complexity/noImportantStyles: Override Starlight defaults */\n  margin: 0 0 0.5rem 0 !important;\n  text-transform: uppercase;\n  font-size: 0.75rem;\n  letter-spacing: 0.05em;\n}\n\n.admonition-note {\n  background-color: #eff6ff;\n  border-left: 4px solid #3b82f6;\n}\n\n.admonition-note .admonition-title {\n  color: #1d4ed8;\n}\n\n.admonition-tip {\n  background-color: #f0fdf4;\n  border-left: 4px solid #22c55e;\n}\n\n.admonition-tip .admonition-title {\n  color: #15803d;\n}\n\n.admonition-info {\n  background-color: #f0f9ff;\n  border-left: 4px solid #0ea5e9;\n}\n\n.admonition-info .admonition-title {\n  color: #0369a1;\n}\n\n.admonition-warning {\n  background-color: #fefce8;\n  border-left: 4px solid #eab308;\n}\n\n.admonition-warning .admonition-title {\n  color: #a16207;\n}\n\n.admonition-danger {\n  background-color: #fef2f2;\n  border-left: 4px solid #ef4444;\n}\n\n.admonition-danger .admonition-title {\n  color: #b91c1c;\n}\n\n.admonition p:last-child {\n  margin-bottom: 0;\n}\n\n.admonition code {\n  background-color: rgba(0, 0, 0, 0.1);\n}\n\n/* Dark mode */\n@media (prefers-color-scheme: dark) {\n  :root {\n    --color-primary: #f4a02f;\n    --color-primary-dark: #f39313;\n    --color-primary-light: #f5ad4b;\n  }\n\n  .prose code {\n    background-color: #334155;\n    color: #e2e8f0;\n  }\n\n  .prose table {\n    border-color: #334155;\n  }\n\n  .prose thead {\n    background-color: #1e293b;\n    border-bottom-color: #334155;\n  }\n\n  .prose th {\n    color: #94a3b8;\n  }\n\n  .prose td {\n    border-top-color: #1e293b;\n    color: #cbd5e1;\n  }\n\n  .prose tbody tr:hover {\n    background-color: #1e293b;\n  }\n\n  .admonition-note {\n    background-color: #1e3a5f;\n  }\n\n  .admonition-note .admonition-title {\n    color: #60a5fa;\n  }\n\n  .admonition-tip {\n    background-color: #14532d;\n  }\n\n  .admonition-tip .admonition-title {\n    color: #4ade80;\n  }\n\n  .admonition-info {\n    background-color: #0c4a6e;\n  }\n\n  .admonition-info .admonition-title {\n    color: #38bdf8;\n  }\n\n  .admonition-warning {\n    background-color: #422006;\n  }\n\n  .admonition-warning .admonition-title {\n    color: #fbbf24;\n  }\n\n  .admonition-danger {\n    background-color: #450a0a;\n  }\n\n  .admonition-danger .admonition-title {\n    color: #f87171;\n  }\n\n  .admonition code {\n    background-color: rgba(255, 255, 255, 0.1);\n  }\n}\n"
  },
  {
    "path": "website/tsconfig.json",
    "content": "{\n  \"extends\": \"astro/tsconfigs/strict\",\n  \"compilerOptions\": {\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@/*\": [\"src/*\"]\n    },\n    \"jsx\": \"react-jsx\",\n    \"jsxImportSource\": \"react\",\n    \"resolveJsonModule\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"esModuleInterop\": true\n  },\n  \"include\": [\"src\", \".astro\", \"*.json\", \"*.mjs\"]\n}\n"
  },
  {
    "path": "website/typedoc.json",
    "content": "{\n  \"$schema\": \"https://typedoc.org/schema.json\",\n  \"entryPoints\": [\"../src/index.ts\"],\n  \"tsconfig\": \"../tsconfig.typedoc.json\",\n  \"readme\": \"none\",\n  \"excludePrivate\": true,\n  \"excludeExternals\": true,\n  \"plugin\": [\"typedoc-plugin-markdown\", \"typedoc-plugin-mdn-links\"],\n  \"out\": \"src/content/docs/api\",\n  \"entryFileName\": \"index\",\n  \"router\": \"kind\",\n  \"publicPath\": \"/docs/api/\",\n  \"fileExtension\": \".md\",\n  \"hidePageHeader\": true,\n  \"hideBreadcrumbs\": true,\n  \"useCodeBlocks\": true,\n  \"expandObjects\": true,\n  \"parametersFormat\": \"table\",\n  \"enumMembersFormat\": \"table\",\n  \"typeDeclarationFormat\": \"table\",\n  \"indexFormat\": \"table\",\n  \"sanitizeComments\": true,\n  \"externalSymbolLinkMappings\": {\n    \"domhandler\": {\n      \"Document\": \"https://domhandler.js.org/classes/Document.html\",\n      \"Element\": \"https://domhandler.js.org/classes/Element.html\",\n      \"Node\": \"https://domhandler.js.org/classes/Node.html\",\n      \"ChildNode\": \"https://domhandler.js.org/types/ChildNode.html\",\n      \"ParentNode\": \"https://domhandler.js.org/types/ParentNode.html\",\n      \"AnyNode\": \"https://domhandler.js.org/types/AnyNode.html\"\n    },\n    \"parse5\": {\n      \"TreeAdapterTypeMap\": \"https://parse5.js.org/interfaces/parse5.TreeAdapterTypeMap.html\",\n      \"TreeAdapter\": \"https://parse5.js.org/interfaces/parse5.TreeAdapter.html\"\n    }\n  }\n}\n"
  }
]