Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d0a6c59c62 | |||
| 60f622542f | |||
| e7fcd64b3a | |||
| 3772e7307e | |||
| e853db164d |
@@ -31,7 +31,7 @@ jobs:
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
|
||||
- name: Install tuono
|
||||
run: cargo install tuono@0.8.3
|
||||
run: cargo install tuono@0.10.0
|
||||
|
||||
- name: Install pnpm
|
||||
run: npm i -g pnpm
|
||||
|
||||
@@ -8,6 +8,8 @@ name = "tuono"
|
||||
path = ".tuono/main.rs"
|
||||
|
||||
[dependencies]
|
||||
tuono_lib = "0.9.2"
|
||||
tuono_lib = "0.10.0"
|
||||
glob = "0.3.1"
|
||||
time = { version = "0.3", features = ["macros"] }
|
||||
serde = { version = "1.0.202", features = ["derive"] }
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"clsx": "^2.1.1",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"tuono": "0.9.2"
|
||||
"tuono": "0.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mdx": "^2.0.13",
|
||||
|
||||
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 709 B |
|
After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1 @@
|
||||
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
||||
@@ -35,7 +35,7 @@ export default function Hero(): JSX.Element {
|
||||
size="lg"
|
||||
style={{ border: 'solid 1px var(--mantine-color-violet-1)' }}
|
||||
color="gray"
|
||||
leftSection="cargo install tuono"
|
||||
leftSection="$ cargo install tuono"
|
||||
rightSection={
|
||||
copied ? (
|
||||
<IconCheck style={{ width: rem(20) }} />
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { Title } from '@mantine/core'
|
||||
import type { HTMLAttributes } from 'react'
|
||||
import { Title, type TitleProps } from '@mantine/core'
|
||||
|
||||
export default function MdxTitle(
|
||||
props: HTMLAttributes<HTMLHeadingElement> & { order: number },
|
||||
): JSX.Element {
|
||||
export default function MdxTitle(props: TitleProps): JSX.Element {
|
||||
return (
|
||||
<Title
|
||||
data-heading={props.children}
|
||||
@@ -19,6 +16,4 @@ export default function MdxTitle(
|
||||
|
||||
export const h =
|
||||
(order: 1 | 2 | 3 | 4 | 5 | 6) =>
|
||||
(props: HTMLAttributes<HTMLHeadingElement>): JSX.Element => (
|
||||
<MdxTitle order={order} {...props} />
|
||||
)
|
||||
(props: TitleProps): JSX.Element => <MdxTitle order={order} {...props} />
|
||||
|
||||
@@ -14,7 +14,7 @@ function getHeadingsData(headings: HTMLHeadingElement[]): Heading[] {
|
||||
for (const heading of headings) {
|
||||
if (heading.id) {
|
||||
result.push({
|
||||
depth: parseInt(heading.getAttribute('data-order'), 10),
|
||||
depth: parseInt(heading.getAttribute('data-order') || '1', 10),
|
||||
content: heading.getAttribute('data-heading') || '',
|
||||
id: heading.id,
|
||||
getNode: () =>
|
||||
|
||||
@@ -70,7 +70,7 @@ export function TableOfContents({
|
||||
}
|
||||
|
||||
const items = filteredHeadings.map((heading, index) => (
|
||||
<Text<Link>
|
||||
<Text
|
||||
key={heading.id}
|
||||
component={Link}
|
||||
fz="sm"
|
||||
|
||||
@@ -66,6 +66,26 @@ export default function RootRoute({ children }: RootRouteProps): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<meta charSet="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="/apple-touch-icon.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="/favicon-32x32.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="/favicon-16x16.png"
|
||||
/>
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
<ColorSchemeScript />
|
||||
</Head>
|
||||
<MantineProvider theme={theme}>
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
use tuono_lib::reqwest::{Client, StatusCode};
|
||||
use tuono_lib::{Request, Response};
|
||||
use tuono_lib::axum::http::{header, HeaderMap};
|
||||
use glob::glob;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
const FILE_TO_EXCLUDE: [&str; 2] = ["sitemap.xml", "__root"];
|
||||
|
||||
const SITEMAP: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
[PLACEHOLDER]
|
||||
</urlset>"#;
|
||||
|
||||
fn load_routes() -> Vec<String> {
|
||||
let mut paths: Vec<String> = vec![];
|
||||
|
||||
for entry in glob("./src/routes/**/*").expect("Failed to glob src/routes folder").flatten() {
|
||||
if !entry.is_dir() {
|
||||
let path = clean_path(format!("/{}", entry.to_string_lossy()));
|
||||
|
||||
if !FILE_TO_EXCLUDE.iter().any(|exclude| path.ends_with(exclude)) {
|
||||
paths.push(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
paths
|
||||
}
|
||||
|
||||
fn clean_path(value: String) -> String {
|
||||
value
|
||||
.replace("src/routes/", "")
|
||||
.replace(".mdx", "")
|
||||
.replace(".tsx", "")
|
||||
.replace(".rs", "")
|
||||
.replace("index", "")
|
||||
}
|
||||
|
||||
#[tuono_lib::handler]
|
||||
async fn generate_sitemap(_req: Request, _fetch: Client) -> Response {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(header::CONTENT_TYPE, "text/xml".parse().unwrap());
|
||||
|
||||
let routes = load_routes();
|
||||
|
||||
let mut sitemaps = String::new();
|
||||
|
||||
for path in routes {
|
||||
let mut url = format!("https://tuono.dev{}", path);
|
||||
|
||||
if url.ends_with('/') {
|
||||
url.pop();
|
||||
}
|
||||
|
||||
sitemaps.push_str(
|
||||
&format!(r#"<url><loc>{}</loc><lastmod>{}</lastmod></url>"#,url, OffsetDateTime::now_utc().date())
|
||||
)
|
||||
}
|
||||
|
||||
Response::Custom((StatusCode::OK, headers, SITEMAP.replace("[PLACEHOLDER]", &sitemaps)))
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tuono"
|
||||
version = "0.10.0"
|
||||
version = "0.10.1"
|
||||
edition = "2021"
|
||||
authors = ["V. Ageno <valerioageno@yahoo.it>"]
|
||||
description = "The react/rust fullstack framework"
|
||||
@@ -31,4 +31,3 @@ regex = "1.10.4"
|
||||
reqwest = {version = "0.12.4", features =["blocking", "json"]}
|
||||
serde_json = "1.0"
|
||||
fs_extra = "1.3.0"
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ use std::io;
|
||||
use std::io::prelude::*;
|
||||
use std::path::Path;
|
||||
|
||||
use clap::crate_version;
|
||||
|
||||
use crate::app::App;
|
||||
use crate::mode::Mode;
|
||||
use crate::route::AxumInfo;
|
||||
@@ -42,6 +44,7 @@ const MODE: Mode = /*MODE*/;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
println!("\n ⚡ Tuono v/*VERSION*/");
|
||||
let router = Router::new()
|
||||
// ROUTE_BUILDER
|
||||
;
|
||||
@@ -127,6 +130,7 @@ fn generate_axum_source(app: &App, mode: Mode) -> String {
|
||||
"// MODULE_IMPORTS\n",
|
||||
&create_modules_declaration(&app.route_map),
|
||||
)
|
||||
.replace("/*VERSION*/", crate_version!())
|
||||
.replace("/*MODE*/", mode.as_str());
|
||||
|
||||
let has_server_handlers = app
|
||||
|
||||
@@ -44,7 +44,6 @@ fn build_react_ssr_src() -> Job {
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn watch() -> Result<()> {
|
||||
println!("Starting development environment...");
|
||||
watch_react_src().start().await;
|
||||
|
||||
let run_server = build_rust_src();
|
||||
@@ -76,7 +75,7 @@ pub async fn watch() -> Result<()> {
|
||||
}
|
||||
|
||||
if should_reload_rust_server {
|
||||
println!("Reloading...");
|
||||
println!(" Reloading...");
|
||||
run_server.stop();
|
||||
bundle_axum_source(Mode::Dev).expect("Failed to bunlde rust source");
|
||||
run_server.start();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tuono_lib"
|
||||
version = "0.10.0"
|
||||
version = "0.10.1"
|
||||
edition = "2021"
|
||||
authors = ["V. Ageno <valerioageno@yahoo.it>"]
|
||||
description = "The react/rust fullstack framework"
|
||||
@@ -31,8 +31,9 @@ once_cell = "1.19.0"
|
||||
regex = "1.10.5"
|
||||
either = "1.13.0"
|
||||
tower-http = {version = "0.5.2", features = ["fs"]}
|
||||
colored = "2.1.0"
|
||||
|
||||
tuono_lib_macros = {path = "../tuono_lib_macros", version = "0.10.0"}
|
||||
tuono_lib_macros = {path = "../tuono_lib_macros", version = "0.10.1"}
|
||||
# Match the same version used by axum
|
||||
tokio-tungstenite = "0.21.0"
|
||||
futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] }
|
||||
|
||||
@@ -34,7 +34,7 @@ impl From<Uri> for Location {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Request {
|
||||
uri: Uri,
|
||||
pub uri: Uri,
|
||||
pub headers: HeaderMap,
|
||||
pub params: HashMap<String, String>,
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ use crate::{ssr::Js, Payload};
|
||||
use axum::http::{HeaderMap, StatusCode};
|
||||
use axum::response::{Html, IntoResponse, Redirect};
|
||||
use axum::Json;
|
||||
use colored::*;
|
||||
use erased_serde::Serialize;
|
||||
use tokio::time::Instant;
|
||||
|
||||
pub struct Props {
|
||||
data: Box<dyn Serialize>,
|
||||
@@ -72,13 +74,25 @@ impl Response {
|
||||
pub fn render_to_string(&self, req: Request) -> impl IntoResponse {
|
||||
match self {
|
||||
Self::Props(Props { data, http_code }) => {
|
||||
let start = Instant::now();
|
||||
let payload = Payload::new(&req, data).client_payload().unwrap();
|
||||
|
||||
match Js::render_to_string(Some(&payload)) {
|
||||
let response = match Js::render_to_string(Some(&payload)) {
|
||||
Ok(html) => (*http_code, Html(html)),
|
||||
Err(_) => (*http_code, Html("500 Internal server error".to_string())),
|
||||
}
|
||||
.into_response()
|
||||
.into_response();
|
||||
|
||||
let duration = start.elapsed();
|
||||
|
||||
println!(
|
||||
" GET {} {} in {}ms",
|
||||
req.uri.path(),
|
||||
http_code.as_str().green(),
|
||||
duration.as_millis()
|
||||
);
|
||||
|
||||
response
|
||||
}
|
||||
Self::Redirect(to) => Redirect::permanent(to).into_response(),
|
||||
Self::Custom(response) => response.clone().into_response(),
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::mode::{Mode, GLOBAL_MODE};
|
||||
|
||||
use crate::manifest::load_manifest;
|
||||
use axum::routing::{get, Router};
|
||||
use colored::Colorize;
|
||||
use ssr_rs::Ssr;
|
||||
use tower_http::services::ServeDir;
|
||||
|
||||
@@ -37,7 +38,7 @@ impl Server {
|
||||
let fetch = reqwest::Client::new();
|
||||
|
||||
if self.mode == Mode::Dev {
|
||||
println!("\nDevelopment app ready at http://localhost:3000/");
|
||||
println!(" Ready at: {}\n", "http://localhost:3000".blue().bold());
|
||||
let router = self
|
||||
.router
|
||||
.to_owned()
|
||||
@@ -50,7 +51,10 @@ impl Server {
|
||||
.await
|
||||
.expect("Failed to serve development server");
|
||||
} else {
|
||||
println!("\nProduction app ready at http://localhost:3000/");
|
||||
println!(
|
||||
" Production server at: {}\n",
|
||||
"http://localhost:3000".blue().bold()
|
||||
);
|
||||
let router = self
|
||||
.router
|
||||
.to_owned()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tuono_lib_macros"
|
||||
version = "0.10.0"
|
||||
version = "0.10.1"
|
||||
edition = "2021"
|
||||
description = "The react/rust fullstack framework"
|
||||
keywords = [ "react", "typescript", "fullstack", "web", "ssr"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "tuono-fs-router-vite-plugin",
|
||||
"version": "0.10.0",
|
||||
"version": "0.10.1",
|
||||
"description": "Plugin for the tuono's file system router. Tuono is the react/rust fullstack framework",
|
||||
"scripts": {
|
||||
"dev": "vite build --watch",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "tuono-lazy-fn-vite-plugin",
|
||||
"version": "0.10.0",
|
||||
"version": "0.10.1",
|
||||
"description": "Plugin for the tuono's lazy fn. Tuono is the react/rust fullstack framework",
|
||||
"scripts": {
|
||||
"dev": "vite build --watch",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "tuono-router",
|
||||
"version": "0.10.0",
|
||||
"version": "0.10.1",
|
||||
"description": "React routing component for the framework tuono. Tuono is the react/rust fullstack framework",
|
||||
"scripts": {
|
||||
"dev": "vite build --watch",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "tuono",
|
||||
"version": "0.10.0",
|
||||
"version": "0.10.1",
|
||||
"description": "The react/rust fullstack framework",
|
||||
"scripts": {
|
||||
"dev": "vite build --watch",
|
||||
|
||||