Skip to content

Commit 5baf9bf

Browse files
committed
feat: ✨ added isr
1 parent 1af26dc commit 5baf9bf

File tree

9 files changed

+81
-20
lines changed

9 files changed

+81
-20
lines changed

examples/showcase/app/src/bin/build.rs

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ fn main() {
1212
pages::index::get_page::<SsrNode>(),
1313
pages::about::get_page::<SsrNode>(),
1414
pages::post::get_page::<SsrNode>(),
15+
pages::new_post::get_page::<SsrNode>(),
1516
pages::ip::get_page::<SsrNode>()
1617
], &config_manager).expect("Static generation failed!");
1718

examples/showcase/app/src/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ enum AppRoute {
1212
Index,
1313
#[to("/about")]
1414
About,
15+
#[to("/post/new")]
16+
NewPost,
1517
#[to("/post/<slug>")]
1618
Post {
1719
slug: String
@@ -52,6 +54,10 @@ pub fn run() -> Result<(), JsValue> {
5254
format!("post/{}", slug),
5355
pages::post::template_fn()
5456
),
57+
AppRoute::NewPost => app_shell(
58+
"post/new".to_string(),
59+
pages::new_post::template_fn()
60+
),
5561
AppRoute::Ip => app_shell(
5662
"ip".to_string(),
5763
pages::ip::template_fn()

examples/showcase/app/src/pages/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pub mod index;
22
pub mod about;
33
pub mod post;
44
pub mod ip;
5+
pub mod new_post;
56

67
use perseus::{get_templates_map, template::Template};
78
use sycamore::prelude::GenericNode;
@@ -13,6 +14,7 @@ pub fn get_templates_map<G: GenericNode>() -> HashMap<String, Template<G>> {
1314
index::get_page::<G>(),
1415
about::get_page::<G>(),
1516
post::get_page::<G>(),
17+
new_post::get_page::<G>(),
1618
ip::get_page::<G>()
1719
]
1820
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
use sycamore::prelude::{template, component, GenericNode, Template as SycamoreTemplate};
2+
use perseus::template::Template;
3+
4+
#[component(NewPostPage<G>)]
5+
pub fn new_post_page() -> SycamoreTemplate<G> {
6+
template! {
7+
p { "New post creator." }
8+
}
9+
}
10+
11+
pub fn get_page<G: GenericNode>() -> Template<G> {
12+
Template::new("post/new")
13+
.template(template_fn())
14+
}
15+
16+
pub fn template_fn<G: GenericNode>() -> perseus::template::TemplateFn<G> {
17+
Box::new(|_| template! {
18+
NewPostPage()
19+
}
20+
)
21+
}

examples/showcase/app/src/pages/post.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub fn get_page<G: GenericNode>() -> Template<G> {
3232

3333
pub fn get_static_props(path: String) -> String {
3434
let path_vec: Vec<&str> = path.split('/').collect();
35-
let title_slug = path_vec[0];
35+
let title_slug = path_vec[path_vec.len() - 1];
3636
// This is just an example
3737
let title = urlencoding::decode(title_slug).unwrap();
3838
let content = format!("This is a post entitled '{}'. Its original slug was '{}'.", title, title_slug);

examples/showcase/bonnie.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ build.cmd = [
77
"wasm-pack build --target web",
88
"rollup ./main.js --format iife --file ./pkg/bundle.js"
99
]
10-
build.subcommands.--watch = "find ../../ -not -path \"../../target/*\" -not -path \"../../.git/*\" | entr -s \"bonnie build\""
10+
build.subcommands.--watch = "find ../../ -not -path \"../../target/*\" -not -path \"../../.git/*\" -not -path \"../../examples/showcase/app/dist/*\" | entr -s \"bonnie build\""
1111
serve = [
1212
"cd server",
1313
"cargo watch -w ../../../ -x \"run\""

src/build.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ pub fn build_template(
4141
let full_path = match template.uses_build_paths() {
4242
true => urlencoding::encode(&format!("{}/{}", &template_path, path)).to_string(),
4343
// We don't want to concatenate the name twice if we don't have to
44-
false => template_path.clone()
44+
false => urlencoding::encode(&template_path).to_string()
4545
};
4646

4747
// Handle static initial state generation

src/serve.rs

+47-17
Original file line numberDiff line numberDiff line change
@@ -43,25 +43,25 @@ pub fn get_page(
4343
if let Some(template_root_path) = render_cfg.get(path) {
4444
template_name = template_root_path.to_string();
4545
}
46-
// Next, an ISR match (more complex)
47-
// We progressively look for more and more specificity of the path, adding each segment
48-
// That way, we're searching forwards rather than backwards, which is more efficient
49-
let path_segments: Vec<&str> = path.split('/').collect();
50-
for (idx, _) in path_segments.iter().enumerate() {
51-
// Make a path out of this and all the previous segments
52-
// For some reason, [0..0] gives nothing, so we need to `match` here
53-
let path_to_try = match idx {
54-
0 => path_segments[0].to_string(),
55-
_ => path_segments[0..idx].join("/")
56-
} + "/*";
46+
// Next, an ISR match (more complex), which we only want to run if we didn't get an exact match above
47+
if template_name.is_empty() {
48+
// We progressively look for more and more specificity of the path, adding each segment
49+
// That way, we're searching forwards rather than backwards, which is more efficient
50+
let path_segments: Vec<&str> = path.split('/').collect();
51+
for (idx, _) in path_segments.iter().enumerate() {
52+
// Make a path out of this and all the previous segments
53+
let path_to_try = path_segments[0..(idx + 1)].join("/") + "/*";
5754

58-
// If we find something, keep going until we don't (maximise specificity)
59-
if let Some(template_root_path) = render_cfg.get(&path_to_try) {
60-
template_name = template_root_path.to_string();
61-
} else {
62-
break;
55+
// If we find something, keep going until we don't (maximise specificity)
56+
if let Some(template_root_path) = render_cfg.get(&path_to_try) {
57+
template_name = template_root_path.to_string();
58+
} else {
59+
break;
60+
}
6361
}
6462
}
63+
64+
// if we still have nothing, then the page doesn't exist
6565
if template_name.is_empty() {
6666
bail!(ErrorKind::PageNotFound(path.to_string()))
6767
}
@@ -77,7 +77,37 @@ pub fn get_page(
7777
let state: Option<String>;
7878

7979
// Handle each different type of rendering (static paths have already been done though, so we don't need to deal with them)
80-
if template.uses_build_state() || template.is_basic() {
80+
// TODO make this system completely modular with state amalgamation
81+
if template.uses_incremental() {
82+
// The template uses ISR, check if it's already been rendered before and cached
83+
let html_res = config_manager.read(&format!("../app/dist/static/{}.html", path_encoded));
84+
if matches!(html_res, Ok(_)) && !cfg!(debug_assertions) {
85+
html = html_res.unwrap();
86+
// Get the static JSON (if it exists, but it should)
87+
state = match config_manager.read(&format!("../app/dist/static/{}.json", path_encoded)) {
88+
Ok(state) => Some(state),
89+
Err(_) => None
90+
};
91+
} else {
92+
// Note that we assume ISR is used with SSG (otherwise it would be completely pointless...)
93+
// We need to generate and cache this page for future usage
94+
state = Some(
95+
template.get_build_state(
96+
format!("{}/{}", template.get_path(), path)
97+
)?
98+
);
99+
html = sycamore::render_to_string(
100+
||
101+
template.render_for_template(state.clone())
102+
);
103+
// Cache all that
104+
config_manager
105+
.write(&format!("../app/dist/static/{}.json", path_encoded), &state.clone().unwrap())?;
106+
// Write that prerendered HTML to a static file
107+
config_manager
108+
.write(&format!("../app/dist/static/{}.html", path_encoded), &html)?;
109+
}
110+
} else if template.uses_build_state() || template.is_basic() {
81111
// Get the static HTML
82112
html = config_manager.read(&format!("../app/dist/static/{}.html", path_encoded))?;
83113
// Get the static JSON

src/template.rs

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub struct Template<G: GenericNode>
4646
/// A length of time after which to prerender the template again. This is equivalent to ISR in NextJS.
4747
revalidate_after: Option<String>,
4848
}
49+
// TODO mandate usage conditions (e.g. ISR needs SSG)
4950
impl<G: GenericNode> Template<G> {
5051
/// Creates a new template definition.
5152
pub fn new(path: impl Into<String> + std::fmt::Display) -> Self {

0 commit comments

Comments
 (0)