Skip to content

Commit

Permalink
docs: merged next and 0.3.x
Browse files Browse the repository at this point in the history
  • Loading branch information
arctic-hen7 committed Dec 13, 2021
1 parent 37acece commit dbb47fb
Show file tree
Hide file tree
Showing 9 changed files with 44 additions and 30 deletions.
1 change: 1 addition & 0 deletions docs/0.3.x/en-US/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
- [The `tinker` Action](/docs/plugins/tinker)
- [Writing Plugins](/docs/plugins/writing)
- [Security Considerations](/docs/plugins/security)
- [Publishing Plugins](/docs/plugins/publishing)
- [Deploying](/docs/deploying/intro)
- [Server Deployment](/docs/deploying/serverful)
- [Serverless Deployment](/docs/deploying/serverless)
Expand Down
20 changes: 9 additions & 11 deletions docs/0.3.x/en-US/advanced/arch.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,23 @@ What is intended to be used directly is the `Template<G>` `struct`, which is int

The other commonly used system from this crate is the `Translator` system, explained in detail in [the i18n section](:i18n/intro). `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
## Server Integrations

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).
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, server integration crates are used to make this process easy. If you've ejected, you'll be working with these directly, which should be relatively simple, as they just accept configuration options and then should simply work.

## CLI

As documented in [this section](:cli), 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))
As documented in [this section](:cli), 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
### CLI Builder

This system can be further broken down into two parts.

### Static Generator
#### 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.
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
#### App Shell

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

Expand All @@ -46,6 +44,6 @@ This is encapsulated in `.perseus/src/lib.rs`, and it performs a number of integ
- Invokes the core app shell to manage initial/subsequent loads and translations
- Handles error page displaying

## CLI Server
### 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.
This is just an invocation of the the appropriate server integration'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.
7 changes: 3 additions & 4 deletions docs/0.3.x/en-US/hello-world.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,11 @@ Now, create a new directory called `src` and add a new file inside called `lib.r
First, we import some things that'll be useful:

