{"id":28146380,"url":"https://github.com/rs-tml/rstml","last_synced_at":"2025-05-14T23:13:06.564Z","repository":{"id":155241945,"uuid":"629982154","full_name":"rs-tml/rstml","owner":"rs-tml","description":"Rust+html (JSX-like) parser for TokenStreams aka rsx","archived":false,"fork":true,"pushed_at":"2025-02-24T10:04:40.000Z","size":945,"stargazers_count":109,"open_issues_count":14,"forks_count":11,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-10T15:56:30.622Z","etag":null,"topics":["html","jsx","parser","proc-macro","recoverable","rsx","rust","syn","templating","xml"],"latest_commit_sha":null,"homepage":"https://crates.io/crates/rstml","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"stoically/syn-rsx","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rs-tml.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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}},"created_at":"2023-04-19T12:26:25.000Z","updated_at":"2025-05-02T15:02:55.000Z","dependencies_parsed_at":"2023-10-11T14:07:32.468Z","dependency_job_id":null,"html_url":"https://github.com/rs-tml/rstml","commit_stats":null,"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rs-tml%2Frstml","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rs-tml%2Frstml/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rs-tml%2Frstml/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rs-tml%2Frstml/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rs-tml","download_url":"https://codeload.github.com/rs-tml/rstml/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254243313,"owners_count":22038046,"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","parser","proc-macro","recoverable","rsx","rust","syn","templating","xml"],"created_at":"2025-05-14T23:12:31.872Z","updated_at":"2025-05-14T23:13:06.498Z","avatar_url":"https://github.com/rs-tml.png","language":"Rust","readme":"# rstml\n\n[![crates.io page](https://img.shields.io/crates/v/rstml.svg)](https://crates.io/crates/rstml)\n[![docs.rs page](https://docs.rs/rstml/badge.svg)](https://docs.rs/rstml/)\n[![codecov](https://codecov.io/gh/rs-tml/rstml/branch/main/graph/badge.svg?token=2LMJ8YEV92)](https://codecov.io/gh/rs-tml/rs-tml)\n![build](https://github.com/rs-tml/rstml/workflows/ci/badge.svg)\n![license: MIT](https://img.shields.io/crates/l/rstml.svg)\nRust templating for XML-based formats (HTML, SVG, MathML) implemented on top of `proc-macro::TokenStream`s.\nSimilar to JSX but for Rust (commonly named RSX).The parsed result is a nested `Node` structure, similar to the browser DOM, where node name and value are syn expressions to support building proc macros.\n\nThe fork of original [syn-rsx](https://github.com/stoically/syn-rsx) repo.\nIt was created because of various reasons:\n- The author of original `syn-rsx` [have passed away](https://github.com/stoically/temporary-containers/issues/618).\n- Syn v2 was released and `syn-rsx` need to be mooved to new version.\n- The idea of [lossless parsing](https://github.com/stoically/syn-rsx/issues/53) was left unattended.\n- [Unquoted text](https://github.com/stoically/syn-rsx/issues/2) feature should advance.\n- Interest in recoverable parsing and better IDE support.\n\n\nSee [comparsion](/comparsion-with-syn-rsx.md) for more detail.\n\n```rust\nuse std::convert::TryFrom;\n\nuse eyre::bail;\nuse quote::quote;\nuse rstml::{\n    node::{Node, NodeAttribute, NodeElement, NodeText},\n    parse2,\n};\n\n// Create HTML `TokenStream`.\nlet tokens = quote! { \u003chello world\u003e\"hi\"\u003c/hello\u003e };\n\n// Parse the tokens into a tree of `Node`s.\nlet nodes = parse2(tokens)?;\n\n// Extract some specific nodes from the tree.\nlet Node::Element(element) = \u0026nodes[0] else {\n    bail!(\"element\")\n};\nlet NodeAttribute::Attribute(attribute) = \u0026element.attributes()[0] else {\n    bail!(\"attribute\")\n};\nlet Node::Text(text) = \u0026element.children[0] else {\n    bail!(\"text\")\n};\n\n// Work with the nodes.\nassert_eq!(element.name().to_string(), \"hello\");\nassert_eq!(attribute.key.to_string(), \"world\");\nassert_eq!(text.value_string(), \"hi\");\n```\n\n## Powered by rstml\n\n- [html-to-string-macro](https://github.com/rs-tml/rstml/tree/main/examples/html-to-string-macro) - basic example of \nrstml usage that uses `format!` macro to stringify html.\n- [html-node](https://github.com/vidhanio/html-node) - more powerfull version of `html-to-string` macro that convert html representation to [Rust types](https://docs.rs/html-node/latest/html_node/enum.Node.html), which can be used to runtime introspection. Each of this type has `Display` and `Debug` \nimplementation and therefore can be used to pretty print html node.\n- [leptos](https://github.com/leptos-rs/leptos) - framework for web application. `Rstml` is used inside `view`/`template` macros and for `hot-reload` feature.\n- [leptosfmt](https://github.com/bram209/leptosfmt) - a wrapper of rustfmt tool for `leptos` which can format html/xml code inside `view` macros.\n- [sauron](https://github.com/ivanceras/sauron) - a versatile web framework and library for building client-side and/or server-side web application.\n  \n\n## Features\n\n- **Not opinionated**\n\n  Every tag or attribute name is valid\n\n  ```html\n  \u003chello world /\u003e\n  ```\n\n- **Text nodes**\n\n\n  ```html\n  \u003cdiv\u003e\"String literal\"\u003c/div\u003e\n  ```\n\n- **Unquoted text nodes**\n\n  Unquoted text is supported with few limitations:\n  - Only valid Rust TokenStream can be unquoted text (no single quote text is supported, no unclosed braces, etc.)\n  - Unquoted text not always can save spaces. It uses [`Span::source_text`] and [`Span::join`] to retrive info about spaces, and it is not always available.\n  - Quoted text near unquoted treated as diferent Node, end library user should decide whenever to preserve quotation.\n\n  ```html\n  \u003cdiv\u003e Some string that is valid rust token stream \u003c/div\u003e\n  ```\n\n- **Node names separated by dash, colon or double colon**\n\n  ```html\n  \u003ctag-name some:attribute-key=\"value\" /\u003e\n  \u003ctag::name attribute::key=\"value\" /\u003e\n  ```\n\n- **Node names with reserved keywords**\n\n  ```html\n  \u003cinput type=\"submit\" /\u003e\n  ```\n\n- **Doctypes, Comments and Fragments**\n\n  ```html\n  \u003c!DOCTYPE html\u003e\n  \u003c!-- \"comment\" --\u003e\n  \u003c\u003e\u003c/\u003e\n  ```\n\n- **Braced blocks are parsed as arbitrary Rust code**\n\n  ```html\n  \u003c{ let block = \"in node name position\"; } /\u003e\n  \u003cdiv\u003e{ let block = \"in node position\"; }\u003c/div\u003e\n  \u003cdiv { let block = \"in attribute position\"; } /\u003e\n  \u003cdiv key={ let block = \"in attribute value position\"; } /\u003e\n  ```\n\n- **Attribute values can be any valid syn expression without requiring braces**\n\n  ```html\n  \u003cdiv key=some::value() /\u003e\n  ```\n\n- **Helpful error reporting out of the box**\n\n  ```rust,no-run\n  error: open tag has no corresponding close tag and is not self-closing\n  --\u003e examples/html-to-string-macro/tests/lib.rs:5:24\n    |\n  5 |     html_to_string! { \u003cdiv\u003e };\n    |                        ^^^\n  ```\n\n- **Possibility to get the span for a whole node**\n\n  This can be used to improve error reporting, e.g.\n\n  ```rust,no-run\n  error: Invalid element\n  --\u003e examples/src/main.rs:14:13\n     |\n  14 | /             \u003cdiv\u003e\n  15 | |                 \"invalid node for some consumer specific reason\"\n  16 | |             \u003c/div\u003e\n     | |__________________^\n     \n  ```\n\n- **Recoverable parser**\n\n  Can parse html with multiple mistakes.\n  As result library user get array of errors that can be reported, and tree of nodes that was parsed.\n\n  ```html\n   \u003cdiv hello={world.} /\u003e \u003c!-- dot after world is invalid syn expression --\u003e\n    \u003c\u003e\n        \u003cdiv\u003e\"1\"\u003c/x\u003e \u003c!-- incorrect closed tag --\u003e\n        \u003cdiv\u003e\"2\"\u003c/div\u003e\n        \u003cdiv\u003e\"3\"\u003c/div\u003e\n        \u003cdiv {\"some-attribute-from-rust-block\"}/\u003e\n    \u003c/\u003e\n  ```\n\n  Using this feature one can write macro in IDE friendly way.\n  This macro will work faster (because on invalid syntax it change output slightly, instead of removing it completely, so IDE can check diff quicly). And give completion (goto definition, and other semantic related feature) more often.\n\n- **Customization**\n\n  A `ParserConfig` to customize parsing behavior is available, so if you have\n  slightly different requirements for parsing and it's not yet customizable\n  feel free to open an issue or pull request to extend the configuration.\n\n  One highlight with regards to customization is the `transform_block`\n  configuration, which takes a closure that receives raw block content as\n  `ParseStream` and lets you optionally convert it to a `TokenStream`. That makes it\n  possible to have custom syntax in blocks. More details in [#9]\n\n[`syn`]: https://github.com/dtolnay/syn\n[`Span::join`]: https://doc.rust-lang.org/proc_macro/struct.Span.html#method.join\n[`Span::source_text`]: https://doc.rust-lang.org/proc_macro/struct.Span.html#method.source_text\n[`tokenstream`]: https://doc.rust-lang.org/proc_macro/struct.TokenStream.html\n[html-to-string-macro example]: https://github.com/rs-tml/rstml/tree/main/examples/html-to-string-macro\n[#9]: https://github.com/stoically/syn-rsx/issues/9\n","funding_links":[],"categories":["Rust"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frs-tml%2Frstml","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frs-tml%2Frstml","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frs-tml%2Frstml/lists"}