1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#![deny(missing_docs)]

//! ## Installation
//! Add to your `Cargo.toml`:
//!
//! ```toml
//! ssr = "0.0.3"
//! ```
//!
//! And install node worker from `npm`:
//!
//! ```sh
//! // using npm
//! npm install --save ssr-rs
//!
//! // or yarn
//! yarn add ssr-rs
//! ```
//!
//! ## How it works
//! On application start, you create an [`Ssr`](Ssr) instance. Under the hood, it spins up a
//! Node.js worker ready to accept rendering requests. [`Ssr`](Ssr) instance should be stored in a
//! web server's state, so handlers can access it during a handling of incoming requests.
//!
//! [`Ssr`](Ssr) exposes a single method [`render`](Ssr::render), which accepts [`Uri`](http::Uri)
//! and serializable data as an input. If everything went smooth, it returns a rendered `String`.
//! This string can be a plain HTML or an app-specific encoded object with additional
//! metadata—whatever returned from a JS renderer, supplied by the app.
//!
//! ## Initialization
//!
//! ```rust
//! let ssr =
//!   Ssr::new(
//!     SsrConfig {
//!       port: 9000,
//!       js_worker: PathBuf::from("./node_modules/ssr-rs/worker.js"),
//!       global_js_renderer: Some(PathBuf::from("./js/ssr.js")),
//!     }
//!   );
//! ```
//!
//! ### `port`
//! A port that Node.js worker will be listening on.
//!
//! ### `js_worker`
//! Path to Node.js worker installed from `npm`. It should be relative to the
//! [`std::env::current_dir`](std::env::current_dir).
//!
//! ### `global_js_renderer`
//! If your web app is a SPA (Single Page Application), then you should have a single entry point
//! for all rendering requests. If it's the case, provide a path to this file here and it will be
//! used by the worker to render all responses. Another option is to provide a JS renderer per
//! request but keep in mind that it would introduce additional runtime overhead since JS module
//! has to be required during a request as opposed to requiring it once on application startup.
//!
//! ## Rendering
//! In request handlers, you need to get [`Ssr`](Ssr) instance from your server's state. Once you
//! have it (as well as all the required data to handle the current request), call
//! [`ssr.render`](Ssr::render) function with the following input:
//! - [`Uri`](http::Uri): uri of the current request
//! - `Data: impl Serialize`: anything that implements [`Serialize`](serde::Serialize)
//! - [`JsRenderer`](JsRenderer): an enum that tells to use either a global JS renderer or a
//! renderer specific to this request.
//!
//! ```rust
//! let uri = req.uri();
//! let data = db::get_data();
//! match ssr.render(uri, &data, JsRenderer::Global).await {
//!     Ok(html) => HttpResponse::Ok().body(html),
//!     Err(error) => {
//!         error!("Error: {}", error);
//!         HttpResponse::InternalServerError().finish()
//!     }
//! }
//! ```

#[macro_use]
extern crate log;
#[macro_use]
extern crate serde_json;

mod error;
mod json;
mod ssr;
mod worker;

pub use ssr::{JsRenderer, Ssr, SsrConfig};