on master help sign in
Add an API for defining JavaScript rendering components!
commit52bd38b6d9362eeb775b373464807ae07c444746 authorNick Fitzgerald <fitzgen@gmail.com> dateWed 27 Feb 2019, 17:08 (8 years ago) parentfaec92e
9 files changed+571−54
Cargo.lock+68−49 Cargo.toml+1 crates/js-api/Cargo.toml+5 crates/js-api/src/lib.rs+326−5 examples/js-component/Cargo.toml+26 examples/js-component/README.md+23 examples/js-component/greeting.js+41 examples/js-component/index.html+14 examples/js-component/src/lib.rs+67
Cargo.lock+68−49
@@ -66,7 +66,7 @@ version = "0.1.5"
6666source = "registry+https://github.com/rust-lang/crates.io-index"
6767dependencies = [
6868 "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
69− "wasm-bindgen 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
69+ "wasm-bindgen 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
7070]
7171
7272[[package]]
@@ -75,7 +75,7 @@ version = "0.1.2"
7575source = "registry+https://github.com/rust-lang/crates.io-index"
7676dependencies = [
7777 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
78− "web-sys 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
78+ "web-sys 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
7979]
8080
8181[[package]]
@@ -86,12 +86,12 @@ dependencies = [
8686 "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
8787 "console_log 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
8888 "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
89− "js-sys 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
89+ "js-sys 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
9090 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
91− "wasm-bindgen 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
92− "wasm-bindgen-futures 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
91+ "wasm-bindgen 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
92+ "wasm-bindgen-futures 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
9393 "wasm-bindgen-test 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
94− "web-sys 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
94+ "web-sys 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
9595]
9696
9797[[package]]
@@ -102,9 +102,9 @@ dependencies = [
102102 "console_log 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
103103 "dodrio 0.1.0",
104104 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
105− "wasm-bindgen 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
105+ "wasm-bindgen 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
106106 "wasm-bindgen-test 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
107− "web-sys 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
107+ "web-sys 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
108108]
109109
110110[[package]]
@@ -113,9 +113,9 @@ version = "0.1.0"
113113dependencies = [
114114 "console_error_panic_hook 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
115115 "dodrio 0.1.0",
116− "wasm-bindgen 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
116+ "wasm-bindgen 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
117117 "wasm-bindgen-test 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
118− "web-sys 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
118+ "web-sys 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
119119]
120120
121121[[package]]
@@ -124,14 +124,33 @@ version = "0.1.0"
124124dependencies = [
125125 "console_error_panic_hook 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
126126 "dodrio 0.1.0",
127− "wasm-bindgen 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
127+ "wasm-bindgen 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
128128 "wasm-bindgen-test 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
129− "web-sys 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
129+ "web-sys 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
130130]
131131
132132[[package]]
133133name = "dodrio-js-api"
134134version = "0.1.0"
135+dependencies = [
136+ "dodrio 0.1.0",
137+ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
138+ "js-sys 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
139+ "wasm-bindgen 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
140+ "wasm-bindgen-futures 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
141+]
142+
143+[[package]]
144+name = "dodrio-js-component"
145+version = "0.1.0"
146+dependencies = [
147+ "console_error_panic_hook 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
148+ "dodrio 0.1.0",
149+ "dodrio-js-api 0.1.0",
150+ "js-sys 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
151+ "wasm-bindgen 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
152+ "web-sys 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
153+]
135154
136155[[package]]
137156name = "dodrio-todomvc"
@@ -142,14 +161,14 @@ dependencies = [
142161 "console_log 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
143162 "dodrio 0.1.0",
144163 "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
145− "js-sys 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
164+ "js-sys 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
146165 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
147166 "serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
148167 "serde_json 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)",
149− "wasm-bindgen 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
150− "wasm-bindgen-futures 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
168+ "wasm-bindgen 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
169+ "wasm-bindgen-futures 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
151170 "wasm-bindgen-test 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
152− "web-sys 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
171+ "web-sys 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
153172]
154173
155174[[package]]
@@ -212,10 +231,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
212231
213232[[package]]
214233name = "js-sys"
215−version = "0.3.13"
234+version = "0.3.14"
216235source = "registry+https://github.com/rust-lang/crates.io-index"
217236dependencies = [
218− "wasm-bindgen 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
237+ "wasm-bindgen 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
219238]
220239
221240[[package]]
@@ -426,15 +445,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
426445
427446[[package]]
428447name = "wasm-bindgen"
429−version = "0.2.36"
448+version = "0.2.37"
430449source = "registry+https://github.com/rust-lang/crates.io-index"
431450dependencies = [
432− "wasm-bindgen-macro 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
451+ "wasm-bindgen-macro 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
433452]
434453
435454[[package]]
436455name = "wasm-bindgen-backend"
437−version = "0.2.36"
456+version = "0.2.37"
438457source = "registry+https://github.com/rust-lang/crates.io-index"
439458dependencies = [
440459 "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -442,43 +461,43 @@ dependencies = [
442461 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
443462 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
444463 "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)",
445− "wasm-bindgen-shared 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
464+ "wasm-bindgen-shared 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
446465]
447466
448467[[package]]
449468name = "wasm-bindgen-futures"
450−version = "0.3.13"
469+version = "0.3.14"
451470source = "registry+https://github.com/rust-lang/crates.io-index"
452471dependencies = [
453472 "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
454− "js-sys 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
455− "wasm-bindgen 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
473+ "js-sys 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
474+ "wasm-bindgen 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
456475]
457476
458477[[package]]
459478name = "wasm-bindgen-macro"
460−version = "0.2.36"
479+version = "0.2.37"
461480source = "registry+https://github.com/rust-lang/crates.io-index"
462481dependencies = [
463482 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
464− "wasm-bindgen-macro-support 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
483+ "wasm-bindgen-macro-support 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
465484]
466485
467486[[package]]
468487name = "wasm-bindgen-macro-support"
469−version = "0.2.36"
488+version = "0.2.37"
470489source = "registry+https://github.com/rust-lang/crates.io-index"
471490dependencies = [
472491 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
473492 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
474493 "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)",
475− "wasm-bindgen-backend 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
476− "wasm-bindgen-shared 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
494+ "wasm-bindgen-backend 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
495+ "wasm-bindgen-shared 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
477496]
478497
479498[[package]]
480499name = "wasm-bindgen-shared"
481−version = "0.2.36"
500+version = "0.2.37"
482501source = "registry+https://github.com/rust-lang/crates.io-index"
483502
484503[[package]]
@@ -488,10 +507,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
488507dependencies = [
489508 "console_error_panic_hook 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
490509 "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
491− "js-sys 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
510+ "js-sys 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
492511 "scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
493− "wasm-bindgen 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
494− "wasm-bindgen-futures 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
512+ "wasm-bindgen 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
513+ "wasm-bindgen-futures 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
495514 "wasm-bindgen-test-macro 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
496515]
497516
@@ -506,7 +525,7 @@ dependencies = [
506525
507526[[package]]
508527name = "wasm-bindgen-webidl"
509−version = "0.2.30"
528+version = "0.2.31"
510529source = "registry+https://github.com/rust-lang/crates.io-index"
511530dependencies = [
512531 "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -515,21 +534,21 @@ dependencies = [
515534 "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
516535 "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
517536 "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)",
518− "wasm-bindgen-backend 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
537+ "wasm-bindgen-backend 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
519538 "weedle 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
520539]
521540
522541[[package]]
523542name = "web-sys"
524−version = "0.3.13"
543+version = "0.3.14"
525544source = "registry+https://github.com/rust-lang/crates.io-index"
526545dependencies = [
527546 "env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
528547 "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
529− "js-sys 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
548+ "js-sys 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
530549 "sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
531− "wasm-bindgen 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
532− "wasm-bindgen-webidl 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)",
550+ "wasm-bindgen 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
551+ "wasm-bindgen-webidl 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)",
533552]
534553
535554[[package]]
@@ -594,7 +613,7 @@ dependencies = [
594613"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
595614"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114"
596615"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
597−"checksum js-sys 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "9639a5b9944fc99bc02992f25ac25f8617e81860b0b31f61eb15a1f4fe4b39f3"
616+"checksum js-sys 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4dbd938475b240c421c270c2c0dd0f9acc3642e02d7419afd37587a34cd04e40"
598617"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
599618"checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047"
600619"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
@@ -624,16 +643,16 @@ dependencies = [
624643"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
625644"checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
626645"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
627−"checksum wasm-bindgen 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "b5a07590bf415481c15debf0fb459e4ee7bc5f7063b3adf450cea87cfc5451ff"
628−"checksum wasm-bindgen-backend 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "93c3e81841784d407e9ce7a047a88f093861b70584de6ae9072d1c6985de62b8"
629−"checksum wasm-bindgen-futures 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "09c886eacd2ea2662f02daa11bdd836778dfd97b5e7b5320bc7c6c7824835fd5"
630−"checksum wasm-bindgen-macro 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "702fae71b06d1c0527bf920cca51b75b2332aace0be6dc28abb50567f5f8e8d6"
631−"checksum wasm-bindgen-macro-support 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "0bb7feb04fa2c73d0c28579143c4104c7edbb84299f27bbc1dacc0dda3b56e19"
632−"checksum wasm-bindgen-shared 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "d191314b24a75d0798a22632aad4b18ed0098df42d0fcb0f759b394cf3bba02f"
646+"checksum wasm-bindgen 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)" = "2d85ba4917abb23ee7788330797a7cb5ac7e5e997cdc67fde6f2c8d02cc08db3"
647+"checksum wasm-bindgen-backend 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)" = "51f9c26bd7d30f21ce334785ec10c5ce10cba28313979d793b8dddede19da3a4"
648+"checksum wasm-bindgen-futures 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "25ee19bc252f9fbe54dadc21713b98d7a72f1d5f4b497ef9d9c112897131c3c6"
649+"checksum wasm-bindgen-macro 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)" = "316e6e568644bfa2009698217cec8f612a4e2cd7460b127f52573991c7ad9f53"
650+"checksum wasm-bindgen-macro-support 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)" = "29982ea76a7a35b5c3c052b6035abe9b84d8b6422d8881b68bd3472270a53eec"
651+"checksum wasm-bindgen-shared 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)" = "59305c5b959d51437636db05bac82de2b52da2c05157e38e018c0bd54cfd9bbf"
633652"checksum wasm-bindgen-test 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "e403218d15f3a0be08ad984a4b97f4ce4d05f6d3df98ce6708c32f58cdd76286"
634653"checksum wasm-bindgen-test-macro 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "e1feb1af4e075b19eab744a8ed0da087b024ae79afc0b35e49c3ca9a4a1c327b"
635−"checksum wasm-bindgen-webidl 0.2.30 (registry+https://github.com/rust-lang/crates.io-index)" = "142b34c5209bd1b6562b9fee0e4bddd1bebf00717b6cb4ccc9f0a9b3cc9f5f03"
636−"checksum web-sys 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "5cec4b17aece169adf93e8d05919e9ba7fb2156ebcf2280e6b5987868f287600"
654+"checksum wasm-bindgen-webidl 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)" = "780c16a1a26d28695ac9af7176e11dbc3c188e6d0b3095ad51cc2d0e9e31fcc9"
655+"checksum web-sys 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "02c19f8ab50a089f9d7c8070c191a919c85d10a4a37c5777a08e8f94ec12c0fc"
637656"checksum weedle 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "26a4c67f132386d965390b8a734d5d10adbcd30eb5cc74bd9229af8b83f10044"
638657"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
639658"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
Cargo.toml+1
@@ -50,5 +50,6 @@ members = [
5050 "./examples/counter",
5151 "./examples/hello-world",
5252 "./examples/input-form",
53+ "./examples/js-component",
5354 "./examples/todomvc",
5455]
crates/js-api/Cargo.toml+5
@@ -5,3 +5,8 @@ authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
55edition = "2018"
66
77[dependencies]
8+dodrio = { version = "=0.1.0", path = "../.." }
9+wasm-bindgen = "0.2.37"
10+js-sys = "0.3.14"
11+wasm-bindgen-futures = "0.3.14"
12+futures = "0.1.25"
crates/js-api/src/lib.rs+326−5
@@ -1,7 +1,328 @@
1−#[cfg(test)]
2−mod tests {
3− #[test]
4− fn it_works() {
5− assert_eq!(2 + 2, 4);
1+/*!
2+
3+Implementing `dodrio` render components with JavaScript.
4+
5+This crate provides a Rust type `JsRender` that wraps a JavaScript object with a
6+`render` method. `JsRender` implements `dodrio::Render` by calling its wrapped
7+object's `render` method to get a JavaScript virtual DOM represented as a tree
8+of JavaScript values. It then converts this tree of JavaScript values into
9+`dodrio`'s normal bump-allocated virtual DOM representation.
10+
11+This is likely much slower than rendering virtual DOMs directly into the bump
12+allocator from the Rust side of things! Additionally, the shape of the
13+JavaScript virtual DOM is a bit funky and unidiomatic. Keep in mind that this
14+crate exists as a proof of concept for integrating JavaScript components into
15+`dodrio` -- which is itself *also* experimental -- and so this crate definitely
16+has some rough edges.
17+
18+# Example
19+
20+Here is a JavaScript implementation of a rendering component:
21+
22+```javascript
23+class Greeting {
24+ constructor(who) {
25+ this.who = who;
26+ }
27+
28+ render() {
29+ return {
30+ tagName: "p",
31+ attributes: [
32+ {
33+ name: "class",
34+ value: "greeting",
35+ },
36+ ],
37+ listeners: [
38+ {
39+ on: "click",
40+ callback: this.onClick.bind(this),
41+ }
42+ ],
43+ children: [
44+ "Hello, ",
45+ {
46+ tagName: "strong",
47+ children: [this.who],
48+ }
49+ ],
50+ };
51+ }
52+
53+ async onClick(vdom, event) {
54+ // Be more excited!
55+ this.who += "!";
56+
57+ // Schedule a re-render.
58+ await vdom.render();
59+
60+ console.log("re-rendering finished!");
61+ }
62+}
63+```
64+
65+And here is a Rust rendering component that internally uses the JS rendering
66+component:
67+
68+```rust,no_run
69+use dodrio::bumpalo::Bump;
70+use dodrio::{Node, Render, Vdom};
71+use dodrio_js_api::JsRender;
72+use js_sys::Object;
73+use wasm_bindgen::prelude::*;
74+
75+#[wasm_bindgen]
76+extern {
77+ // Import the JS `Greeting` class.
78+ #[wasm_bindgen(extends = Object)]
79+ #[derive(Clone, Debug)]
80+ type Greeting;
81+
82+ // And the `Greeting` class's constructor.
83+ #[wasm_bindgen(constructor)]
84+ fn new(who: &str) -> Greeting;
85+}
86+
87+/// This is our Rust rendering component that wraps the JS rendering component.
88+pub struct GreetingViaJs {
89+ js: JsRender,
90+}
91+
92+impl GreetingViaJs {
93+ /// Create a new `GreetingViaJs`, which will internally create a new JS
94+ /// `Greeting`.
95+ pub fn new(who: &str) -> GreetingViaJs {
96+ let js = JsRender::new(Greeting::new(who));
97+ GreetingViaJs { js }
98+ }
99+}
100+
101+/// And finally the `Render` implementation! This adds a `<p>` element and some
102+/// text around whatever the inner JS `Greeting` component renders.
103+impl Render for GreetingViaJs {
104+ fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
105+ where
106+ 'a: 'bump,
107+ {
108+ use dodrio::builder::*;
109+ p(bump)
110+ .children([
111+ text("JavaScript says: "),
112+ self.js.render(bump),
113+ ])
114+ .finish()
115+ }
116+}
117+```
118+
119+ */
120+#![deny(missing_docs, missing_debug_implementations)]
121+
122+use dodrio::{
123+ builder,
124+ bumpalo::{self, Bump},
125+ Node, Render,
126+};
127+use futures::prelude::*;
128+use js_sys::{Object, Promise, Reflect};
129+use wasm_bindgen::prelude::*;
130+use wasm_bindgen::JsCast;
131+
132+#[wasm_bindgen]
133+extern "C" {
134+ /// A rendering component implemented in JavaScript.
135+ ///
136+ /// The rendering API is a bit duck-typed: any JS object with a `render`
137+ /// method that returns a virtual DOM as JS values with the right shape
138+ /// works.
139+ ///
140+ /// See `JsRender::new` for converting existing JS objects into `JsRender`s.
141+ #[derive(Debug, Clone)]
142+ pub type JsRender;
143+ #[wasm_bindgen(structural, method)]
144+ fn render(this: &JsRender) -> JsValue;
145+
146+ #[wasm_bindgen(extends = Object)]
147+ #[derive(Debug, Clone)]
148+ type Element;
149+ #[wasm_bindgen(structural, getter, method, js_name = tagName)]
150+ fn tag_name(this: &Element) -> String;
151+ #[wasm_bindgen(structural, getter, method)]
152+ fn listeners(this: &Element) -> js_sys::Array;
153+ #[wasm_bindgen(structural, getter, method)]
154+ fn attributes(this: &Element) -> js_sys::Array;
155+ #[wasm_bindgen(structural, getter, method)]
156+ fn children(this: &Element) -> js_sys::Array;
157+
158+ #[wasm_bindgen(extends = Object)]
159+ #[derive(Debug, Clone)]
160+ type Listener;
161+ #[wasm_bindgen(structural, getter, method)]
162+ fn on(this: &Listener) -> String;
163+ #[wasm_bindgen(structural, getter, method)]
164+ fn callback(this: &Listener) -> js_sys::Function;
165+
166+ #[wasm_bindgen(extends = Object)]
167+ #[derive(Debug, Clone)]
168+ type Attribute;
169+ #[wasm_bindgen(structural, getter, method)]
170+ fn name(this: &Attribute) -> String;
171+ #[wasm_bindgen(structural, getter, method)]
172+ fn value(this: &Attribute) -> String;
173+}
174+
175+/// A weak handle to a virtual DOM.
176+///
177+/// This is essentially the same as `dodrio::VdomWeak`, but exposed to
178+/// JavaScript.
179+#[wasm_bindgen]
180+#[derive(Clone, Debug)]
181+pub struct VdomWeak {
182+ inner: dodrio::VdomWeak,
183+}
184+
185+impl VdomWeak {
186+ fn new(inner: dodrio::VdomWeak) -> VdomWeak {
187+ VdomWeak { inner }
188+ }
189+}
190+
191+#[wasm_bindgen]
192+impl VdomWeak {
193+ /// Schedule re-rendering of the virtual DOM. A promise is returned that is
194+ /// resolved after the rendering has happened.
195+ pub fn render(&self) -> Promise {
196+ wasm_bindgen_futures::future_to_promise(
197+ self.inner.render().map(|_| JsValue::null()).map_err(|e| {
198+ let msg = e.to_string();
199+ js_sys::Error::new(&msg).into()
200+ }),
201+ )
202+ }
203+}
204+
205+impl JsRender {
206+ /// TODO
207+ pub fn new<O>(object: O) -> JsRender
208+ where
209+ O: Into<Object>,
210+ {
211+ let object = object.into();
212+ debug_assert!(
213+ has_property(&object, "render"),
214+ "JS rendering components must have a `render` method"
215+ );
216+ object.unchecked_into::<JsRender>()
217+ }
218+}
219+
220+impl Render for JsRender {
221+ fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
222+ where
223+ 'a: 'bump,
224+ {
225+ create(bump, self.render())
226+ }
227+}
228+
229+fn has_property(obj: &Object, property: &str) -> bool {
230+ Reflect::has(obj, &property.into()).unwrap_or_default()
231+}
232+
233+fn create(bump: &Bump, val: JsValue) -> Node {
234+ if let Some(txt) = val.as_string() {
235+ let text = bumpalo::collections::String::from_str_in(&txt, bump);
236+ return builder::text(text.into_bump_str());
6237 }
238+
239+ let elem = val.unchecked_into::<Element>();
240+ debug_assert!(
241+ elem.is_instance_of::<Object>(),
242+ "JS render methods should only return strings for text nodes or objects for elements"
243+ );
244+ debug_assert!(
245+ has_property(&elem, "tagName"),
246+ "element objects returned by JS render methods must have a `tagName` property"
247+ );
248+
249+ let tag_name = elem.tag_name();
250+ let tag_name = bumpalo::collections::String::from_str_in(&tag_name, bump);
251+
252+ builder::ElementBuilder::new(bump, tag_name.into_bump_str())
253+ .listeners({
254+ let mut listeners =
255+ bumpalo::collections::Vec::new_in(bump);
256+ if has_property(&elem, "listeners") {
257+ let js_listeners = elem.listeners();
258+ listeners.reserve(js_listeners.length() as usize);
259+ js_listeners.for_each(&mut |listener, _index, _array| {
260+ let listener = listener.unchecked_into::<Listener>();
261+ debug_assert!(
262+ listener.is_instance_of::<Object>(),
263+ "listeners returned by JS render methods must be objects"
264+ );
265+ debug_assert!(
266+ has_property(&listener, "on"),
267+ "listener objects returned by JS render methods must have an `on` property"
268+ );
269+ debug_assert!(
270+ has_property(&listener, "callback"),
271+ "listener objects returned by JS render methods must have an `callback` property"
272+ );
273+ let on = listener.on();
274+ let on = bumpalo::collections::String::from_str_in(&on, bump);
275+ let callback = listener.callback();
276+ let elem = elem.clone();
277+ listeners.push(builder::on(bump, on.into_bump_str(), move |_root, vdom, event| {
278+ let vdom = VdomWeak::new(vdom);
279+ let vdom: JsValue = vdom.into();
280+ if let Err(e) = callback.call2(&elem, &vdom, &event) {
281+ wasm_bindgen::throw_val(e);
282+ }
283+ }));
284+ });
285+ }
286+ listeners
287+ })
288+ .attributes({
289+ let mut attributes = bumpalo::collections::Vec::new_in(bump);
290+ if has_property(&elem, "attributes") {
291+ let js_attributes = elem.attributes();
292+ attributes.reserve(js_attributes.length() as usize);
293+ js_attributes.for_each(&mut |attribute, _index, _array| {
294+ let attribute = attribute.unchecked_into::<Attribute>();
295+ debug_assert!(
296+ attribute.is_instance_of::<Object>(),
297+ "attributes returned by JS render methods must be objects"
298+ );
299+ debug_assert!(
300+ has_property(&attribute, "name"),
301+ "attribute objects returned by JS render methods must have a `name` property"
302+ );
303+ debug_assert!(
304+ has_property(&attribute, "value"),
305+ "attribute objects returned by JS render methods must have a `value` property"
306+ );
307+ let name = attribute.name();
308+ let name = bumpalo::collections::String::from_str_in(&name, bump);
309+ let value = attribute.value();
310+ let value = bumpalo::collections::String::from_str_in(&value, bump);
311+ attributes.push(builder::attr(name.into_bump_str(), value.into_bump_str()));
312+ });
313+ }
314+ attributes
315+ })
316+ .children({
317+ let mut children = bumpalo::collections::Vec::new_in(bump);
318+ if has_property(&elem, "children") {
319+ let js_children = elem.children();
320+ children.reserve(js_children.length() as usize);
321+ js_children.for_each(&mut |child, _index, _array| {
322+ children.push(create(bump, child));
323+ });
324+ }
325+ children
326+ })
327+ .finish()
7328}
examples/js-component/Cargo.tomlnew file+26
@@ -0,0 +1,26 @@
1+[package]
2+name = "dodrio-js-component"
3+version = "0.1.0"
4+authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
5+edition = "2018"
6+
7+[lib]
8+crate-type = ["cdylib"]
9+
10+[features]
11+
12+[dependencies]
13+dodrio = { path = "../.." }
14+dodrio-js-api = { path = "../../crates/js-api" }
15+wasm-bindgen = "0.2.36"
16+console_error_panic_hook = "0.1.5"
17+js-sys = "0.3.14"
18+
19+[dependencies.web-sys]
20+version = "0.3.13"
21+features = [
22+ "Document",
23+ "HtmlElement",
24+ "Node",
25+ "Window",
26+]
examples/js-component/README.mdnew file+23
@@ -0,0 +1,23 @@
1+# Defining a Rendering Component in JavaScript
2+
3+Heterogeneous Rust and JavaScript rendering component trees!
4+
5+## Source
6+
7+See `greeting.js` for the JavaScript code.
8+
9+See `src/lib.rs` for the Rust code.
10+
11+## Build
12+
13+```
14+wasm-pack build --target no-modules
15+```
16+
17+## Serve
18+
19+Use any HTTP server, for example:
20+
21+```
22+python -m SimpleHTTPServer
23+```
examples/js-component/greeting.jsnew file+41
@@ -0,0 +1,41 @@
1+// A rendering component defined in JavaScript!
2+class Greeting {
3+ constructor(who) {
4+ this.who = who;
5+ }
6+
7+ render() {
8+ return {
9+ tagName: "p",
10+ attributes: [
11+ {
12+ name: "class",
13+ value: "greeting",
14+ },
15+ ],
16+ listeners: [
17+ {
18+ on: "click",
19+ callback: this.onClick.bind(this),
20+ }
21+ ],
22+ children: [
23+ "Hello, ",
24+ {
25+ tagName: "strong",
26+ children: [this.who],
27+ }
28+ ],
29+ };
30+ }
31+
32+ async onClick(vdom, event) {
33+ // Be more excited!
34+ this.who += "!";
35+
36+ // Schedule a re-render.
37+ await vdom.render();
38+
39+ console.log("re-rendering finished!");
40+ }
41+}
examples/js-component/index.htmlnew file+14
@@ -0,0 +1,14 @@
1+<!DOCTYPE html>
2+<html>
3+ <head>
4+ <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
5+ <title>A JavaScript component!</title>
6+ </head>
7+ <body>
8+ <script src="greeting.js"></script>
9+ <script src="pkg/dodrio_js_component.js"></script>
10+ <script>
11+ wasm_bindgen("pkg/dodrio_js_component_bg.wasm");
12+ </script>
13+ </body>
14+</html>
examples/js-component/src/lib.rsnew file+67
@@ -0,0 +1,67 @@
1+use dodrio::bumpalo::Bump;
2+use dodrio::{Node, Render, Vdom};
3+use dodrio_js_api::JsRender;
4+use js_sys::Object;
5+use wasm_bindgen::prelude::*;
6+
7+#[wasm_bindgen]
8+extern "C" {
9+ // Import the JS `Greeting` class.
10+ #[wasm_bindgen(extends = Object)]
11+ #[derive(Clone, Debug)]
12+ type Greeting;
13+
14+ // And the `Greeting` class's constructor.
15+ #[wasm_bindgen(constructor)]
16+ fn new(who: &str) -> Greeting;
17+}
18+
19+/// This is our Rust rendering component that wraps the JS rendering component.
20+pub struct GreetingViaJs {
21+ js: JsRender,
22+}
23+
24+impl GreetingViaJs {
25+ /// Create a new `GreetingViaJs`, which will internally create a new JS
26+ /// `Greeting`.
27+ pub fn new(who: &str) -> GreetingViaJs {
28+ let js = JsRender::new(Greeting::new(who));
29+ GreetingViaJs { js }
30+ }
31+}
32+
33+/// Here's the `Render` implementation! This adds a `<p>` element and some text
34+/// around whatever the inner JS `Greeting` component renders.
35+impl Render for GreetingViaJs {
36+ fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
37+ where
38+ 'a: 'bump,
39+ {
40+ use dodrio::builder::*;
41+ p(bump)
42+ .children([text("JavaScript says: "), self.js.render(bump)])
43+ .finish()
44+ }
45+}
46+
47+/// Finally our main initialization function that kicks everything off!
48+#[wasm_bindgen(start)]
49+pub fn run() {
50+ // Set up the panic hook for debugging when things go wrong.
51+ console_error_panic_hook::set_once();
52+
53+ // Grab the document's `<body>`.
54+ let window = web_sys::window().unwrap_throw();
55+ let document = window.document().unwrap_throw();
56+ let body = document.body().unwrap_throw();
57+
58+ // Create a new `GreetingViaJs` render component.
59+ let component = GreetingViaJs::new("World");
60+
61+ // Create a virtual DOM and mount it and the `Hello` render component to the
62+ // `<body>`.
63+ let vdom = Vdom::new(body.as_ref(), component);
64+
65+ // Run the virtual DOM forever and don't unmount it.
66+ vdom.forget();
67+}