-
Notifications
You must be signed in to change notification settings - Fork 87
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: ✨ added static exporting (#23)
* feat: ✨ `FsConfigManager` now creates directory structures automatically * feat(templates): ✨ prerendered `<head>` at build time where possible Also laid foundations for static exporting (still WIP). Closes #22. * feat(exporting): ✨ made exporting system create full html files for initial loads * feat(exporting): ✨ made exporting system create json partials for subsequent loads * style: 🎨 ran `cargo fmt` * feat(exporting): ✨ created cli export command Still WIP... * refactor(testing): ♻️ changed exporting emoji to 📦 * fix(exporting): 🐛 wrote initial load files as `.html` so they aren't considered octet streams This means servers have to strip extensions. * feat(exporting): ✨ made exporting systems copy `static` directory Support for static aliases coming next. * refactor(routing): ♻️ added `.json` extension to page partials Makes interpretation easier for most static file servers. * refactor: 🔥 removed unnecessary serving of `render_conf.json` * feat(exporting): ✨ added export support for static aliases * style: 🎨 ran `cargo fmt` * docs(book): 📝 added docs on static exporting * docs(book): 📝 merged `next` docs into `0.2.x` * docs(book): 📝 added docs for exporting file extension pitfall * refactor: 🚨 addressed clippy warnings
- Loading branch information
1 parent
29344ad
commit 4838ba4
Showing
32 changed files
with
835 additions
and
188 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,8 @@ | |
"cli", | ||
"book", | ||
"examples", | ||
"testing" | ||
"testing", | ||
"templates", | ||
"exporting" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Static Exporting | ||
|
||
The easiest way to deploy Perseus is as a set of static files, which is supported if your app uses only the _build state_ and _build paths_ strategies, or none at all. If you use _incremental generation_, _revalidation_, or _request state_ in any of your templates though, you can't export your app to static file, because these strategies require a custom server. For these cases, please continue to the rest of this section to learn how to deploy your more complex setup. | ||
|
||
However, if your app only needs to run server-side computations at build-time, then you can export it to a set of static files without changing anything, simply by running `perseus export`. This will create a new directory called `.perseus/dist/exported`, the contents of which can be served on a system like [GitHub Pages](https:://pages.github.com). Your app should behave in the exact same way with exporting as with normal serving. If this isn't the case, please [open an issue](https://github.com/arctic-hen7/perseus/issues/new/choose). | ||
|
||
There is only one known difference between the behavior of your exported site and your normally served site, and that's regarding [static aliases](../static-content.md). In a normal serving scenario, any static aliases that conflicted with a Perseus page or internal asset would be ignored, but, in an exporting context, **any static aliases that conflict with Perseus pages will override them**! If you suspect this might be happening to you, try exporting without those aliases and make sure the URL of your alias file doesn't already exist (in which case it would be a Perseus component). | ||
|
||
## File Extensions | ||
|
||
One slight hiccup with Perseus' static exporting system comes with regards to the `.html` file extension. Perseus' server expects that pages shouldn't have such extensions (hence `/about` rather than `/about.html`), but, when statically generated, they must have these extensions in the filesystem. So, if you don't want these extensions for your users (and if you want consistent behavior between exporting and serving), it's up to whatever system you're hosting your files with to strip these extensions. Many systems do this automatically, though some (like Python's `http.server`) do not. | ||
|
||
One of the best systems for testing static exporting on your local machine is the [`serve`](https://github.com/versel/serve) JavaScript package, which can be run from the command-line without touching any JavaScript, and it handles this problem automatically. However, other solutions certainly exist if you don't want any JS polluting your system! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Deploying | ||
|
||
Perseus is a complex system, but we aim to make deploying it as easy as possible. This section will describe a few different types of Perseus deployments, and how they can be managed. | ||
|
||
*Note: Perseus deployment is still under design and development, so this information in particular is subject to rapid change before v1.0.0.* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Static Exporting | ||
|
||
The easiest way to deploy Perseus is as a set of static files, which is supported if your app uses only the _build state_ and _build paths_ strategies, or none at all. If you use _incremental generation_, _revalidation_, or _request state_ in any of your templates though, you can't export your app to static file, because these strategies require a custom server. For these cases, please continue to the rest of this section to learn how to deploy your more complex setup. | ||
|
||
However, if your app only needs to run server-side computations at build-time, then you can export it to a set of static files without changing anything, simply by running `perseus export`. This will create a new directory called `.perseus/dist/exported`, the contents of which can be served on a system like [GitHub Pages](https:://pages.github.com). Your app should behave in the exact same way with exporting as with normal serving. If this isn't the case, please [open an issue](https://github.com/arctic-hen7/perseus/issues/new/choose). | ||
|
||
There is only one known difference between the behavior of your exported site and your normally served site, and that's regarding [static aliases](../static-content.md). In a normal serving scenario, any static aliases that conflicted with a Perseus page or internal asset would be ignored, but, in an exporting context, **any static aliases that conflict with Perseus pages will override them**! If you suspect this might be happening to you, try exporting without those aliases and make sure the URL of your alias file doesn't already exist (in which case it would be a Perseus component). | ||
|
||
## File Extensions | ||
|
||
One slight hiccup with Perseus' static exporting system comes with regards to the `.html` file extension. Perseus' server expects that pages shouldn't have such extensions (hence `/about` rather than `/about.html`), but, when statically generated, they must have these extensions in the filesystem. So, if you don't want these extensions for your users (and if you want consistent behavior between exporting and serving), it's up to whatever system you're hosting your files with to strip these extensions. Many systems do this automatically, though some (like Python's `http.server`) do not. | ||
|
||
One of the best systems for testing static exporting on your local machine is the [`serve`](https://github.com/versel/serve) JavaScript package, which can be run from the command-line without touching any JavaScript, and it handles this problem automatically. However, other solutions certainly exist if you don't want any JS polluting your system! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Deploying | ||
|
||
Perseus is a complex system, but we aim to make deploying it as easy as possible. This section will describe a few different types of Perseus deployments, and how they can be managed. | ||
|
||
*Note: Perseus deployment is still under design and development, so this information in particular is subject to rapid change before v1.0.0.* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
use app::{ | ||
get_config_manager, get_locales, get_static_aliases, get_templates_map, get_templates_vec, | ||
get_translations_manager, APP_ROOT, | ||
}; | ||
use fs_extra::dir::{copy as copy_dir, CopyOptions}; | ||
use futures::executor::block_on; | ||
use perseus::{build_app, export_app, SsrNode}; | ||
use std::fs; | ||
use std::path::PathBuf; | ||
|
||
fn main() { | ||
let exit_code = real_main(); | ||
std::process::exit(exit_code) | ||
} | ||
|
||
fn real_main() -> i32 { | ||
let config_manager = get_config_manager(); | ||
let translations_manager = block_on(get_translations_manager()); | ||
let locales = get_locales(); | ||
|
||
// Build the site for all the common locales (done in parallel), denying any non-exportable features | ||
let build_fut = build_app( | ||
get_templates_vec::<SsrNode>(), | ||
&locales, | ||
&config_manager, | ||
&translations_manager, | ||
// We use another binary to handle normal building | ||
true, | ||
); | ||
if let Err(err) = block_on(build_fut) { | ||
eprintln!("Static exporting failed: '{}'.", err); | ||
return 1; | ||
} | ||
// Turn the build artifacts into self-contained static files | ||
let export_fut = export_app( | ||
get_templates_map(), | ||
"../index.html", | ||
&locales, | ||
APP_ROOT, | ||
&config_manager, | ||
&translations_manager, | ||
); | ||
if let Err(err) = block_on(export_fut) { | ||
eprintln!("Static exporting failed: '{}'.", err); | ||
return 1; | ||
} | ||
|
||
// Copy the `static` directory into the export package if it exists | ||
// We don't use a config manager here because static files are always handled on-disk in Perseus (for now) | ||
let static_dir = PathBuf::from("../static"); | ||
if static_dir.exists() { | ||
if let Err(err) = copy_dir(&static_dir, "dist/exported/.perseus/", &CopyOptions::new()) { | ||
eprintln!( | ||
"Static exporting failed: 'couldn't copy static directory: '{}''", | ||
err.to_string() | ||
); | ||
return 1; | ||
} | ||
} | ||
// Loop through any static aliases and copy them in too | ||
// Unlike with the server, these could override pages! | ||
// We'll copy from the alias to the path (it could be a directory or a file) | ||
// Remember: `alias` has a leading `/`! | ||
for (alias, path) in get_static_aliases() { | ||
let from = PathBuf::from(path); | ||
let to = format!("dist/exported{}", alias); | ||
|
||
if from.is_dir() { | ||
if let Err(err) = copy_dir(&from, &to, &CopyOptions::new()) { | ||
eprintln!( | ||
"Static exporting failed: 'couldn't copy static alias directory: '{}''", | ||
err.to_string() | ||
); | ||
return 1; | ||
} | ||
} else if let Err(err) = fs::copy(&from, &to) { | ||
eprintln!( | ||
"Static exporting failed: 'couldn't copy static alias file: '{}''", | ||
err.to_string() | ||
); | ||
return 1; | ||
} | ||
} | ||
|
||
println!("Static exporting successfully completed!"); | ||
0 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
pub mod about; | ||
pub mod index; | ||
pub mod post; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
use perseus::{StringResultWithCause, Template}; | ||
use serde::{Deserialize, Serialize}; | ||
use std::rc::Rc; | ||
use sycamore::prelude::{component, template, GenericNode, Template as SycamoreTemplate}; | ||
|
||
#[derive(Serialize, Deserialize)] | ||
pub struct PostPageProps { | ||
title: String, | ||
content: String, | ||
} | ||
|
||
#[component(PostPage<G>)] | ||
pub fn post_page(props: PostPageProps) -> SycamoreTemplate<G> { | ||
let title = props.title; | ||
let content = props.content; | ||
template! { | ||
h1 { | ||
(title) | ||
} | ||
p { | ||
(content) | ||
} | ||
} | ||
} | ||
|
||
pub fn get_template<G: GenericNode>() -> Template<G> { | ||
Template::new("post") | ||
.build_paths_fn(Rc::new(get_static_paths)) | ||
.build_state_fn(Rc::new(get_static_props)) | ||
.template(template_fn()) | ||
} | ||
|
||
pub async fn get_static_props(path: String) -> StringResultWithCause<String> { | ||
// This is just an example | ||
let title = urlencoding::decode(&path).unwrap(); | ||
let content = format!( | ||
"This is a post entitled '{}'. Its original slug was '{}'.", | ||
title, path | ||
); | ||
|
||
Ok(serde_json::to_string(&PostPageProps { | ||
title: title.to_string(), | ||
content, | ||
}) | ||
.unwrap()) | ||
} | ||
|
||
pub async fn get_static_paths() -> Result<Vec<String>, String> { | ||
Ok(vec!["test".to_string(), "blah/test/blah".to_string()]) | ||
} | ||
|
||
pub fn template_fn<G: GenericNode>() -> perseus::template::TemplateFn<G> { | ||
Rc::new(|props| { | ||
template! { | ||
PostPage( | ||
serde_json::from_str::<PostPageProps>(&props.unwrap()).unwrap() | ||
) | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.