refactor to eliminate duplicate code (#380)

This commit is contained in:
Gentle 2023-01-26 19:04:59 +01:00 committed by GitHub
parent fc7199f188
commit be1343fa88
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 1 additions and 172 deletions

View File

@ -442,178 +442,7 @@ pub fn render_app_to_stream<IV>(
where
IV: IntoView,
{
move |req: Request<Body>| {
Box::pin({
let options = options.clone();
let app_fn = app_fn.clone();
let default_res_options = ResponseOptions::default();
let res_options2 = default_res_options.clone();
let res_options3 = default_res_options.clone();
async move {
// Need to get the path and query string of the Request
// For reasons that escape me, if the incoming URI protocol is https, it provides the absolute URI
// if http, it returns a relative path. Adding .path() seems to make it explicitly return the relative uri
let path = req.uri().path_and_query().unwrap().as_str();
let full_path = format!("http://leptos.dev{path}");
let pkg_path = &options.site_pkg_dir;
let output_name = &options.output_name;
// Because wasm-pack adds _bg to the end of the WASM filename, and we want to mantain compatibility with it's default options
// we add _bg to the wasm files if cargo-leptos doesn't set the env var LEPTOS_OUTPUT_NAME
// Otherwise we need to add _bg because wasm_pack always does. This is not the same as options.output_name, which is set regardless
let mut wasm_output_name = output_name.clone();
if std::env::var("LEPTOS_OUTPUT_NAME").is_err() {
wasm_output_name.push_str("_bg");
}
let site_ip = &options.site_address.ip().to_string();
let reload_port = options.reload_port;
let leptos_autoreload = match std::env::var("LEPTOS_WATCH").is_ok() {
true => format!(
r#"
<script crossorigin="">(function () {{
var ws = new WebSocket('ws://{site_ip}:{reload_port}/live_reload');
ws.onmessage = (ev) => {{
let msg = JSON.parse(ev.data);
if (msg.all) window.location.reload();
if (msg.css) {{
const link = document.querySelector("link#leptos");
if (link) {{
let href = link.getAttribute('href').split('?')[0];
let newHref = href + '?version=' + new Date().getMilliseconds();
link.setAttribute('href', newHref);
}} else {{
console.warn("Could not find link#leptos");
}}
}};
}};
ws.onclose = () => console.warn('Live-reload stopped. Manual reload necessary.');
}})()
</script>
"#
),
false => "".to_string(),
};
let head = format!(
r#"<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<link rel="modulepreload" href="/{pkg_path}/{output_name}.js">
<link rel="preload" href="/{pkg_path}/{wasm_output_name}.wasm" as="fetch" type="application/wasm" crossorigin="">
<script type="module">import init, {{ hydrate }} from '/{pkg_path}/{output_name}.js'; init('/{pkg_path}/{wasm_output_name}.wasm').then(hydrate);</script>
{leptos_autoreload}
"#
);
let tail = "</body></html>";
let (mut tx, rx) = futures::channel::mpsc::channel(8);
spawn_blocking({
let app_fn = app_fn.clone();
move || {
tokio::runtime::Runtime::new()
.expect("couldn't spawn runtime")
.block_on({
let app_fn = app_fn.clone();
async move {
tokio::task::LocalSet::new()
.run_until(async {
let app = {
let full_path = full_path.clone();
let req_parts = generate_request_parts(req).await;
move |cx| {
let integration = ServerIntegration {
path: full_path.clone(),
};
provide_context(
cx,
RouterIntegrationContext::new(integration),
);
provide_context(cx, MetaContext::new());
provide_context(cx, req_parts);
provide_context(cx, default_res_options);
app_fn(cx).into_view(cx)
}
};
let (bundle, runtime, scope) =
render_to_stream_with_prefix_undisposed(
app,
|cx| {
let head = use_context::<MetaContext>(cx)
.map(|meta| meta.dehydrate())
.unwrap_or_default();
format!("{head}</head><body>").into()
},
);
let mut shell = Box::pin(bundle);
while let Some(fragment) = shell.next().await {
_ = tx.send(fragment).await;
}
// Extract the value of ResponseOptions from here
let cx = Scope { runtime, id: scope };
let res_options =
use_context::<ResponseOptions>(cx).unwrap();
let new_res_parts = res_options.0.read().await.clone();
let mut writable = res_options2.0.write().await;
*writable = new_res_parts;
runtime.dispose();
tx.close_channel();
})
.await;
}
});
}
});
let mut stream = Box::pin(
futures::stream::once(async move { head.clone() })
.chain(rx)
.chain(futures::stream::once(async { tail.to_string() }))
.map(|html| Ok(Bytes::from(html))),
);
// Get the first, second, and third chunks in the stream, which renders the app shell, and thus allows Resources to run
let first_chunk = stream.next().await;
let second_chunk = stream.next().await;
let third_chunk = stream.next().await;
// Extract the resources now that they've been rendered
let res_options = res_options3.0.read().await;
let complete_stream = futures::stream::iter([
first_chunk.unwrap(),
second_chunk.unwrap(),
third_chunk.unwrap(),
])
.chain(stream);
let mut res = Response::new(StreamBody::new(
Box::pin(complete_stream) as PinnedHtmlStream
));
if let Some(status) = res_options.status {
*res.status_mut() = status
}
let mut res_headers = res_options.headers.clone();
res.headers_mut().extend(res_headers.drain());
res
}
})
}
render_app_to_stream_with_context(options, |_| {}, app_fn)
}
/// Returns an Axum [Handler](axum::handler::Handler) that listens for a `GET` request and tries