{"id":47457356,"url":"https://github.com/OrlovEvgeny/zigquery","last_synced_at":"2026-04-07T08:01:17.289Z","repository":{"id":343756636,"uuid":"1177285483","full_name":"OrlovEvgeny/zigquery","owner":"OrlovEvgeny","description":"Zig HTML parser and CSS selector engine for DOM querying and manipulation","archived":false,"fork":false,"pushed_at":"2026-03-11T16:32:38.000Z","size":45,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-03-11T21:53:00.294Z","etag":null,"topics":["dom-parser","html","parser","zig","zig-library","zig-package"],"latest_commit_sha":null,"homepage":"","language":"Zig","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/OrlovEvgeny.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"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-03-09T22:06:34.000Z","updated_at":"2026-03-11T20:50:06.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/OrlovEvgeny/zigquery","commit_stats":null,"previous_names":["orlovevgeny/zigquery"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/OrlovEvgeny/zigquery","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OrlovEvgeny%2Fzigquery","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OrlovEvgeny%2Fzigquery/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OrlovEvgeny%2Fzigquery/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OrlovEvgeny%2Fzigquery/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/OrlovEvgeny","download_url":"https://codeload.github.com/OrlovEvgeny/zigquery/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OrlovEvgeny%2Fzigquery/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31504897,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-07T03:10:19.677Z","status":"ssl_error","status_checked_at":"2026-04-07T03:10:13.982Z","response_time":105,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["dom-parser","html","parser","zig","zig-library","zig-package"],"created_at":"2026-03-24T00:00:35.806Z","updated_at":"2026-04-07T08:01:17.276Z","avatar_url":"https://github.com/OrlovEvgeny.png","language":"Zig","readme":"# zigquery\n\n[![CI](https://github.com/OrlovEvgeny/zigquery/actions/workflows/ci.yml/badge.svg)](https://github.com/OrlovEvgeny/zigquery/actions/workflows/ci.yml)\n[![Release](https://img.shields.io/github/v/release/OrlovEvgeny/zigquery)](https://github.com/OrlovEvgeny/zigquery/releases/latest)\n[![Zig](https://img.shields.io/badge/Zig-0.15.2-f7a41d?logo=zig)](https://ziglang.org/)\n\njQuery-like HTML DOM manipulation library for Zig. Parse HTML, query elements with CSS selectors, traverse the tree, and manipulate the document\n## Quick start\n\n```zig\nconst std = @import(\"std\");\nconst zq = @import(\"zigquery\");\n\npub fn main() !void {\n    const allocator = std.heap.page_allocator;\n\n    var doc = try zq.Document.initFromSlice(allocator,\n        \\\\\u003chtml\u003e\n        \\\\  \u003cbody\u003e\n        \\\\    \u003cdiv class=\"content\"\u003e\n        \\\\      \u003ch1\u003eHello\u003c/h1\u003e\n        \\\\      \u003cp\u003eFirst paragraph\u003c/p\u003e\n        \\\\      \u003cp\u003eSecond paragraph\u003c/p\u003e\n        \\\\      \u003ca href=\"/about\" class=\"active\"\u003eAbout\u003c/a\u003e\n        \\\\    \u003c/div\u003e\n        \\\\  \u003c/body\u003e\n        \\\\\u003c/html\u003e\n    );\n    defer doc.deinit();\n\n    // Find all paragraphs.\n    const paragraphs = try doc.find(\"p\");\n    std.debug.print(\"Found {} paragraphs\\n\", .{paragraphs.len()});\n\n    // Get text content.\n    const title = try (try doc.find(\"h1\")).text();\n    std.debug.print(\"Title: {s}\\n\", .{title});\n\n    // Read attributes.\n    const link = try doc.find(\"a.active\");\n    const href = link.attr(\"href\") orelse \"\";\n    std.debug.print(\"Link: {s}\\n\", .{href});\n}\n```\n\n## Installation\n\nAdd zigquery as a dependency in your `build.zig.zon`:\n\n```sh\nzig fetch --save git+https://github.com/evgenyorlov/zigquery\n```\n\nThen in your `build.zig`:\n\n```zig\nconst zigquery = b.dependency(\"zigquery\", .{\n    .target = target,\n    .optimize = optimize,\n});\nmodule.addImport(\"zigquery\", zigquery.module(\"zigquery\"));\n```\n\nRequires **Zig 0.15.2** or later.\n\n## CSS selectors\n\nSupported selector syntax:\n\n| Selector | Example | Description |\n|---|---|---|\n| Type | `div`, `p`, `a` | Match by tag name |\n| Class | `.active`, `.foo.bar` | Match by class (compound supported) |\n| ID | `#main` | Match by ID |\n| Universal | `*` | Match any element |\n| Attribute | `[href]`, `[type=\"text\"]` | Attribute existence / value |\n| Attribute operators | `[class~=\"foo\"]`, `[lang\\|=\"en\"]`, `[href^=\"/\"]`, `[src$=\".png\"]`, `[data*=\"val\"]` | Includes, dash-match, prefix, suffix, substring |\n| Descendant | `div p` | `p` anywhere inside `div` |\n| Child | `div \u003e p` | Direct child only |\n| Adjacent sibling | `h1 + p` | Immediately after |\n| General sibling | `h1 ~ p` | Any sibling after |\n| Group | `h1, h2, h3` | Match any in the list |\n| Negation | `:not(.hidden)` | Exclude matches |\n| `:has()` | `div:has(\u003e p)` | Parent has matching descendant |\n| `:contains()` | `p:contains(\"hello\")` | Element contains text |\n| `:first-child`, `:last-child`, `:only-child` | `li:first-child` | Structural pseudo-classes |\n| `:first-of-type`, `:last-of-type`, `:only-of-type` | `p:first-of-type` | Type-based structural pseudo-classes |\n| `:nth-child()`, `:nth-last-child()` | `tr:nth-child(2n+1)` | Positional with `an+b` formula |\n| `:nth-of-type()`, `:nth-last-of-type()` | `p:nth-of-type(odd)` | Type-positional |\n| `:empty`, `:root` | `div:empty` | Content / root pseudo-classes |\n| `:enabled`, `:disabled`, `:checked` | `input:enabled` | Form pseudo-classes |\n\n## API overview\n\n### Document\n\n```zig\n// Parse HTML into a document. All allocations use an internal arena.\nvar doc = try zq.Document.initFromSlice(allocator, html);\ndefer doc.deinit();\n\n// Query from the document root.\nconst sel = try doc.find(\"div.content\");\n\n// Deep-clone the entire document.\nvar copy = try doc.clone(allocator);\ndefer copy.deinit();\n```\n\n### Selection — Traversal\n\n```zig\nconst sel = try doc.find(\"div\");\n\n// Descendants matching a selector.\nconst links = try sel.find(\"a\");\n\n// Direct children.\nconst kids = sel.children();\nconst filtered_kids = try sel.childrenFiltered(\"p\");\n\n// Parents.\nconst p = sel.parent();\nconst all_parents = sel.parents();\nconst until = sel.parentsUntil(.{ .selector = \"body\" });\n\n// Closest ancestor (or self) matching a selector.\nconst wrapper = try sel.closest(\".wrapper\");\n\n// Siblings.\nconst sibs = sel.siblings();\nconst next_el = sel.next();\nconst prev_all = sel.prevAll();\nconst next_until = sel.nextUntil(.{ .selector = \"hr\" });\n```\n\n### Selection — Filtering\n\n```zig\nconst items = try doc.find(\"li\");\n\nconst active = try items.filter(\".active\");\nconst inactive = try items.not(\".active\");\nconst with_links = try items.has(\"a\");\n\nconst first = items.first();\nconst last = items.last();\nconst third = items.eq(2);        // zero-based\nconst from_end = items.eq(-1);    // negative indexes from end\nconst middle = items.sliceRange(1, 3);\n\n// Boolean checks.\nconst is_active = items.is(\".active\");\n```\n\n### Selection — Properties\n\n```zig\nconst el = try doc.find(\"a.nav\");\n\n// Attributes.\nconst href = el.attr(\"href\");\nconst title = el.attrOr(\"title\", \"default\");\nel.setAttr(\"target\", \"_blank\");\nel.removeAttr(\"rel\");\n\n// Classes.\nel.addClass(\"highlight bold\");\nel.removeClass(\"nav\");\nel.toggleClass(\"active\");\nconst has = el.hasClass(\"highlight\");\n\n// Content.\nconst inner = try el.html();\nconst outer = try zq.outerHtml(el);\nconst txt = try el.text();\nconst name = zq.nodeName(el);\n```\n\n### Selection — Manipulation\n\n```zig\nconst div = try doc.find(\"div\");\n\n// Insert content.\ndiv.appendHtml(\"\u003cp\u003eappended\u003c/p\u003e\");\ndiv.prependHtml(\"\u003cp\u003eprepended\u003c/p\u003e\");\n\n// Insert around selection.\nconst p = try doc.find(\"p\");\np.afterHtml(\"\u003chr/\u003e\");\np.beforeHtml(\"\u003c!-- marker --\u003e\");\n\n// Replace and remove.\n_ = p.replaceWithHtml(\"\u003cdiv\u003ereplaced\u003c/div\u003e\");\n_ = p.remove();\n_ = div.empty();   // remove all children\n\n// Set content.\ndiv.setHtml(\"\u003cb\u003enew content\u003c/b\u003e\");\ndiv.setText(\"plain text\");\n\n// Wrap / unwrap.\ndiv.wrapHtml(\"\u003csection\u003e\u003c/section\u003e\");\ndiv.unwrap();\n```\n\n### Selection — Iteration\n\n```zig\nconst rows = try doc.find(\"tr\");\n\n// Iterator (idiomatic Zig).\nvar it = rows.iterator();\nwhile (it.next()) |row| {\n    const cells = try row.find(\"td\");\n    // ...\n}\n\n// Callback-based.\nrows.each(struct {\n    fn f(i: usize, sel: zq.Selection) void {\n        _ = i;\n        _ = sel;\n    }\n}.f);\n```\n\n### Selection — Set operations\n\n```zig\nconst a = try doc.find(\".foo\");\nconst b = try doc.find(\".bar\");\n\nconst combined = try a.add(\".bar\");\nconst merged = a.addSelection(b);\nconst union_sel = a.@\"union\"(b);\nconst common = a.intersection(b);\n```\n\n## Running tests\n\n```sh\nzig build test\n```\n\nInspired by Go's [goquery](https://github.com/PuerkitoBio/goquery) and, by extension, jQuery\n\n## License\n[MIT](LICENSE)\n","funding_links":[],"categories":["Language Essentials"],"sub_categories":["Parser Library"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FOrlovEvgeny%2Fzigquery","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FOrlovEvgeny%2Fzigquery","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FOrlovEvgeny%2Fzigquery/lists"}