mirror of
https://github.com/zaphar/wasm-web-components.git
synced 2025-07-21 19:40:30 -04:00
Proc macro
This commit is contained in:
parent
ef59c1769c
commit
4daaa03093
43
Cargo.toml
43
Cargo.toml
@ -1,41 +1,2 @@
|
||||
[package]
|
||||
name = "web-component-rs"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
author = "Jeremy Wall <jeremy@marzhillstudios.com>"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies.wasm-bindgen-test]
|
||||
version = "0.3"
|
||||
|
||||
[dependencies.wasm-bindgen]
|
||||
version = "= 0.2.81"
|
||||
|
||||
[dependencies.js-sys]
|
||||
version = "0.3"
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3"
|
||||
features = [
|
||||
"CustomElementRegistry",
|
||||
"Document",
|
||||
#"DocumentFragment",
|
||||
"KeyboardEvent",
|
||||
"Event",
|
||||
"EventTarget",
|
||||
"Element",
|
||||
"Node",
|
||||
"Text",
|
||||
"HtmlBaseElement",
|
||||
"HtmlElement",
|
||||
"HtmlTemplateElement",
|
||||
"HtmlSlotElement",
|
||||
"Node",
|
||||
"ShadowRoot",
|
||||
"ShadowRootInit",
|
||||
"ShadowRootMode",
|
||||
"Window",
|
||||
]
|
||||
[workspace]
|
||||
members = ["web-component", "derive"]
|
17
derive/Cargo.toml
Normal file
17
derive/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "web-component-derive"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
quote = "1.0"
|
||||
proc-macro2 = "1.0"
|
||||
|
||||
[dependencies.syn]
|
||||
version = "1.0.101"
|
||||
features = ["full"]
|
126
derive/src/lib.rs
Normal file
126
derive/src/lib.rs
Normal file
@ -0,0 +1,126 @@
|
||||
// Copyright 2022 Jeremy Wall (Jeremy@marzhilsltudios.com)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Span;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, AttributeArgs, ItemStruct, Lit, LitStr, Meta, NestedMeta};
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn web_component(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
// TODO(jwall): Attrs for class name and element name
|
||||
// Gather our attributes
|
||||
let args = parse_macro_input!(attr as AttributeArgs);
|
||||
let mut class_name = None;
|
||||
let mut element_name = None;
|
||||
for arg in args {
|
||||
if let NestedMeta::Meta(Meta::NameValue(nv)) = arg {
|
||||
if nv.path.is_ident("class_name") {
|
||||
if let Lit::Str(nm) = nv.lit {
|
||||
class_name = Some(nm);
|
||||
}
|
||||
} else if nv.path.is_ident("element_name") {
|
||||
if let Lit::Str(nm) = nv.lit {
|
||||
element_name = Some(nm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let element_name = element_name
|
||||
.map(|n| n.token())
|
||||
.unwrap_or_else(|| LitStr::new("", Span::call_site()).token());
|
||||
let class_name = class_name
|
||||
.map(|n| n.token())
|
||||
.unwrap_or_else(|| LitStr::new("", Span::call_site()).token());
|
||||
let item_struct = parse_macro_input!(item as ItemStruct);
|
||||
let struct_name = item_struct.ident.clone();
|
||||
let expanded = quote! {
|
||||
#[wasm_bindgen]
|
||||
#item_struct
|
||||
|
||||
impl #struct_name {
|
||||
pub fn element_name() -> &'static str {
|
||||
#element_name
|
||||
}
|
||||
|
||||
pub fn class_name() -> &'static str {
|
||||
#class_name
|
||||
}
|
||||
|
||||
pub fn define() -> Result<WebComponentHandle<#struct_name>> {
|
||||
use js_sys::Function;
|
||||
use web_sys::{window, Element, HtmlElement};
|
||||
let registry = web_sys::window().unwrap().custom_elements();
|
||||
let maybe_element = registry.get(Self::element_name());
|
||||
if maybe_element.is_truthy() {
|
||||
return Err("Custom Element has already been defined".into());
|
||||
}
|
||||
let body = format!(
|
||||
"class {name} extends HTMLElement {{
|
||||
constructor() {{
|
||||
super();
|
||||
this._impl = impl();
|
||||
}}
|
||||
|
||||
connectedCallback() {{
|
||||
this._impl.connected_impl(this);
|
||||
}}
|
||||
|
||||
disconnectedCallback() {{
|
||||
this._impl.disconnected_impl(this);
|
||||
}}
|
||||
|
||||
static get observedAttributes() {{
|
||||
console.log('observed attributes: ', attrs);
|
||||
return attrs;
|
||||
}}
|
||||
|
||||
adoptedCallback() {{
|
||||
console.log('In adoptedCallback');
|
||||
this._impl.adopted_impl(this);
|
||||
}}
|
||||
|
||||
attributeChangedCallback(name, oldValue, newValue) {{
|
||||
this._impl.attribute_changed_impl(this, name, oldValue, newValue);
|
||||
}}
|
||||
}}
|
||||
customElements.define(\"{element_name}\", {name});
|
||||
var element = customElements.get(\"{element_name}\");
|
||||
return element;",
|
||||
name = Self::class_name(),
|
||||
element_name = Self::element_name(),
|
||||
);
|
||||
let fun = Function::new_with_args("impl, attrs", &body);
|
||||
let f: Box<dyn FnMut() -> Self> = Box::new(|| {
|
||||
let obj = Self::new();
|
||||
obj
|
||||
});
|
||||
let constructor_handle = Closure::wrap(f);
|
||||
let element = fun
|
||||
.call2(
|
||||
&window().unwrap(),
|
||||
constructor_handle.as_ref().unchecked_ref::<Function>(),
|
||||
&Self::observed_attributes(),
|
||||
)?
|
||||
.dyn_into()?;
|
||||
Ok(WebComponentHandle {
|
||||
element_constructor: element,
|
||||
impl_handle: constructor_handle,
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TokenStream::from(expanded)
|
||||
}
|
200
web-component/Cargo.lock
generated
Normal file
200
web-component/Cargo.lock
generated
Normal file
@ -0,0 +1,200 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "console_error_panic_hook"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7bd7356a8122b6c4a24a82b278680c73357984ca2fc79a0f9fa6dea7dced7c58"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68b30cf2cba841a812f035c40c50f53eb9c56181192a9dd2c71b65e6a87a05ba"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"js-sys",
|
||||
"scoped-tls",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasm-bindgen-test-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test-macro"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88ad594bf33e73cafcac2ae9062fc119d4f75f9c77e25022f91c9a64bd5b6463"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-component-rs"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-test",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
45
web-component/Cargo.toml
Normal file
45
web-component/Cargo.toml
Normal file
@ -0,0 +1,45 @@
|
||||
[package]
|
||||
name = "web-component-rs"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
author = "Jeremy Wall <jeremy@marzhillstudios.com>"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
web-component-derive = {path = "../derive"}
|
||||
|
||||
[dependencies.wasm-bindgen-test]
|
||||
version = "0.3"
|
||||
|
||||
[dependencies.wasm-bindgen]
|
||||
version = "= 0.2.81"
|
||||
|
||||
[dependencies.js-sys]
|
||||
version = "0.3"
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3"
|
||||
features = [
|
||||
"CustomElementRegistry",
|
||||
"Document",
|
||||
#"DocumentFragment",
|
||||
"KeyboardEvent",
|
||||
"Event",
|
||||
"EventTarget",
|
||||
"Element",
|
||||
"Node",
|
||||
"Text",
|
||||
"HtmlBaseElement",
|
||||
"HtmlElement",
|
||||
"HtmlTemplateElement",
|
||||
"HtmlSlotElement",
|
||||
"Node",
|
||||
"ShadowRoot",
|
||||
"ShadowRootInit",
|
||||
"ShadowRootMode",
|
||||
"Window",
|
||||
]
|
@ -2,6 +2,8 @@ use js_sys::Function;
|
||||
use wasm_bindgen::{convert::IntoWasmAbi, prelude::*, JsCast, JsValue};
|
||||
use web_sys::{window, Element, HtmlElement};
|
||||
|
||||
use web_component_derive::web_component;
|
||||
|
||||
type Result<T> = std::result::Result<T, JsValue>;
|
||||
|
||||
// TODO(jwall): Trait methods can't be exported out to js yet so we'll need a wrapper object or we'll need to `Derive` this api in a prop-macro.
|
||||
@ -20,82 +22,12 @@ mod tests {
|
||||
use web_sys::Text;
|
||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[web_component(class_name = "MyElement", element_name = "my-element")]
|
||||
#[derive(Default, Debug)]
|
||||
pub struct MyElementImpl {}
|
||||
|
||||
impl CustomElementImpl for MyElementImpl {}
|
||||
|
||||
impl MyElementImpl {
|
||||
pub fn class_name() -> &'static str {
|
||||
"MyElement"
|
||||
}
|
||||
|
||||
pub fn element_name() -> &'static str {
|
||||
"my-element"
|
||||
}
|
||||
|
||||
pub fn define() -> Result<WebComponentHandle<Self>> {
|
||||
let registry = window().unwrap().custom_elements();
|
||||
let maybe_element = registry.get(Self::element_name());
|
||||
if maybe_element.is_truthy() {
|
||||
return Err("Custom Element has already been defined".into());
|
||||
}
|
||||
let body = format!(
|
||||
"class {name} extends HTMLElement {{
|
||||
constructor() {{
|
||||
super();
|
||||
this._impl = impl();
|
||||
}}
|
||||
|
||||
connectedCallback() {{
|
||||
this._impl.connected_impl(this);
|
||||
}}
|
||||
|
||||
disconnectedCallback() {{
|
||||
this._impl.disconnected_impl(this);
|
||||
}}
|
||||
|
||||
static get observedAttributes() {{
|
||||
console.log('observed attributes: ', attrs);
|
||||
return attrs;
|
||||
}}
|
||||
|
||||
adoptedCallback() {{
|
||||
console.log('In adoptedCallback');
|
||||
this._impl.adopted_impl(this);
|
||||
}}
|
||||
|
||||
attributeChangedCallback(name, oldValue, newValue) {{
|
||||
this._impl.attribute_changed_impl(this, name, oldValue, newValue);
|
||||
}}
|
||||
}}
|
||||
customElements.define(\"{element_name}\", {name});
|
||||
var element = customElements.get(\"{element_name}\");
|
||||
return element;",
|
||||
name = Self::class_name(),
|
||||
element_name = Self::element_name(),
|
||||
);
|
||||
let fun = Function::new_with_args("impl, attrs", &body);
|
||||
let f: Box<dyn FnMut() -> Self> = Box::new(|| {
|
||||
let obj = Self::new();
|
||||
obj
|
||||
});
|
||||
let constructor_handle = Closure::wrap(f);
|
||||
let element = fun
|
||||
.call2(
|
||||
&window().unwrap(),
|
||||
constructor_handle.as_ref().unchecked_ref::<Function>(),
|
||||
&Self::observed_attributes(),
|
||||
)?
|
||||
.dyn_into()?;
|
||||
Ok(WebComponentHandle {
|
||||
element_constructor: element,
|
||||
impl_handle: constructor_handle,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl MyElementImpl {
|
||||
#[wasm_bindgen(constructor)]
|
Loading…
x
Reference in New Issue
Block a user