{"id":13626287,"url":"https://github.com/rbalicki2/smithy","last_synced_at":"2025-04-04T08:05:49.308Z","repository":{"id":42950879,"uuid":"140975274","full_name":"rbalicki2/smithy","owner":"rbalicki2","description":"A framework for building WebAssembly apps in Rust","archived":false,"fork":false,"pushed_at":"2020-02-20T03:37:33.000Z","size":369,"stargazers_count":351,"open_issues_count":6,"forks_count":10,"subscribers_count":10,"default_branch":"master","last_synced_at":"2024-04-22T16:29:45.295Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://www.smithy.rs","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rbalicki2.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-07-14T20:16:05.000Z","updated_at":"2024-01-30T06:05:12.000Z","dependencies_parsed_at":"2022-09-26T20:32:01.156Z","dependency_job_id":null,"html_url":"https://github.com/rbalicki2/smithy","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rbalicki2%2Fsmithy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rbalicki2%2Fsmithy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rbalicki2%2Fsmithy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rbalicki2%2Fsmithy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rbalicki2","download_url":"https://codeload.github.com/rbalicki2/smithy/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247142032,"owners_count":20890651,"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":[],"created_at":"2024-08-01T21:02:14.742Z","updated_at":"2025-04-04T08:05:49.286Z","avatar_url":"https://github.com/rbalicki2.png","language":"Rust","funding_links":[],"categories":["Rust","Alternatives"],"sub_categories":["Frameworks"],"readme":"# Smithy\n\n\u003e [Smithy](https://www.smithy.rs) is a front-end framework for Rust\n\n[Home page](https://www.smithy.rs) ◇ [API Docs](https://docs.smithy.rs/smithy/) ◇ [Repository](https://github.com/rbalicki2/smithy) ◇ [Example Sites](https://www.smithy.rs/examples)\n\n## What is Smithy?\n\nSmithy is a framework for writing WebAssembly applications entirely in Rust. Its goal is to allow you to do so using idiomatic Rust, without giving up any of the compiler's safety guarantees.\n\n## Smithy works on nightly\n\nSmithy v0.0.7 currently works on `1.39.0-nightly (dfd43f0fd 2019-09-01)`.\n\n## Getting started\n\nGetting started in Smithy is easy!\n\n```sh\nnpm init smithy-app my_smithy_app\ncd my_smithy_app\nnpm start\n```\n\nNavigate to `localhost:8080` to see your app in action!\n\nSee [the create-smithy-app repository](https://github.com/rbalicki2/create-smithy-app/) for more details.\n\n## A simple Smithy app\n\nA simple click counter is as follows:\n\n```rust\n#[wasm_bindgen(start)]\npub fn start() -\u003e Result\u003c(), wasm_bindgen::JsValue\u003e {\n  let root_element = get_root_element()?;\n  let mut count = 0;\n  let app = smithy::smd!(\n    \u003cdiv on_click={|_| count = count + 1}\u003e\n      I have been clicked {count}{' '}times.\n    \u003c/div\u003e\n  );\n  smithy::mount(Box::new(app), root_element);\n  Ok(())\n}\n\nfn get_root_element() -\u003e Result\u003cweb_sys::Element, wasm_bindgen::JsValue\u003e {\n  let document = web_sys::window().unwrap().document().unwrap();\n  document.get_element_by_id(\"app\")\n    .ok_or(wasm_bindgen::JsValue::NULL)\n}\n```\n\n## How Smithy works\n\n### `smd!` macro\n\nThe `smd!` and `smd_borrowed!` macros convert something that looks like JSX into a wrapper around an `FnMut(smithy::types::Phase) -\u003e smithy::types::PhaseResult`. For example, the `smd!` call in:\n\n```rust\nlet mut count = 0;\nlet app = smithy::smd!(\n  \u003cdiv on_click={|_| count = count + 1}\u003e\n    I have been clicked {count}{' '}times.\n  \u003c/div\u003e\n);\n```\n\nis converted into\n\n```rust\nlet mut app = {\n  #[allow(dead_code)]\n  use smithy::types::Component;\n  let component: smithy::types::SmithyComponent =\n    smithy::types::SmithyComponent(Box::new(move |phase| match phase {\n      smithy::types::Phase::Rendering =\u003e {\n        smithy::types::PhaseResult::Rendering(smithy::types::Node::Vec(vec![\n          smithy::types::Node::Dom(smithy::types::HtmlToken {\n            node_type: \"div\".into(),\n            attributes: std::collections::HashMap::new(),\n            children: {\n              let mut children = Vec::with_capacity(4usize);\n              children.push(smithy::types::Node::Text(\"I have been clicked \".into()));\n              children.push({ count }.render());\n              children.push({ ' ' }.render());\n              children.push(smithy::types::Node::Text(\"times.\".into()));\n              children\n            },\n          }),\n        ]))\n      },\n      smithy::types::Phase::UiEventHandling(ui_event_handling) =\u003e match ui_event_handling {\n        (evt, [0usize, 1usize, rest @ ..]) =\u003e {\n          smithy::types::PhaseResult::UiEventHandling({ count }.handle_ui_event(evt, rest))\n        },\n        (evt, [0usize, 2usize, rest @ ..]) =\u003e {\n          smithy::types::PhaseResult::UiEventHandling({ ' ' }.handle_ui_event(evt, rest))\n        },\n        (smithy::types::UiEvent::OnClick(val), [0usize, rest @ ..]) =\u003e {\n          ({ |_| count = count + 1 })(val);\n          smithy::types::PhaseResult::UiEventHandling(true)\n        },\n        _ =\u003e smithy::types::PhaseResult::UiEventHandling(false),\n      },\n      smithy::types::Phase::WindowEventHandling(window_event) =\u003e {\n        let mut event_handled = false;\n        event_handled = ({ count }).handle_window_event(window_event) || event_handled;\n        event_handled = ({ ' ' }).handle_window_event(window_event) || event_handled;\n        match window_event {\n          _ =\u003e smithy::types::PhaseResult::WindowEventHandling(event_handled),\n        }\n      },\n      smithy::types::Phase::PostRendering =\u003e {\n        {\n          {\n            ({ count }).handle_post_render();\n          }\n          ({ ' ' }).handle_post_render();\n        }\n        smithy::types::PhaseResult::PostRendering\n      },\n      smithy::types::Phase::RefAssignment(path_so_far) =\u003e {\n        let new_path = path_so_far\n          .clone()\n          .into_iter()\n          .chain(vec![0usize, 1usize])\n          .collect();\n        ({ count }).handle_ref_assignment(new_path);\n        let new_path = path_so_far\n          .clone()\n          .into_iter()\n          .chain(vec![0usize, 2usize])\n          .collect();\n        ({ ' ' }).handle_ref_assignment(new_path);\n        smithy::types::PhaseResult::RefAssignment\n      },\n    }));\n  component\n};\n```\n\nNotice that the `|_| count = count + 1` and `{count}` are in separate branches of the match arm. If they had not been (e.g. if `smd!` created a struct instead of an `FnMut`), this would not have compiled. The borrow checker would have complained that you cannot immutably borrow `count`, as it is already borrowed mutably in the `on_click` callback.\n\n### Smithy phases\n\nAs you can see from the expansion of the `smd!` macro above, phases are a core concept in Smithy. In particular, an app is driven through five phases:\n\n* rendering, in which the app is asked to return a struct containing the information about what it will write to the DOM.\n* ref assignment, in which any app with `ref={\u0026mut optional_web_sys_html_element}` will have `Some(some_html_element)` assigned to that ref.\n* post rendering, in which any `post_render={|_| ...}` callbacks will be executed. These callbacks are guaranteed to have all refs already assigned, thus allowing you to do any direct DOM manipulation you need to do.\n* UI event handling and window event handling, in which Smithy executes callbacks in response to events. After a callback is executed, Smithy will re-run the app through the different phases.\n  * (UI event handling and window event handling are treated as separate phases, though conceptually they are very similar.)\n\n## `smd!` vs `smd_borrowed!`\n\nAs you can see in the macro expansion above, the `smd!` macro creates a move closure. This is not always desirable. If you do not wish to create a move closure, use `smd_borrowed!` instead.\n\n## How to get involved\n\nSmithy is always looking for contributors! Please tweet at me `@statisticsftw` or take a look at the [Smithy roadmap](https://github.com/rbalicki2/smithy/issues/2).\n\nIn addition, please take Smithy out for a spin using [create-smithy-app](https://github.com/rbalicki2/create-smithy-app/).\n\nThanks! Happy coding!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frbalicki2%2Fsmithy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frbalicki2%2Fsmithy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frbalicki2%2Fsmithy/lists"}