Skip to content

Commit

Permalink
docs(book): 📝 wrote large parts of advanced docs and some other pages
Browse files Browse the repository at this point in the history
  • Loading branch information
arctic-hen7 committed Sep 21, 2021
1 parent e321c38 commit d8fd43f
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 11 deletions.
16 changes: 8 additions & 8 deletions docs/next/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@
- [Incremental Generation]()
- [CLI](./cli.md)
- [Ejecting](./ejecting.md)
- [Config Managers]()
- [Testing]()
- [Styling]()
- [Config Managers](./config-managers.md)
- [Testing](./testing.md)
- [Styling](./styling.md)
***
# Advanced

- [Under the Hood]()
- [Architecture]()
- [Initial Loads]()
- [Subsequent Loads]()
- [Routing]()
- [Under the Hood](./advanced/intro.md)
- [Architecture](./advanced/arch.md)
- [Initial Loads](./advanced/initial-loads.md)
- [Subsequent Loads](./advanced/subsequent-loads.md)
- [Routing](./advanced/routing.md)
50 changes: 50 additions & 0 deletions docs/next/src/advanced/arch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Architecture

Perseus has five main components:

- `perseus` -- the core module that defines everything necessary to build a Perseus app if you try hard enough
- `perseus-actix-web` -- an integration that makes it easy to run Perseus on the [Actix Web](https://actix.rs) framework
- `perseus-cli` -- the command-line interface used to run Perseus apps conveniently
- `perseus-cli-builder` -- an internal crate created by the CLI responsible for building an app
- `perseus-cli-server` -- an internal crate created by the CLI responsible for serving an app and performing runtime logic

## Core

At the core of Perseus is the [`perseus`](https://docs.rs/perseus) module, which is used for nearly everything in Perseus. In theory, you could build a fully-functional app based on this crate alone, but you'd be reinventing the wheel at least three times. This crate exposes types for the i18n systems, configuration management, routing, and asset fetching, most of which aren't intended to be used directly by the user.

What is intended to be used directly is the `Template<G>` `struct`, which is integral to Perseus. This stores closures for every rendering strategy, which are executed as provided and necessary at build and runtime. Note that these are all stored in `Rc`s, and `Template<G>`s are cloned.

The other commonly used system from this crate is the `Translator` system, explained in detail in [the i18n section](../i18n/intro.md). `Translator`s are passed around in `Rc`s, and `TranslationsManager` on the server caches all translations by default in memory on the server.

## Actix Web Integration

The core of Perseus provides very few systems to set up a functional Perseus server though, which requires a significant amount of additional work. To this end, [`perseus-actix-web`](https://docs.rs/perseus-actix-web) is used to make this process easy. If you've ejected, you'll be working with this directly, which should be relatively simple, as it just accepts configuration options and then should simply work.

Note that this module provides a `configurer` function, which allows it to be modularly added to any existing Actix Web server, which is particularly useful if you want to run other endpoint on your server, or a system like [Diana](https://github.com/arctic-hen7/diana).

## CLI

As documented in [this section](../cli.md), the CLI simply runs commands to execute the last two components of the Perseus system, acting as a convenience. It also contains these two components inside its binary (using [`include_dir!`](https://github.com/Michael-F-Bryan/include_dir))

## CLI Builder

This system can be further broken down into two parts.

### Static Generator

This is a single binary that just imports the user's templates and some other information (like locales) and then calls `build_app`. This will result in generating a number of files to `.perseus/dist`, which will be served by the server to any clients, which will then hydrate those static pages into fully-fledged Sycamore templates.

### App Shell

This is encapsulated in `.perseus/src/lib.rs`, and it performs a number of integral functions:

- Ensures that any `panic!`s or the like ar printed properly in the browser console
- Creates and manages the internal router
- Renders your actual app
- Handles locale detection
- Invokes the core app shell to manage initial/subsequent loads and translations
- Handles error page displaying

## CLI Server

This is just an invocation of the `perseus-actix-web` module's systems with the data provided by the user through the `define_app!` macro. This also sets the default location for static content and the `index.html` file.
23 changes: 23 additions & 0 deletions docs/next/src/advanced/initial-loads.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Initial Loads

Perseus handles *initial loads* very differently from *subsequent loads*. The former refers to what's done when a user visits a page on a Perseus app from an external source (e.g. visiting from a search engine, redirected from another site), and this requires a full HTMl page to be sent that can be interpreted by the browser. By contrast, subsequent loads are loads between pages within the same Perseus app, which can be performed by the app shell (described in the next section).

The process of initial loads is slightly complex, and occurs like so (this example is for a page called `/posts/test`, rendered with incremental generation):

1. Browser requests `/posts/test` from the server.
2. Server matches requested URL to wildcard (`*`) and handles it with the server-side inferred router, determining which `Template<G>` to use.
3. Server calls internal core methods to render the page (using incremental generation strategy, but it doesn't need to know that), producing an HTML snippet and a set of JSON properties.
4. Server calls `template.render_head_str()` and injects the result into the document's `<head>` (avoiding `<title>` flashes and improving SEO) after a delimiter comment that separates it from the metadata on every page (which is hardcoded into `index.html`).
5. Server interpolates JSON state into `index.html` as a global variable in a `<script>`.
6. Server interpolates HTML snippet directly into the user's `index.html` file.
7. Server sends final HTML package to client, including Wasm (injected at build-time).
8. Browser renders HTML package, user sees content immediately.
9. Browser invokes Wasm, hands control to the app shell.
10. App shell checks if initial state declaration global variable is present, finds that it is and unsets it (so that it doesn't interfere with subsequent loads).
11. App shell moves server-rendered content out of `__perseus_content` and into `__perseus_content_rx`, which Sycamore's router had control over (allowing it to catch links and use the subsequent loads system).
12. App shell gets a translator if the app uses i18n.
13. App shell hydrates content at `__perseus_content_rx` with Sycamore and returns, the page is now interactive and has a translator context.

Note: if this app had used i18n, the server would've returned the app shell with no content, and the app shell, when invoked, would've immediately redirected the user to their preferred locale (or the closest equivalent).

The two files integral to this process are [`initial_load.rs`](https://github.com/arctic-hen7/perseus/blob/main/packages/perseus-actix-web/src/initial_load.rs) and [`shell.rs`](https://github.com/arctic-hen7/perseus/blob/main/packages/perseus/src/shell.rs).
3 changes: 3 additions & 0 deletions docs/next/src/advanced/intro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Under the Hood

This section of the documentation is devoted to explaining the inner workings of Perseus, which will be particularly useful if you choose to eject from the CLI's harness or if you want to contribute to Perseus!
1 change: 1 addition & 0 deletions docs/next/src/advanced/routing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Routing
21 changes: 21 additions & 0 deletions docs/next/src/advanced/subsequent-loads.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Subsequent Loads

if the user follows a link inside a Perseus app to another page within that same app, the Sycamore router will catch it and prevent the browser from requesting the new file from the server. The following will then occur (for an `/about` page rendered simply):

1. Sycamore router calls Perseus inferred router logic.
2. Perseus inferred router determines from new URL that template `about` should be used, returns to Sycamore router.
3. Sycamore router passes that to closure in `perseus-cli-builder` shell, which executes core app shell.
4. App shell checks if an initial load declaration global variable is present and finds none, hence it will proceed with the subsequent load system.
5. App shell fetches page data from `/.perseus/page/<locale>/about?template_name=about` (if the app isn't using i18n, `<locale>` will verbatim be `xx-XX`).
6. Server checks to ensure that locale is supported.
7. Server renders page using internal systems (in this case that will just return the static HTML file from `.perseus/dist/static/`).
8. Server returns JSON of HTML snippet (not complete file) and stringified properties.
9. App shell deserializes page data into state and HTML snippet.
10. App shell interpolates HTML snippet directly into `__perseus_content_rx` (which Sycamore router controls), user can now see new page.
11. App shell initializes translator if the app is using i18n.
12. App shell renders new `<head>` and updates it (needs the translator to do this).
13. App shell hydrates content at `__perseus_content_rx`, page is now interactive.

The two files integral to this process are [`page_data.rs`](https://github.com/arctic-hen7/perseus/blob/main/packages/perseus-actix-web/src/page_data.rs) and [`shell.rs`](https://github.com/arctic-hen7/perseus/blob/main/packages/perseus/src/shell.rs).

Note that this process still has one improvement to be made before v0.2.0: rendering the document head on the server and interpolating that simultaneously before the translator is fetched on the client side. This further eliminates the need for rendering the `<head>` on the client at all. The tracking issue is [here](https://github.com/arctic-hen7/perseus/issues/15).
5 changes: 5 additions & 0 deletions docs/next/src/config-managers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Config Managers

Perseus generates a number of files in its build process, which allow it to make your app extremely performant on the client and the server. By default, these are stored under `.perseus/dist/`, however there may be cases in which you want to store these files elsewhere, particularly given that **they need to modified at runtime**. While not recommended, it may be necessary in some deployments to move these files to a database or CMS. Note that this will have a notable performance impact on your app, and the default is always recommended.

That said, Perseus allows you to store content wherever you'd like with the `ConfigManager` trait, the default implementation of which is `FsConfigManager`, which takes a directory to use. Very similar to the [translations manager]() system, you can customize the options to this by providing your own instance of it under `define_app!`'s `config_manager` property, or you can build your own for interfacing with a non-filesystem storage apparatus. To do the latte, you'll need to consult [this file](https://github.com/arctic-hen7/perseus/blob/main/packages/perseus/src/config_manager.rs), and, if you're stuck, don't hesitate to ask a question under [discussions](https://github.com/arctic-hen7/perseus/discussions/new) on GitHub!
5 changes: 5 additions & 0 deletions docs/next/src/styling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Styling

Perseus uses [Sycamore](https://github.com/sycamore-rs/sycamore) for writing views, and styling is still [in development](https://sycamore-rs.netlify.app/docs/v0.6/advanced/css) there.

*Note: a blog post will be released soon on setting up TailwindCSS with Perseus if you're interested in that.*
3 changes: 3 additions & 0 deletions docs/next/src/testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Testing

Right now, no Rust web frameworks have great support for testing your components because the technology is still in development. For now, have a look at [`wasm_bindgen_test`](https://rustwasm.github.io/wasm-bindgen/wasm-bindgen-test/index.html)!
3 changes: 0 additions & 3 deletions docs/next/src/what-is-perseus.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@ let counter = Signal::new(0);
let increment = cloned!((counter) => move |_| counter.set(*counter.get() + 1));
template! {
p {(props.greeting)}
a(href = "/about") { "About!" }
p { (counter.get()) }
button(on:click = increment) { "Increment" }
}
Expand Down

0 comments on commit d8fd43f

Please sign in to comment.