Compare commits

...

4 Commits

Author SHA1 Message Date
Valerio Ageno bf77ed1794 chore: update version to v0.17.10 (#615) 2025-02-28 17:41:14 +01:00
Valerio Ageno e34c10bc34 fix: build script for creation of parent directories (#614) 2025-02-28 17:41:01 +01:00
Marco Pasqualetti 310579cf76 fix(examples/tuono-tutorial): use pokemon name as key instead of index (#613) 2025-02-28 14:17:26 +01:00
pveierland 1ba94238ed feat: add origin config option (#582)
Co-authored-by: Marco Pasqualetti <marco.pasqualetti@live.com>
2025-02-27 18:25:03 +01:00
19 changed files with 139 additions and 40 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "tuono"
version = "0.17.9"
version = "0.17.10"
edition = "2021"
authors = ["V. Ageno <valerioageno@yahoo.it>"]
description = "Superfast React fullstack framework"
@@ -32,7 +32,7 @@ reqwest = { version = "0.12.4", features = ["blocking", "json"] }
serde_json = "1.0"
fs_extra = "1.3.0"
http = "1.1.0"
tuono_internal = {path = "../tuono_internal", version = "0.17.9"}
tuono_internal = {path = "../tuono_internal", version = "0.17.10"}
spinners = "4.1.1"
console = "0.15.10"
+7 -1
View File
@@ -86,12 +86,18 @@ pub fn build(mut app: App, ssg: bool, no_js_emit: bool) {
while !is_server_ready {
trace!("Checking server availability");
let server_url = format!("http://{}:{}", config.server.host, config.server.port);
let server_url = match &config.server.origin {
Some(origin) => origin.clone(),
None => format!("http://{}:{}", config.server.host, config.server.port),
};
if reqwest_client.get(&server_url).send().is_ok() {
is_server_ready = true;
} else {
trace!("Server not ready yet. Sleeping for 1 second");
}
sleep(Duration::from_secs(1));
}
+14 -10
View File
@@ -180,11 +180,13 @@ impl Route {
}
};
if !parent_dir.is_dir() || create_all(parent_dir, false).is_err() {
return Err(format!(
"Failed to create the parent directory {:?}",
parent_dir
));
if !parent_dir.is_dir() {
if let Err(err) = create_all(parent_dir, false) {
return Err(format!(
"Failed to create the parent directory {:?}\nError: {err}",
parent_dir
));
}
}
trace!("Saving the HTML file: {:?}", file_path);
@@ -214,11 +216,13 @@ impl Route {
}
};
if !data_parent_dir.is_dir() && create_all(data_parent_dir, false).is_err() {
return Err(format!(
"Failed to create the parent directory {:?}",
data_parent_dir
));
if !data_parent_dir.is_dir() {
if let Err(err) = create_all(data_parent_dir, false) {
return Err(format!(
"Failed to create the parent directory {:?}\n Error: {err}",
data_parent_dir
));
}
}
let base = Url::parse("http://localhost:3000/__tuono/data").unwrap();
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tuono_internal"
version = "0.17.9"
version = "0.17.10"
edition = "2021"
authors = ["V. Ageno <valerioageno@yahoo.it>"]
description = "Superfast React fullstack framework"
+1
View File
@@ -6,6 +6,7 @@ use std::path::PathBuf;
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct ServerConfig {
pub host: String,
pub origin: Option<String>,
pub port: u16,
}
+24
View File
@@ -20,6 +20,30 @@ fn should_correctly_read_the_config_file() {
let config = config.unwrap();
assert_eq!(config.server.host, "localhost");
assert_eq!(config.server.origin, None);
assert_eq!(config.server.port, 3000);
}
#[test]
#[serial]
fn should_correctly_read_the_config_file_with_origin() {
let folder = TempTuonoProject::new();
folder.add_file_with_content(
"./.tuono/config/config.json",
r#"{ "server": {"host": "localhost", "origin": "https://tuono.localhost", "port": 3000}}"#,
);
let config = Config::get();
assert!(config.is_ok());
let config = config.unwrap();
assert_eq!(config.server.host, "localhost".to_string());
assert_eq!(
config.server.origin,
Some("https://tuono.localhost".to_string())
);
assert_eq!(config.server.port, 3000);
}
+3 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "tuono_lib"
version = "0.17.9"
version = "0.17.10"
edition = "2021"
authors = ["V. Ageno <valerioageno@yahoo.it>"]
description = "Superfast React fullstack framework"
@@ -32,8 +32,8 @@ either = "1.13.0"
tower-http = {version = "0.6.0", features = ["fs"]}
colored = "2.1.0"
tuono_lib_macros = {path = "../tuono_lib_macros", version = "0.17.9"}
tuono_internal = {path = "../tuono_internal", version = "0.17.9"}
tuono_lib_macros = {path = "../tuono_lib_macros", version = "0.17.10"}
tuono_internal = {path = "../tuono_internal", version = "0.17.10"}
# Match the same version used by axum
tokio-tungstenite = "0.26.0"
futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] }
+24 -10
View File
@@ -25,9 +25,31 @@ pub struct Server {
mode: Mode,
pub listener: tokio::net::TcpListener,
pub address: String,
pub origin: Option<String>,
}
impl Server {
fn display_start_message(&self) {
/*
* Format the server address as a valid URL so that it becomes clickable in the CLI
* @see https://github.com/tuono-labs/tuono/issues/460
*/
let server_base_url = format!("http://{}", self.address);
if self.mode == Mode::Dev {
println!(" Ready at: {}\n", server_base_url.blue().bold());
} else {
println!(
" Production server at: {}\n",
server_base_url.blue().bold()
);
}
if let Some(origin) = &self.origin {
println!(" Origin: {}\n", origin.blue().bold());
}
}
pub async fn init(router: Router, mode: Mode) -> Server {
let config = Config::get().expect("[SERVER] Failed to load config");
@@ -44,6 +66,7 @@ impl Server {
router,
mode,
address: server_address.clone(),
origin: config.server.origin.clone(),
listener: tokio::net::TcpListener::bind(&server_address)
.await
.expect("[SERVER] Failed to bind to address"),
@@ -51,14 +74,9 @@ impl Server {
}
pub async fn start(self) {
/*
* Format the server address as a valid URL so that it becomes clickable in the CLI
* @see https://github.com/tuono-labs/tuono/issues/460
*/
let server_base_url = format!("http://{}", self.address);
self.display_start_message();
if self.mode == Mode::Dev {
println!(" Ready at: {}\n", server_base_url.blue().bold());
let router = self
.router
.to_owned()
@@ -74,10 +92,6 @@ impl Server {
.await
.expect("Failed to serve development server");
} else {
println!(
" Production server at: {}\n",
server_base_url.blue().bold()
);
let router = self
.router
.to_owned()
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tuono_lib_macros"
version = "0.17.9"
version = "0.17.10"
edition = "2021"
description = "Superfast React fullstack framework"
homepage = "https://tuono.dev"
+1 -1
View File
@@ -46,7 +46,7 @@ export default function IndexPage({
<PokemonLink name="GOAT" id={0} />
{data.results.map((pokemon, i) => (
<PokemonLink key={i + 1} name={pokemon.name} id={i + 1} />
<PokemonLink key={pokemon.name} name={pokemon.name} id={i + 1} />
))}
</ul>
</>
@@ -1,6 +1,6 @@
{
"name": "tuono-fs-router-vite-plugin",
"version": "0.17.9",
"version": "0.17.10",
"description": "Plugin for the tuono's file system router. Tuono is the react/rust fullstack framework",
"homepage": "https://tuono.dev",
"scripts": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "tuono-router",
"version": "0.17.9",
"version": "0.17.10",
"description": "React routing component for the framework tuono. Tuono is the react/rust fullstack framework",
"homepage": "https://tuono.dev",
"scripts": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "tuono",
"version": "0.17.9",
"version": "0.17.10",
"description": "Superfast React fullstack framework",
"homepage": "https://tuono.dev",
"scripts": {
@@ -1,4 +1,4 @@
import fs from 'fs/promises'
import fs from 'node:fs/promises'
import { beforeEach, describe, expect, it, vitest } from 'vitest'
import react from '@vitejs/plugin-react-swc'
@@ -12,9 +12,9 @@ describe('createJsonConfig', () => {
writeFileSpy.mockClear()
})
const sampleConfig = { server: { host: 'h', port: 1 } }
it('should process config with only server property', async () => {
const sampleConfig = { server: { host: 'h', origin: null, port: 1 } }
await createJsonConfig(sampleConfig)
expect(writeFileSpy).toHaveBeenCalledWith(
@@ -25,6 +25,8 @@ describe('createJsonConfig', () => {
})
it('should process config with plugins', async () => {
const sampleConfig = { server: { host: 'h', origin: null, port: 1 } }
await createJsonConfig({ ...sampleConfig, vite: { plugins: [react()] } })
expect(writeFileSpy).toHaveBeenCalledWith(
@@ -33,4 +35,16 @@ describe('createJsonConfig', () => {
expect.any(String),
)
})
it('should process config with only server property including origin', async () => {
const sampleConfig = { server: { host: 'h', origin: 'o', port: 1 } }
await createJsonConfig(sampleConfig)
expect(writeFileSpy).toHaveBeenCalledWith(
expect.any(String),
expect.stringContaining(JSON.stringify(sampleConfig)),
expect.any(String),
)
})
})
@@ -15,7 +15,11 @@ describe('normalizeConfig', () => {
const config: TuonoConfig = {}
expect(normalizeConfig(config)).toStrictEqual({
server: { host: 'localhost', port: 3000 },
server: {
host: 'localhost',
origin: null,
port: 3000,
},
vite: {
alias: undefined,
css: undefined,
@@ -28,7 +32,11 @@ describe('normalizeConfig', () => {
it('should return an empty config if invalid values are provided', () => {
// @ts-expect-error testing invalid config
expect(normalizeConfig({ invalid: true })).toStrictEqual({
server: { host: 'localhost', port: 3000 },
server: {
host: 'localhost',
origin: null,
port: 3000,
},
vite: {
alias: undefined,
css: undefined,
@@ -55,6 +63,28 @@ describe('normalizeConfig', () => {
})
})
describe('server - origin', () => {
it('should assign the origin defined by the user', () => {
const config: TuonoConfig = {
server: {
host: '0.0.0.0',
origin: 'https://tuono.localhost',
port: 8080,
},
}
expect(normalizeConfig(config)).toStrictEqual(
expect.objectContaining({
server: expect.objectContaining({
host: '0.0.0.0',
origin: 'https://tuono.localhost',
port: 8080,
}) as unknown,
}),
)
})
})
describe('vite - alias', () => {
it('should not modify alias pointing to packages', () => {
const libraryName = '@tabler/icons-react'
@@ -71,6 +71,7 @@ export const normalizeConfig = (config: TuonoConfig): InternalTuonoConfig => {
return {
server: {
host: config.server?.host ?? 'localhost',
origin: config.server?.origin ?? null,
port: config.server?.port ?? 3000,
},
vite: {
+1
View File
@@ -11,6 +11,7 @@ import type {
export interface TuonoConfig {
server?: {
host?: string
origin?: string | null
port?: number
}
vite?: {
+6 -3
View File
@@ -3,13 +3,16 @@ import type { JSX } from 'react'
import { useTuonoContextServerPayload } from './TuonoContext'
const VITE_PROXY_PATH = '/vite-server'
const DEFAULT_SERVER_CONFIG = { host: 'localhost', port: 3000 }
const DEFAULT_SERVER_CONFIG = { host: 'localhost', origin: null, port: 3000 }
export const DevResources = (): JSX.Element => {
const { devServerConfig } = useTuonoContextServerPayload()
const { host, port } = devServerConfig ?? DEFAULT_SERVER_CONFIG
const { host, origin, port } = devServerConfig ?? DEFAULT_SERVER_CONFIG
const viteBaseUrl = `http://${host}:${port}${VITE_PROXY_PATH}`
const viteBaseUrl =
origin != null
? `${origin}${VITE_PROXY_PATH}`
: `http://${host}:${port}${VITE_PROXY_PATH}`
return (
<>
+1
View File
@@ -25,6 +25,7 @@ export interface ServerPayload<TData = unknown> {
/** Available only on 'Dev' mode */
devServerConfig?: {
port: number
origin: string | null
host: string
}
}