{"id":15879978,"url":"https://github.com/axodotdev/axohtml","last_synced_at":"2025-04-05T16:11:01.756Z","repository":{"id":64970036,"uuid":"580150059","full_name":"axodotdev/axohtml","owner":"axodotdev","description":"👩‍💻 type-checked JSX for Rust","archived":false,"fork":false,"pushed_at":"2024-02-09T10:36:50.000Z","size":312,"stargazers_count":118,"open_issues_count":18,"forks_count":10,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-28T18:24:18.020Z","etag":null,"topics":["html","jsx","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/axodotdev.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-12-19T21:00:53.000Z","updated_at":"2025-02-27T21:51:55.000Z","dependencies_parsed_at":"2024-10-27T13:02:25.106Z","dependency_job_id":null,"html_url":"https://github.com/axodotdev/axohtml","commit_stats":{"total_commits":163,"total_committers":20,"mean_commits":8.15,"dds":0.5828220858895705,"last_synced_commit":"d2c0379311357f0e9cec591eeeae480f17eec39a"},"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/axodotdev%2Faxohtml","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/axodotdev%2Faxohtml/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/axodotdev%2Faxohtml/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/axodotdev%2Faxohtml/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/axodotdev","download_url":"https://codeload.github.com/axodotdev/axohtml/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247361695,"owners_count":20926643,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["html","jsx","rust"],"created_at":"2024-10-06T03:06:58.089Z","updated_at":"2025-04-05T16:11:01.725Z","avatar_url":"https://github.com/axodotdev.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"# axohtml\n\n[![Github Actions Rust](https://github.com/axodotdev/axohtml/actions/workflows/rust.yml/badge.svg)](https://github.com/axodotdev/axohtml/actions)\n[![crates.io](https://img.shields.io/crates/v/axohtml.svg)](https://crates.io/crates/axohtml)\n[![License: MPL 2.0](https://img.shields.io/badge/License-MPL_2.0-brightgreen.svg)](https://opensource.org/licenses/MPL-2.0)\n\nThis crate provides the `html!` macro for building fully type checked HTML\ndocuments inside your Rust code using roughly [JSX] compatible syntax. \n\nThis crate is a fork of the great [Bodil Stokke's] [typed-html] crate. Opted \nfor a fork instead of maintainership because not currently intending to use or\nmaintain the Wasm compatibility (for now).\n\n[Bodil Stokke's]: https://github.com/bodil\n[typed-html]: https://github.com/bodil/typed-html\n\n## Quick Preview\n\n```rust\nlet mut doc: DOMTree\u003cString\u003e = html!(\n    \u003chtml\u003e\n        \u003chead\u003e\n            \u003ctitle\u003e\"Hello Axo\"\u003c/title\u003e\n            \u003cmeta name=Metadata::Author content=\"Axo Developer Co.\"/\u003e\n        \u003c/head\u003e\n        \u003cbody\u003e\n            \u003ch1\u003e\"\u003eo_o\u003c\"\u003c/h1\u003e\n            \u003cp class=\"official\"\u003e\n                \"The tool company for tool companies\"\n            \u003c/p\u003e\n            { (0..3).map(|_| html!(\n                \u003cp class=\"emphasis\"\u003e\n                    \"\u003eo_o\u003c\"\n                \u003c/p\u003e\n            )) }\n            \u003cp class=\"citation-needed\"\u003e\n                \"Every company should be a developer experience company.\"\n            \u003c/p\u003e\n        \u003c/body\u003e\n    \u003c/html\u003e\n);\nlet doc_str = doc.to_string();\n```\n\n## Syntax\n\nThis macro largely follows [JSX] syntax, but with some differences:\n\n* Text nodes must be quoted, because there's only so much Rust's tokeniser can\n  handle outside string literals. So, instead of `\u003cp\u003eHello\u003c/p\u003e`, you need to\n  write `\u003cp\u003e\"Hello\"\u003c/p\u003e`. (The parser will throw an error asking you to do this\n  if you forget.)\n* Element attributes will accept simple Rust expressions, but the parser has\n  its limits, as it's not a full Rust parser. You can use literals,\n  variables, dotted properties, type constructors and single function or\n  method calls. If you use something the parser isn't currently capable of\n  handling, it will complain. You can put braces or parentheses around the\n  expression if the parser doesn't understand\n  it. You can use any Rust code inside a brace or parenthesis block.\n\n## Valid HTML5\n\nThe macro will only accept valid HTML5 tags, with no tags or attributes marked\nexperimental or obsolete. If it won't accept something you want it to accept, we\ncan discuss it over a pull request (experimental tags and attributes, in\nparticular, are mostly omitted just for brevity, and you're welcome to implement\nthem).\n\nThe structure validation is simplistic by necessity, as it defers to the type\nsystem: a few elements will have one or more required children, and any element\nwhich accepts children will have a restriction on the type of the children,\nusually a broad group as defined by the HTML spec. Many elements have\nrestrictions on children of children, or require a particular ordering of\noptional elements, which isn't currently validated.\n\n## Attribute Values\n\nBrace blocks in the attribute value position should return the expected type for\nthe attribute. The type checker will complain if you return an unsupported type.\nYou can also use literals or a few simple Rust expressions as attribute values\n(see the Syntax section above).\n\nThe `html!` macro will add an [`.into()`][Into::into] call to the value\nexpression, so that you can use any type that has an [`Into\u003cA\u003e`][Into] trait\ndefined for the actual attribute type `A`.\n\nAs a special case, if you use a string literal, the macro will instead use the\n[`FromStr\u003cA\u003e`][FromStr] trait to try and parse the string literal into the\nexpected type. This is extremely useful for eg. CSS classes, letting you type\n`class=\"css-class-1 css-class-2\"` instead of going to the trouble of\nconstructing a [`SpacedSet\u003cClass\u003e`][SpacedSet]. The big caveat for this:\ncurrently, the macro is not able to validate the string at compile time, and the\nconversion will panic at runtime if the string is invalid.\n\n### Example\n\n```rust\nlet classList: SpacedSet\u003cClass\u003e = [\"foo\", \"bar\", \"baz\"].try_into()?;\nhtml!(\n    \u003cdiv\u003e\n        \u003cdiv class=\"foo bar baz\" /\u003e         // parses a string literal\n        \u003cdiv class=[\"foo\", \"bar\", \"baz\"] /\u003e // uses From\u003c[\u0026str, \u0026str, \u0026str]\u003e\n        \u003cdiv class=classList /\u003e             // uses a variable in scope\n        \u003cdiv class={                        // evaluates a code block\n            SpacedSet::try_from([\"foo\", \"bar\", \"baz\"])?\n        } /\u003e\n    \u003c/div\u003e\n)\n```\n\n## Generated Nodes\n\nBrace blocks in the child node position are expected to return an\n[`IntoIterator`][IntoIterator] of [`DOMTree`][DOMTree]s. You can return single\nelements or text nodes, as they both implement `IntoIterator` for themselves.\nThe macro will consume this iterator at runtime and insert the generated nodes\nas children in the expected position.\n\n### Example\n\n```rust\nhtml!(\n    \u003cul\u003e\n        { (1..=5).map(|i| html!(\n            \u003cli\u003e{ text!(\"{}\", i) }\u003c/li\u003e\n        )) }\n    \u003c/ul\u003e\n)\n```\n\n## Rendering\n\nYou have two options for actually producing something useful from the DOM tree\nthat comes out of the macro.\n\n### Render to a string\n\nThe DOM tree data structure implements [`Display`][Display], so you can call\n[`to_string()`][to_string] on it to render it to a [`String`][String]. If you\nplan to do this, the type of the tree should be [`DOMTree\u003cString\u003e`][DOMTree] to\nensure you're not using any event handlers that can't be printed.\n\n```rust\nlet doc: DOMTree\u003cString\u003e = html!(\n    \u003cp\u003e\"Hello Axo\"\u003c/p\u003e\n);\nlet doc_str = doc.to_string();\nassert_eq!(\"\u003cp\u003eHello Axo\u003c/p\u003e\", doc_str);\n```\n\n### Render to a virtual DOM\n\nThe DOM tree structure also implements a method called `vnode()`, which renders\nthe tree to a tree of [`VNode`][VNode]s, which is a mirror of the generated tree\nwith every attribute value rendered into `String`s. You can walk this virtual\nDOM tree and pass it on to your favourite virtual DOM system.\n\n## License\n\n\nThis software is subject to the terms of the Mozilla Public License, v. 2.0. If\na copy of the MPL was not distributed with this file, You can obtain one at\n\u003chttp://mozilla.org/MPL/2.0/\u003e.\n\nCopyright 2018 Bodil Stokke, 2022 Axo Developer Co.\n\n[JSX]: https://reactjs.org/docs/introducing-jsx.html\n[Display]: https://doc.rust-lang.org/std/fmt/trait.Display.html\n[String]: https://doc.rust-lang.org/std/string/struct.String.html\n[to_string]: https://doc.rust-lang.org/std/string/trait.ToString.html#tymethod.to_string\n[VNode]: https://docs.rs/axohtml/latest/axohtml/dom/enum.VNode.html\n[FromStr]: https://doc.rust-lang.org/std/str/trait.FromStr.html\n[SpacedSet]: https://docs.rs/axohtml/latest/axohtml/types/struct.SpacedSet.html\n[IntoIterator]: https://doc.rust-lang.org/std/iter/trait.IntoIterator.html\n[Into]: https://doc.rust-lang.org/std/convert/trait.Into.html\n[Into::into]: https://doc.rust-lang.org/std/convert/trait.Into.html#method.into\n[DOMTree]: https://docs.rs/axohtml/latest/axohtml/dom/type.DOMTree.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faxodotdev%2Faxohtml","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faxodotdev%2Faxohtml","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faxodotdev%2Faxohtml/lists"}