Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More support for phoenix liveview e2e and unit tests #189

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 17 additions & 27 deletions crates/core/src/diff/fragment/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,14 @@ impl Fragment {
// templates and statics are suppose to wrap the inner
// contents of the children.
for (i, static_item) in statics.iter().enumerate().skip(1) {
let child = children
.get(&(i - 1).to_string())
.ok_or(RenderError::ChildNotFoundForStatic((i - 1) as i32))?;
let val = child.render(
components,
cousin_statics.clone(),
parent_templates.clone(),
)?;
out.push_str(&val);
if let Some(child) = children.get(&(i - 1).to_string()) {
let val = child.render(
components,
cousin_statics.clone(),
parent_templates.clone(),
)?;
out.push_str(&val);
}
out.push_str(static_item);
}
}
Expand All @@ -156,6 +155,7 @@ impl Fragment {
out.push_str(template_item);
}
}
Statics::String(_) => {}
}
}
Fragment::Comprehension {
Expand Down Expand Up @@ -242,6 +242,7 @@ impl Fragment {
return Err(RenderError::NoTemplates);
}
}
Statics::String(_) => {}
}
}
(Some(_statics), Some(_cousin_templates)) => {
Expand Down Expand Up @@ -403,7 +404,7 @@ pub enum Fragment {
Comprehension {
#[serde(rename = "d")]
dynamics: Dynamics,
#[serde(rename = "s")]
#[serde(rename = "s", skip_serializing_if = "Option::is_none")]
statics: Option<Statics>,
#[serde(rename = "r", skip_serializing_if = "Option::is_none")]
reply: Option<i8>,
Expand Down Expand Up @@ -570,6 +571,7 @@ impl TryFrom<FragmentDiff> for Fragment {
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum Statics {
String(String),
Statics(Vec<String>),
TemplateRef(i32),
}
Expand Down Expand Up @@ -686,22 +688,6 @@ impl FragmentMerge for Root {
fn merge(self, diff: Self::DiffItem) -> Result<Self, MergeError> {
let fragment = self.fragment.merge(diff.fragment)?;
let components = self.components.merge(diff.components)?;
/*
let components = match (self.components, diff.components) {
(None, None) => None,
(None, Some(component_diff)) => {
let mut components: HashMap<String, Component> = HashMap::new();
for (key, comp) in component_diff.into_iter() {
components.insert(key, comp.to_new_component()?);
}
Some(components)
}
(Some(components), None) => Some(components),
(Some(new_components), Some(component_diff)) => {
Some(new_components.merge(component_diff)?)
}
};
*/
Ok(Self {
fragment,
components,
Expand All @@ -718,11 +704,12 @@ impl FragmentMerge for Fragment {
(
Fragment::Regular {
children: current_children,
statics: current_statics,
statics: mut current_statics,
reply: current_reply,
},
FragmentDiff::UpdateRegular {
children: children_diffs,
statics: new_statics,
reply: new_reply,
..
},
Expand All @@ -734,6 +721,9 @@ impl FragmentMerge for Fragment {
(Some(r), None) => Some(r),
(Some(_old), Some(new)) => Some(new),
};
if let Some(new_statics) = new_statics {
current_statics = new_statics;
};
Ok(Self::Regular {
children: new_children,
statics: current_statics,
Expand Down
4 changes: 2 additions & 2 deletions crates/wasm/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ if [ $TARGET = "web" ] ; then
${SED} -i 's/liveview_native_core_wasm_bg.wasm/liveview_native_core_wasm_bg.wasm.js/' liveview-native-core-wasm-web/liveview_native_core_wasm.js
${SED} -i 's/liveview_native_core_wasm_bg.wasm/liveview_native_core_wasm_bg.wasm.js/' liveview-native-core-wasm-web/liveview_native_core_wasm_bg.wasm.js
${SED} -i 's/liveview_native_core_wasm_bg.wasm/liveview_native_core_wasm_bg.wasm.js/' liveview-native-core-wasm-web/package.json
jq '.files += ["snippets/*"]' liveview-native-core-wasm-web/package.json > tmp.json && mv tmp.json ./liveview-native-core-wasm-web/package.json
jq '.files += ["snippets/*"]' liveview-native-core-wasm-web/package.json > liveview-native-core-wasm-web/tmp.json && mv liveview-native-core-wasm-web/tmp.json ./liveview-native-core-wasm-web/package.json
npm pack ./liveview-native-core-wasm-web
mv liveview_native_core_wasm*tgz ./liveview-native-core-wasm-web.tgz
fi
if [ $TARGET = "nodejs" ] ; then
wasm-pack build --no-typescript --target nodejs --out-dir ./liveview-native-core-wasm-nodejs
jq '.files += ["snippets/*"]' liveview-native-core-wasm-nodejs/package.json > tmp.json && mv tmp.json ./liveview-native-core-wasm-nodejs/package.json
jq '.files += ["snippets/*"]' liveview-native-core-wasm-nodejs/package.json > ./liveview-native-core-wasm-nodejs/tmp.json && mv ./liveview-native-core-wasm-nodejs/tmp.json ./liveview-native-core-wasm-nodejs/package.json
npm pack ./liveview-native-core-wasm-nodejs/
mv liveview_native_core_wasm*tgz ./liveview-native-core-wasm-nodejs.tgz
fi
70 changes: 44 additions & 26 deletions crates/wasm/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,27 @@
use liveview_native_core::diff::fragment::{FragmentMerge, Root, RootDiff};
use serde::Serialize;
use wasm_bindgen::prelude::*;

#[wasm_bindgen(inline_js = "
export function map_to_object(map) {
const out = Object.create(null);
map.forEach((value, key) => {
if (value instanceof Map) {
out[key] = map_to_object(value)
} else {
out[key] = value
}
});
return out;
}")]
extern "C" {
fn map_to_object(map: JsValue) -> JsValue;
}

#[wasm_bindgen]
pub struct Rendered {
inner: Root,
view_id: String,
}

#[derive(serde::Deserialize)]
#[derive(serde::Deserialize, Debug)]
pub struct RenderedExtractedInput {
#[serde(rename = "r")]
reply: Option<bool>,
#[serde(rename = "t")]
title: Option<String>,
#[serde(rename = "e", default = "Vec::new")]
events: Vec<String>,
#[serde(rename = "fingerprint")]
_finger_print: Option<i32>,
#[serde(flatten)]
diff: RootDiff,
}
#[derive(serde::Serialize)]
#[derive(serde::Serialize, Debug)]
pub struct RenderedExtractedOutput {
reply: Option<bool>,
title: Option<String>,
Expand All @@ -50,23 +38,30 @@ impl From<RenderedExtractedInput> for RenderedExtractedOutput {
}
}
}
use log::{debug, info};

#[wasm_bindgen]
impl Rendered {
#[wasm_bindgen(constructor)]
pub fn new(_view_id: i32, rendered: JsValue) -> Result<Rendered, JsError> {
pub fn new(view_id: String, rendered: JsValue) -> Result<Rendered, JsError> {
console_error_panic_hook::set_once();
let _ = console_log::init_with_level(log::Level::Debug);
debug!("New rendered: {rendered:?}");
let root_diff: RootDiff = serde_wasm_bindgen::from_value(rendered)?;
let root: Root = root_diff.try_into()?;
Ok(Rendered { inner: root })
Ok(Rendered {
inner: root,
view_id,
})
}
#[wasm_bindgen(js_name = "mergeDiff")]
pub fn merge_diff(&mut self, diff: JsValue) -> Result<(), JsError> {
debug!("Merging diff: {diff:?}");
let diff: RootDiff = serde_wasm_bindgen::from_value(diff)?;
log::info!("DIFF: {diff:#?}");
info!("DIFF: {diff:#?}");
info!("BEFORE MERGE: {:#?}", self.inner);
self.inner = self.inner.clone().merge(diff)?;
log::info!("MERGED: {:#?}", self.inner);
info!("MERGED: {:#?}", self.inner);
Ok(())
}
#[wasm_bindgen(js_name = "isComponentOnlyDiff")]
Expand All @@ -82,6 +77,12 @@ impl Rendered {
let root: Root = diff.try_into()?;
Ok(root.component_cids())
}

#[wasm_bindgen(js_name = "resetRender")]
pub fn reset_render(&self, _cid: i32) -> Result<JsValue, JsError> {
todo!()
}

#[wasm_bindgen(js_name = "getComponent")]
pub fn get_component(&self, diff: JsValue, cid: i32) -> Result<JsValue, JsError> {
let diff: RootDiff = serde_wasm_bindgen::from_value(diff)?;
Expand All @@ -108,23 +109,40 @@ impl Rendered {
root.is_new_fingerprint()
}
pub fn get(&self) -> Result<JsValue, JsError> {
let map = serde_wasm_bindgen::to_value(&self.inner)?;
Ok(map_to_object(map))
let serializer = serde_wasm_bindgen::Serializer::json_compatible();
let map = self
.inner
.serialize(&serializer)
.expect("Failed to serialize");
Ok(map)
}
#[wasm_bindgen(js_name = "toString")]
pub fn to_string(&self) -> Result<JsValue, JsError> {
let out = js_sys::Array::new();
let _ = console_log::init_with_level(log::Level::Debug);
let rendered: String = self.inner.clone().try_into()?;
out.push(&rendered.into());
let streams = js_sys::Set::default();
out.push(&streams);

Ok(out.into())
}
#[wasm_bindgen(js_name = "parentViewId")]
pub fn parent_view_id(&self) -> String {
self.view_id.clone()
}

pub fn extract(diff: JsValue) -> Result<JsValue, JsError> {
let extracted: RenderedExtractedInput = serde_wasm_bindgen::from_value(diff)?;
let extracted: RenderedExtractedOutput = extracted.into();
let map = serde_wasm_bindgen::to_value(&extracted)?;
Ok(map)
// This is needed because various fields in RootDiff won't be included.
// The json compatible serializer is a bit more costly.
// https://github.com/RReverser/serde-wasm-bindgen?tab=readme-ov-file#supported-types
let serializer = serde_wasm_bindgen::Serializer::json_compatible();

let out = extracted
.serialize(&serializer)
.expect("Failed to serialize");
Ok(out)
}
}
Loading