Compare commits

...

29 Commits

Author SHA1 Message Date
Valerio Ageno 53ab927544 feat: update version to v0.14.5 2024-11-27 21:07:50 +01:00
Valerio Ageno 897cc2daf2 feat: add --no-js-emit flag to tuono build command (#175)
Co-authored-by: Valerio Ageno <valerio.ageno@qonto.com>
2024-11-27 21:05:45 +01:00
Marco Pasqualetti 73ff31daf5 ci(rust): avoid beta and nightly toolchains to be executed on macos and windows (#174) 2024-11-27 20:40:00 +01:00
Marco Pasqualetti c387a9e17e chore(renovate): enable schedule (#172) 2024-11-27 15:33:14 +01:00
Valerio Ageno 1f92931076 Fix optional application state parameters (#173)
Co-authored-by: Valerio Ageno <valerio.ageno@qonto.com>
2024-11-27 15:25:05 +01:00
renovate[bot] d3105a1532 fix(deps): update dependency turbo to v2.3.3 (#169) 2024-11-27 08:26:54 +01:00
Marco Pasqualetti 7f50f31837 ci: trigger workflow only on push only on main (#166) 2024-11-26 21:49:14 +01:00
renovate[bot] 4c3e90ea4b chore(deps): update dependency @vitejs/plugin-react to v4.3.4 (#160)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-26 21:48:57 +01:00
renovate[bot] 8d80fc4aa9 chore(deps): update dependency vitest to v2.1.6 (#162)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-26 21:48:38 +01:00
renovate[bot] 78a04ffcf1 chore(deps): update dependency prettier to v3.4.1 (#165)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-26 21:48:23 +01:00
Valerio Ageno bcb2c91823 fix: windows path resolution (#167) 2024-11-26 21:37:00 +01:00
Marco Pasqualetti 9058f209dd ci: add Windows and MacOS to test matrix for both rust and typescript (#158) 2024-11-26 17:50:53 +01:00
renovate[bot] 1f172d583e fix(deps): update dependency @vitejs/plugin-react-swc to v3.7.2 (#161)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-26 13:48:13 +01:00
Valerio Ageno 439b4757df Turn main.rs into app.rs (#159)
Co-authored-by: Valerio Ageno <valerio.ageno@qonto.com>
2024-11-26 09:20:10 +01:00
renovate[bot] 9f8809bd51 fix(deps): update dependency @types/node to v22.10.0 (#157) 2024-11-26 08:02:27 +01:00
renovate[bot] 4e326650cc fix(deps): update dependency @types/node to v22.10.0 (#154) 2024-11-26 07:59:15 +01:00
renovate[bot] e27be27692 fix(deps): update dependency turbo to v2.3.2 (#155) 2024-11-26 07:58:58 +01:00
renovate[bot] 88eb2a1897 chore(deps): update dependency prettier to v3.4.0 (#156) 2024-11-26 07:58:43 +01:00
Valerio Ageno 8c84ef73c8 feat: update version to v0.14.1 2024-11-24 18:33:45 +01:00
renovate[bot] 859e49f16c fix(deps): update mantine monorepo to v7.14.2 (#145)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-24 18:16:13 +01:00
renovate[bot] 14ba8b0a23 chore(deps): update dependency typescript to v5.7.2 (#138)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-23 21:28:03 +01:00
Marco Pasqualetti d7c70f75f3 docs(CONTRIBUTING): add “Local development” (#136) 2024-11-23 21:04:01 +01:00
Valerio Ageno 2760e3ea08 feat(ci): update spellchecker workflow (#143) 2024-11-23 17:30:05 +01:00
Marco Pasqualetti f2f041fe55 ci: test spellcheck via reviewdog (#137)
* ci: use GITHUB_TOKEN in spell checking workflow

* ci(spell-checking): use different reporter based on github context
2024-11-23 10:56:45 +01:00
renovate[bot] 9ec9700ed4 fix(deps): update dependency @types/node to v22.9.3 (#140) 2024-11-23 10:27:51 +01:00
renovate[bot] c6b047fc02 fix(deps): update dependency @types/node to v22.9.2 (#139) 2024-11-23 09:14:22 +01:00
Myan V. 6727264e39 Better error handling when trying to overwrite an existing folder and using a non-existent template (#135)
* fix: prevent overwriting existing project directories

* refactor: improve scaffold error messages
2024-11-22 13:38:54 +01:00
Valerio Ageno d1a5fe234d fix: examples folder 2024-11-21 19:16:06 +01:00
renovate[bot] e84c7cd276 fix(deps): update dependency turbo to v2.3.1 (#132)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-21 19:02:52 +01:00
32 changed files with 878 additions and 371 deletions
+5 -5
View File
@@ -2,6 +2,8 @@ name: Documentation Website CI
on:
push:
branches:
- main
paths:
- '.github/**'
- 'apps/documentation/**'
@@ -12,10 +14,9 @@ on:
jobs:
build:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
name: Check build
timeout-minutes: 15
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout code
@@ -34,9 +35,8 @@ jobs:
working-directory: ./apps/documentation
run: tuono build --static
fmt-lint-and-types:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
name: Check format and lint
code_check:
name: Check format, lint and types
runs-on: ubuntu-latest
timeout-minutes: 15
@@ -1,4 +1,5 @@
name: Deploy documentation website on AWS S3
on:
push:
branches:
@@ -9,6 +10,7 @@ on:
jobs:
deploy-documentation:
name: Deploy
runs-on: ubuntu-latest
env:
+15 -6
View File
@@ -1,11 +1,14 @@
name: Publish on npm and crates
on:
release:
types: [published]
jobs:
publish-on-npm-dry-run:
publish_npm_dry_run:
name: NPM publish (dry run)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
@@ -23,8 +26,10 @@ jobs:
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
publish-on-crates-dry-run:
publish_crates_dry_run:
name: Crates publish (dry run)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
@@ -41,9 +46,11 @@ jobs:
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
check-repo: true
publish-on-npm:
needs: [publish-on-crates-dry-run, publish-on-npm-dry-run]
publish_npm:
name: NPM publish
needs: [publish_npm_dry_run, publish_crates_dry_run]
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
@@ -64,9 +71,11 @@ jobs:
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
publish-on-crates:
needs: [publish-on-crates-dry-run, publish-on-npm-dry-run]
publish_crates:
name: Crates publish
needs: [publish_npm_dry_run, publish_crates_dry_run]
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
+4 -3
View File
@@ -5,6 +5,8 @@ name: Repo root CI
on:
push:
branches:
- main
paths:
- '.github/**'
- './*'
@@ -15,10 +17,9 @@ on:
jobs:
build-and-test:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
name: Check format
timeout-minutes: 15
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout code
@@ -28,4 +29,4 @@ jobs:
uses: ./.github/actions/install-node-dependencies
- name: Test project
run: pnpm repo:root:format
run: pnpm repo:root:format:check
+33 -19
View File
@@ -2,6 +2,8 @@ name: Rust CI
on:
push:
branches:
- main
paths:
- '.github/**'
- 'crates/**'
@@ -14,8 +16,38 @@ env:
CARGO_TERM_COLOR: always
jobs:
build_and_test:
name: Build and test crates on ${{ matrix.os }} with rust ${{ matrix.toolchain }}
strategy:
fail-fast: true
matrix:
os:
- 'ubuntu-latest'
- 'macos-latest'
- 'windows-latest'
toolchain:
- 'stable'
# Test `beta` and `nightly` toolchains only on `ubuntu`
# https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/running-variations-of-jobs-in-a-workflow#expanding-or-adding-matrix-configurations
include:
- toolchain: 'beta'
os: 'ubuntu-latest'
- toolchain: 'nightly'
os: 'ubuntu-latest'
runs-on: ${{ matrix.os }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
- run: cargo build --verbose
- run: cargo test --verbose
lint_and_fmt:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
name: Check format and lint rust crates
runs-on: ubuntu-latest
@@ -25,21 +57,3 @@ jobs:
- run: cargo fmt --all -- --check
- run: cargo clippy -- -D warnings
build_and_test:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
name: Build and test rust crates
runs-on: ubuntu-latest
strategy:
matrix:
toolchain:
- stable
- beta
- nightly
steps:
- name: Checkout code
uses: actions/checkout@v4
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
- run: cargo build --verbose
- run: cargo test --verbose
+51 -8
View File
@@ -1,23 +1,66 @@
name: Spell Checker
on: [pull_request]
on:
push:
branches:
- main
pull_request:
jobs:
spell_checking:
name: runner / LanguageTools
name: LanguageTool
runs-on: ubuntu-latest
permissions:
checks: write
contents: read
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- uses: reviewdog/action-languagetool@v1
with:
github_token: ${{ secrets.REVIEWDOG_TOKEN }}
# Keep this value in order to show the error comments
# also on edited files.
fetch-depth: 2
# If the workflow is triggered by a push use check reporter
#
# If the workflow is triggered by a PR coming from the
# same repository, the author has write access so we can
# use pr-review reporter since the GITHUB_TOKEN will have review
# write permissions
#
# Otherwise use annotation reporter that do not require any
# particular permission
#
# Why this?
# https://github.com/Valerioageno/tuono/pull/137#pullrequestreview-2455455272
- name: Set reporter
id: reporter
env:
EVENT_NAME: ${{ github.event_name }}
FROM_MAIN_REPO: ${{ github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name }}
run: |
echo "EVENT_NAME: $EVENT_NAME FROM_MAIN_REPO: $FROM_MAIN_REPO"
if [ "$EVENT_NAME" == 'push' ]; then
echo "name=github-check" >> $GITHUB_OUTPUT
elif [ "$FROM_MAIN_REPO" == 'true' ]; then
echo "name=github-pr-review" >> $GITHUB_OUTPUT
else
echo "name=github-pr-annotations" >> $GITHUB_OUTPUT
fi
- name: Review dog languagetool
uses: reviewdog/action-languagetool@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
# Change reviewdog reporter if you need [github-pr-check,github-check,github-pr-review].
reporter: 'github-pr-review'
reporter: ${{ steps.reporter.outputs.name }}
# Change reporter level if you need.
level: 'error'
level: 'warning'
patterns: '**/*.md **/*.txt **/*.mdx'
#enabled_categories: 'STYLE,PLAIN_ENGLISH,WHITESPACE_RULE,EN_QUOTES,DASH_RULE,WORD_CONTAINS_UNDERSCORE,UPPERCASE_SENTENCE_START,ARROWS,COMMA_PARENTHESIS_WHITESPACE,UNLIKELY_OPENING_PUNCTUATION,SENTENCE_WHITESPACE,EN_UNPAIRED_BRACKETS,PHRASE_REPETITION,PUNCTUATION_PARAGRAPH_END'
# enabled_categories: "STYLE,PLAIN_ENGLISH,WHITESPACE_RULE,EN_QUOTES,DASH_RULE,WORD_CONTAINS_UNDERSCORE,UPPERCASE_SENTENCE_START,ARROWS,COMMA_PARENTHESIS_WHITESPACE,UNLIKELY_OPENING_PUNCTUATION,SENTENCE_WHITESPACE,EN_UNPAIRED_BRACKETS,PHRASE_REPETITION,PUNCTUATION_PARAGRAPH_END"
disabled_categories: ''
disabled_rules: ''
+14 -5
View File
@@ -2,6 +2,8 @@ name: Typescript CI
on:
push:
branches:
- main
paths:
- '.github/**'
- 'packages/**'
@@ -11,11 +13,19 @@ on:
- 'packages/**'
jobs:
build-and-test:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
build_and_test:
name: Check build and test
timeout-minutes: 15
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
os:
- 'ubuntu-latest'
- 'macos-latest'
- 'windows-latest'
runs-on: ${{ matrix.os }}
steps:
- name: Checkout code
@@ -30,8 +40,7 @@ jobs:
- name: Test project
run: pnpm test
fmt-lint-and-types:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
lint_and_fmt:
name: Check format and lint
runs-on: ubuntu-latest
timeout-minutes: 15
@@ -90,12 +90,19 @@ export default function Sidebar({ close }: SidebarProps): JSX.Element {
onClick={close}
/>
</SidebarLink>
<SidebarLink
label="Contributing"
href="/documentation/contributing"
onClick={close}
leftSection="✨"
/>
onClick={close}
>
<SidebarLink
href="/documentation/contributing/local-development"
label="Local development"
onClick={close}
/>
</SidebarLink>
</AppShell.Navbar>
)
}
@@ -14,11 +14,11 @@ import Breadcrumbs, { Element } from '../../components/breadcrumbs'
The main reason Tuono is fast is that it loads just the features that are needed for the project.
To define them, you need to fill the `ApplicationState` struct in the `./src/main.rs` file, and
To define them, you need to fill the `ApplicationState` struct in the `./src/app.rs` file, and
they will be automatically available in all the handlers you will define across the application.
```rs
// src/main.rs
// src/app.rs
#[derive(Clone)]
pub struct ApplicationState {
@@ -0,0 +1,57 @@
import MetaTags from '../../../components/meta-tags'
<MetaTags
title="Tuono - Contributing"
canonical="https://tuono.dev/documentation/contributing"
description="The project is massive - if you like it, do consider contributing!"
/>
import Breadcrumbs, { Element } from '../../../components/breadcrumbs'
<Breadcrumbs breadcrumbs={[{ label: '✨ Contributing' }]} />
# Contributing
## TL;DR
The project is massive - if you like it, do consider contributing!
## Getting started
The `tuono` project can mostly be split into the following subdomains:
- The CLI
- The Rust backend
- The React frontend
- The documentation website (which is written with tuono 🚀)
To check what the knowledge requirements are for each domain, check the
[requirements](#requirements) section below.
Currently, I'm keeping a private dashboard to prioritize new features and bug fixes, but if you
want to propose something, please open a new issue on GitHub or reach out to me using
my email address [valerioageno@yahoo.it](mailto:valerioageno@yahoo.it). I'm also available
on Twitter (X) DMs `@valerioageno`, [Linkedin](https://www.linkedin.com/in/valerioageno)
and discord `@__v__v__`.
## Requirements
It's not strictly required to know both React (& typescript) and Rust (even though it
would be a great nice to have).
Without taking into account specific cases, we can mostly split the domain requirements by:
- The `CLI` needs Rust knowledge (even though a couple of scenarios might also need Typescript)
- The Backend needs just `Rust`
- The Frontend needs just `React` & `Typescript`
- The documentation website needs just `React` & `Typescript` (or even less, since most of the
code is markdown).
import NavigationButtons from '../../../components/navigation-buttons'
<NavigationButtons
next={{
title: 'Local development',
href: '/documentation/contributing/local-development',
}}
/>
@@ -0,0 +1,113 @@
import MetaTags from '../../../components/meta-tags'
<MetaTags
title="Tuono - Contributing - Local development"
canonical="https://tuono.dev/documentation/contributing/local-development"
description="Contribute to Tuono. Learn here how to setup the repository for local development"
/>
import Breadcrumbs, { Element } from '../../../components/breadcrumbs'
<Breadcrumbs
breadcrumbs={[
{ label: '✨ Contributing', href: '/documentation/contributing' },
{ label: 'Local development' },
]}
/>
# Local development
Thanks for your interest! In this page, you find the instructions to set up `tuono` on your local environment!
## Setup
### Fork and clone repository
After [forking the repo on GitHub](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo):
```sh
git clone https://github.com/<your-name-here>/tuono
cd tuono
```
### Rust tool chain
Install the Rust programming language tool chain (`rust` and `cargo`).
Follows instructions in the official [docs](https://rustup.rs/)
### Node.js — runtime
Install `Node.js`.
You can follow the instructions from the [Node official site](https://nodejs.org/en/download/package-manager)
> 💡 This project has a `.nvmrc` file to specify the node version used in development.
>
> Consider to use [nvm](https://github.com/nvm-sh/nvm) so you can run
>
> ```sh
> nvm use
> ```
>
> to simply pick up the correct version!
### Node.js — package manager
We use [`pnpm`](https://pnpm.io) as Node.js package manager.
You can see which version of yarn we use by checking the `packageManager` field in the root `package.json`.
### Pre-flight checks
To check that everything is working properly, run:
```sh
turbo run check-all
cargo build
```
## Tuono development
1. Start tuono frontend build using
```sh
turbo run dev
```
2. In another terminal run
```sh
cargo build
```
To automatically rebuild crates on code change, consider using `cargo-watch` crate
```sh
cargo watch -x build -w crates/
```
3. You can now use the binary inside `/target/debug/tuono` in another folder on your local machine
> Consider adding an alias to your shell setup file
>
> ```sh
> alias t="/path-to-repo/target/debug/tuono"
> ```
## Documentation development
1. Change the current working directory to the documentation folder:
```sh
cd apps/documentation
```
2. Run
```sh
tuono dev
```
3. Open the localhost URL.
> On the documentation remember that `tuono` `npm` package is installed from the registry and
> it is not linked to the repository.
@@ -70,7 +70,7 @@ Let's fix these in the next section.
Compared to the common Javascript runtimes, Tuono is fast because only the features you need for your project will be loaded.
You can load them in the `ApplicationState` of your app inside the `./src/main.rs` file. This is the file that will be executed just once at the very beginning of your application.
You can load them in the `ApplicationState` of your app inside the `./src/app.rs` file. This is the file that will be executed just once at the very beginning of your application.
> For the tutorial we will use [Reqwest](https://docs.rs/reqwest/latest/reqwest/) which is one of the most famous HTTP library.
@@ -101,7 +101,7 @@ serde = { version = "1.0.202", features = ["derive"] }
> The `Cargo.toml` is the manifest file of your application, in which you handle Rust's dependencies
> (similarly as the package.json for Javascript).
Now let's define the `ApplicationState` in the `./src/main.rs` file.
Now let's define the `ApplicationState` in the `./src/app.rs` file.
```rs
// Import here the just added reqwest library
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tuono"
version = "0.14.0"
version = "0.14.5"
edition = "2021"
authors = ["V. Ageno <valerioageno@yahoo.it>"]
description = "Superfast React fullstack framework"
+116 -26
View File
@@ -16,15 +16,25 @@ use crate::route::Route;
const IGNORE_EXTENSIONS: [&str; 3] = ["css", "scss", "sass"];
const IGNORE_FILES: [&str; 1] = ["__root"];
#[cfg(target_os = "windows")]
const ROUTES_FOLDER_PATH: &str = "\\src\\routes";
#[cfg(target_os = "windows")]
const BUILD_JS_SCRIPT: &str = ".\\node_modules\\.bin\\tuono-build-prod.cmd";
#[cfg(not(target_os = "windows"))]
const ROUTES_FOLDER_PATH: &str = "/src/routes";
#[cfg(not(target_os = "windows"))]
const BUILD_JS_SCRIPT: &str = "./node_modules/.bin/tuono-build-prod";
#[derive(Debug)]
pub struct App {
pub route_map: HashMap<String, Route>,
pub base_path: PathBuf,
pub has_main_file: bool,
pub has_app_state: bool,
}
fn has_main_file(base_path: PathBuf) -> std::io::Result<bool> {
let file = File::open(base_path.join("src/main.rs"))?;
fn has_app_state(base_path: PathBuf) -> std::io::Result<bool> {
let file = File::open(base_path.join("src/app.rs"))?;
let mut buf_reader = BufReader::new(file);
let mut contents = String::new();
buf_reader.read_to_string(&mut contents)?;
@@ -33,12 +43,12 @@ fn has_main_file(base_path: PathBuf) -> std::io::Result<bool> {
impl App {
pub fn new() -> Self {
let base_path = std::env::current_dir().unwrap();
let base_path = std::env::current_dir().expect("Failed to read current_dir");
let mut app = App {
route_map: HashMap::new(),
base_path: base_path.clone(),
has_main_file: has_main_file(base_path).unwrap_or(false),
has_app_state: has_app_state(base_path).unwrap_or(false),
};
app.collect_routes();
@@ -47,18 +57,31 @@ impl App {
}
fn collect_routes(&mut self) {
glob(self.base_path.join("src/routes/**/*.*").to_str().unwrap())
.expect("Failed to read glob pattern")
.for_each(|entry| {
if self.should_collect_route(&entry) {
self.collect_route(entry)
}
})
glob(
self.base_path
.join("src/routes/**/*.*")
.to_str()
.expect("Failed to glob routes folder"),
)
.expect("Failed to read glob pattern")
.for_each(|entry| {
if self.should_collect_route(&entry) {
self.collect_route(entry)
}
})
}
fn should_collect_route(&self, entry: &Result<PathBuf, GlobError>) -> bool {
let file_extension = entry.as_ref().unwrap().extension().unwrap();
let file_name = entry.as_ref().unwrap().file_stem().unwrap();
let file_extension = entry
.as_ref()
.unwrap()
.extension()
.expect("Failed to read file extension");
let file_name = entry
.as_ref()
.unwrap()
.file_stem()
.expect("Failed to read file name");
if IGNORE_EXTENSIONS.iter().any(|val| val == &file_extension) {
return false;
@@ -71,17 +94,23 @@ impl App {
}
fn collect_route(&mut self, path_buf: Result<PathBuf, GlobError>) {
let entry = path_buf.unwrap();
let base_path_str = self.base_path.to_str().unwrap();
let entry = path_buf.expect("Failed to read glob path");
let base_path_str = self
.base_path
.to_str()
.expect("Failed to read as str base_path");
let path = entry
.to_str()
.unwrap()
.replace(&format!("{base_path_str}/src/routes"), "")
.expect("Failed to read entry as str")
.replace(&format!("{base_path_str}{ROUTES_FOLDER_PATH}"), "")
// Cleanup windows paths
.replace("\\", "/")
.replace(".rs", "")
.replace(".mdx", "")
.replace(".tsx", "");
if entry.extension().unwrap() == "rs" {
if entry.extension().expect("failed to read entry extension") == "rs" {
if let Entry::Vacant(route_map) = self.route_map.entry(path.clone()) {
let mut route = Route::new(path);
route.update_axum_info();
@@ -103,7 +132,7 @@ impl App {
}
pub fn build_react_prod(&self) {
Command::new("./node_modules/.bin/tuono-build-prod")
Command::new(BUILD_JS_SCRIPT)
.output()
.expect("Failed to build the react source");
}
@@ -145,8 +174,27 @@ mod tests {
#[test]
fn should_collect_routes() {
let mut app = App::new();
app.base_path = "/home/user/Documents/tuono".into();
#[cfg(target_os = "windows")]
let base_path = "\\home\\user\\Documents\\tuono";
#[cfg(not(target_os = "windows"))]
let base_path = "/home/user/Documents/tuono";
app.base_path = base_path.into();
#[cfg(target_os = "windows")]
let routes = [
"\\home\\user\\Documents\\tuono\\src\\routes\\about.rs",
"\\home\\user\\Documents\\tuono\\src\\routes\\index.rs",
"\\home\\user\\Documents\\tuono\\src\\routes\\posts\\index.rs",
"\\home\\user\\Documents\\tuono\\src\\routes\\posts\\[post].rs",
"\\home\\user\\Documents\\tuono\\src\\routes\\posts\\handle-this.rs",
"\\home\\user\\Documents\\tuono\\src\\routes\\posts\\handle-this\\[post].rs",
"\\home\\user\\Documents\\tuono\\src\\routes\\posts\\UPPERCASE.rs",
"\\home\\user\\Documents\\tuono\\src\\routes\\sitemap.xml.rs",
];
#[cfg(not(target_os = "windows"))]
let routes = [
"/home/user/Documents/tuono/src/routes/about.rs",
"/home/user/Documents/tuono/src/routes/index.rs",
@@ -193,8 +241,25 @@ mod tests {
#[test]
fn should_create_multi_level_axum_paths() {
let mut app = App::new();
app.base_path = "/home/user/Documents/tuono".into();
#[cfg(target_os = "windows")]
let base_path = "\\home\\user\\Documents\\tuono";
#[cfg(not(target_os = "windows"))]
let base_path = "/home/user/Documents/tuono";
app.base_path = base_path.into();
#[cfg(target_os = "windows")]
let routes = [
"\\home\\user\\Documents\\tuono\\src\\routes\\about.rs",
"\\home\\user\\Documents\\tuono\\src\\routes\\index.rs",
"\\home\\user\\Documents\\tuono\\src\\routes\\posts\\index.rs",
"\\home\\user\\Documents\\tuono\\src\\routes\\posts\\any-post.rs",
"\\home\\user\\Documents\\tuono\\src\\routes\\posts\\[post].rs",
];
#[cfg(not(target_os = "windows"))]
let routes = [
"/home/user/Documents/tuono/src/routes/about.rs",
"/home/user/Documents/tuono/src/routes/index.rs",
@@ -219,7 +284,7 @@ mod tests {
assert_eq!(
app.route_map
.get(path)
.unwrap()
.expect("Failed to get route path")
.axum_info
.as_ref()
.unwrap()
@@ -271,8 +336,23 @@ mod tests {
#[test]
fn should_correctly_parse_routes_with_server_handler() {
let mut app = App::new();
app.base_path = "/home/user/Documents/tuono".into();
#[cfg(target_os = "windows")]
let base_path = "\\home\\user\\Documents\\tuono";
#[cfg(not(target_os = "windows"))]
let base_path = "/home/user/Documents/tuono";
app.base_path = base_path.into();
#[cfg(target_os = "windows")]
let routes = [
"\\home\\user\\Documents\\tuono\\src\\routes\\about.rs",
"\\home\\user\\Documents/tuono\\src\\routes\\about.tsx",
"\\home\\user\\Documents\\tuono\\src\\routes\\index.tsx",
];
#[cfg(not(target_os = "windows"))]
let routes = [
"/home/user/Documents/tuono/src/routes/about.rs",
"/home/user/Documents/tuono/src/routes/about.tsx",
@@ -289,9 +369,19 @@ mod tests {
.into_iter()
.for_each(|(path, expected_has_server_handler)| {
if expected_has_server_handler {
assert!(app.route_map.get(path).unwrap().axum_info.is_some())
assert!(app
.route_map
.get(path)
.expect("Failed to get route path")
.axum_info
.is_some())
} else {
assert!(app.route_map.get(path).unwrap().axum_info.is_none())
assert!(app
.route_map
.get(path)
.expect("Failed to get route path")
.axum_info
.is_none())
}
})
}
+15 -7
View File
@@ -23,6 +23,10 @@ enum Actions {
#[arg(short, long = "static")]
/// Statically generate the website HTML
ssg: bool,
#[arg(short, long)]
/// Prevent to export the js assets
no_js_emit: bool,
},
/// Scaffold a new project
New {
@@ -42,12 +46,12 @@ struct Args {
action: Actions,
}
fn init_tuono_folder(mode: Mode) -> std::io::Result<()> {
fn init_tuono_folder(mode: Mode) -> std::io::Result<App> {
check_tuono_folder()?;
bundle_axum_source(mode)?;
let app = bundle_axum_source(mode)?;
create_client_entry_files()?;
Ok(())
Ok(app)
}
fn check_ports(mode: Mode) {
@@ -83,13 +87,17 @@ pub fn app() -> std::io::Result<()> {
Actions::Dev => {
check_ports(Mode::Dev);
init_tuono_folder(Mode::Dev)?;
let _ = init_tuono_folder(Mode::Dev)?;
watch::watch().unwrap();
}
Actions::Build { ssg } => {
init_tuono_folder(Mode::Prod)?;
let app = App::new();
Actions::Build { ssg, no_js_emit } => {
let app = init_tuono_folder(Mode::Prod)?;
if no_js_emit {
println!("Rust build successfully finished");
return Ok(());
}
if ssg && app.has_dynamic_routes() {
// TODO: allow dynamic routes static generation
+8 -2
View File
@@ -63,11 +63,17 @@ pub fn create_new_project(folder_name: Option<String>, template: Option<String>)
.collect::<Vec<&GithubFile>>();
if new_project_files.is_empty() {
println!("Template not found: {template}");
return;
eprintln!("Error: Template '{template}' not found");
println!("Hint: you can view the available templates at https://github.com/Valerioageno/tuono/tree/main/examples");
std::process::exit(1);
}
if folder != "." {
if Path::new(&folder).exists() {
eprintln!("Error: Directory '{folder}' already exists");
println!("Hint: you can scaffold a tuono project within an existing folder with 'cd {folder} && tuono new .'");
std::process::exit(1);
}
create_dir(&folder).unwrap();
}
+6 -6
View File
@@ -123,7 +123,7 @@ fn create_modules_declaration(routes: &HashMap<String, Route>) -> String {
route_declarations
}
pub fn bundle_axum_source(mode: Mode) -> io::Result<()> {
pub fn bundle_axum_source(mode: Mode) -> io::Result<App> {
let base_path = std::env::current_dir().unwrap();
let app = App::new();
@@ -132,7 +132,7 @@ pub fn bundle_axum_source(mode: Mode) -> io::Result<()> {
create_main_file(&base_path, &bundled_file);
Ok(())
Ok(app)
}
fn generate_axum_source(app: &App, mode: Mode) -> String {
@@ -149,8 +149,8 @@ fn generate_axum_source(app: &App, mode: Mode) -> String {
.replace("/*MODE*/", mode.as_str())
.replace(
"//MAIN_FILE_IMPORT//",
if app.has_main_file {
r#"#[path="../src/main.rs"]
if app.has_app_state {
r#"#[path="../src/app.rs"]
mod tuono_main_state;
"#
} else {
@@ -159,7 +159,7 @@ fn generate_axum_source(app: &App, mode: Mode) -> String {
)
.replace(
"//MAIN_FILE_DEFINITION//",
if app.has_main_file {
if app.has_app_state {
"let user_custom_state = tuono_main_state::main();"
} else {
""
@@ -167,7 +167,7 @@ fn generate_axum_source(app: &App, mode: Mode) -> String {
)
.replace(
"//MAIN_FILE_USAGE//",
if app.has_main_file {
if app.has_app_state {
".with_state(user_custom_state)"
} else {
""
+12 -2
View File
@@ -9,10 +9,20 @@ use watchexec_supervisor::job::{start_job, Job};
use crate::mode::Mode;
use crate::source_builder::bundle_axum_source;
#[cfg(target_os = "windows")]
const DEV_WATCH_BIN_SRC: &str = "node_modules\\.bin\\tuono-dev-watch.cmd";
#[cfg(target_os = "windows")]
const DEV_SSR_BIN_SRC: &str = "node_modules\\.bin\\tuono-dev-ssr.cmd";
#[cfg(not(target_os = "windows"))]
const DEV_WATCH_BIN_SRC: &str = "node_modules/.bin/tuono-dev-watch";
#[cfg(not(target_os = "windows"))]
const DEV_SSR_BIN_SRC: &str = "node_modules/.bin/tuono-dev-ssr";
fn watch_react_src() -> Job {
start_job(Arc::new(Command {
program: Program::Exec {
prog: "node_modules/.bin/tuono-dev-watch".into(),
prog: DEV_WATCH_BIN_SRC.into(),
args: vec![],
},
options: Default::default(),
@@ -34,7 +44,7 @@ fn build_rust_src() -> Job {
fn build_react_ssr_src() -> Job {
start_job(Arc::new(Command {
program: Program::Exec {
prog: "node_modules/.bin/tuono-dev-ssr".into(),
prog: DEV_SSR_BIN_SRC.into(),
args: vec![],
},
options: Default::default(),
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "tuono_lib"
version = "0.14.0"
version = "0.14.5"
edition = "2021"
authors = ["V. Ageno <valerioageno@yahoo.it>"]
description = "Superfast React fullstack framework"
@@ -31,7 +31,7 @@ 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.14.0"}
tuono_lib_macros = {path = "../tuono_lib_macros", version = "0.14.5"}
# Match the same version used by axum
tokio-tungstenite = "0.24.0"
futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] }
+12 -3
View File
@@ -9,6 +9,16 @@ use std::path::PathBuf;
/// update the SSR result without reloading the whole server.
pub struct Js;
#[cfg(target_os = "windows")]
const PROD_BUNDLE_PATH: &str = ".\\out\\server\\prod-server.js";
#[cfg(target_os = "windows")]
const DEV_BUNDLE_PATH: &str = ".\\.tuono\\server\\dev-server.js";
#[cfg(not(target_os = "windows"))]
const PROD_BUNDLE_PATH: &str = "./out/server/prod-server.js";
#[cfg(not(target_os = "windows"))]
const DEV_BUNDLE_PATH: &str = "./.tuono/server/dev-server.js";
impl Js {
pub fn render_to_string(payload: Option<&str>) -> Result<String, SsrError> {
let mode = GLOBAL_MODE.get().expect("Failed to get GLOBAL_MODE");
@@ -27,7 +37,7 @@ impl ProdJs {
thread_local! {
pub static SSR: RefCell<Ssr<'static, 'static>> = RefCell::new(
Ssr::from(
read_to_string(PathBuf::from("./out/server/prod-server.js")).expect("Server bundle not found"), ""
read_to_string(PathBuf::from(PROD_BUNDLE_PATH)).expect("Server bundle not found"), ""
).unwrap()
)
}
@@ -38,8 +48,7 @@ struct DevJs;
impl DevJs {
pub fn render_to_string(params: Option<&str>) -> Result<String, SsrError> {
Ssr::from(
read_to_string(PathBuf::from("./.tuono/server/dev-server.js"))
.expect("Server bundle not found"),
read_to_string(PathBuf::from(DEV_BUNDLE_PATH)).expect("Server bundle not found"),
"",
)
.unwrap()
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "tuono_lib_macros"
version = "0.14.0"
version = "0.14.5"
edition = "2021"
description = "Superfast React fullstack framework"
homepage = "https://tuono.dev"
+1 -1
View File
@@ -23,7 +23,7 @@ pub fn import_main_application_state(argument_names: Punctuated<Pat, Comma>) ->
pub fn crate_application_state_extractor(argument_names: Punctuated<Pat, Comma>) -> Option<Stmt> {
if !argument_names.is_empty() {
let use_item: Stmt = parse_quote!(let ApplicationState { #argument_names } = state;);
let use_item: Stmt = parse_quote!(let ApplicationState { #argument_names, .. } = state;);
return Some(use_item);
}
@@ -1,5 +1,5 @@
use tuono_lib::axum::http::StatusCode;
use tuono_lib::Request;
use tuono_lib::axum::http::StatusCode;
#[tuono_lib::api(GET)]
pub async fn my_get_request(_req: Request) -> StatusCode {
+1
View File
@@ -14,6 +14,7 @@
"docs:format": "turbo format --filter=documentation",
"docs:format:check": "turbo format:check --filter=documentation",
"docs:types": "turbo types --filter=documentation",
"repo:root:format:check": "prettier . !./apps/** !./assets/** !./benches/** !./crates !./examples !./packages/** --check",
"repo:root:format": "prettier . !./apps/** !./assets/** !./benches/** !./crates !./examples !./packages/** --write",
"check-all": "turbo build lint format:check types --filter=!./examples"
},
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "tuono-fs-router-vite-plugin",
"version": "0.14.0",
"version": "0.14.5",
"description": "Plugin for the tuono's file system router. Tuono is the react/rust fullstack framework",
"homepage": "https://tuono.dev",
"scripts": {
@@ -2,40 +2,28 @@ import fs from 'fs/promises'
import { describe, it, expect } from 'vitest'
import { routeGenerator } from '../src/generator'
function makeFolderDir(folder: string) {
return process.cwd() + `/tests/generator/${folder}`
}
async function getRouteTreeFileText(folder: string) {
const dir = makeFolderDir(folder)
return await fs.readFile(dir + '/routeTree.gen.ts', 'utf-8')
}
async function getExpectedRouteTreeFileText(folder: string) {
const dir = makeFolderDir(folder)
const location = dir + '/routeTree.expected.ts'
return await fs.readFile(location, 'utf-8')
}
describe('generator works', async () => {
const folderNames = await fs.readdir(process.cwd() + '/tests/generator')
it.each(folderNames.map((folder) => [folder]))(
it.each(folderNames)(
'should wire-up the routes for a "%s" tree',
async (folderName) => {
const currentFolder = `${process.cwd()}/tests/generator/${folderName}`
const testDirPath = `${process.cwd()}/tests/generator/${folderName}`
await routeGenerator({
folderName: `${currentFolder}/routes`,
generatedRouteTree: `${currentFolder}/routeTree.gen.ts`,
folderName: `${testDirPath}/routes`,
generatedRouteTree: `${testDirPath}/routeTree.gen.ts`,
})
const [expectedRouteTree, generatedRouteTree] = await Promise.all([
getExpectedRouteTreeFileText(folderName),
getRouteTreeFileText(folderName),
])
const generatedFilePath = `${testDirPath}/routeTree.gen.ts`
const expectedFilePath = `${testDirPath}/routeTree.expected.ts`
expect(generatedRouteTree).equal(expectedRouteTree)
const generatedFileContent = await fs.readFile(generatedFilePath, 'utf-8')
await expect(generatedFileContent).toMatchFileSnapshot(
expectedFilePath,
`${generatedFilePath} content should be equal to ${expectedFilePath}`,
)
},
)
})
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "tuono-lazy-fn-vite-plugin",
"version": "0.14.0",
"version": "0.14.5",
"description": "Plugin for the tuono's lazy fn. 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.14.0",
"version": "0.14.5",
"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.14.0",
"version": "0.14.5",
"description": "Superfast React fullstack framework",
"homepage": "https://tuono.dev",
"scripts": {
+377 -238
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -1,5 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended"],
"ignorePaths": ["**/benches/**"]
"ignorePaths": ["**/benches/**"],
"schedule": ["every 2 weeks on monday"]
}