{"id":51232201,"url":"https://github.com/xcrap-dev/html-parser","last_synced_at":"2026-06-28T17:02:13.545Z","repository":{"id":339543757,"uuid":"1162306300","full_name":"xcrap-dev/html-parser","owner":"xcrap-dev","description":"Xcrap HTML Parser is an experimental library written in Rust, built with the NAPI-RS framework for compatibility with Node.js. Its goal is to be fast, lightweight, and support both CSS and XPath queries. Designed for the Xcrap framework ecosystem — but not limited to it — it natively provides query options and limits on processed elements.","archived":false,"fork":false,"pushed_at":"2026-02-22T00:05:05.000Z","size":1173,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-03-12T14:52:33.031Z","etag":null,"topics":["fast","html","javascript","napi-rs","nodejs","parser","rust","typescript","xcrap"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@xcrap/html-parser","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xcrap-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-20T05:13:03.000Z","updated_at":"2026-02-22T14:55:24.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/xcrap-dev/html-parser","commit_stats":null,"previous_names":["xcrap-cloud/html-parser","xcrap-dev/html-parser"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/xcrap-dev/html-parser","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xcrap-dev%2Fhtml-parser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xcrap-dev%2Fhtml-parser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xcrap-dev%2Fhtml-parser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xcrap-dev%2Fhtml-parser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xcrap-dev","download_url":"https://codeload.github.com/xcrap-dev/html-parser/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xcrap-dev%2Fhtml-parser/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34896652,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-28T02:00:05.809Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fast","html","javascript","napi-rs","nodejs","parser","rust","typescript","xcrap"],"created_at":"2026-06-28T17:02:12.488Z","updated_at":"2026-06-28T17:02:13.533Z","avatar_url":"https://github.com/xcrap-dev.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# 🕷️ @xcrap/html-parser\n\n**A blazing-fast HTML parser for Node.js, powered by Rust and NAPI-RS**\n\n[![npm version](https://img.shields.io/npm/v/@xcrap/html-parser?style=flat-square\u0026color=e05d44)](https://www.npmjs.com/package/@xcrap/html-parser)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue?style=flat-square)](./LICENSE)\n[![Node.js \u003e= 18](https://img.shields.io/badge/Node.js-%3E%3D18-339933?style=flat-square\u0026logo=node.js)](https://nodejs.org)\n[![Built with Rust](https://img.shields.io/badge/Built%20with-Rust-orange?style=flat-square\u0026logo=rust)](https://www.rust-lang.org/)\n\n[@xcrap/html-parser](https://www.npmjs.com/package/@xcrap/html-parser) is an **experimental** HTML parsing library written in **Rust**, exposed to Node.js through the [NAPI-RS](https://napi.rs/) framework. It is designed to be **fast**, **lightweight**, and to support both **CSS selectors** and **XPath** queries — with built-in support for result limits and element nesting.\n\nAlthough part of the [Xcrap](https://github.com/Xcrap-Cloud) scraping ecosystem, this library can be used as a **standalone package** in any Node.js project.\n\n\u003c/div\u003e\n\n---\n\n## 📋 Table of Contents\n\n- [✨ Features](#-features)\n- [⚡ Performance](#-performance)\n- [📦 Installation](#-installation)\n- [🚀 Quick Start](#-quick-start)\n- [📖 API Reference](#-api-reference)\n  - [`HtmlParser` / `HTMLParser`](#htmlparser--htmlparser)\n  - [`HTMLElement`](#htmlelement)\n  - [`css()` and `xpath()`](#css-and-xpath)\n  - [Types](#types)\n- [🔍 Usage Examples](#-usage-examples)\n  - [CSS Selectors](#css-selectors)\n  - [XPath Queries](#xpath-queries)\n  - [Navigating Nested Elements](#navigating-nested-elements)\n  - [Working with Attributes](#working-with-attributes)\n- [🏗️ Architecture](#️-architecture)\n- [🛠️ Development](#️-development)\n- [🤝 Contributing](#-contributing)\n- [📝 License](#-license)\n\n---\n\n## ✨ Features\n\n- **⚡ Blazing Fast** — Core parsing done in Rust; significantly faster than JS-based parsers at instance initialization.\n- **🎯 Dual Query Support** — Query elements using both **CSS selectors** (via `scraper`) and **XPath** expressions (via `sxd-xpath`).\n- **🦥 Lazy Loading** — Internal CSS and XPath engines are only initialized when first needed, reducing unnecessary overhead.\n- **🔢 Built-in Limits** — Pass a `limit` option to `selectMany` to cap the number of returned elements.\n- **🌲 Element Traversal** — Navigate nested elements using `selectFirst` and `selectMany` directly on `HTMLElement` instances.\n- **🔒 Type-Safe** — Fully typed TypeScript declarations included (`index.d.ts`).\n- **🖥️ Platform Support** — Pre-built native binary currently available for **Windows x64** only. Other platforms require compilation from source (see [Development](#️-development)).\n\n---\n\n## ⚡ Performance\n\nBenchmarks below compare parser **initialization speed** (instantiation time per file):\n\n```\n@xcrap/html-parser    :  0.246214 ms/file  ±  0.136808  ✅ Fastest\nhtml-parser           : 36.825500 ms/file  ± 28.855100\nhtmljs-parser         :  0.501577 ms/file  ±  1.210800\nhtml-dom-parser       :  2.180280 ms/file  ±  1.796170\nhtml5parser           :  1.674640 ms/file  ±  1.222790\ncheerio               :  8.679980 ms/file  ±  6.328520\nparse5                :  4.821180 ms/file  ±  2.668220\nhtmlparser2           :  1.497390 ms/file  ±  1.398040\nhtmlparser            : 16.171200 ms/file  ± 109.076000\nhigh5                 :  2.982290 ms/file  ±  1.927480\nnode-html-parser      :  2.901670 ms/file  ±  1.908040\n```\n\n\u003e Benchmarks sourced from [node-html-parser repository](https://github.com/taoqf/node-html-parser).\n\nThe performance advantage comes from lazy loading: the internal `Html` (CSS engine) and `Package` (XPath engine) instances are only initialized on first use and reused across subsequent calls on the same parser instance.\n\n---\n\n## 📦 Installation\n\nInstall via your preferred package manager:\n\n```bash\n# npm\nnpm install @xcrap/html-parser\n\n# yarn\nyarn add @xcrap/html-parser\n\n# pnpm\npnpm add @xcrap/html-parser\n```\n\n**Requirements:**\n- Node.js **\u003e= 18.0.0**\n\nNative binaries are pre-built and distributed for the following platforms:\n\n| Platform         | Architecture | Support         |\n|------------------|--------------|-----------------|\n| Windows          | x64          | ✅ Pre-built    |\n| macOS            | x64          | 🔧 Build from source |\n| macOS            | ARM64        | 🔧 Build from source |\n| Linux            | x64 (GNU)    | 🔧 Build from source |\n\n\u003e **⚠️ Note:** Currently only the **Windows x64** binary is pre-built and included in the published package. Users on other platforms must compile the native addon locally — see the [Development](#️-development) section for instructions.\n\n---\n\n## 🚀 Quick Start\n\n```ts\nimport { HtmlParser, css, xpath } from \"@xcrap/html-parser\"\n\nconst html = `\n  \u003chtml\u003e\n    \u003cbody\u003e\n      \u003ch1 class=\"title\"\u003eHello World\u003c/h1\u003e\n      \u003cul\u003e\n        \u003cli class=\"item\"\u003eItem 1\u003c/li\u003e\n        \u003cli class=\"item\"\u003eItem 2\u003c/li\u003e\n        \u003cli class=\"item\"\u003eItem 3\u003c/li\u003e\n      \u003c/ul\u003e\n    \u003c/body\u003e\n  \u003c/html\u003e\n`\n\nconst parser = new HtmlParser(html)\n\n// Select a single element using a CSS selector\nconst heading = parser.selectFirst({ query: css(\"h1\") })\nconsole.log(heading?.text) // \"Hello World\"\n\n// Select multiple elements and limit results\nconst items = parser.selectMany({ query: css(\"li.item\"), limit: 2 })\nconsole.log(items.map(el =\u003e el.text)) // [\"Item 1\", \"Item 2\"]\n\n// Use XPath instead\nconst firstItem = parser.selectFirst({ query: xpath(\"//li[@class='item']\") })\nconsole.log(firstItem?.text) // \"Item 1\"\n```\n\n\u003e **CommonJS** is also fully supported via `require`:\n\u003e\n\u003e ```js\n\u003e const { parse, css, xpath } = require(\"@xcrap/html-parser\")\n\u003e const parser = parse(html)\n\u003e ```\n\n---\n\n## 📖 API Reference\n\n### `HtmlParser` / `HTMLParser`\n\nThe main entry point for parsing an HTML string. CSS and XPath engines are lazily initialized on first use and reused across subsequent queries.\n\n#### Constructor\n\n```ts\nnew HtmlParser(content: string): HtmlParser\n```\n\n| Parameter | Type     | Description                    |\n|-----------|----------|--------------------------------|\n| `content` | `string` | The raw HTML string to parse.  |\n\n\u003e **Alias:** You can also use the `parse(content: string)` function as a convenience wrapper:\n\u003e ```ts\n\u003e import { parse } from \"@xcrap/html-parser\"\n\u003e const parser = parse(html)\n\u003e ```\n\n#### `selectFirst(options)`\n\nSelects the **first** element matching the given query.\n\n```ts\nparser.selectFirst(options: SelectFirstOptions): HTMLElement | null\n```\n\n| Parameter        | Type              | Description                              |\n|------------------|-------------------|------------------------------------------|\n| `options.query`  | `QueryConfig`     | A query config built with `css()` or `xpath()`. |\n\nReturns `HTMLElement | null` — `null` if no element matches.\n\n#### `selectMany(options)`\n\nSelects **all** elements matching the given query.\n\n```ts\nparser.selectMany(options: SelectManyOptions): HTMLElement[]\n```\n\n| Parameter        | Type              | Description                              |\n|------------------|-------------------|------------------------------------------|\n| `options.query`  | `QueryConfig`     | A query config built with `css()` or `xpath()`. |\n| `options.limit`  | `number?`         | Optional. Maximum number of elements to return. Values `\u003c= 0` are ignored (returns all). |\n\nReturns `HTMLElement[]` — an empty array if no matches.\n\n---\n\n### `HTMLElement`\n\nRepresents a matched DOM element. Provides properties and methods to inspect and traverse its content.\n\n\u003e **Note:** `HTMLElement` instances also support `selectFirst` and `selectMany`, allowing scoped queries within a found element.\n\n#### Properties\n\n| Property     | Type                      | Description                                                        |\n|--------------|---------------------------|--------------------------------------------------------------------|\n| `outerHTML`  | `string`                  | The full HTML of the element, including its opening and closing tags. |\n| `innerHTML`  | `string` *(getter)*       | The inner HTML content (children only, excluding the element's own tags). |\n| `text`       | `string` *(getter)*       | The concatenated plain-text content of the element and its descendants. |\n| `id`         | `string \\| null` *(getter)* | The element's `id` attribute, or `null` if not present.           |\n| `tagName`    | `string` *(getter)*       | The element's tag name in **UPPERCASE** (e.g., `\"DIV\"`, `\"H1\"`).  |\n| `className`  | `string` *(getter)*       | The full `class` attribute string (e.g., `\"post featured\"`).       |\n| `classList`  | `string[]` *(getter)*     | An array of individual class names. Empty array if no class.       |\n| `attributes` | `Record\u003cstring, string\u003e` *(getter)* | All attributes as a key-value object.                  |\n| `firstChild` | `HTMLElement \\| null` *(getter)* | The first child element, or `null` if none.              |\n| `lastChild`  | `HTMLElement \\| null` *(getter)* | The last child element, or `null` if none.               |\n\n#### Methods\n\n##### `getAttribute(name)`\n\n```ts\nelement.getAttribute(name: string): string | null\n```\n\nReturns the value of the named attribute, or `null` if the attribute does not exist.\n\n##### `selectFirst(options)`\n\n```ts\nelement.selectFirst(options: SelectFirstOptions): HTMLElement | null\n```\n\nScoped version of `HtmlParser.selectFirst`. Searches **within** the current element.\n\n##### `selectMany(options)`\n\n```ts\nelement.selectMany(options: SelectManyOptions): HTMLElement[]\n```\n\nScoped version of `HtmlParser.selectMany`. Searches **within** the current element.\n\n##### `toString()`\n\n```ts\nelement.toString(): string\n```\n\nReturns the `outerHTML` string of the element.\n\n---\n\n### `css()` and `xpath()`\n\nHelper functions to create typed `QueryConfig` objects.\n\n```ts\ncss(query: string): QueryConfig\nxpath(query: string): QueryConfig\n```\n\nThese functions are the **recommended way** to build query configurations. They ensure the correct query type is set.\n\n```ts\nimport { css, xpath } from \"@xcrap/html-parser\"\n\ncss(\"article.post\")           // → { query: \"article.post\", type: QueryType.CSS }\nxpath(\"//article[@class]\")    // → { query: \"//article[@class]\", type: QueryType.XPath }\n```\n\n---\n\n### Types\n\n```ts\n// Identifies the query engine to use\nexport declare const enum QueryType {\n  CSS   = 0,\n  XPath = 1,\n}\n\n// Holds a raw query string and its associated engine type\nexport interface QueryConfig {\n  query: string\n  type: QueryType\n}\n\n// Options for single-element selection\nexport interface SelectFirstOptions {\n  query: QueryConfig\n}\n\n// Options for multi-element selection\nexport interface SelectManyOptions {\n  query: QueryConfig\n  limit?: number  // \u003c= 0 or undefined means no limit\n}\n```\n\n---\n\n## 🔍 Usage Examples\n\n### CSS Selectors\n\n```ts\nimport { HtmlParser, css } from \"@xcrap/html-parser\"\n\nconst html = `\n  \u003cmain\u003e\n    \u003carticle id=\"post-1\" class=\"post featured\" data-author=\"alice\"\u003e\n      \u003ch2 class=\"post-title\"\u003eFirst Post\u003c/h2\u003e\n      \u003cp class=\"excerpt\"\u003eA short description.\u003c/p\u003e\n    \u003c/article\u003e\n    \u003carticle id=\"post-2\" class=\"post\" data-author=\"bob\"\u003e\n      \u003ch2 class=\"post-title\"\u003eSecond Post\u003c/h2\u003e\n      \u003cp class=\"excerpt\"\u003eAnother description.\u003c/p\u003e\n    \u003c/article\u003e\n  \u003c/main\u003e\n`\n\nconst parser = new HtmlParser(html)\n\n// Select by tag name\nconst firstArticle = parser.selectFirst({ query: css(\"article\") })\nconsole.log(firstArticle?.id) // \"post-1\"\n\n// Select by class\nconst allPosts = parser.selectMany({ query: css(\".post\") })\nconsole.log(allPosts.length) // 2\n\n// Select by attribute\nconst featuredPost = parser.selectFirst({ query: css(\"[data-author='alice']\") })\nconsole.log(featuredPost?.getAttribute(\"data-author\")) // \"alice\"\n\n// Select with limit\nconst limited = parser.selectMany({ query: css(\"article\"), limit: 1 })\nconsole.log(limited.length) // 1\n```\n\n### XPath Queries\n\n```ts\nimport { HtmlParser, xpath } from \"@xcrap/html-parser\"\n\nconst html = `\n  \u003cul\u003e\n    \u003cli class=\"tag\"\u003erust\u003c/li\u003e\n    \u003cli class=\"tag\"\u003enapi\u003c/li\u003e\n    \u003cli class=\"tag\"\u003enodejs\u003c/li\u003e\n  \u003c/ul\u003e\n`\n\nconst parser = new HtmlParser(html)\n\n// Select all \u003cli\u003e with class \"tag\"\nconst tags = parser.selectMany({ query: xpath(\"//li[@class='tag']\") })\nconsole.log(tags.map(t =\u003e t.text)) // [\"rust\", \"napi\", \"nodejs\"]\n\n// Limit XPath results\nconst limited = parser.selectMany({ query: xpath(\"//li\"), limit: 2 })\nconsole.log(limited.length) // 2\n```\n\n### Navigating Nested Elements\n\n```ts\nimport { HtmlParser, css } from \"@xcrap/html-parser\"\n\nconst html = `\n  \u003cnav id=\"main-nav\"\u003e\n    \u003cul\u003e\n      \u003cli\u003e\u003ca href=\"/home\"\u003eHome\u003c/a\u003e\u003c/li\u003e\n      \u003cli\u003e\u003ca href=\"/about\"\u003eAbout\u003c/a\u003e\u003c/li\u003e\n      \u003cli\u003e\u003ca href=\"/contact\"\u003eContact\u003c/a\u003e\u003c/li\u003e\n    \u003c/ul\u003e\n  \u003c/nav\u003e\n`\n\nconst parser = new HtmlParser(html)\n\n// Find the nav, then narrow down inside it\nconst nav = parser.selectFirst({ query: css(\"#main-nav\") })\n\nif (nav) {\n  const links = nav.selectMany({ query: css(\"a\") })\n  links.forEach(link =\u003e {\n    console.log(`${link.text} → ${link.getAttribute(\"href\")}`)\n    // \"Home → /home\"\n    // \"About → /about\"\n    // \"Contact → /contact\"\n  })\n\n  // First and last child shortcuts\n  console.log(nav.firstChild?.tagName)  // \"UL\"\n  console.log(nav.lastChild?.tagName)   // \"UL\"\n}\n```\n\n### Working with Attributes\n\n```ts\nimport { HtmlParser, css } from \"@xcrap/html-parser\"\n\nconst html = `\n  \u003ca\n    id=\"cta\"\n    class=\"btn btn-primary\"\n    href=\"https://example.com\"\n    target=\"_blank\"\n    data-track=\"click\"\n  \u003e\n    Click here\n  \u003c/a\u003e\n`\n\nconst parser = new HtmlParser(html)\nconst link = parser.selectFirst({ query: css(\"a\") })\n\nif (link) {\n  console.log(link.id)                        // \"cta\"\n  console.log(link.tagName)                   // \"A\"\n  console.log(link.className)                 // \"btn btn-primary\"\n  console.log(link.classList)                 // [\"btn\", \"btn-primary\"]\n  console.log(link.getAttribute(\"href\"))      // \"https://example.com\"\n  console.log(link.getAttribute(\"target\"))    // \"_blank\"\n  console.log(link.getAttribute(\"missing\"))   // null\n  console.log(link.attributes)\n  // {\n  //   id: \"cta\",\n  //   class: \"btn btn-primary\",\n  //   href: \"https://example.com\",\n  //   target: \"_blank\",\n  //   \"data-track\": \"click\"\n  // }\n}\n```\n\n---\n\n## 🏗️ Architecture\n\nThe library is structured as a native Node.js addon written in Rust, bridged via [NAPI-RS](https://napi.rs/).\n\n```\nsrc/\n├── lib.rs             # Crate entry point; exposes the `parse()` function via NAPI\n├── parser.rs          # HTMLParser struct — lazy-loads CSS (scraper) and XPath (sxd) engines\n├── types.rs           # HTMLElement struct — all DOM properties and methods\n├── engines.rs         # Internal: select_first/many by CSS and XPath (pure Rust)\n└── query_builders.rs  # css() and xpath() helper functions exposed to JS\n```\n\n### Key Design Decisions\n\n- **Lazy Initialization**: `HTMLParser` holds `Option\u003cHtml\u003e` and `Option\u003cPackage\u003e` fields. Each engine is only allocated on first use and reused automatically, so calling `selectFirst` (CSS) and then `selectMany` (XPath) on the same parser creates only two parsing passes total — one per engine.\n\n- **Dual Engine**: CSS queries use the [`scraper`](https://crates.io/crates/scraper) crate; XPath queries use [`sxd-xpath`](https://crates.io/crates/sxd-xpath) with [`sxd_html`](https://crates.io/crates/sxd_html) for HTML→XML normalization.\n\n- **Zero-copy Approach**: Elements are represented by their `outerHTML` string, avoiding complex lifetime management across the FFI boundary.\n\n### Internal Rust Dependencies\n\n| Crate         | Version  | Role                                      |\n|---------------|----------|-------------------------------------------|\n| `napi`        | `3.0.0`  | NAPI-RS runtime for Node.js integration   |\n| `napi-derive` | `3.0.0`  | Procedural macros for NAPI bindings       |\n| `scraper`     | `0.25.0` | HTML parsing and CSS selector engine      |\n| `sxd-document`| `0.3.2`  | XML document model (used for XPath)       |\n| `sxd-xpath`   | `0.4.2`  | XPath expression evaluator                |\n| `sxd_html`    | `0.1.2`  | HTML → sxd document converter            |\n\n---\n\n## 🛠️ Development\n\n### Prerequisites\n\n- **Rust** (stable toolchain) — [Install](https://rustup.rs/)\n- **Node.js** \u003e= 18 — [Install](https://nodejs.org/)\n- **Yarn** \u003e= 4 — `npm install -g yarn`\n- **NAPI-RS CLI** — installed automatically via dev dependencies\n\n### Setup\n\n```bash\n# Clone the repository\ngit clone https://github.com/Xcrap-Cloud/html-parser.git\ncd html-parser\n\n# Install Node.js dependencies\nyarn install\n```\n\n### Building\n\n```bash\n# Build native addon in release mode\nyarn build\n\n# Build in debug mode (faster compilation, slower runtime)\nyarn build:debug\n```\n\nThe output binary (`html-parser.\u003cplatform\u003e.node`) will be placed in the project root.\n\n### Running Tests\n\n```bash\nyarn test\n```\n\nTests are written with [AVA](https://github.com/avajs/ava) and located in the `__test__/` directory.\n\n### Formatting\n\n```bash\n# Format all (TypeScript/JS, Rust, TOML)\nyarn format\n\n# Individual formatters\nyarn format:prettier   # Prettier for TS/JS/JSON/YAML/Markdown\nyarn format:rs         # cargo fmt for Rust\nyarn format:toml       # Taplo for TOML files\n```\n\n### Linting\n\n```bash\nyarn lint   # OXLint for TypeScript/JavaScript files\n```\n\n---\n\n## 🤝 Contributing\n\nContributions are welcome! Please follow these steps:\n\n1. **Fork** the repository.\n2. **Create a branch**: `git checkout -b feat/your-feature` or `git checkout -b fix/your-bug`.\n3. **Make your changes**, ensuring all tests pass: `yarn test`.\n4. **Format your code**: `yarn format`.\n5. **Commit** with a descriptive message: `git commit -m \"feat: add support for XYZ\"`.\n6. **Push** your branch: `git push origin feat/your-feature`.\n7. **Open a Pull Request** with a clear description of the changes.\n\nPlease see [CONTRIBUTING.md](./CONTRIBUTING.md) for detailed guidelines.\n\n---\n\n## 📝 License\n\nDistributed under the [MIT License](./LICENSE).  \n© [Marcuth](https://github.com/Marcuth) and contributors.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxcrap-dev%2Fhtml-parser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxcrap-dev%2Fhtml-parser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxcrap-dev%2Fhtml-parser/lists"}