{"id":18120109,"url":"https://github.com/stoically/syn-rsx","last_synced_at":"2025-04-04T14:07:40.128Z","repository":{"id":40488561,"uuid":"267828222","full_name":"stoically/syn-rsx","owner":"stoically","description":"syn-powered parser for JSX-like TokenStreams","archived":false,"fork":false,"pushed_at":"2023-05-24T15:51:33.000Z","size":231,"stargazers_count":186,"open_issues_count":17,"forks_count":27,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-03-28T13:09:33.376Z","etag":null,"topics":["html","jsx","macros","rsx","syn"],"latest_commit_sha":null,"homepage":"https://crates.io/crates/syn-rsx","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/stoically.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,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-05-29T10:15:36.000Z","updated_at":"2025-02-17T16:06:55.000Z","dependencies_parsed_at":"2024-06-19T01:48:34.149Z","dependency_job_id":"b6a60857-9ca5-4c70-a608-fac921b3782b","html_url":"https://github.com/stoically/syn-rsx","commit_stats":{"total_commits":161,"total_committers":4,"mean_commits":40.25,"dds":"0.11801242236024845","last_synced_commit":"149109f1420df7a11f5a69e4a9fb90bf57ec4f02"},"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stoically%2Fsyn-rsx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stoically%2Fsyn-rsx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stoically%2Fsyn-rsx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stoically%2Fsyn-rsx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stoically","download_url":"https://codeload.github.com/stoically/syn-rsx/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247190250,"owners_count":20898702,"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","macros","rsx","syn"],"created_at":"2024-11-01T05:18:21.837Z","updated_at":"2025-04-04T14:07:40.103Z","avatar_url":"https://github.com/stoically.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# syn-rsx\n\n[![crates.io page](https://img.shields.io/crates/v/syn-rsx.svg)](https://crates.io/crates/syn-rsx)\n[![docs.rs page](https://docs.rs/syn-rsx/badge.svg)](https://docs.rs/syn-rsx/)\n[![codecov](https://codecov.io/gh/stoically/syn-rsx/branch/main/graph/badge.svg?token=2LMJ8YEV92)](https://codecov.io/gh/stoically/syn-rsx)\n![build](https://github.com/stoically/syn-rsx/workflows/ci/badge.svg)\n![license: MIT](https://img.shields.io/crates/l/syn-rsx.svg)\n\n[`syn`]-powered parser for JSX-like [`TokenStream`]s, aka 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\n```rust\nuse std::convert::TryFrom;\n\nuse eyre::bail;\nuse quote::quote;\nuse syn_rsx::{parse2, Node, NodeAttribute, NodeElement, NodeText};\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 { bail!(\"element\") };\nlet Node::Attribute(attribute) = \u0026element.attributes[0] else { bail!(\"attribute\") };\nlet Node::Text(text) = \u0026element.children[0] else { bail!(\"text\") };\n\n// Work with the nodes.\nassert_eq!(element.name.to_string(), \"hello\");\nassert_eq!(attribute.key.to_string(), \"world\");\nassert_eq!(String::try_from(\u0026text.value)?, \"hi\");\n```\n\nYou might want to check out the [html-to-string-macro example] as well.\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  Support for [unquoted text is planned].\n\n  ```html\n  \u003cdiv\u003e\"String literal\"\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- **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[`tokenstream`]: https://doc.rust-lang.org/proc_macro/struct.TokenStream.html\n[html-to-string-macro example]: https://github.com/stoically/syn-rsx/tree/main/examples/html-to-string-macro\n[unquoted text is planned]: https://github.com/stoically/syn-rsx/issues/2\n[#9]: https://github.com/stoically/syn-rsx/issues/9\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstoically%2Fsyn-rsx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstoically%2Fsyn-rsx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstoically%2Fsyn-rsx/lists"}