* wip: fix manganis import path * upgrade tauri bundler, convert todo!() to unimplemented!() * feat: mobile hotreloading + ios asset configuration * remove oid demo and its associated env vars * nuke all the things that cause cache thrashing * swap to Asset type * add some more logs * display impl for attrvalue * dont panic on collect failure * cut down web deps to 150 * clean up deps in a lot of places, simplify build scripts * clean up asset resolution and cli-dev profile * wire up absolute paths for manganis asset in a particular mode * move document related things around to shorten compile times * move most things to `document::Item` and then decompose the cli-config * switch link to stylesheet for clarity in examples * move manganis workspace example to examples folder * decompose manganis-cli-support * dont need mobile demo anymore * remove gloo dialogs * html doesnt need document * rename hotreload to devtools * really clean up the html crate * fix weird rsx spacing * clean up desktop to use callback * clean up document a bit * re-wire up devsocket * fix utf css * yeeeet that js out of here * synthetic web system works * web crate almost done being cleaned up * desktop mostly cleaned up too * clean up cli a bit * more cli cleanup * cli builds again * clean up cli, inline structs, cut down number of unique types where possible * hotreload works again for desktop and this time, mobile too * cfg out tungstenite * devserver err * more refactor to cli builder * switch to unbounded_send * new structure is much cleaner * add http serve subcommand * bundled hotreload * kick stylsheets * clean up a bit more, split up eventloop * amazingly serve seems done * change from pub to pub(crate) in cli * remove tools * bit more polish to cli * fix issue with join * gracefully handle fullstack without a server * fullstack mobile demo * fix launch function, move projects into example-projects folder * hoist examples * add "run" command * clean up launch * remove old manual websocket receiver * doctor command * allow desktop to scroll * cut apart router crate * dont put launch in prelude * use dioxus::launch where possible * rename rsx, cut out hotreload tests * remove liveview project * bump native * fix compile for renderers * move sync event response out of interpreter * move render in serve * rollback settings change * cli compiles, huzzah! * change uris for asset * fix asset * new tui screen * new cargo-like tui works * very very verty close * it works! very small bug with incorrect grapheme calc * Clean up devserver a bit * status sytem * tidy up debug filters * clean up logging situation * Fix a number of bugs with log printing * new printing system is more reliable * wire up more stuff * things working but fullstack is having issues * fullstack works again! * hotreloading bundled assets works again * bundled hotreload and beginnings of macos bundling * Hotreload desktop * combined server builds * add build handle * fix fullstack assets * make open async, add some hooks for ios * migrate filemap to runner * wip global crate system * fixup bundles + organize asset * fix asset location bug * all the bundled reloading! * open ios simulator! * full hotreload support for mobile + serverfn * basic cleanups * clean up dx * Move filemap * fix cutting of newlines * assets workibg, some android * hoist wry/tao * use sync locks and headers to fix issues with android * desktop -> mac/win/linux with alias * better logging * feat: workspace (entire computer!) hotreload * should rebuild toggle, ios simulator bootup * proper mobile support in launch * more robust handling of assets * fix cargo * bring back some of tauri bundle * make warnings go away, clippy happy on cli * some final clippy cleanups * fmt * move manganis to its own folder * upgrade bundle to stable * drastically slim down manganis, prepping for merge * typos, failing test, docsrs config * remove static gen test * nix static gen test * we use --platform web instead of --platform fullstack now * only bind dev urls in desktop/mobile * install gtk * nix static gen * split build dir by app name
169 lines
6.3 KiB
Rust
169 lines
6.3 KiB
Rust
use dioxus_core::internal::{
|
|
HotReloadTemplateWithLocation, HotReloadedTemplate, TemplateGlobalKey,
|
|
};
|
|
use dioxus_core_types::HotReloadingContext;
|
|
use dioxus_rsx::CallBody;
|
|
use dioxus_rsx_hotreload::{ChangedRsx, HotReloadResult};
|
|
use std::path::PathBuf;
|
|
use std::{collections::HashMap, path::Path};
|
|
use syn::spanned::Spanned;
|
|
|
|
/// A struct that stores state of rsx! files and their parsed bodies.
|
|
///
|
|
/// This keeps track of changes to rsx files and helps determine if a file can be hotreloaded or if
|
|
/// the project needs to be rebuilt.
|
|
pub(crate) struct HotreloadFilemap {
|
|
/// Map of rust files to their contents
|
|
///
|
|
/// Once this is created, we won't change the contents, to preserve the ability to hotreload
|
|
/// from the original source mapping, unless the file change results in a full rebuild.
|
|
map: HashMap<PathBuf, CachedFile>,
|
|
}
|
|
|
|
struct CachedFile {
|
|
contents: String,
|
|
most_recent: Option<String>,
|
|
templates: HashMap<TemplateGlobalKey, HotReloadedTemplate>,
|
|
}
|
|
|
|
pub enum HotreloadResult {
|
|
Rsx(Vec<HotReloadTemplateWithLocation>),
|
|
Notreloadable,
|
|
NotParseable,
|
|
}
|
|
|
|
impl HotreloadFilemap {
|
|
/// Create a new empty filemap.
|
|
///
|
|
/// Make sure to fill the filemap, either automatically with `fill_from_filesystem` or manually with `add_file`;
|
|
pub fn new() -> Self {
|
|
Self {
|
|
map: Default::default(),
|
|
}
|
|
}
|
|
|
|
/// Add a file to the filemap.
|
|
pub(crate) fn add_file(&mut self, path: PathBuf, contents: String) {
|
|
self.map.insert(
|
|
path,
|
|
CachedFile {
|
|
contents,
|
|
most_recent: None,
|
|
templates: Default::default(),
|
|
},
|
|
);
|
|
}
|
|
|
|
/// Commit the changes to the filemap, overwriting the contents of the files
|
|
///
|
|
/// Removes any cached templates and replaces the contents of the files with the most recent
|
|
///
|
|
/// todo: we should-reparse the contents so we never send a new version, ever
|
|
pub fn force_rebuild(&mut self) {
|
|
for cached_file in self.map.values_mut() {
|
|
if let Some(most_recent) = cached_file.most_recent.take() {
|
|
cached_file.contents = most_recent;
|
|
}
|
|
cached_file.templates.clear();
|
|
}
|
|
}
|
|
|
|
/// Try to update the rsx in a file, returning the templates that were hotreloaded
|
|
///
|
|
/// If the templates could not be hotreloaded, this will return an error. This error isn't fatal, per se,
|
|
/// but it does mean that we could not successfully hotreload the file in-place.
|
|
///
|
|
/// It's expected that the file path you pass in is relative the crate root. We have no way of
|
|
/// knowing if it's *not*, so we'll assume it is.
|
|
///
|
|
/// This does not do any caching on what intermediate state, like previous hotreloads, so you need
|
|
/// to do that yourself.
|
|
pub(crate) fn update_rsx<Ctx: HotReloadingContext>(
|
|
&mut self,
|
|
path: &Path,
|
|
new_contents: String,
|
|
) -> HotreloadResult {
|
|
// Get the cached file if it exists
|
|
let Some(cached_file) = self.map.get_mut(path) else {
|
|
return HotreloadResult::NotParseable;
|
|
};
|
|
|
|
// We assume we can parse the old file and the new file
|
|
// We should just ignore hotreloading files that we can't parse
|
|
// todo(jon): we could probably keep the old `File` around instead of re-parsing on every hotreload
|
|
let (Ok(old_file), Ok(new_file)) = (
|
|
syn::parse_file(&cached_file.contents),
|
|
syn::parse_file(&new_contents),
|
|
) else {
|
|
tracing::debug!("Diff rsx returned not parseable");
|
|
return HotreloadResult::NotParseable;
|
|
};
|
|
|
|
// Update the most recent version of the file, so when we force a rebuild, we keep operating on the most recent version
|
|
cached_file.most_recent = Some(new_contents);
|
|
|
|
// todo(jon): allow server-fn hotreloading
|
|
// also whyyyyyyyyy is this (new, old) instead of (old, new)? smh smh smh
|
|
let Some(changed_rsx) = dioxus_rsx_hotreload::diff_rsx(&new_file, &old_file) else {
|
|
tracing::debug!("Diff rsx returned notreladable");
|
|
return HotreloadResult::Notreloadable;
|
|
};
|
|
|
|
let mut out_templates = vec![];
|
|
for ChangedRsx { old, new } in changed_rsx {
|
|
let old_start = old.span().start();
|
|
|
|
let old_parsed = syn::parse2::<CallBody>(old.tokens);
|
|
let new_parsed = syn::parse2::<CallBody>(new.tokens);
|
|
let (Ok(old_call_body), Ok(new_call_body)) = (old_parsed, new_parsed) else {
|
|
continue;
|
|
};
|
|
|
|
// Format the template location, normalizing the path
|
|
let file_name: String = path
|
|
.components()
|
|
.map(|c| c.as_os_str().to_string_lossy())
|
|
.collect::<Vec<_>>()
|
|
.join("/");
|
|
|
|
// Returns a list of templates that are hotreloadable
|
|
let results = HotReloadResult::new::<Ctx>(
|
|
&old_call_body.body,
|
|
&new_call_body.body,
|
|
file_name.clone(),
|
|
);
|
|
|
|
// If no result is returned, we can't hotreload this file and need to keep the old file
|
|
let Some(results) = results else {
|
|
return HotreloadResult::Notreloadable;
|
|
};
|
|
|
|
// Only send down templates that have roots, and ideally ones that have changed
|
|
// todo(jon): maybe cache these and don't send them down if they're the same
|
|
for (index, template) in results.templates {
|
|
if template.roots.is_empty() {
|
|
continue;
|
|
}
|
|
|
|
// Create the key we're going to use to identify this template
|
|
let key = TemplateGlobalKey {
|
|
file: file_name.clone(),
|
|
line: old_start.line,
|
|
column: old_start.column + 1,
|
|
index,
|
|
};
|
|
|
|
// if the template is the same, don't send its
|
|
if cached_file.templates.get(&key) == Some(&template) {
|
|
continue;
|
|
};
|
|
|
|
cached_file.templates.insert(key.clone(), template.clone());
|
|
out_templates.push(HotReloadTemplateWithLocation { template, key });
|
|
}
|
|
}
|
|
|
|
HotreloadResult::Rsx(out_templates)
|
|
}
|
|
}
|