- `perseus::{define_app, ErrorPages, Template}` -- the -`define_app!` macro, which tells Perseus how your app works; the `ErrorPages` `struct`, which lets you tell Perseus how to handle errors (like _404 Not Found_ if the user goes to a nonexistent page); and the `Template` `struct`, which is how Perseus manages pages in your app
- `std::rc::Rc` -- a [reference-counted smart pointer](https://doc.rust-lang.org/std/rc/struct.Rc.html) (you don't _have_ to understand these to use Perseus, but reading that link would be helpful)
- `sycamore::template` -- Sycamore's [`template!` macro], which lets you write HTML-like code in Rust
- `sycamore::view` -- Sycamore's `view!` macro, which lets you write HTML-like code in Rust

Then, we use the `define_app!` macro to declare the different aspects of the app, starting with the _templates_. We only have one template, which we've called `index` (a special name that makes it render at the root of your app), and then we define how that should look, creating a paragraph (`p`) containing the text `Hello World!`. Perseus does all kinds of clever stuff with this under the hood, and we put it in an `Rc` to enable that.
Then, we use the `define_app!` macro to declare the different aspects of the app, starting with the _templates_. We only have one template, which we've called `index` (a special name that makes it render at the root of your app), and then we define how that should look, creating a paragraph (`p`) containing the text `Hello World!`.

Finally, we tell Perseus what to do if something in your app fails, like if the user goes to a page that doesn't exist. This requires creating a new instance of `ErrorPages`, which is a `struct` that lets you define a separate error page for every [HTTP status code](https://httpstatuses.com), as well as a fallback. Here, we've just defined the fallback. That page is given the URL that caused the error, the HTTP status code, and the actual error message, all of which we display with a Sycamore `template!`, with seamless interpolation.
Finally, we tell Perseus what to do if something in your app fails, like if the user goes to a page that doesn't exist. This requires creating a new instance of `ErrorPages`, which is a `struct` that lets you define a separate error page for every [HTTP status code](https://httpstatuses.com), as well as a fallback. Here, we've just defined the fallback. That page is given the URL that caused the error, the HTTP status code, and the actual error message, all of which we display with a Sycamore `view!`, with seamless interpolation.

</details>

Expand Down
4 changes: 4 additions & 0 deletions docs/0.3.x/en-US/pitfalls-and-bugs.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,7 @@ wasm-opt = false
```

This will disable optimizations for your Wasm bundle, which prevents this issue from occurring. Note however that you'll end up with very large bundles if you compile on your M1 Mac. Again though, this issue is set to be fixed very soon.

## I want to apply X to my `Cargo.toml`, but it doesn't work

Perseus has a rather unique code structure that will foil most attempts at modifying your own `Cargo.toml`. For example, if you wanted to change the `codegen_units` in the release profile of your app, you couldn't do this in your own `Cargo.toml`, it would have no effect. The reason for this is that the code your write is actually a library that's imported by the CLI's engines, so any custom configuration has to be made directly on the engines. In other words, you'll need to apply your changes on `.perseus/Cargo.toml` instead. You can also apply customizations on the server and the builder, which are separate crates under `.perseus/`. Note that modifying `.perseus/` and retaining your changes requires [ejecting](:ejecting), or you could [write a plugin](:plugins/writing) if it's a change you make a lot.
13 changes: 13 additions & 0 deletions docs/0.3.x/en-US/plugins/publishing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Publishing Plugins

After you've written a plugin, you can either use it locally, or you can publish it to the world on <https://crates.io>, Rust's package registry. That will mean anyone in the world can use it in their own code, and you'll be contributing to the Perseus community! It's usual to name plugins beginning with `perseus-` (e.g. `perseus-size-opt`), but this isn't required.

Perseus also maintains a registry of all plugins that have been published, but we rely on users to let us know about their plugins. You can do this by [opening an issue](https://github.com/arctic-hen7/perseus/issues/new/choose) on the Perseus repository, and we'll be happy to include your project!

## Trusted Plugins

You may have noticed that some plugins in the Perseus registry have ticks next to them. These plugins are _trusted_, meaning they've been reviewed by the Perseus team and are considered to be high quality and safe to use. Note however that this is in no way a guarantee of quality, and that a trusted plugin may still contain malware or bugs, and that the Perseus team is in no way responsible for any plugin on the registry.

If you'd like to apply for your plugin to be trusted after it's been listed on the registry, reach out to the Perseus maintainer [by email](mailto:arctic_hen7@pm.me), and a code review will be happily undertaken.

By the same token though, an untrusted plugin is not in any way an indication that a plugin is low quality or malicious, it just means it hasn't been reviewed by the Perseus team. If you don't want to have your plugin reviewed, no problem!
17 changes: 8 additions & 9 deletions docs/0.3.x/en-US/second-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Before we get to the cool part of building the actual pages of the app, we shoul

This is a little more advanced than the last time we did this, and there are a few things we should note.

The first is the import of `GenericNode`, which we define as a type parameter on the `get_error_pages` function. As we said before, this means your error pages will work on the client or the server, and they're needed in both environments. If you're interested, this separation of browser and server elements is done by Sycamore, and you can learn more about it [here](https://docs.rs/sycamore/0.6/sycamore/generic_node/trait.GenericNode.html).
The first is the import of [`Html`](https://docs.rs/sycamore/0.7/sycamore/generic_node/trait.Html.html), which we define as a type parameter on the `get_error_pages` function. This makes sure that we can compile these views on the client or the server as long as they're targeting HTML (Sycamore can also target other templating formats for completely different systems, like MacOS desktop apps).

In this function, we also define a different error page for a 404 error, which will occur when a user tries to go to a page that doesn't exist. The fallback page (which we initialize `ErrorPages` with) is the same as last time, and will be called for any errors other than a _404 Not Found_.

Expand All @@ -78,16 +78,15 @@ First, we import a whole ton of stuff:
- `perseus`
- `RenderFnResultWithCause` -- see below for an explanation of this
- `Template` -- as before
- `GenericNode` -- as before
- `Html` -- as before (this is from Sycamore, but is re-exported by Perseus for convenience)
- `http::header::{HeaderMap, HeaderName}` -- some types for adding HTTP headers to our page
- `serde`
- `Serialize` -- a trait for `struct`s that can be turned into a string (like JSON)
- `Deserialize` -- a trait for `struct`s that can be *de*serialized from a string (like JSON)
- `std::rc::Rc` -- same as before, you can read more about `Rc`s [here](https://doc.rust-lang.org/std/rc/struct.Rc.html)
- `sycamore`
- `component` -- a macro that turns a function into a Sycamore component
- `template` -- the `template!` macro, same as before
- `Template as SycamoreTemplate` -- the output of the `template!` macro, aliased as `SycamoreTemplate` so it doesn't conflict with `perseus::Template`, which is very different
- `view` -- the `view!` macro, same as before
- `View` -- the output of the `view!` macro
- `SsrNode` -- Sycamore's representation of a node that will only be rendered on the server (this is re-exported from Perseus as well for convenience)

Then we define a number of different functions and a `struct`, each of which gets a section now.
Expand All @@ -100,17 +99,17 @@ Any template can take arguments in Perseus, which should always be given inside

### `index_page()`

This is the actual component that your page is. By annotating it with `#[component(IndexPage<G>)]`, we tell Sycamore to turn it into a complex `struct` that can be called inside `template!` (which we do in `template_fn()`), and the `#[perseus::template(IndexPage)]` tells Perseus to do a little bit of work behind the scenes so that you can use `index_page` directly in the later `.template()` call. In previous versions of Perseus, you needed to do that boilerplate work yourself.
This is the actual component that your page is. By annotating it with `#[component(IndexPage<G>)]`, we tell Sycamore to turn it into a complex `struct` that can be called inside `view!` (which we do in `template_fn()`), and the `#[perseus::template(IndexPage)]` tells Perseus to do a little bit of work behind the scenes so that you can use `index_page` directly in the later `.template()` call. In previous versions of Perseus, you needed to do that boilerplate work yourself.

Note that `index_page()` takes `IndexPageProps` as an argument, which it can then access in the `template!`. This is Sycamore's interpolation system, which you can read about [here](https://sycamore-rs.netlify.app/docs/basics/template), but all you need to know is that it's basically seamless and works exactly as you'd expect.
Note that `index_page()` takes `IndexPageProps` as an argument, which it can then access in the `view!`. This is Sycamore's interpolation system, which you can read about [here](https://sycamore-rs.netlify.app/docs/basics/template), but all you need to know is that it's basically seamless and works exactly as you'd expect.

The only other thing we do here is define an `<a>` (an HTML link) to `/about`. This link, and any others you define, will automatically be detected by Sycamore's systems, which will pass them to Perseus' routing logic, which means your users **never leave the page**. In this way, Perseus only pulls in the content that needs to change, and gives your users the feeling of a lightning-fast and weightless app.

_Note: external links will automatically be excluded from this, and you can exclude manually by adding `rel="external"` if you need._

### `head()`

This function is very similar to `index_page()`, except that it isn't a fully fledged Sycamore component, it just returns a `template! {}` instead. What this is used for is to define the content of the `<head>`, which is metadata for your website, like its `<title>`. As you can see, this is given the properties that `index_page()` takes, but we aren't using them for anything in this example. The `#[perseus::head]` macro tells Perseus to do some boilerplate work behind the scenes that's very similar to that done with `index_page`, but specialized for the `<head>`.
This function is very similar to `index_page()`, except that it isn't a fully fledged Sycamore component, it just returns a `view! {}` instead. What this is used for is to define the content of the `<head>`, which is metadata for your website, like its `<title>`. As you can see, this is given the properties that `index_page()` takes, but we aren't using them for anything in this example. The `#[perseus::head]` macro tells Perseus to do some boilerplate work behind the scenes that's very similar to that done with `index_page`, but specialized for the `<head>`.

What's really important to note about this function is that it only renders to an `SsrNode`, which means you cannot use reactivity in here! Whatever is rendered the first time will be turned into a `String` and then statically interpolated into the document's `<head>`.

Expand Down Expand Up @@ -140,7 +139,7 @@ This is just the equivalent of `.template()` for the `head()` function, and it d

This function is part of Perseus' secret sauce (actually _open_ sauce), and it will be called when the CLI builds your app to create properties that the template will take (it expects a string, hence the serialization). Here, we just hard-code a greeting in to be used, but the real power of this comes when you start using the fact that this function is `async`. You might query a database to get a list of blog posts, or pull in a Markdown documentation page and parse it, the possibilities are endless!

This function returns a rather special type, `RenderFnResultWithCause<IndexPageProps>`, which declares that your function will return `IndexPageProps` if it succeeds, and a special error if it fails. That error can be anything you want (it's a `Box<dyn std::error::Error>` internally), but it will also have a blame assigned to it that records whether it was the server or the client that caused the error, which will impact the final HTTP status code. You can use the `blame_err!` macro to create these errors easily, but any time you use `?` in functions that return this type will simply use the default of blaming the server and returning an HTTP status code of *500 Internal Server Error*.
This function returns a rather special type, `RenderFnResultWithCause<IndexPageProps>`, which declares that your function will return `IndexPageProps` if it succeeds, and a special error if it fails. That error can be anything you want (it's a `Box<dyn std::error::Error>` internally), but it will also have a blame assigned to it that records whether it was the server or the client that caused the error, which will impact the final HTTP status code. You can use the `blame_err!` macro to create these errors easily, but any time you use `?` in functions that return this type will simply use the default of blaming the server and returning an HTTP status code of _500 Internal Server Error_.

It may seem a little pointless to blame the client in the build process, but the reason this can happen is because, in more advanced uses of Perseus (particularly [incremental generation](:strategies/incremental)), this function could be called as a result of a client's request with parameters that it provides, which could be invalid. Essentially, know that it's a thing that's important in more complex use-cases of Perseus.

Expand Down
2 changes: 1 addition & 1 deletion docs/0.3.x/en-US/templates/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,4 @@ It's often necessary to make sure you're only running some logic on the client-s

This is a very contrived example, but what you should note if you try this is the flash from `server` to `client` (when you go to the page from the URL bar, not when you go in from the link on the index page), because the page is pre-rendered on the server and then hydrated on the client. This is an important principle of Perseus, and you should be aware of this potential flashing (easily solved by a less contrived example) when your users [initially load](:advanced/initial-loads) a page.

One important thing to note with this macro is that it will only work in a _reactive scope_ because it uses Sycamore's [context system](https://sycamore-rs.netlify.app/docs/advanced/contexts). In other words, you can only use it inside a `template!`, `create_effect`, or the like.
One important thing to note with this macro is that it will only work in a _reactive scope_ because it uses Sycamore's [context system](https://sycamore-rs.netlify.app/docs/advanced/contexts). In other words, you can only use it inside a `view!`, `create_effect`, or the like.
Loading

0 comments on commit dbb47fb

Please sign in to comment.